cd6c6dc103a0ba84aea84668897cf4da206ccaea
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         cfg.id = this.id || Roo.id();
105         
106         // fill in the extra attributes 
107         if (this.xattr && typeof(this.xattr) =='object') {
108             for (var i in this.xattr) {
109                 cfg[i] = this.xattr[i];
110             }
111         }
112         
113         if(this.dataId){
114             cfg.dataId = this.dataId;
115         }
116         
117         if (this.cls) {
118             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
119         }
120         
121         if (this.style) { // fixme needs to support more complex style data.
122             cfg.style = this.style;
123         }
124         
125         if(this.name){
126             cfg.name = this.name;
127         }
128         
129         this.el = ct.createChild(cfg, position);
130         
131         if (this.tooltip) {
132             this.tooltipEl().attr('tooltip', this.tooltip);
133         }
134         
135         if(this.tabIndex !== undefined){
136             this.el.dom.setAttribute('tabIndex', this.tabIndex);
137         }
138         this.initEvents();
139         
140         
141     },
142     /**
143      * Fetch the element to add children to
144      * @return {Roo.Element} defaults to this.el
145      */
146     getChildContainer : function()
147     {
148         return this.el;
149     },
150     /**
151      * Fetch the element to display the tooltip on.
152      * @return {Roo.Element} defaults to this.el
153      */
154     tooltipEl : function()
155     {
156         return this.el;
157     },
158         
159     addxtype  : function(tree,cntr)
160     {
161         var cn = this;
162         
163         cn = Roo.factory(tree);
164            
165         cn.parentType = this.xtype; //??
166         cn.parentId = this.id;
167         
168         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
169         if (typeof(cn.container_method) == 'string') {
170             cntr = cn.container_method;
171         }
172         
173         
174         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
175         
176         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
177         
178         var build_from_html =  Roo.XComponent.build_from_html;
179           
180         var is_body  = (tree.xtype == 'Body') ;
181           
182         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
183           
184         var self_cntr_el = Roo.get(this[cntr](false));
185         
186         // do not try and build conditional elements 
187         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
188             return false;
189         }
190         
191         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
192             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
193                 return this.addxtypeChild(tree,cntr);
194             }
195             
196             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
197                 
198             if(echild){
199                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
200             }
201             
202             Roo.log('skipping render');
203             return cn;
204             
205         }
206         
207         var ret = false;
208         if (!build_from_html) {
209             return false;
210         }
211         
212         // this i think handles overlaying multiple children of the same type
213         // with the sam eelement.. - which might be buggy..
214         while (true) {
215             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
216             
217             if (!echild) {
218                 break;
219             }
220             
221             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
222                 break;
223             }
224             
225             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
226         }
227         return ret;
228     },
229     
230     addxtypeChild : function (tree, cntr)
231     {
232         Roo.debug && Roo.log('addxtypeChild:' + cntr);
233         var cn = this;
234         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
235         
236         
237         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
238                     (typeof(tree['flexy:foreach']) != 'undefined');
239           
240         
241         
242          skip_children = false;
243         // render the element if it's not BODY.
244         if (tree.xtype != 'Body') {
245            
246             cn = Roo.factory(tree);
247            
248             cn.parentType = this.xtype; //??
249             cn.parentId = this.id;
250             
251             var build_from_html =  Roo.XComponent.build_from_html;
252             
253             
254             // does the container contain child eleemnts with 'xtype' attributes.
255             // that match this xtype..
256             // note - when we render we create these as well..
257             // so we should check to see if body has xtype set.
258             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
259                
260                 var self_cntr_el = Roo.get(this[cntr](false));
261                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
262                 if (echild) { 
263                     //Roo.log(Roo.XComponent.build_from_html);
264                     //Roo.log("got echild:");
265                     //Roo.log(echild);
266                 }
267                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
268                 // and are not displayed -this causes this to use up the wrong element when matching.
269                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
270                 
271                 
272                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
273                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
274                   
275                   
276                   
277                     cn.el = echild;
278                   //  Roo.log("GOT");
279                     //echild.dom.removeAttribute('xtype');
280                 } else {
281                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
282                     Roo.debug && Roo.log(self_cntr_el);
283                     Roo.debug && Roo.log(echild);
284                     Roo.debug && Roo.log(cn);
285                 }
286             }
287            
288             
289            
290             // if object has flexy:if - then it may or may not be rendered.
291             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
292                 // skip a flexy if element.
293                 Roo.debug && Roo.log('skipping render');
294                 Roo.debug && Roo.log(tree);
295                 if (!cn.el) {
296                     Roo.debug && Roo.log('skipping all children');
297                     skip_children = true;
298                 }
299                 
300              } else {
301                  
302                 // actually if flexy:foreach is found, we really want to create 
303                 // multiple copies here...
304                 //Roo.log('render');
305                 //Roo.log(this[cntr]());
306                 cn.render(this[cntr](true));
307              }
308             // then add the element..
309         }
310         
311         
312         // handle the kids..
313         
314         var nitems = [];
315         /*
316         if (typeof (tree.menu) != 'undefined') {
317             tree.menu.parentType = cn.xtype;
318             tree.menu.triggerEl = cn.el;
319             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
320             
321         }
322         */
323         if (!tree.items || !tree.items.length) {
324             cn.items = nitems;
325             return cn;
326         }
327         var items = tree.items;
328         delete tree.items;
329         
330         //Roo.log(items.length);
331             // add the items..
332         if (!skip_children) {    
333             for(var i =0;i < items.length;i++) {
334                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
335             }
336         }
337         
338         cn.items = nitems;
339         
340         this.fireEvent('childrenrendered', this);
341         
342         return cn;
343     },
344     /**
345      * Show a component - removes 'hidden' class
346      */
347     show : function()
348     {
349         if (this.el) {
350             this.el.removeClass('hidden');
351         }
352     },
353     /**
354      * Hide a component - adds 'hidden' class
355      */
356     hide: function()
357     {
358         if (this.el && !this.el.hasClass('hidden')) {
359             this.el.addClass('hidden');
360         }
361         
362     }
363 });
364
365  /*
366  * - LGPL
367  *
368  * Body
369  * 
370  */
371
372 /**
373  * @class Roo.bootstrap.Body
374  * @extends Roo.bootstrap.Component
375  * Bootstrap Body class
376  * 
377  * @constructor
378  * Create a new body
379  * @param {Object} config The config object
380  */
381
382 Roo.bootstrap.Body = function(config){
383     Roo.bootstrap.Body.superclass.constructor.call(this, config);
384     this.el = Roo.get(document.body);
385     if (this.cls && this.cls.length) {
386         Roo.get(document.body).addClass(this.cls);
387     }
388 };
389
390 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
391       
392         autoCreate : {
393         cls: 'container'
394     },
395     onRender : function(ct, position)
396     {
397        /* Roo.log("Roo.bootstrap.Body - onRender");
398         if (this.cls && this.cls.length) {
399             Roo.get(document.body).addClass(this.cls);
400         }
401         // style??? xttr???
402         */
403     }
404     
405     
406  
407    
408 });
409
410  /*
411  * - LGPL
412  *
413  * button group
414  * 
415  */
416
417
418 /**
419  * @class Roo.bootstrap.ButtonGroup
420  * @extends Roo.bootstrap.Component
421  * Bootstrap ButtonGroup class
422  * @cfg {String} size lg | sm | xs (default empty normal)
423  * @cfg {String} align vertical | justified  (default none)
424  * @cfg {String} direction up | down (default down)
425  * @cfg {Boolean} toolbar false | true
426  * @cfg {Boolean} btn true | false
427  * 
428  * 
429  * @constructor
430  * Create a new Input
431  * @param {Object} config The config object
432  */
433
434 Roo.bootstrap.ButtonGroup = function(config){
435     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
436 };
437
438 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
439     
440     size: '',
441     align: '',
442     direction: '',
443     toolbar: false,
444     btn: true,
445
446     getAutoCreate : function(){
447         var cfg = {
448             cls: 'btn-group',
449             html : null
450         }
451         
452         cfg.html = this.html || cfg.html;
453         
454         if (this.toolbar) {
455             cfg = {
456                 cls: 'btn-toolbar',
457                 html: null
458             }
459             
460             return cfg;
461         }
462         
463         if (['vertical','justified'].indexOf(this.align)!==-1) {
464             cfg.cls = 'btn-group-' + this.align;
465             
466             if (this.align == 'justified') {
467                 console.log(this.items);
468             }
469         }
470         
471         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
472             cfg.cls += ' btn-group-' + this.size;
473         }
474         
475         if (this.direction == 'up') {
476             cfg.cls += ' dropup' ;
477         }
478         
479         return cfg;
480     }
481    
482 });
483
484  /*
485  * - LGPL
486  *
487  * button
488  * 
489  */
490
491 /**
492  * @class Roo.bootstrap.Button
493  * @extends Roo.bootstrap.Component
494  * Bootstrap Button class
495  * @cfg {String} html The button content
496  * @cfg {String} weight (  primary | success | info | warning | danger | link ) default 
497  * @cfg {String} size ( lg | sm | xs)
498  * @cfg {String} tag ( a | input | submit)
499  * @cfg {String} href empty or href
500  * @cfg {Boolean} disabled default false;
501  * @cfg {Boolean} isClose default false;
502  * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
503  * @cfg {String} badge text for badge
504  * @cfg {String} theme default 
505  * @cfg {Boolean} inverse 
506  * @cfg {Boolean} toggle 
507  * @cfg {String} ontext text for on toggle state
508  * @cfg {String} offtext text for off toggle state
509  * @cfg {Boolean} defaulton 
510  * @cfg {Boolean} preventDefault  default true
511  * @cfg {Boolean} removeClass remove the standard class..
512  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
513  * 
514  * @constructor
515  * Create a new button
516  * @param {Object} config The config object
517  */
518
519
520 Roo.bootstrap.Button = function(config){
521     Roo.bootstrap.Button.superclass.constructor.call(this, config);
522     this.addEvents({
523         // raw events
524         /**
525          * @event click
526          * When a butotn is pressed
527          * @param {Roo.bootstrap.Button} this
528          * @param {Roo.EventObject} e
529          */
530         "click" : true,
531          /**
532          * @event toggle
533          * After the button has been toggles
534          * @param {Roo.EventObject} e
535          * @param {boolean} pressed (also available as button.pressed)
536          */
537         "toggle" : true
538     });
539 };
540
541 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
542     html: false,
543     active: false,
544     weight: '',
545     size: '',
546     tag: 'button',
547     href: '',
548     disabled: false,
549     isClose: false,
550     glyphicon: '',
551     badge: '',
552     theme: 'default',
553     inverse: false,
554     
555     toggle: false,
556     ontext: 'ON',
557     offtext: 'OFF',
558     defaulton: true,
559     preventDefault: true,
560     removeClass: false,
561     name: false,
562     target: false,
563     
564     
565     pressed : null,
566      
567     
568     getAutoCreate : function(){
569         
570         var cfg = {
571             tag : 'button',
572             cls : 'roo-button',
573             html: ''
574         };
575         
576         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
577             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
578             this.tag = 'button';
579         } else {
580             cfg.tag = this.tag;
581         }
582         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
583         
584         if (this.toggle == true) {
585             cfg={
586                 tag: 'div',
587                 cls: 'slider-frame roo-button',
588                 cn: [
589                     {
590                         tag: 'span',
591                         'data-on-text':'ON',
592                         'data-off-text':'OFF',
593                         cls: 'slider-button',
594                         html: this.offtext
595                     }
596                 ]
597             };
598             
599             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
600                 cfg.cls += ' '+this.weight;
601             }
602             
603             return cfg;
604         }
605         
606         if (this.isClose) {
607             cfg.cls += ' close';
608             
609             cfg["aria-hidden"] = true;
610             
611             cfg.html = "&times;";
612             
613             return cfg;
614         }
615         
616          
617         if (this.theme==='default') {
618             cfg.cls = 'btn roo-button';
619             
620             //if (this.parentType != 'Navbar') {
621             this.weight = this.weight.length ?  this.weight : 'default';
622             //}
623             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
624                 
625                 cfg.cls += ' btn-' + this.weight;
626             }
627         } else if (this.theme==='glow') {
628             
629             cfg.tag = 'a';
630             cfg.cls = 'btn-glow roo-button';
631             
632             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
633                 
634                 cfg.cls += ' ' + this.weight;
635             }
636         }
637    
638         
639         if (this.inverse) {
640             this.cls += ' inverse';
641         }
642         
643         
644         if (this.active) {
645             cfg.cls += ' active';
646         }
647         
648         if (this.disabled) {
649             cfg.disabled = 'disabled';
650         }
651         
652         if (this.items) {
653             Roo.log('changing to ul' );
654             cfg.tag = 'ul';
655             this.glyphicon = 'caret';
656         }
657         
658         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
659          
660         //gsRoo.log(this.parentType);
661         if (this.parentType === 'Navbar' && !this.parent().bar) {
662             Roo.log('changing to li?');
663             
664             cfg.tag = 'li';
665             
666             cfg.cls = '';
667             cfg.cn =  [{
668                 tag : 'a',
669                 cls : 'roo-button',
670                 html : this.html,
671                 href : this.href || '#'
672             }];
673             if (this.menu) {
674                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
675                 cfg.cls += ' dropdown';
676             }   
677             
678             delete cfg.html;
679             
680         }
681         
682        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
683         
684         if (this.glyphicon) {
685             cfg.html = ' ' + cfg.html;
686             
687             cfg.cn = [
688                 {
689                     tag: 'span',
690                     cls: 'glyphicon glyphicon-' + this.glyphicon
691                 }
692             ];
693         }
694         
695         if (this.badge) {
696             cfg.html += ' ';
697             
698             cfg.tag = 'a';
699             
700 //            cfg.cls='btn roo-button';
701             
702             cfg.href=this.href;
703             
704             var value = cfg.html;
705             
706             if(this.glyphicon){
707                 value = {
708                             tag: 'span',
709                             cls: 'glyphicon glyphicon-' + this.glyphicon,
710                             html: this.html
711                         };
712                 
713             }
714             
715             cfg.cn = [
716                 value,
717                 {
718                     tag: 'span',
719                     cls: 'badge',
720                     html: this.badge
721                 }
722             ];
723             
724             cfg.html='';
725         }
726         
727         if (this.menu) {
728             cfg.cls += ' dropdown';
729             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
730         }
731         
732         if (cfg.tag !== 'a' && this.href !== '') {
733             throw "Tag must be a to set href.";
734         } else if (this.href.length > 0) {
735             cfg.href = this.href;
736         }
737         
738         if(this.removeClass){
739             cfg.cls = '';
740         }
741         
742         if(this.target){
743             cfg.target = this.target;
744         }
745         
746         return cfg;
747     },
748     initEvents: function() {
749        // Roo.log('init events?');
750 //        Roo.log(this.el.dom);
751         // add the menu...
752         
753         if (typeof (this.menu) != 'undefined') {
754             this.menu.parentType = this.xtype;
755             this.menu.triggerEl = this.el;
756             this.addxtype(Roo.apply({}, this.menu));
757         }
758
759
760        if (this.el.hasClass('roo-button')) {
761             this.el.on('click', this.onClick, this);
762        } else {
763             this.el.select('.roo-button').on('click', this.onClick, this);
764        }
765        
766        if(this.removeClass){
767            this.el.on('click', this.onClick, this);
768        }
769        
770        this.el.enableDisplayMode();
771         
772     },
773     onClick : function(e)
774     {
775         if (this.disabled) {
776             return;
777         }
778         
779         
780         Roo.log('button on click ');
781         if(this.preventDefault){
782             e.preventDefault();
783         }
784         if (this.pressed === true || this.pressed === false) {
785             this.pressed = !this.pressed;
786             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
787             this.fireEvent('toggle', this, e, this.pressed);
788         }
789         
790         
791         this.fireEvent('click', this, e);
792     },
793     
794     /**
795      * Enables this button
796      */
797     enable : function()
798     {
799         this.disabled = false;
800         this.el.removeClass('disabled');
801     },
802     
803     /**
804      * Disable this button
805      */
806     disable : function()
807     {
808         this.disabled = true;
809         this.el.addClass('disabled');
810     },
811      /**
812      * sets the active state on/off, 
813      * @param {Boolean} state (optional) Force a particular state
814      */
815     setActive : function(v) {
816         
817         this.el[v ? 'addClass' : 'removeClass']('active');
818     },
819      /**
820      * toggles the current active state 
821      */
822     toggleActive : function()
823     {
824        var active = this.el.hasClass('active');
825        this.setActive(!active);
826        
827         
828     },
829     setText : function(str)
830     {
831         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
832     },
833     getText : function()
834     {
835         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
836     },
837     hide: function() {
838        
839      
840         this.el.hide();   
841     },
842     show: function() {
843        
844         this.el.show();   
845     }
846     
847     
848 });
849
850  /*
851  * - LGPL
852  *
853  * column
854  * 
855  */
856
857 /**
858  * @class Roo.bootstrap.Column
859  * @extends Roo.bootstrap.Component
860  * Bootstrap Column class
861  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
862  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
863  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
864  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
865  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
866  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
867  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
868  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
869  *
870  * 
871  * @cfg {Boolean} hidden (true|false) hide the element
872  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
873  * @cfg {String} fa (ban|check|...) font awesome icon
874  * @cfg {Number} fasize (1|2|....) font awsome size
875
876  * @cfg {String} icon (info-sign|check|...) glyphicon name
877
878  * @cfg {String} html content of column.
879  * 
880  * @constructor
881  * Create a new Column
882  * @param {Object} config The config object
883  */
884
885 Roo.bootstrap.Column = function(config){
886     Roo.bootstrap.Column.superclass.constructor.call(this, config);
887 };
888
889 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
890     
891     xs: false,
892     sm: false,
893     md: false,
894     lg: false,
895     xsoff: false,
896     smoff: false,
897     mdoff: false,
898     lgoff: false,
899     html: '',
900     offset: 0,
901     alert: false,
902     fa: false,
903     icon : false,
904     hidden : false,
905     fasize : 1,
906     
907     getAutoCreate : function(){
908         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
909         
910         cfg = {
911             tag: 'div',
912             cls: 'column'
913         };
914         
915         var settings=this;
916         ['xs','sm','md','lg'].map(function(size){
917             //Roo.log( size + ':' + settings[size]);
918             
919             if (settings[size+'off'] !== false) {
920                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
921             }
922             
923             if (settings[size] === false) {
924                 return;
925             }
926             Roo.log(settings[size]);
927             if (!settings[size]) { // 0 = hidden
928                 cfg.cls += ' hidden-' + size;
929                 return;
930             }
931             cfg.cls += ' col-' + size + '-' + settings[size];
932             
933         });
934         
935         if (this.hidden) {
936             cfg.cls += ' hidden';
937         }
938         
939         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
940             cfg.cls +=' alert alert-' + this.alert;
941         }
942         
943         
944         if (this.html.length) {
945             cfg.html = this.html;
946         }
947         if (this.fa) {
948             var fasize = '';
949             if (this.fasize > 1) {
950                 fasize = ' fa-' + this.fasize + 'x';
951             }
952             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
953             
954             
955         }
956         if (this.icon) {
957             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
958         }
959         
960         return cfg;
961     }
962    
963 });
964
965  
966
967  /*
968  * - LGPL
969  *
970  * page container.
971  * 
972  */
973
974
975 /**
976  * @class Roo.bootstrap.Container
977  * @extends Roo.bootstrap.Component
978  * Bootstrap Container class
979  * @cfg {Boolean} jumbotron is it a jumbotron element
980  * @cfg {String} html content of element
981  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
982  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
983  * @cfg {String} header content of header (for panel)
984  * @cfg {String} footer content of footer (for panel)
985  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
986  * @cfg {String} tag (header|aside|section) type of HTML tag.
987  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
988  * @cfg {String} fa (ban|check|...) font awesome icon
989  * @cfg {String} icon (info-sign|check|...) glyphicon name
990  * @cfg {Boolean} hidden (true|false) hide the element
991  * @cfg {Boolean} expandable (true|false) default false
992  * @cfg {Boolean} expanded (true|false) default true
993  * @cfg {String} rheader contet on the right of header
994
995  *     
996  * @constructor
997  * Create a new Container
998  * @param {Object} config The config object
999  */
1000
1001 Roo.bootstrap.Container = function(config){
1002     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1003     
1004     this.addEvents({
1005         // raw events
1006          /**
1007          * @event expand
1008          * After the panel has been expand
1009          * 
1010          * @param {Roo.bootstrap.Container} this
1011          */
1012         "expand" : true,
1013         /**
1014          * @event collapse
1015          * After the panel has been collapsed
1016          * 
1017          * @param {Roo.bootstrap.Container} this
1018          */
1019         "collapse" : true
1020     });
1021 };
1022
1023 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1024     
1025     jumbotron : false,
1026     well: '',
1027     panel : '',
1028     header: '',
1029     footer : '',
1030     sticky: '',
1031     tag : false,
1032     alert : false,
1033     fa: false,
1034     icon : false,
1035     expandable : false,
1036     rheader : '',
1037     expanded : true,
1038   
1039      
1040     getChildContainer : function() {
1041         
1042         if(!this.el){
1043             return false;
1044         }
1045         
1046         if (this.panel.length) {
1047             return this.el.select('.panel-body',true).first();
1048         }
1049         
1050         return this.el;
1051     },
1052     
1053     
1054     getAutoCreate : function(){
1055         
1056         var cfg = {
1057             tag : this.tag || 'div',
1058             html : '',
1059             cls : ''
1060         };
1061         if (this.jumbotron) {
1062             cfg.cls = 'jumbotron';
1063         }
1064         
1065         
1066         
1067         // - this is applied by the parent..
1068         //if (this.cls) {
1069         //    cfg.cls = this.cls + '';
1070         //}
1071         
1072         if (this.sticky.length) {
1073             
1074             var bd = Roo.get(document.body);
1075             if (!bd.hasClass('bootstrap-sticky')) {
1076                 bd.addClass('bootstrap-sticky');
1077                 Roo.select('html',true).setStyle('height', '100%');
1078             }
1079              
1080             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1081         }
1082         
1083         
1084         if (this.well.length) {
1085             switch (this.well) {
1086                 case 'lg':
1087                 case 'sm':
1088                     cfg.cls +=' well well-' +this.well;
1089                     break;
1090                 default:
1091                     cfg.cls +=' well';
1092                     break;
1093             }
1094         }
1095         
1096         if (this.hidden) {
1097             cfg.cls += ' hidden';
1098         }
1099         
1100         
1101         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1102             cfg.cls +=' alert alert-' + this.alert;
1103         }
1104         
1105         var body = cfg;
1106         
1107         if (this.panel.length) {
1108             cfg.cls += ' panel panel-' + this.panel;
1109             cfg.cn = [];
1110             if (this.header.length) {
1111                 
1112                 var h = [];
1113                 
1114                 if(this.expandable){
1115                     
1116                     cfg.cls = cfg.cls + ' expandable';
1117                     
1118                     h.push({
1119                         tag: 'i',
1120                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1121                     });
1122                     
1123                 }
1124                 
1125                 h.push(
1126                     {
1127                         tag: 'span',
1128                         cls : 'panel-title',
1129                         html : (this.expandable ? '&nbsp;' : '') + this.header
1130                     },
1131                     {
1132                         tag: 'span',
1133                         cls: 'panel-header-right',
1134                         html: this.rheader
1135                     }
1136                 );
1137                 
1138                 cfg.cn.push({
1139                     cls : 'panel-heading',
1140                     style : this.expandable ? 'cursor: pointer' : '',
1141                     cn : h
1142                 });
1143                 
1144             }
1145             
1146             body = false;
1147             cfg.cn.push({
1148                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1149                 html : this.html
1150             });
1151             
1152             
1153             if (this.footer.length) {
1154                 cfg.cn.push({
1155                     cls : 'panel-footer',
1156                     html : this.footer
1157                     
1158                 });
1159             }
1160             
1161         }
1162         
1163         if (body) {
1164             body.html = this.html || cfg.html;
1165             // prefix with the icons..
1166             if (this.fa) {
1167                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1168             }
1169             if (this.icon) {
1170                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1171             }
1172             
1173             
1174         }
1175         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1176             cfg.cls =  'container';
1177         }
1178         
1179         return cfg;
1180     },
1181     
1182     initEvents: function() 
1183     {
1184         if(!this.expandable){
1185             return;
1186         }
1187         
1188         var headerEl = this.headerEl();
1189         
1190         if(!headerEl){
1191             return;
1192         }
1193         
1194         headerEl.on('click', this.onToggleClick, this);
1195         
1196     },
1197     
1198     onToggleClick : function()
1199     {
1200         var headerEl = this.headerEl();
1201         
1202         if(!headerEl){
1203             return;
1204         }
1205         
1206         if(this.expanded){
1207             this.collapse();
1208             return;
1209         }
1210         
1211         this.expand();
1212     },
1213     
1214     expand : function()
1215     {
1216         if(this.fireEvent('expand', this)) {
1217             
1218             this.expanded = true;
1219             
1220             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1221             
1222             this.el.select('.panel-body',true).first().removeClass('hide');
1223             
1224             var toggleEl = this.toggleEl();
1225
1226             if(!toggleEl){
1227                 return;
1228             }
1229
1230             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1231         }
1232         
1233     },
1234     
1235     collapse : function()
1236     {
1237         if(this.fireEvent('collapse', this)) {
1238             
1239             this.expanded = false;
1240             
1241             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1242             this.el.select('.panel-body',true).first().addClass('hide');
1243         
1244             var toggleEl = this.toggleEl();
1245
1246             if(!toggleEl){
1247                 return;
1248             }
1249
1250             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1251         }
1252     },
1253     
1254     toggleEl : function()
1255     {
1256         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1257             return;
1258         }
1259         
1260         return this.el.select('.panel-heading .fa',true).first();
1261     },
1262     
1263     headerEl : function()
1264     {
1265         if(!this.el || !this.panel.length || !this.header.length){
1266             return;
1267         }
1268         
1269         return this.el.select('.panel-heading',true).first()
1270     },
1271     
1272     titleEl : function()
1273     {
1274         if(!this.el || !this.panel.length || !this.header.length){
1275             return;
1276         }
1277         
1278         return this.el.select('.panel-title',true).first();
1279     },
1280     
1281     setTitle : function(v)
1282     {
1283         var titleEl = this.titleEl();
1284         
1285         if(!titleEl){
1286             return;
1287         }
1288         
1289         titleEl.dom.innerHTML = v;
1290     },
1291     
1292     getTitle : function()
1293     {
1294         
1295         var titleEl = this.titleEl();
1296         
1297         if(!titleEl){
1298             return '';
1299         }
1300         
1301         return titleEl.dom.innerHTML;
1302     },
1303     
1304     setRightTitle : function(v)
1305     {
1306         var t = this.el.select('.panel-header-right',true).first();
1307         
1308         if(!t){
1309             return;
1310         }
1311         
1312         t.dom.innerHTML = v;
1313     }
1314    
1315 });
1316
1317  /*
1318  * - LGPL
1319  *
1320  * image
1321  * 
1322  */
1323
1324
1325 /**
1326  * @class Roo.bootstrap.Img
1327  * @extends Roo.bootstrap.Component
1328  * Bootstrap Img class
1329  * @cfg {Boolean} imgResponsive false | true
1330  * @cfg {String} border rounded | circle | thumbnail
1331  * @cfg {String} src image source
1332  * @cfg {String} alt image alternative text
1333  * @cfg {String} href a tag href
1334  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1335  * @cfg {String} xsUrl xs image source
1336  * @cfg {String} smUrl sm image source
1337  * @cfg {String} mdUrl md image source
1338  * @cfg {String} lgUrl lg image source
1339  * 
1340  * @constructor
1341  * Create a new Input
1342  * @param {Object} config The config object
1343  */
1344
1345 Roo.bootstrap.Img = function(config){
1346     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1347     
1348     this.addEvents({
1349         // img events
1350         /**
1351          * @event click
1352          * The img click event for the img.
1353          * @param {Roo.EventObject} e
1354          */
1355         "click" : true
1356     });
1357 };
1358
1359 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1360     
1361     imgResponsive: true,
1362     border: '',
1363     src: '',
1364     href: false,
1365     target: false,
1366     xsUrl: '',
1367     smUrl: '',
1368     mdUrl: '',
1369     lgUrl: '',
1370
1371     getAutoCreate : function()
1372     {   
1373         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1374             return this.createSingleImg();
1375         }
1376         
1377         var cfg = {
1378             tag: 'div',
1379             cls: 'roo-image-responsive-group',
1380             cn: []
1381         }
1382         var _this = this;
1383         
1384         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1385             
1386             if(!_this[size + 'Url']){
1387                 return;
1388             }
1389             
1390             var img = {
1391                 tag: 'img',
1392                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1393                 html: _this.html || cfg.html,
1394                 src: _this[size + 'Url']
1395             }
1396             
1397             img.cls += ' roo-image-responsive-' + size;
1398             
1399             var s = ['xs', 'sm', 'md', 'lg'];
1400             
1401             s.splice(s.indexOf(size), 1);
1402             
1403             Roo.each(s, function(ss){
1404                 img.cls += ' hidden-' + ss;
1405             });
1406             
1407             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1408                 cfg.cls += ' img-' + _this.border;
1409             }
1410             
1411             if(_this.alt){
1412                 cfg.alt = _this.alt;
1413             }
1414             
1415             if(_this.href){
1416                 var a = {
1417                     tag: 'a',
1418                     href: _this.href,
1419                     cn: [
1420                         img
1421                     ]
1422                 }
1423
1424                 if(this.target){
1425                     a.target = _this.target;
1426                 }
1427             }
1428             
1429             cfg.cn.push((_this.href) ? a : img);
1430             
1431         });
1432         
1433         return cfg;
1434     },
1435     
1436     createSingleImg : function()
1437     {
1438         var cfg = {
1439             tag: 'img',
1440             cls: (this.imgResponsive) ? 'img-responsive' : '',
1441             html : null
1442         }
1443         
1444         cfg.html = this.html || cfg.html;
1445         
1446         cfg.src = this.src || cfg.src;
1447         
1448         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1449             cfg.cls += ' img-' + this.border;
1450         }
1451         
1452         if(this.alt){
1453             cfg.alt = this.alt;
1454         }
1455         
1456         if(this.href){
1457             var a = {
1458                 tag: 'a',
1459                 href: this.href,
1460                 cn: [
1461                     cfg
1462                 ]
1463             }
1464             
1465             if(this.target){
1466                 a.target = this.target;
1467             }
1468             
1469         }
1470         
1471         return (this.href) ? a : cfg;
1472     },
1473     
1474     initEvents: function() 
1475     {
1476         if(!this.href){
1477             this.el.on('click', this.onClick, this);
1478         }
1479         
1480     },
1481     
1482     onClick : function(e)
1483     {
1484         Roo.log('img onclick');
1485         this.fireEvent('click', this, e);
1486     }
1487    
1488 });
1489
1490  /*
1491  * - LGPL
1492  *
1493  * image
1494  * 
1495  */
1496
1497
1498 /**
1499  * @class Roo.bootstrap.Link
1500  * @extends Roo.bootstrap.Component
1501  * Bootstrap Link Class
1502  * @cfg {String} alt image alternative text
1503  * @cfg {String} href a tag href
1504  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1505  * @cfg {String} html the content of the link.
1506  * @cfg {String} anchor name for the anchor link
1507
1508  * @cfg {Boolean} preventDefault (true | false) default false
1509
1510  * 
1511  * @constructor
1512  * Create a new Input
1513  * @param {Object} config The config object
1514  */
1515
1516 Roo.bootstrap.Link = function(config){
1517     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1518     
1519     this.addEvents({
1520         // img events
1521         /**
1522          * @event click
1523          * The img click event for the img.
1524          * @param {Roo.EventObject} e
1525          */
1526         "click" : true
1527     });
1528 };
1529
1530 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1531     
1532     href: false,
1533     target: false,
1534     preventDefault: false,
1535     anchor : false,
1536     alt : false,
1537
1538     getAutoCreate : function()
1539     {
1540         
1541         var cfg = {
1542             tag: 'a'
1543         };
1544         // anchor's do not require html/href...
1545         if (this.anchor === false) {
1546             cfg.html = this.html || '';
1547             cfg.href = this.href || '#';
1548         } else {
1549             cfg.name = this.anchor;
1550             if (this.html !== false) {
1551                 cfg.html = this.html;
1552             }
1553             if (this.href !== false) {
1554                 cfg.href = this.href;
1555             }
1556         }
1557         
1558         if(this.alt !== false){
1559             cfg.alt = this.alt;
1560         }
1561         
1562         
1563         if(this.target !== false) {
1564             cfg.target = this.target;
1565         }
1566         
1567         return cfg;
1568     },
1569     
1570     initEvents: function() {
1571         
1572         if(!this.href || this.preventDefault){
1573             this.el.on('click', this.onClick, this);
1574         }
1575     },
1576     
1577     onClick : function(e)
1578     {
1579         if(this.preventDefault){
1580             e.preventDefault();
1581         }
1582         //Roo.log('img onclick');
1583         this.fireEvent('click', this, e);
1584     }
1585    
1586 });
1587
1588  /*
1589  * - LGPL
1590  *
1591  * header
1592  * 
1593  */
1594
1595 /**
1596  * @class Roo.bootstrap.Header
1597  * @extends Roo.bootstrap.Component
1598  * Bootstrap Header class
1599  * @cfg {String} html content of header
1600  * @cfg {Number} level (1|2|3|4|5|6) default 1
1601  * 
1602  * @constructor
1603  * Create a new Header
1604  * @param {Object} config The config object
1605  */
1606
1607
1608 Roo.bootstrap.Header  = function(config){
1609     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1610 };
1611
1612 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1613     
1614     //href : false,
1615     html : false,
1616     level : 1,
1617     
1618     
1619     
1620     getAutoCreate : function(){
1621         
1622         
1623         
1624         var cfg = {
1625             tag: 'h' + (1 *this.level),
1626             html: this.html || ''
1627         } ;
1628         
1629         return cfg;
1630     }
1631    
1632 });
1633
1634  
1635
1636  /*
1637  * Based on:
1638  * Ext JS Library 1.1.1
1639  * Copyright(c) 2006-2007, Ext JS, LLC.
1640  *
1641  * Originally Released Under LGPL - original licence link has changed is not relivant.
1642  *
1643  * Fork - LGPL
1644  * <script type="text/javascript">
1645  */
1646  
1647 /**
1648  * @class Roo.bootstrap.MenuMgr
1649  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1650  * @singleton
1651  */
1652 Roo.bootstrap.MenuMgr = function(){
1653    var menus, active, groups = {}, attached = false, lastShow = new Date();
1654
1655    // private - called when first menu is created
1656    function init(){
1657        menus = {};
1658        active = new Roo.util.MixedCollection();
1659        Roo.get(document).addKeyListener(27, function(){
1660            if(active.length > 0){
1661                hideAll();
1662            }
1663        });
1664    }
1665
1666    // private
1667    function hideAll(){
1668        if(active && active.length > 0){
1669            var c = active.clone();
1670            c.each(function(m){
1671                m.hide();
1672            });
1673        }
1674    }
1675
1676    // private
1677    function onHide(m){
1678        active.remove(m);
1679        if(active.length < 1){
1680            Roo.get(document).un("mouseup", onMouseDown);
1681             
1682            attached = false;
1683        }
1684    }
1685
1686    // private
1687    function onShow(m){
1688        var last = active.last();
1689        lastShow = new Date();
1690        active.add(m);
1691        if(!attached){
1692           Roo.get(document).on("mouseup", onMouseDown);
1693            
1694            attached = true;
1695        }
1696        if(m.parentMenu){
1697           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1698           m.parentMenu.activeChild = m;
1699        }else if(last && last.isVisible()){
1700           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1701        }
1702    }
1703
1704    // private
1705    function onBeforeHide(m){
1706        if(m.activeChild){
1707            m.activeChild.hide();
1708        }
1709        if(m.autoHideTimer){
1710            clearTimeout(m.autoHideTimer);
1711            delete m.autoHideTimer;
1712        }
1713    }
1714
1715    // private
1716    function onBeforeShow(m){
1717        var pm = m.parentMenu;
1718        if(!pm && !m.allowOtherMenus){
1719            hideAll();
1720        }else if(pm && pm.activeChild && active != m){
1721            pm.activeChild.hide();
1722        }
1723    }
1724
1725    // private this should really trigger on mouseup..
1726    function onMouseDown(e){
1727         Roo.log("on Mouse Up");
1728         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu") && !e.getTarget('.user-menu')){
1729             Roo.log("hideAll");
1730             hideAll();
1731             e.stopEvent();
1732         }
1733         
1734         
1735    }
1736
1737    // private
1738    function onBeforeCheck(mi, state){
1739        if(state){
1740            var g = groups[mi.group];
1741            for(var i = 0, l = g.length; i < l; i++){
1742                if(g[i] != mi){
1743                    g[i].setChecked(false);
1744                }
1745            }
1746        }
1747    }
1748
1749    return {
1750
1751        /**
1752         * Hides all menus that are currently visible
1753         */
1754        hideAll : function(){
1755             hideAll();  
1756        },
1757
1758        // private
1759        register : function(menu){
1760            if(!menus){
1761                init();
1762            }
1763            menus[menu.id] = menu;
1764            menu.on("beforehide", onBeforeHide);
1765            menu.on("hide", onHide);
1766            menu.on("beforeshow", onBeforeShow);
1767            menu.on("show", onShow);
1768            var g = menu.group;
1769            if(g && menu.events["checkchange"]){
1770                if(!groups[g]){
1771                    groups[g] = [];
1772                }
1773                groups[g].push(menu);
1774                menu.on("checkchange", onCheck);
1775            }
1776        },
1777
1778         /**
1779          * Returns a {@link Roo.menu.Menu} object
1780          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1781          * be used to generate and return a new Menu instance.
1782          */
1783        get : function(menu){
1784            if(typeof menu == "string"){ // menu id
1785                return menus[menu];
1786            }else if(menu.events){  // menu instance
1787                return menu;
1788            }
1789            /*else if(typeof menu.length == 'number'){ // array of menu items?
1790                return new Roo.bootstrap.Menu({items:menu});
1791            }else{ // otherwise, must be a config
1792                return new Roo.bootstrap.Menu(menu);
1793            }
1794            */
1795            return false;
1796        },
1797
1798        // private
1799        unregister : function(menu){
1800            delete menus[menu.id];
1801            menu.un("beforehide", onBeforeHide);
1802            menu.un("hide", onHide);
1803            menu.un("beforeshow", onBeforeShow);
1804            menu.un("show", onShow);
1805            var g = menu.group;
1806            if(g && menu.events["checkchange"]){
1807                groups[g].remove(menu);
1808                menu.un("checkchange", onCheck);
1809            }
1810        },
1811
1812        // private
1813        registerCheckable : function(menuItem){
1814            var g = menuItem.group;
1815            if(g){
1816                if(!groups[g]){
1817                    groups[g] = [];
1818                }
1819                groups[g].push(menuItem);
1820                menuItem.on("beforecheckchange", onBeforeCheck);
1821            }
1822        },
1823
1824        // private
1825        unregisterCheckable : function(menuItem){
1826            var g = menuItem.group;
1827            if(g){
1828                groups[g].remove(menuItem);
1829                menuItem.un("beforecheckchange", onBeforeCheck);
1830            }
1831        }
1832    };
1833 }();/*
1834  * - LGPL
1835  *
1836  * menu
1837  * 
1838  */
1839
1840 /**
1841  * @class Roo.bootstrap.Menu
1842  * @extends Roo.bootstrap.Component
1843  * Bootstrap Menu class - container for MenuItems
1844  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1845  * 
1846  * @constructor
1847  * Create a new Menu
1848  * @param {Object} config The config object
1849  */
1850
1851
1852 Roo.bootstrap.Menu = function(config){
1853     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1854     if (this.registerMenu) {
1855         Roo.bootstrap.MenuMgr.register(this);
1856     }
1857     this.addEvents({
1858         /**
1859          * @event beforeshow
1860          * Fires before this menu is displayed
1861          * @param {Roo.menu.Menu} this
1862          */
1863         beforeshow : true,
1864         /**
1865          * @event beforehide
1866          * Fires before this menu is hidden
1867          * @param {Roo.menu.Menu} this
1868          */
1869         beforehide : true,
1870         /**
1871          * @event show
1872          * Fires after this menu is displayed
1873          * @param {Roo.menu.Menu} this
1874          */
1875         show : true,
1876         /**
1877          * @event hide
1878          * Fires after this menu is hidden
1879          * @param {Roo.menu.Menu} this
1880          */
1881         hide : true,
1882         /**
1883          * @event click
1884          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1885          * @param {Roo.menu.Menu} this
1886          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1887          * @param {Roo.EventObject} e
1888          */
1889         click : true,
1890         /**
1891          * @event mouseover
1892          * Fires when the mouse is hovering over this menu
1893          * @param {Roo.menu.Menu} this
1894          * @param {Roo.EventObject} e
1895          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1896          */
1897         mouseover : true,
1898         /**
1899          * @event mouseout
1900          * Fires when the mouse exits this menu
1901          * @param {Roo.menu.Menu} this
1902          * @param {Roo.EventObject} e
1903          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1904          */
1905         mouseout : true,
1906         /**
1907          * @event itemclick
1908          * Fires when a menu item contained in this menu is clicked
1909          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1910          * @param {Roo.EventObject} e
1911          */
1912         itemclick: true
1913     });
1914     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1915 };
1916
1917 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1918     
1919    /// html : false,
1920     //align : '',
1921     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1922     type: false,
1923     /**
1924      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1925      */
1926     registerMenu : true,
1927     
1928     menuItems :false, // stores the menu items..
1929     
1930     hidden:true,
1931     
1932     parentMenu : false,
1933     
1934     getChildContainer : function() {
1935         return this.el;  
1936     },
1937     
1938     getAutoCreate : function(){
1939          
1940         //if (['right'].indexOf(this.align)!==-1) {
1941         //    cfg.cn[1].cls += ' pull-right'
1942         //}
1943         
1944         
1945         var cfg = {
1946             tag : 'ul',
1947             cls : 'dropdown-menu' ,
1948             style : 'z-index:1000'
1949             
1950         }
1951         
1952         if (this.type === 'submenu') {
1953             cfg.cls = 'submenu active';
1954         }
1955         if (this.type === 'treeview') {
1956             cfg.cls = 'treeview-menu';
1957         }
1958         
1959         return cfg;
1960     },
1961     initEvents : function() {
1962         
1963        // Roo.log("ADD event");
1964        // Roo.log(this.triggerEl.dom);
1965         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
1966         
1967         this.triggerEl.addClass('dropdown-toggle');
1968         this.el.on(Roo.isTouch ? 'touchstart' : 'click' , this.onClick, this);
1969
1970         this.el.on("mouseover", this.onMouseOver, this);
1971         this.el.on("mouseout", this.onMouseOut, this);
1972         
1973         
1974     },
1975     findTargetItem : function(e){
1976         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1977         if(!t){
1978             return false;
1979         }
1980         //Roo.log(t);         Roo.log(t.id);
1981         if(t && t.id){
1982             //Roo.log(this.menuitems);
1983             return this.menuitems.get(t.id);
1984             
1985             //return this.items.get(t.menuItemId);
1986         }
1987         
1988         return false;
1989     },
1990     onClick : function(e){
1991         Roo.log("menu.onClick");
1992         var t = this.findTargetItem(e);
1993         if(!t || t.isContainer){
1994             return;
1995         }
1996         Roo.log(e);
1997         /*
1998         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1999             if(t == this.activeItem && t.shouldDeactivate(e)){
2000                 this.activeItem.deactivate();
2001                 delete this.activeItem;
2002                 return;
2003             }
2004             if(t.canActivate){
2005                 this.setActiveItem(t, true);
2006             }
2007             return;
2008             
2009             
2010         }
2011         */
2012        
2013         Roo.log('pass click event');
2014         
2015         t.onClick(e);
2016         
2017         this.fireEvent("click", this, t, e);
2018         
2019         this.hide();
2020     },
2021      onMouseOver : function(e){
2022         var t  = this.findTargetItem(e);
2023         //Roo.log(t);
2024         //if(t){
2025         //    if(t.canActivate && !t.disabled){
2026         //        this.setActiveItem(t, true);
2027         //    }
2028         //}
2029         
2030         this.fireEvent("mouseover", this, e, t);
2031     },
2032     isVisible : function(){
2033         return !this.hidden;
2034     },
2035      onMouseOut : function(e){
2036         var t  = this.findTargetItem(e);
2037         
2038         //if(t ){
2039         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2040         //        this.activeItem.deactivate();
2041         //        delete this.activeItem;
2042         //    }
2043         //}
2044         this.fireEvent("mouseout", this, e, t);
2045     },
2046     
2047     
2048     /**
2049      * Displays this menu relative to another element
2050      * @param {String/HTMLElement/Roo.Element} element The element to align to
2051      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2052      * the element (defaults to this.defaultAlign)
2053      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2054      */
2055     show : function(el, pos, parentMenu){
2056         this.parentMenu = parentMenu;
2057         if(!this.el){
2058             this.render();
2059         }
2060         this.fireEvent("beforeshow", this);
2061         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2062     },
2063      /**
2064      * Displays this menu at a specific xy position
2065      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2066      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2067      */
2068     showAt : function(xy, parentMenu, /* private: */_e){
2069         this.parentMenu = parentMenu;
2070         if(!this.el){
2071             this.render();
2072         }
2073         if(_e !== false){
2074             this.fireEvent("beforeshow", this);
2075             //xy = this.el.adjustForConstraints(xy);
2076         }
2077         
2078         //this.el.show();
2079         this.hideMenuItems();
2080         this.hidden = false;
2081         this.triggerEl.addClass('open');
2082         
2083         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2084             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2085         }
2086         
2087         this.el.setXY(xy);
2088         this.focus();
2089         this.fireEvent("show", this);
2090     },
2091     
2092     focus : function(){
2093         return;
2094         if(!this.hidden){
2095             this.doFocus.defer(50, this);
2096         }
2097     },
2098
2099     doFocus : function(){
2100         if(!this.hidden){
2101             this.focusEl.focus();
2102         }
2103     },
2104
2105     /**
2106      * Hides this menu and optionally all parent menus
2107      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2108      */
2109     hide : function(deep){
2110         
2111         this.hideMenuItems();
2112         if(this.el && this.isVisible()){
2113             this.fireEvent("beforehide", this);
2114             if(this.activeItem){
2115                 this.activeItem.deactivate();
2116                 this.activeItem = null;
2117             }
2118             this.triggerEl.removeClass('open');;
2119             this.hidden = true;
2120             this.fireEvent("hide", this);
2121         }
2122         if(deep === true && this.parentMenu){
2123             this.parentMenu.hide(true);
2124         }
2125     },
2126     
2127     onTriggerPress  : function(e)
2128     {
2129         
2130         Roo.log('trigger press');
2131         //Roo.log(e.getTarget());
2132        // Roo.log(this.triggerEl.dom);
2133         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
2134             return;
2135         }
2136         
2137         if (this.isVisible()) {
2138             Roo.log('hide');
2139             this.hide();
2140         } else {
2141             Roo.log('show');
2142             this.show(this.triggerEl, false, false);
2143         }
2144         
2145         e.stopEvent();
2146     },
2147     
2148          
2149        
2150     
2151     hideMenuItems : function()
2152     {
2153         //$(backdrop).remove()
2154         Roo.select('.open',true).each(function(aa) {
2155             
2156             aa.removeClass('open');
2157           //var parent = getParent($(this))
2158           //var relatedTarget = { relatedTarget: this }
2159           
2160            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2161           //if (e.isDefaultPrevented()) return
2162            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2163         })
2164     },
2165     addxtypeChild : function (tree, cntr) {
2166         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2167           
2168         this.menuitems.add(comp);
2169         return comp;
2170
2171     },
2172     getEl : function()
2173     {
2174         Roo.log(this.el);
2175         return this.el;
2176     }
2177 });
2178
2179  
2180  /*
2181  * - LGPL
2182  *
2183  * menu item
2184  * 
2185  */
2186
2187
2188 /**
2189  * @class Roo.bootstrap.MenuItem
2190  * @extends Roo.bootstrap.Component
2191  * Bootstrap MenuItem class
2192  * @cfg {String} html the menu label
2193  * @cfg {String} href the link
2194  * @cfg {Boolean} preventDefault (true | false) default true
2195  * @cfg {Boolean} isContainer (true | false) default false
2196  * 
2197  * 
2198  * @constructor
2199  * Create a new MenuItem
2200  * @param {Object} config The config object
2201  */
2202
2203
2204 Roo.bootstrap.MenuItem = function(config){
2205     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2206     this.addEvents({
2207         // raw events
2208         /**
2209          * @event click
2210          * The raw click event for the entire grid.
2211          * @param {Roo.bootstrap.MenuItem} this
2212          * @param {Roo.EventObject} e
2213          */
2214         "click" : true
2215     });
2216 };
2217
2218 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2219     
2220     href : false,
2221     html : false,
2222     preventDefault: true,
2223     isContainer : false,
2224     
2225     getAutoCreate : function(){
2226         
2227         if(this.isContainer){
2228             return {
2229                 tag: 'li',
2230                 cls: 'dropdown-menu-item'
2231             };
2232         }
2233         
2234         var cfg= {
2235             tag: 'li',
2236             cls: 'dropdown-menu-item',
2237             cn: [
2238                     {
2239                         tag : 'a',
2240                         href : '#',
2241                         html : 'Link'
2242                     }
2243                 ]
2244         };
2245         if (this.parent().type == 'treeview') {
2246             cfg.cls = 'treeview-menu';
2247         }
2248         
2249         cfg.cn[0].href = this.href || cfg.cn[0].href ;
2250         cfg.cn[0].html = this.html || cfg.cn[0].html ;
2251         return cfg;
2252     },
2253     
2254     initEvents: function() {
2255         
2256         //this.el.select('a').on('click', this.onClick, this);
2257         
2258     },
2259     onClick : function(e)
2260     {
2261         Roo.log('item on click ');
2262         //if(this.preventDefault){
2263         //    e.preventDefault();
2264         //}
2265         //this.parent().hideMenuItems();
2266         
2267         this.fireEvent('click', this, e);
2268     },
2269     getEl : function()
2270     {
2271         return this.el;
2272     }
2273 });
2274
2275  
2276
2277  /*
2278  * - LGPL
2279  *
2280  * menu separator
2281  * 
2282  */
2283
2284
2285 /**
2286  * @class Roo.bootstrap.MenuSeparator
2287  * @extends Roo.bootstrap.Component
2288  * Bootstrap MenuSeparator class
2289  * 
2290  * @constructor
2291  * Create a new MenuItem
2292  * @param {Object} config The config object
2293  */
2294
2295
2296 Roo.bootstrap.MenuSeparator = function(config){
2297     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2298 };
2299
2300 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2301     
2302     getAutoCreate : function(){
2303         var cfg = {
2304             cls: 'divider',
2305             tag : 'li'
2306         };
2307         
2308         return cfg;
2309     }
2310    
2311 });
2312
2313  
2314
2315  
2316 /*
2317 * Licence: LGPL
2318 */
2319
2320 /**
2321  * @class Roo.bootstrap.Modal
2322  * @extends Roo.bootstrap.Component
2323  * Bootstrap Modal class
2324  * @cfg {String} title Title of dialog
2325  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2326  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn 
2327  * @cfg {Boolean} specificTitle default false
2328  * @cfg {Array} buttons Array of buttons or standard button set..
2329  * @cfg {String} buttonPosition (left|right|center) default right
2330  * @cfg {Boolean} animate default true
2331  * @cfg {Boolean} allow_close default true
2332  * 
2333  * @constructor
2334  * Create a new Modal Dialog
2335  * @param {Object} config The config object
2336  */
2337
2338 Roo.bootstrap.Modal = function(config){
2339     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2340     this.addEvents({
2341         // raw events
2342         /**
2343          * @event btnclick
2344          * The raw btnclick event for the button
2345          * @param {Roo.EventObject} e
2346          */
2347         "btnclick" : true
2348     });
2349     this.buttons = this.buttons || [];
2350      
2351     if (this.tmpl) {
2352         this.tmpl = Roo.factory(this.tmpl);
2353     }
2354     
2355 };
2356
2357 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2358     
2359     title : 'test dialog',
2360    
2361     buttons : false,
2362     
2363     // set on load...
2364      
2365     html: false,
2366     
2367     tmp: false,
2368     
2369     specificTitle: false,
2370     
2371     buttonPosition: 'right',
2372     
2373     allow_close : true,
2374     
2375     animate : true,
2376     
2377     
2378      // private
2379     bodyEl:  false,
2380     footerEl:  false,
2381     titleEl:  false,
2382     closeEl:  false,
2383     
2384     
2385     onRender : function(ct, position)
2386     {
2387         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2388      
2389         if(!this.el){
2390             var cfg = Roo.apply({},  this.getAutoCreate());
2391             cfg.id = Roo.id();
2392             //if(!cfg.name){
2393             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2394             //}
2395             //if (!cfg.name.length) {
2396             //    delete cfg.name;
2397            // }
2398             if (this.cls) {
2399                 cfg.cls += ' ' + this.cls;
2400             }
2401             if (this.style) {
2402                 cfg.style = this.style;
2403             }
2404             this.el = Roo.get(document.body).createChild(cfg, position);
2405         }
2406         //var type = this.el.dom.type;
2407         
2408         
2409         
2410         
2411         if(this.tabIndex !== undefined){
2412             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2413         }
2414         
2415         
2416         this.bodyEl = this.el.select('.modal-body',true).first();
2417         this.closeEl = this.el.select('.modal-header .close', true).first();
2418         this.footerEl = this.el.select('.modal-footer',true).first();
2419         this.titleEl = this.el.select('.modal-title',true).first();
2420         
2421         
2422          
2423         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2424         this.maskEl.enableDisplayMode("block");
2425         this.maskEl.hide();
2426         //this.el.addClass("x-dlg-modal");
2427     
2428         if (this.buttons.length) {
2429             Roo.each(this.buttons, function(bb) {
2430                 var b = Roo.apply({}, bb);
2431                 b.xns = b.xns || Roo.bootstrap;
2432                 b.xtype = b.xtype || 'Button';
2433                 if (typeof(b.listeners) == 'undefined') {
2434                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2435                 }
2436                 
2437                 var btn = Roo.factory(b);
2438                 
2439                 btn.onRender(this.el.select('.modal-footer div').first());
2440                 
2441             },this);
2442         }
2443         // render the children.
2444         var nitems = [];
2445         
2446         if(typeof(this.items) != 'undefined'){
2447             var items = this.items;
2448             delete this.items;
2449
2450             for(var i =0;i < items.length;i++) {
2451                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2452             }
2453         }
2454         
2455         this.items = nitems;
2456         
2457         // where are these used - they used to be body/close/footer
2458         
2459        
2460         this.initEvents();
2461         //this.el.addClass([this.fieldClass, this.cls]);
2462         
2463     },
2464     
2465     getAutoCreate : function(){
2466         
2467         
2468         var bdy = {
2469                 cls : 'modal-body',
2470                 html : this.html || ''
2471         };
2472         
2473         var title = {
2474             tag: 'h4',
2475             cls : 'modal-title',
2476             html : this.title
2477         };
2478         
2479         if(this.specificTitle){
2480             title = this.title;
2481             
2482         };
2483         
2484         var header = [];
2485         if (this.allow_close) {
2486             header.push({
2487                 tag: 'button',
2488                 cls : 'close',
2489                 html : '&times'
2490             });
2491         }
2492         header.push(title);
2493         
2494         var modal = {
2495             cls: "modal",
2496             style : 'display: none',
2497             cn : [
2498                 {
2499                     cls: "modal-dialog",
2500                     cn : [
2501                         {
2502                             cls : "modal-content",
2503                             cn : [
2504                                 {
2505                                     cls : 'modal-header',
2506                                     cn : header
2507                                 },
2508                                 bdy,
2509                                 {
2510                                     cls : 'modal-footer',
2511                                     cn : [
2512                                         {
2513                                             tag: 'div',
2514                                             cls: 'btn-' + this.buttonPosition
2515                                         }
2516                                     ]
2517                                     
2518                                 }
2519                                 
2520                                 
2521                             ]
2522                             
2523                         }
2524                     ]
2525                         
2526                 }
2527             ]
2528         };
2529         
2530         if(this.animate){
2531             modal.cls += ' fade';
2532         }
2533         
2534         return modal;
2535           
2536     },
2537     getChildContainer : function() {
2538          
2539          return this.bodyEl;
2540         
2541     },
2542     getButtonContainer : function() {
2543          return this.el.select('.modal-footer div',true).first();
2544         
2545     },
2546     initEvents : function()
2547     {
2548         if (this.allow_close) {
2549             this.closeEl.on('click', this.hide, this);
2550         }
2551         
2552         var _this = this;
2553         
2554         window.addEventListener("resize", function() { _this.resize(); } );
2555
2556     },
2557     
2558     resize : function()
2559     {
2560         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2561     },
2562     
2563     show : function() {
2564         
2565         if (!this.rendered) {
2566             this.render();
2567         }
2568         
2569         this.el.setStyle('display', 'block');
2570         
2571         if(this.animate){
2572             var _this = this;
2573             (function(){ _this.el.addClass('in'); }).defer(50);
2574         }else{
2575             this.el.addClass('in');
2576         }
2577         
2578         // not sure how we can show data in here.. 
2579         //if (this.tmpl) {
2580         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2581         //}
2582         
2583         Roo.get(document.body).addClass("x-body-masked");
2584         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2585         this.maskEl.show();
2586         this.el.setStyle('zIndex', '10001');
2587        
2588         this.fireEvent('show', this);
2589         
2590         
2591     },
2592     hide : function()
2593     {
2594         this.maskEl.hide();
2595         Roo.get(document.body).removeClass("x-body-masked");
2596         this.el.removeClass('in');
2597         
2598         if(this.animate){
2599             var _this = this;
2600             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2601         }else{
2602             this.el.setStyle('display', 'none');
2603         }
2604         
2605         this.fireEvent('hide', this);
2606     },
2607     
2608     addButton : function(str, cb)
2609     {
2610          
2611         
2612         var b = Roo.apply({}, { html : str } );
2613         b.xns = b.xns || Roo.bootstrap;
2614         b.xtype = b.xtype || 'Button';
2615         if (typeof(b.listeners) == 'undefined') {
2616             b.listeners = { click : cb.createDelegate(this)  };
2617         }
2618         
2619         var btn = Roo.factory(b);
2620            
2621         btn.onRender(this.el.select('.modal-footer div').first());
2622         
2623         return btn;   
2624        
2625     },
2626     
2627     setDefaultButton : function(btn)
2628     {
2629         //this.el.select('.modal-footer').()
2630     },
2631     resizeTo: function(w,h)
2632     {
2633         // skip..
2634     },
2635     setContentSize  : function(w, h)
2636     {
2637         
2638     },
2639     onButtonClick: function(btn,e)
2640     {
2641         //Roo.log([a,b,c]);
2642         this.fireEvent('btnclick', btn.name, e);
2643     },
2644      /**
2645      * Set the title of the Dialog
2646      * @param {String} str new Title
2647      */
2648     setTitle: function(str) {
2649         this.titleEl.dom.innerHTML = str;    
2650     },
2651     /**
2652      * Set the body of the Dialog
2653      * @param {String} str new Title
2654      */
2655     setBody: function(str) {
2656         this.bodyEl.dom.innerHTML = str;    
2657     },
2658     /**
2659      * Set the body of the Dialog using the template
2660      * @param {Obj} data - apply this data to the template and replace the body contents.
2661      */
2662     applyBody: function(obj)
2663     {
2664         if (!this.tmpl) {
2665             Roo.log("Error - using apply Body without a template");
2666             //code
2667         }
2668         this.tmpl.overwrite(this.bodyEl, obj);
2669     }
2670     
2671 });
2672
2673
2674 Roo.apply(Roo.bootstrap.Modal,  {
2675     /**
2676          * Button config that displays a single OK button
2677          * @type Object
2678          */
2679         OK :  [{
2680             name : 'ok',
2681             weight : 'primary',
2682             html : 'OK'
2683         }], 
2684         /**
2685          * Button config that displays Yes and No buttons
2686          * @type Object
2687          */
2688         YESNO : [
2689             {
2690                 name  : 'no',
2691                 html : 'No'
2692             },
2693             {
2694                 name  :'yes',
2695                 weight : 'primary',
2696                 html : 'Yes'
2697             }
2698         ],
2699         
2700         /**
2701          * Button config that displays OK and Cancel buttons
2702          * @type Object
2703          */
2704         OKCANCEL : [
2705             {
2706                name : 'cancel',
2707                 html : 'Cancel'
2708             },
2709             {
2710                 name : 'ok',
2711                 weight : 'primary',
2712                 html : 'OK'
2713             }
2714         ],
2715         /**
2716          * Button config that displays Yes, No and Cancel buttons
2717          * @type Object
2718          */
2719         YESNOCANCEL : [
2720             {
2721                 name : 'yes',
2722                 weight : 'primary',
2723                 html : 'Yes'
2724             },
2725             {
2726                 name : 'no',
2727                 html : 'No'
2728             },
2729             {
2730                 name : 'cancel',
2731                 html : 'Cancel'
2732             }
2733         ]
2734 });
2735  
2736  /*
2737  * - LGPL
2738  *
2739  * messagebox - can be used as a replace
2740  * 
2741  */
2742 /**
2743  * @class Roo.MessageBox
2744  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2745  * Example usage:
2746  *<pre><code>
2747 // Basic alert:
2748 Roo.Msg.alert('Status', 'Changes saved successfully.');
2749
2750 // Prompt for user data:
2751 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2752     if (btn == 'ok'){
2753         // process text value...
2754     }
2755 });
2756
2757 // Show a dialog using config options:
2758 Roo.Msg.show({
2759    title:'Save Changes?',
2760    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2761    buttons: Roo.Msg.YESNOCANCEL,
2762    fn: processResult,
2763    animEl: 'elId'
2764 });
2765 </code></pre>
2766  * @singleton
2767  */
2768 Roo.bootstrap.MessageBox = function(){
2769     var dlg, opt, mask, waitTimer;
2770     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2771     var buttons, activeTextEl, bwidth;
2772
2773     
2774     // private
2775     var handleButton = function(button){
2776         dlg.hide();
2777         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2778     };
2779
2780     // private
2781     var handleHide = function(){
2782         if(opt && opt.cls){
2783             dlg.el.removeClass(opt.cls);
2784         }
2785         //if(waitTimer){
2786         //    Roo.TaskMgr.stop(waitTimer);
2787         //    waitTimer = null;
2788         //}
2789     };
2790
2791     // private
2792     var updateButtons = function(b){
2793         var width = 0;
2794         if(!b){
2795             buttons["ok"].hide();
2796             buttons["cancel"].hide();
2797             buttons["yes"].hide();
2798             buttons["no"].hide();
2799             //dlg.footer.dom.style.display = 'none';
2800             return width;
2801         }
2802         dlg.footerEl.dom.style.display = '';
2803         for(var k in buttons){
2804             if(typeof buttons[k] != "function"){
2805                 if(b[k]){
2806                     buttons[k].show();
2807                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2808                     width += buttons[k].el.getWidth()+15;
2809                 }else{
2810                     buttons[k].hide();
2811                 }
2812             }
2813         }
2814         return width;
2815     };
2816
2817     // private
2818     var handleEsc = function(d, k, e){
2819         if(opt && opt.closable !== false){
2820             dlg.hide();
2821         }
2822         if(e){
2823             e.stopEvent();
2824         }
2825     };
2826
2827     return {
2828         /**
2829          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2830          * @return {Roo.BasicDialog} The BasicDialog element
2831          */
2832         getDialog : function(){
2833            if(!dlg){
2834                 dlg = new Roo.bootstrap.Modal( {
2835                     //draggable: true,
2836                     //resizable:false,
2837                     //constraintoviewport:false,
2838                     //fixedcenter:true,
2839                     //collapsible : false,
2840                     //shim:true,
2841                     //modal: true,
2842                   //  width:400,
2843                   //  height:100,
2844                     //buttonAlign:"center",
2845                     closeClick : function(){
2846                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2847                             handleButton("no");
2848                         }else{
2849                             handleButton("cancel");
2850                         }
2851                     }
2852                 });
2853                 dlg.render();
2854                 dlg.on("hide", handleHide);
2855                 mask = dlg.mask;
2856                 //dlg.addKeyListener(27, handleEsc);
2857                 buttons = {};
2858                 this.buttons = buttons;
2859                 var bt = this.buttonText;
2860                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2861                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2862                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2863                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2864                 Roo.log(buttons)
2865                 bodyEl = dlg.bodyEl.createChild({
2866
2867                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2868                         '<textarea class="roo-mb-textarea"></textarea>' +
2869                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2870                 });
2871                 msgEl = bodyEl.dom.firstChild;
2872                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2873                 textboxEl.enableDisplayMode();
2874                 textboxEl.addKeyListener([10,13], function(){
2875                     if(dlg.isVisible() && opt && opt.buttons){
2876                         if(opt.buttons.ok){
2877                             handleButton("ok");
2878                         }else if(opt.buttons.yes){
2879                             handleButton("yes");
2880                         }
2881                     }
2882                 });
2883                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2884                 textareaEl.enableDisplayMode();
2885                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2886                 progressEl.enableDisplayMode();
2887                 var pf = progressEl.dom.firstChild;
2888                 if (pf) {
2889                     pp = Roo.get(pf.firstChild);
2890                     pp.setHeight(pf.offsetHeight);
2891                 }
2892                 
2893             }
2894             return dlg;
2895         },
2896
2897         /**
2898          * Updates the message box body text
2899          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2900          * the XHTML-compliant non-breaking space character '&amp;#160;')
2901          * @return {Roo.MessageBox} This message box
2902          */
2903         updateText : function(text){
2904             if(!dlg.isVisible() && !opt.width){
2905                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2906             }
2907             msgEl.innerHTML = text || '&#160;';
2908       
2909             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2910             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2911             var w = Math.max(
2912                     Math.min(opt.width || cw , this.maxWidth), 
2913                     Math.max(opt.minWidth || this.minWidth, bwidth)
2914             );
2915             if(opt.prompt){
2916                 activeTextEl.setWidth(w);
2917             }
2918             if(dlg.isVisible()){
2919                 dlg.fixedcenter = false;
2920             }
2921             // to big, make it scroll. = But as usual stupid IE does not support
2922             // !important..
2923             
2924             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2925                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2926                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2927             } else {
2928                 bodyEl.dom.style.height = '';
2929                 bodyEl.dom.style.overflowY = '';
2930             }
2931             if (cw > w) {
2932                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2933             } else {
2934                 bodyEl.dom.style.overflowX = '';
2935             }
2936             
2937             dlg.setContentSize(w, bodyEl.getHeight());
2938             if(dlg.isVisible()){
2939                 dlg.fixedcenter = true;
2940             }
2941             return this;
2942         },
2943
2944         /**
2945          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2946          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2947          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2948          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2949          * @return {Roo.MessageBox} This message box
2950          */
2951         updateProgress : function(value, text){
2952             if(text){
2953                 this.updateText(text);
2954             }
2955             if (pp) { // weird bug on my firefox - for some reason this is not defined
2956                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2957             }
2958             return this;
2959         },        
2960
2961         /**
2962          * Returns true if the message box is currently displayed
2963          * @return {Boolean} True if the message box is visible, else false
2964          */
2965         isVisible : function(){
2966             return dlg && dlg.isVisible();  
2967         },
2968
2969         /**
2970          * Hides the message box if it is displayed
2971          */
2972         hide : function(){
2973             if(this.isVisible()){
2974                 dlg.hide();
2975             }  
2976         },
2977
2978         /**
2979          * Displays a new message box, or reinitializes an existing message box, based on the config options
2980          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2981          * The following config object properties are supported:
2982          * <pre>
2983 Property    Type             Description
2984 ----------  ---------------  ------------------------------------------------------------------------------------
2985 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2986                                    closes (defaults to undefined)
2987 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2988                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2989 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2990                                    progress and wait dialogs will ignore this property and always hide the
2991                                    close button as they can only be closed programmatically.
2992 cls               String           A custom CSS class to apply to the message box element
2993 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2994                                    displayed (defaults to 75)
2995 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2996                                    function will be btn (the name of the button that was clicked, if applicable,
2997                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2998                                    Progress and wait dialogs will ignore this option since they do not respond to
2999                                    user actions and can only be closed programmatically, so any required function
3000                                    should be called by the same code after it closes the dialog.
3001 icon              String           A CSS class that provides a background image to be used as an icon for
3002                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3003 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3004 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3005 modal             Boolean          False to allow user interaction with the page while the message box is
3006                                    displayed (defaults to true)
3007 msg               String           A string that will replace the existing message box body text (defaults
3008                                    to the XHTML-compliant non-breaking space character '&#160;')
3009 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3010 progress          Boolean          True to display a progress bar (defaults to false)
3011 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3012 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3013 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3014 title             String           The title text
3015 value             String           The string value to set into the active textbox element if displayed
3016 wait              Boolean          True to display a progress bar (defaults to false)
3017 width             Number           The width of the dialog in pixels
3018 </pre>
3019          *
3020          * Example usage:
3021          * <pre><code>
3022 Roo.Msg.show({
3023    title: 'Address',
3024    msg: 'Please enter your address:',
3025    width: 300,
3026    buttons: Roo.MessageBox.OKCANCEL,
3027    multiline: true,
3028    fn: saveAddress,
3029    animEl: 'addAddressBtn'
3030 });
3031 </code></pre>
3032          * @param {Object} config Configuration options
3033          * @return {Roo.MessageBox} This message box
3034          */
3035         show : function(options)
3036         {
3037             
3038             // this causes nightmares if you show one dialog after another
3039             // especially on callbacks..
3040              
3041             if(this.isVisible()){
3042                 
3043                 this.hide();
3044                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3045                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3046                 Roo.log("New Dialog Message:" +  options.msg )
3047                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3048                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3049                 
3050             }
3051             var d = this.getDialog();
3052             opt = options;
3053             d.setTitle(opt.title || "&#160;");
3054             d.closeEl.setDisplayed(opt.closable !== false);
3055             activeTextEl = textboxEl;
3056             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3057             if(opt.prompt){
3058                 if(opt.multiline){
3059                     textboxEl.hide();
3060                     textareaEl.show();
3061                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3062                         opt.multiline : this.defaultTextHeight);
3063                     activeTextEl = textareaEl;
3064                 }else{
3065                     textboxEl.show();
3066                     textareaEl.hide();
3067                 }
3068             }else{
3069                 textboxEl.hide();
3070                 textareaEl.hide();
3071             }
3072             progressEl.setDisplayed(opt.progress === true);
3073             this.updateProgress(0);
3074             activeTextEl.dom.value = opt.value || "";
3075             if(opt.prompt){
3076                 dlg.setDefaultButton(activeTextEl);
3077             }else{
3078                 var bs = opt.buttons;
3079                 var db = null;
3080                 if(bs && bs.ok){
3081                     db = buttons["ok"];
3082                 }else if(bs && bs.yes){
3083                     db = buttons["yes"];
3084                 }
3085                 dlg.setDefaultButton(db);
3086             }
3087             bwidth = updateButtons(opt.buttons);
3088             this.updateText(opt.msg);
3089             if(opt.cls){
3090                 d.el.addClass(opt.cls);
3091             }
3092             d.proxyDrag = opt.proxyDrag === true;
3093             d.modal = opt.modal !== false;
3094             d.mask = opt.modal !== false ? mask : false;
3095             if(!d.isVisible()){
3096                 // force it to the end of the z-index stack so it gets a cursor in FF
3097                 document.body.appendChild(dlg.el.dom);
3098                 d.animateTarget = null;
3099                 d.show(options.animEl);
3100             }
3101             return this;
3102         },
3103
3104         /**
3105          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3106          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3107          * and closing the message box when the process is complete.
3108          * @param {String} title The title bar text
3109          * @param {String} msg The message box body text
3110          * @return {Roo.MessageBox} This message box
3111          */
3112         progress : function(title, msg){
3113             this.show({
3114                 title : title,
3115                 msg : msg,
3116                 buttons: false,
3117                 progress:true,
3118                 closable:false,
3119                 minWidth: this.minProgressWidth,
3120                 modal : true
3121             });
3122             return this;
3123         },
3124
3125         /**
3126          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3127          * If a callback function is passed it will be called after the user clicks the button, and the
3128          * id of the button that was clicked will be passed as the only parameter to the callback
3129          * (could also be the top-right close button).
3130          * @param {String} title The title bar text
3131          * @param {String} msg The message box body text
3132          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3133          * @param {Object} scope (optional) The scope of the callback function
3134          * @return {Roo.MessageBox} This message box
3135          */
3136         alert : function(title, msg, fn, scope){
3137             this.show({
3138                 title : title,
3139                 msg : msg,
3140                 buttons: this.OK,
3141                 fn: fn,
3142                 scope : scope,
3143                 modal : true
3144             });
3145             return this;
3146         },
3147
3148         /**
3149          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3150          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3151          * You are responsible for closing the message box when the process is complete.
3152          * @param {String} msg The message box body text
3153          * @param {String} title (optional) The title bar text
3154          * @return {Roo.MessageBox} This message box
3155          */
3156         wait : function(msg, title){
3157             this.show({
3158                 title : title,
3159                 msg : msg,
3160                 buttons: false,
3161                 closable:false,
3162                 progress:true,
3163                 modal:true,
3164                 width:300,
3165                 wait:true
3166             });
3167             waitTimer = Roo.TaskMgr.start({
3168                 run: function(i){
3169                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3170                 },
3171                 interval: 1000
3172             });
3173             return this;
3174         },
3175
3176         /**
3177          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3178          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3179          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3180          * @param {String} title The title bar text
3181          * @param {String} msg The message box body text
3182          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3183          * @param {Object} scope (optional) The scope of the callback function
3184          * @return {Roo.MessageBox} This message box
3185          */
3186         confirm : function(title, msg, fn, scope){
3187             this.show({
3188                 title : title,
3189                 msg : msg,
3190                 buttons: this.YESNO,
3191                 fn: fn,
3192                 scope : scope,
3193                 modal : true
3194             });
3195             return this;
3196         },
3197
3198         /**
3199          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3200          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3201          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3202          * (could also be the top-right close button) and the text that was entered will be passed as the two
3203          * parameters to the callback.
3204          * @param {String} title The title bar text
3205          * @param {String} msg The message box body text
3206          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3207          * @param {Object} scope (optional) The scope of the callback function
3208          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3209          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3210          * @return {Roo.MessageBox} This message box
3211          */
3212         prompt : function(title, msg, fn, scope, multiline){
3213             this.show({
3214                 title : title,
3215                 msg : msg,
3216                 buttons: this.OKCANCEL,
3217                 fn: fn,
3218                 minWidth:250,
3219                 scope : scope,
3220                 prompt:true,
3221                 multiline: multiline,
3222                 modal : true
3223             });
3224             return this;
3225         },
3226
3227         /**
3228          * Button config that displays a single OK button
3229          * @type Object
3230          */
3231         OK : {ok:true},
3232         /**
3233          * Button config that displays Yes and No buttons
3234          * @type Object
3235          */
3236         YESNO : {yes:true, no:true},
3237         /**
3238          * Button config that displays OK and Cancel buttons
3239          * @type Object
3240          */
3241         OKCANCEL : {ok:true, cancel:true},
3242         /**
3243          * Button config that displays Yes, No and Cancel buttons
3244          * @type Object
3245          */
3246         YESNOCANCEL : {yes:true, no:true, cancel:true},
3247
3248         /**
3249          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3250          * @type Number
3251          */
3252         defaultTextHeight : 75,
3253         /**
3254          * The maximum width in pixels of the message box (defaults to 600)
3255          * @type Number
3256          */
3257         maxWidth : 600,
3258         /**
3259          * The minimum width in pixels of the message box (defaults to 100)
3260          * @type Number
3261          */
3262         minWidth : 100,
3263         /**
3264          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3265          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3266          * @type Number
3267          */
3268         minProgressWidth : 250,
3269         /**
3270          * An object containing the default button text strings that can be overriden for localized language support.
3271          * Supported properties are: ok, cancel, yes and no.
3272          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3273          * @type Object
3274          */
3275         buttonText : {
3276             ok : "OK",
3277             cancel : "Cancel",
3278             yes : "Yes",
3279             no : "No"
3280         }
3281     };
3282 }();
3283
3284 /**
3285  * Shorthand for {@link Roo.MessageBox}
3286  */
3287 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3288 Roo.Msg = Roo.Msg || Roo.MessageBox;
3289 /*
3290  * - LGPL
3291  *
3292  * navbar
3293  * 
3294  */
3295
3296 /**
3297  * @class Roo.bootstrap.Navbar
3298  * @extends Roo.bootstrap.Component
3299  * Bootstrap Navbar class
3300
3301  * @constructor
3302  * Create a new Navbar
3303  * @param {Object} config The config object
3304  */
3305
3306
3307 Roo.bootstrap.Navbar = function(config){
3308     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3309     
3310 };
3311
3312 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3313     
3314     
3315    
3316     // private
3317     navItems : false,
3318     loadMask : false,
3319     
3320     
3321     getAutoCreate : function(){
3322         
3323         
3324         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3325         
3326     },
3327     
3328     initEvents :function ()
3329     {
3330         //Roo.log(this.el.select('.navbar-toggle',true));
3331         this.el.select('.navbar-toggle',true).on('click', function() {
3332            // Roo.log('click');
3333             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3334         }, this);
3335         
3336         var mark = {
3337             tag: "div",
3338             cls:"x-dlg-mask"
3339         }
3340         
3341         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3342         
3343         var size = this.el.getSize();
3344         this.maskEl.setSize(size.width, size.height);
3345         this.maskEl.enableDisplayMode("block");
3346         this.maskEl.hide();
3347         
3348         if(this.loadMask){
3349             this.maskEl.show();
3350         }
3351     },
3352     
3353     
3354     getChildContainer : function()
3355     {
3356         if (this.el.select('.collapse').getCount()) {
3357             return this.el.select('.collapse',true).first();
3358         }
3359         
3360         return this.el;
3361     },
3362     
3363     mask : function()
3364     {
3365         this.maskEl.show();
3366     },
3367     
3368     unmask : function()
3369     {
3370         this.maskEl.hide();
3371     } 
3372     
3373     
3374     
3375     
3376 });
3377
3378
3379
3380  
3381
3382  /*
3383  * - LGPL
3384  *
3385  * navbar
3386  * 
3387  */
3388
3389 /**
3390  * @class Roo.bootstrap.NavSimplebar
3391  * @extends Roo.bootstrap.Navbar
3392  * Bootstrap Sidebar class
3393  *
3394  * @cfg {Boolean} inverse is inverted color
3395  * 
3396  * @cfg {String} type (nav | pills | tabs)
3397  * @cfg {Boolean} arrangement stacked | justified
3398  * @cfg {String} align (left | right) alignment
3399  * 
3400  * @cfg {Boolean} main (true|false) main nav bar? default false
3401  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3402  * 
3403  * @cfg {String} tag (header|footer|nav|div) default is nav 
3404
3405  * 
3406  * 
3407  * 
3408  * @constructor
3409  * Create a new Sidebar
3410  * @param {Object} config The config object
3411  */
3412
3413
3414 Roo.bootstrap.NavSimplebar = function(config){
3415     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3416 };
3417
3418 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3419     
3420     inverse: false,
3421     
3422     type: false,
3423     arrangement: '',
3424     align : false,
3425     
3426     
3427     
3428     main : false,
3429     
3430     
3431     tag : false,
3432     
3433     
3434     getAutoCreate : function(){
3435         
3436         
3437         var cfg = {
3438             tag : this.tag || 'div',
3439             cls : 'navbar'
3440         };
3441           
3442         
3443         cfg.cn = [
3444             {
3445                 cls: 'nav',
3446                 tag : 'ul'
3447             }
3448         ];
3449         
3450          
3451         this.type = this.type || 'nav';
3452         if (['tabs','pills'].indexOf(this.type)!==-1) {
3453             cfg.cn[0].cls += ' nav-' + this.type
3454         
3455         
3456         } else {
3457             if (this.type!=='nav') {
3458                 Roo.log('nav type must be nav/tabs/pills')
3459             }
3460             cfg.cn[0].cls += ' navbar-nav'
3461         }
3462         
3463         
3464         
3465         
3466         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3467             cfg.cn[0].cls += ' nav-' + this.arrangement;
3468         }
3469         
3470         
3471         if (this.align === 'right') {
3472             cfg.cn[0].cls += ' navbar-right';
3473         }
3474         
3475         if (this.inverse) {
3476             cfg.cls += ' navbar-inverse';
3477             
3478         }
3479         
3480         
3481         return cfg;
3482     
3483         
3484     }
3485     
3486     
3487     
3488 });
3489
3490
3491
3492  
3493
3494  
3495        /*
3496  * - LGPL
3497  *
3498  * navbar
3499  * 
3500  */
3501
3502 /**
3503  * @class Roo.bootstrap.NavHeaderbar
3504  * @extends Roo.bootstrap.NavSimplebar
3505  * Bootstrap Sidebar class
3506  *
3507  * @cfg {String} brand what is brand
3508  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3509  * @cfg {String} brand_href href of the brand
3510  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3511  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3512  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3513  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3514  * 
3515  * @constructor
3516  * Create a new Sidebar
3517  * @param {Object} config The config object
3518  */
3519
3520
3521 Roo.bootstrap.NavHeaderbar = function(config){
3522     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3523       
3524 };
3525
3526 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3527     
3528     position: '',
3529     brand: '',
3530     brand_href: false,
3531     srButton : true,
3532     autohide : false,
3533     desktopCenter : false,
3534    
3535     
3536     getAutoCreate : function(){
3537         
3538         var   cfg = {
3539             tag: this.nav || 'nav',
3540             cls: 'navbar',
3541             role: 'navigation',
3542             cn: []
3543         };
3544         
3545         var cn = cfg.cn;
3546         if (this.desktopCenter) {
3547             cn.push({cls : 'container', cn : []});
3548             cn = cn[0].cn;
3549         }
3550         
3551         if(this.srButton){
3552             cn.push({
3553                 tag: 'div',
3554                 cls: 'navbar-header',
3555                 cn: [
3556                     {
3557                         tag: 'button',
3558                         type: 'button',
3559                         cls: 'navbar-toggle',
3560                         'data-toggle': 'collapse',
3561                         cn: [
3562                             {
3563                                 tag: 'span',
3564                                 cls: 'sr-only',
3565                                 html: 'Toggle navigation'
3566                             },
3567                             {
3568                                 tag: 'span',
3569                                 cls: 'icon-bar'
3570                             },
3571                             {
3572                                 tag: 'span',
3573                                 cls: 'icon-bar'
3574                             },
3575                             {
3576                                 tag: 'span',
3577                                 cls: 'icon-bar'
3578                             }
3579                         ]
3580                     }
3581                 ]
3582             });
3583         }
3584         
3585         cn.push({
3586             tag: 'div',
3587             cls: 'collapse navbar-collapse',
3588             cn : []
3589         });
3590         
3591         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3592         
3593         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3594             cfg.cls += ' navbar-' + this.position;
3595             
3596             // tag can override this..
3597             
3598             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3599         }
3600         
3601         if (this.brand !== '') {
3602             cn[0].cn.push({
3603                 tag: 'a',
3604                 href: this.brand_href ? this.brand_href : '#',
3605                 cls: 'navbar-brand',
3606                 cn: [
3607                 this.brand
3608                 ]
3609             });
3610         }
3611         
3612         if(this.main){
3613             cfg.cls += ' main-nav';
3614         }
3615         
3616         
3617         return cfg;
3618
3619         
3620     },
3621     getHeaderChildContainer : function()
3622     {
3623         if (this.el.select('.navbar-header').getCount()) {
3624             return this.el.select('.navbar-header',true).first();
3625         }
3626         
3627         return this.getChildContainer();
3628     },
3629     
3630     
3631     initEvents : function()
3632     {
3633         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3634         
3635         if (this.autohide) {
3636             
3637             var prevScroll = 0;
3638             var ft = this.el;
3639             
3640             Roo.get(document).on('scroll',function(e) {
3641                 var ns = Roo.get(document).getScroll().top;
3642                 var os = prevScroll;
3643                 prevScroll = ns;
3644                 
3645                 if(ns > os){
3646                     ft.removeClass('slideDown');
3647                     ft.addClass('slideUp');
3648                     return;
3649                 }
3650                 ft.removeClass('slideUp');
3651                 ft.addClass('slideDown');
3652                  
3653               
3654           },this);
3655         }
3656     }    
3657     
3658 });
3659
3660
3661
3662  
3663
3664  /*
3665  * - LGPL
3666  *
3667  * navbar
3668  * 
3669  */
3670
3671 /**
3672  * @class Roo.bootstrap.NavSidebar
3673  * @extends Roo.bootstrap.Navbar
3674  * Bootstrap Sidebar class
3675  * 
3676  * @constructor
3677  * Create a new Sidebar
3678  * @param {Object} config The config object
3679  */
3680
3681
3682 Roo.bootstrap.NavSidebar = function(config){
3683     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3684 };
3685
3686 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3687     
3688     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3689     
3690     getAutoCreate : function(){
3691         
3692         
3693         return  {
3694             tag: 'div',
3695             cls: 'sidebar sidebar-nav'
3696         };
3697     
3698         
3699     }
3700     
3701     
3702     
3703 });
3704
3705
3706
3707  
3708
3709  /*
3710  * - LGPL
3711  *
3712  * nav group
3713  * 
3714  */
3715
3716 /**
3717  * @class Roo.bootstrap.NavGroup
3718  * @extends Roo.bootstrap.Component
3719  * Bootstrap NavGroup class
3720  * @cfg {String} align (left|right)
3721  * @cfg {Boolean} inverse
3722  * @cfg {String} type (nav|pills|tab) default nav
3723  * @cfg {String} navId - reference Id for navbar.
3724
3725  * 
3726  * @constructor
3727  * Create a new nav group
3728  * @param {Object} config The config object
3729  */
3730
3731 Roo.bootstrap.NavGroup = function(config){
3732     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3733     this.navItems = [];
3734    
3735     Roo.bootstrap.NavGroup.register(this);
3736      this.addEvents({
3737         /**
3738              * @event changed
3739              * Fires when the active item changes
3740              * @param {Roo.bootstrap.NavGroup} this
3741              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3742              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3743          */
3744         'changed': true
3745      });
3746     
3747 };
3748
3749 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3750     
3751     align: '',
3752     inverse: false,
3753     form: false,
3754     type: 'nav',
3755     navId : '',
3756     // private
3757     
3758     navItems : false, 
3759     
3760     getAutoCreate : function()
3761     {
3762         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3763         
3764         cfg = {
3765             tag : 'ul',
3766             cls: 'nav' 
3767         }
3768         
3769         if (['tabs','pills'].indexOf(this.type)!==-1) {
3770             cfg.cls += ' nav-' + this.type
3771         } else {
3772             if (this.type!=='nav') {
3773                 Roo.log('nav type must be nav/tabs/pills')
3774             }
3775             cfg.cls += ' navbar-nav'
3776         }
3777         
3778         if (this.parent().sidebar) {
3779             cfg = {
3780                 tag: 'ul',
3781                 cls: 'dashboard-menu sidebar-menu'
3782             }
3783             
3784             return cfg;
3785         }
3786         
3787         if (this.form === true) {
3788             cfg = {
3789                 tag: 'form',
3790                 cls: 'navbar-form'
3791             }
3792             
3793             if (this.align === 'right') {
3794                 cfg.cls += ' navbar-right';
3795             } else {
3796                 cfg.cls += ' navbar-left';
3797             }
3798         }
3799         
3800         if (this.align === 'right') {
3801             cfg.cls += ' navbar-right';
3802         }
3803         
3804         if (this.inverse) {
3805             cfg.cls += ' navbar-inverse';
3806             
3807         }
3808         
3809         
3810         return cfg;
3811     },
3812     /**
3813     * sets the active Navigation item
3814     * @param {Roo.bootstrap.NavItem} the new current navitem
3815     */
3816     setActiveItem : function(item)
3817     {
3818         var prev = false;
3819         Roo.each(this.navItems, function(v){
3820             if (v == item) {
3821                 return ;
3822             }
3823             if (v.isActive()) {
3824                 v.setActive(false, true);
3825                 prev = v;
3826                 
3827             }
3828             
3829         });
3830
3831         item.setActive(true, true);
3832         this.fireEvent('changed', this, item, prev);
3833         
3834         
3835     },
3836     /**
3837     * gets the active Navigation item
3838     * @return {Roo.bootstrap.NavItem} the current navitem
3839     */
3840     getActive : function()
3841     {
3842         
3843         var prev = false;
3844         Roo.each(this.navItems, function(v){
3845             
3846             if (v.isActive()) {
3847                 prev = v;
3848                 
3849             }
3850             
3851         });
3852         return prev;
3853     },
3854     
3855     indexOfNav : function()
3856     {
3857         
3858         var prev = false;
3859         Roo.each(this.navItems, function(v,i){
3860             
3861             if (v.isActive()) {
3862                 prev = i;
3863                 
3864             }
3865             
3866         });
3867         return prev;
3868     },
3869     /**
3870     * adds a Navigation item
3871     * @param {Roo.bootstrap.NavItem} the navitem to add
3872     */
3873     addItem : function(cfg)
3874     {
3875         var cn = new Roo.bootstrap.NavItem(cfg);
3876         this.register(cn);
3877         cn.parentId = this.id;
3878         cn.onRender(this.el, null);
3879         return cn;
3880     },
3881     /**
3882     * register a Navigation item
3883     * @param {Roo.bootstrap.NavItem} the navitem to add
3884     */
3885     register : function(item)
3886     {
3887         this.navItems.push( item);
3888         item.navId = this.navId;
3889     
3890     },
3891     
3892     /**
3893     * clear all the Navigation item
3894     */
3895    
3896     clearAll : function()
3897     {
3898         this.navItems = [];
3899         this.el.dom.innerHTML = '';
3900     },
3901     
3902     getNavItem: function(tabId)
3903     {
3904         var ret = false;
3905         Roo.each(this.navItems, function(e) {
3906             if (e.tabId == tabId) {
3907                ret =  e;
3908                return false;
3909             }
3910             return true;
3911             
3912         });
3913         return ret;
3914     },
3915     
3916     setActiveNext : function()
3917     {
3918         var i = this.indexOfNav(this.getActive());
3919         if (i > this.navItems.length) {
3920             return;
3921         }
3922         this.setActiveItem(this.navItems[i+1]);
3923     },
3924     setActivePrev : function()
3925     {
3926         var i = this.indexOfNav(this.getActive());
3927         if (i  < 1) {
3928             return;
3929         }
3930         this.setActiveItem(this.navItems[i-1]);
3931     },
3932     clearWasActive : function(except) {
3933         Roo.each(this.navItems, function(e) {
3934             if (e.tabId != except.tabId && e.was_active) {
3935                e.was_active = false;
3936                return false;
3937             }
3938             return true;
3939             
3940         });
3941     },
3942     getWasActive : function ()
3943     {
3944         var r = false;
3945         Roo.each(this.navItems, function(e) {
3946             if (e.was_active) {
3947                r = e;
3948                return false;
3949             }
3950             return true;
3951             
3952         });
3953         return r;
3954     }
3955     
3956     
3957 });
3958
3959  
3960 Roo.apply(Roo.bootstrap.NavGroup, {
3961     
3962     groups: {},
3963      /**
3964     * register a Navigation Group
3965     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3966     */
3967     register : function(navgrp)
3968     {
3969         this.groups[navgrp.navId] = navgrp;
3970         
3971     },
3972     /**
3973     * fetch a Navigation Group based on the navigation ID
3974     * @param {string} the navgroup to add
3975     * @returns {Roo.bootstrap.NavGroup} the navgroup 
3976     */
3977     get: function(navId) {
3978         if (typeof(this.groups[navId]) == 'undefined') {
3979             return false;
3980             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3981         }
3982         return this.groups[navId] ;
3983     }
3984     
3985     
3986     
3987 });
3988
3989  /*
3990  * - LGPL
3991  *
3992  * row
3993  * 
3994  */
3995
3996 /**
3997  * @class Roo.bootstrap.NavItem
3998  * @extends Roo.bootstrap.Component
3999  * Bootstrap Navbar.NavItem class
4000  * @cfg {String} href  link to
4001  * @cfg {String} html content of button
4002  * @cfg {String} badge text inside badge
4003  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4004  * @cfg {String} glyphicon name of glyphicon
4005  * @cfg {String} icon name of font awesome icon
4006  * @cfg {Boolean} active Is item active
4007  * @cfg {Boolean} disabled Is item disabled
4008  
4009  * @cfg {Boolean} preventDefault (true | false) default false
4010  * @cfg {String} tabId the tab that this item activates.
4011  * @cfg {String} tagtype (a|span) render as a href or span?
4012  * @cfg {Boolean} animateRef (true|false) link to element default false  
4013   
4014  * @constructor
4015  * Create a new Navbar Item
4016  * @param {Object} config The config object
4017  */
4018 Roo.bootstrap.NavItem = function(config){
4019     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4020     this.addEvents({
4021         // raw events
4022         /**
4023          * @event click
4024          * The raw click event for the entire grid.
4025          * @param {Roo.EventObject} e
4026          */
4027         "click" : true,
4028          /**
4029             * @event changed
4030             * Fires when the active item active state changes
4031             * @param {Roo.bootstrap.NavItem} this
4032             * @param {boolean} state the new state
4033              
4034          */
4035         'changed': true,
4036         /**
4037             * @event scrollto
4038             * Fires when scroll to element
4039             * @param {Roo.bootstrap.NavItem} this
4040             * @param {Object} options
4041             * @param {Roo.EventObject} e
4042              
4043          */
4044         'scrollto': true
4045     });
4046    
4047 };
4048
4049 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4050     
4051     href: false,
4052     html: '',
4053     badge: '',
4054     icon: false,
4055     glyphicon: false,
4056     active: false,
4057     preventDefault : false,
4058     tabId : false,
4059     tagtype : 'a',
4060     disabled : false,
4061     animateRef : false,
4062     was_active : false,
4063     
4064     getAutoCreate : function(){
4065          
4066         var cfg = {
4067             tag: 'li',
4068             cls: 'nav-item'
4069             
4070         }
4071         if (this.active) {
4072             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4073         }
4074         if (this.disabled) {
4075             cfg.cls += ' disabled';
4076         }
4077         
4078         if (this.href || this.html || this.glyphicon || this.icon) {
4079             cfg.cn = [
4080                 {
4081                     tag: this.tagtype,
4082                     href : this.href || "#",
4083                     html: this.html || ''
4084                 }
4085             ];
4086             
4087             if (this.icon) {
4088                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4089             }
4090
4091             if(this.glyphicon) {
4092                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4093             }
4094             
4095             if (this.menu) {
4096                 
4097                 cfg.cn[0].html += " <span class='caret'></span>";
4098              
4099             }
4100             
4101             if (this.badge !== '') {
4102                  
4103                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4104             }
4105         }
4106         
4107         
4108         
4109         return cfg;
4110     },
4111     initEvents: function() 
4112     {
4113         if (typeof (this.menu) != 'undefined') {
4114             this.menu.parentType = this.xtype;
4115             this.menu.triggerEl = this.el;
4116             this.menu = this.addxtype(Roo.apply({}, this.menu));
4117         }
4118         
4119         this.el.select('a',true).on('click', this.onClick, this);
4120         
4121         if(this.tagtype == 'span'){
4122             this.el.select('span',true).on('click', this.onClick, this);
4123         }
4124        
4125         // at this point parent should be available..
4126         this.parent().register(this);
4127     },
4128     
4129     onClick : function(e)
4130     {
4131         if(
4132                 this.preventDefault || 
4133                 this.href == '#' 
4134         ){
4135             
4136             e.preventDefault();
4137         }
4138         
4139         if (this.disabled) {
4140             return;
4141         }
4142         
4143         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4144         if (tg && tg.transition) {
4145             Roo.log("waiting for the transitionend");
4146             return;
4147         }
4148         
4149         
4150         
4151         //Roo.log("fire event clicked");
4152         if(this.fireEvent('click', this, e) === false){
4153             return;
4154         };
4155         
4156         if(this.tagtype == 'span'){
4157             return;
4158         }
4159         
4160         //Roo.log(this.href);
4161         var ael = this.el.select('a',true).first();
4162         //Roo.log(ael);
4163         
4164         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4165             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4166             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4167                 return; // ignore... - it's a 'hash' to another page.
4168             }
4169             
4170             e.preventDefault();
4171             this.scrollToElement(e);
4172         }
4173         
4174         
4175         var p =  this.parent();
4176    
4177         if (['tabs','pills'].indexOf(p.type)!==-1) {
4178             if (typeof(p.setActiveItem) !== 'undefined') {
4179                 p.setActiveItem(this);
4180             }
4181         }
4182         
4183         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4184         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4185             // remove the collapsed menu expand...
4186             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4187         }
4188     },
4189     
4190     isActive: function () {
4191         return this.active
4192     },
4193     setActive : function(state, fire, is_was_active)
4194     {
4195         if (this.active && !state && this.navId) {
4196             this.was_active = true;
4197             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4198             if (nv) {
4199                 nv.clearWasActive(this);
4200             }
4201             
4202         }
4203         this.active = state;
4204         
4205         if (!state ) {
4206             this.el.removeClass('active');
4207         } else if (!this.el.hasClass('active')) {
4208             this.el.addClass('active');
4209         }
4210         if (fire) {
4211             this.fireEvent('changed', this, state);
4212         }
4213         
4214         // show a panel if it's registered and related..
4215         
4216         if (!this.navId || !this.tabId || !state || is_was_active) {
4217             return;
4218         }
4219         
4220         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4221         if (!tg) {
4222             return;
4223         }
4224         var pan = tg.getPanelByName(this.tabId);
4225         if (!pan) {
4226             return;
4227         }
4228         // if we can not flip to new panel - go back to old nav highlight..
4229         if (false == tg.showPanel(pan)) {
4230             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4231             if (nv) {
4232                 var onav = nv.getWasActive();
4233                 if (onav) {
4234                     onav.setActive(true, false, true);
4235                 }
4236             }
4237             
4238         }
4239         
4240         
4241         
4242     },
4243      // this should not be here...
4244     setDisabled : function(state)
4245     {
4246         this.disabled = state;
4247         if (!state ) {
4248             this.el.removeClass('disabled');
4249         } else if (!this.el.hasClass('disabled')) {
4250             this.el.addClass('disabled');
4251         }
4252         
4253     },
4254     
4255     /**
4256      * Fetch the element to display the tooltip on.
4257      * @return {Roo.Element} defaults to this.el
4258      */
4259     tooltipEl : function()
4260     {
4261         return this.el.select('' + this.tagtype + '', true).first();
4262     },
4263     
4264     scrollToElement : function(e)
4265     {
4266         var c = document.body;
4267         
4268         /*
4269          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4270          */
4271         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4272             c = document.documentElement;
4273         }
4274         
4275         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4276         
4277         if(!target){
4278             return;
4279         }
4280
4281         var o = target.calcOffsetsTo(c);
4282         
4283         var options = {
4284             target : target,
4285             value : o[1]
4286         }
4287         
4288         this.fireEvent('scrollto', this, options, e);
4289         
4290         Roo.get(c).scrollTo('top', options.value, true);
4291         
4292         return;
4293     }
4294 });
4295  
4296
4297  /*
4298  * - LGPL
4299  *
4300  * sidebar item
4301  *
4302  *  li
4303  *    <span> icon </span>
4304  *    <span> text </span>
4305  *    <span>badge </span>
4306  */
4307
4308 /**
4309  * @class Roo.bootstrap.NavSidebarItem
4310  * @extends Roo.bootstrap.NavItem
4311  * Bootstrap Navbar.NavSidebarItem class
4312  * @constructor
4313  * Create a new Navbar Button
4314  * @param {Object} config The config object
4315  */
4316 Roo.bootstrap.NavSidebarItem = function(config){
4317     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4318     this.addEvents({
4319         // raw events
4320         /**
4321          * @event click
4322          * The raw click event for the entire grid.
4323          * @param {Roo.EventObject} e
4324          */
4325         "click" : true,
4326          /**
4327             * @event changed
4328             * Fires when the active item active state changes
4329             * @param {Roo.bootstrap.NavSidebarItem} this
4330             * @param {boolean} state the new state
4331              
4332          */
4333         'changed': true
4334     });
4335    
4336 };
4337
4338 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4339     
4340     
4341     getAutoCreate : function(){
4342         
4343         
4344         var a = {
4345                 tag: 'a',
4346                 href : this.href || '#',
4347                 cls: '',
4348                 html : '',
4349                 cn : []
4350         };
4351         var cfg = {
4352             tag: 'li',
4353             cls: '',
4354             cn: [ a ]
4355         }
4356         var span = {
4357             tag: 'span',
4358             html : this.html || ''
4359         }
4360         
4361         
4362         if (this.active) {
4363             cfg.cls += ' active';
4364         }
4365         
4366         // left icon..
4367         if (this.glyphicon || this.icon) {
4368             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4369             a.cn.push({ tag : 'i', cls : c }) ;
4370         }
4371         // html..
4372         a.cn.push(span);
4373         // then badge..
4374         if (this.badge !== '') {
4375             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
4376         }
4377         // fi
4378         if (this.menu) {
4379             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4380             a.cls += 'dropdown-toggle treeview' ;
4381             
4382         }
4383         
4384         
4385         
4386         return cfg;
4387          
4388            
4389     }
4390    
4391      
4392  
4393 });
4394  
4395
4396  /*
4397  * - LGPL
4398  *
4399  * row
4400  * 
4401  */
4402
4403 /**
4404  * @class Roo.bootstrap.Row
4405  * @extends Roo.bootstrap.Component
4406  * Bootstrap Row class (contains columns...)
4407  * 
4408  * @constructor
4409  * Create a new Row
4410  * @param {Object} config The config object
4411  */
4412
4413 Roo.bootstrap.Row = function(config){
4414     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4415 };
4416
4417 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4418     
4419     getAutoCreate : function(){
4420        return {
4421             cls: 'row clearfix'
4422        };
4423     }
4424     
4425     
4426 });
4427
4428  
4429
4430  /*
4431  * - LGPL
4432  *
4433  * element
4434  * 
4435  */
4436
4437 /**
4438  * @class Roo.bootstrap.Element
4439  * @extends Roo.bootstrap.Component
4440  * Bootstrap Element class
4441  * @cfg {String} html contents of the element
4442  * @cfg {String} tag tag of the element
4443  * @cfg {String} cls class of the element
4444  * @cfg {Boolean} preventDefault (true|false) default false
4445  * @cfg {Boolean} clickable (true|false) default false
4446  * 
4447  * @constructor
4448  * Create a new Element
4449  * @param {Object} config The config object
4450  */
4451
4452 Roo.bootstrap.Element = function(config){
4453     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4454     
4455     this.addEvents({
4456         // raw events
4457         /**
4458          * @event click
4459          * When a element is chick
4460          * @param {Roo.bootstrap.Element} this
4461          * @param {Roo.EventObject} e
4462          */
4463         "click" : true
4464     });
4465 };
4466
4467 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4468     
4469     tag: 'div',
4470     cls: '',
4471     html: '',
4472     preventDefault: false, 
4473     clickable: false,
4474     
4475     getAutoCreate : function(){
4476         
4477         var cfg = {
4478             tag: this.tag,
4479             cls: this.cls,
4480             html: this.html
4481         }
4482         
4483         return cfg;
4484     },
4485     
4486     initEvents: function() 
4487     {
4488         Roo.bootstrap.Element.superclass.initEvents.call(this);
4489         
4490         if(this.clickable){
4491             this.el.on('click', this.onClick, this);
4492         }
4493         
4494     },
4495     
4496     onClick : function(e)
4497     {
4498         if(this.preventDefault){
4499             e.preventDefault();
4500         }
4501         
4502         this.fireEvent('click', this, e);
4503     },
4504     
4505     getValue : function()
4506     {
4507         return this.el.dom.innerHTML;
4508     },
4509     
4510     setValue : function(value)
4511     {
4512         this.el.dom.innerHTML = value;
4513     }
4514    
4515 });
4516
4517  
4518
4519  /*
4520  * - LGPL
4521  *
4522  * pagination
4523  * 
4524  */
4525
4526 /**
4527  * @class Roo.bootstrap.Pagination
4528  * @extends Roo.bootstrap.Component
4529  * Bootstrap Pagination class
4530  * @cfg {String} size xs | sm | md | lg
4531  * @cfg {Boolean} inverse false | true
4532  * 
4533  * @constructor
4534  * Create a new Pagination
4535  * @param {Object} config The config object
4536  */
4537
4538 Roo.bootstrap.Pagination = function(config){
4539     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4540 };
4541
4542 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4543     
4544     cls: false,
4545     size: false,
4546     inverse: false,
4547     
4548     getAutoCreate : function(){
4549         var cfg = {
4550             tag: 'ul',
4551                 cls: 'pagination'
4552         };
4553         if (this.inverse) {
4554             cfg.cls += ' inverse';
4555         }
4556         if (this.html) {
4557             cfg.html=this.html;
4558         }
4559         if (this.cls) {
4560             cfg.cls += " " + this.cls;
4561         }
4562         return cfg;
4563     }
4564    
4565 });
4566
4567  
4568
4569  /*
4570  * - LGPL
4571  *
4572  * Pagination item
4573  * 
4574  */
4575
4576
4577 /**
4578  * @class Roo.bootstrap.PaginationItem
4579  * @extends Roo.bootstrap.Component
4580  * Bootstrap PaginationItem class
4581  * @cfg {String} html text
4582  * @cfg {String} href the link
4583  * @cfg {Boolean} preventDefault (true | false) default true
4584  * @cfg {Boolean} active (true | false) default false
4585  * @cfg {Boolean} disabled default false
4586  * 
4587  * 
4588  * @constructor
4589  * Create a new PaginationItem
4590  * @param {Object} config The config object
4591  */
4592
4593
4594 Roo.bootstrap.PaginationItem = function(config){
4595     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4596     this.addEvents({
4597         // raw events
4598         /**
4599          * @event click
4600          * The raw click event for the entire grid.
4601          * @param {Roo.EventObject} e
4602          */
4603         "click" : true
4604     });
4605 };
4606
4607 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4608     
4609     href : false,
4610     html : false,
4611     preventDefault: true,
4612     active : false,
4613     cls : false,
4614     disabled: false,
4615     
4616     getAutoCreate : function(){
4617         var cfg= {
4618             tag: 'li',
4619             cn: [
4620                 {
4621                     tag : 'a',
4622                     href : this.href ? this.href : '#',
4623                     html : this.html ? this.html : ''
4624                 }
4625             ]
4626         };
4627         
4628         if(this.cls){
4629             cfg.cls = this.cls;
4630         }
4631         
4632         if(this.disabled){
4633             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4634         }
4635         
4636         if(this.active){
4637             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4638         }
4639         
4640         return cfg;
4641     },
4642     
4643     initEvents: function() {
4644         
4645         this.el.on('click', this.onClick, this);
4646         
4647     },
4648     onClick : function(e)
4649     {
4650         Roo.log('PaginationItem on click ');
4651         if(this.preventDefault){
4652             e.preventDefault();
4653         }
4654         
4655         if(this.disabled){
4656             return;
4657         }
4658         
4659         this.fireEvent('click', this, e);
4660     }
4661    
4662 });
4663
4664  
4665
4666  /*
4667  * - LGPL
4668  *
4669  * slider
4670  * 
4671  */
4672
4673
4674 /**
4675  * @class Roo.bootstrap.Slider
4676  * @extends Roo.bootstrap.Component
4677  * Bootstrap Slider class
4678  *    
4679  * @constructor
4680  * Create a new Slider
4681  * @param {Object} config The config object
4682  */
4683
4684 Roo.bootstrap.Slider = function(config){
4685     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4686 };
4687
4688 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4689     
4690     getAutoCreate : function(){
4691         
4692         var cfg = {
4693             tag: 'div',
4694             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4695             cn: [
4696                 {
4697                     tag: 'a',
4698                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4699                 }
4700             ]
4701         }
4702         
4703         return cfg;
4704     }
4705    
4706 });
4707
4708  /*
4709  * Based on:
4710  * Ext JS Library 1.1.1
4711  * Copyright(c) 2006-2007, Ext JS, LLC.
4712  *
4713  * Originally Released Under LGPL - original licence link has changed is not relivant.
4714  *
4715  * Fork - LGPL
4716  * <script type="text/javascript">
4717  */
4718  
4719
4720 /**
4721  * @class Roo.grid.ColumnModel
4722  * @extends Roo.util.Observable
4723  * This is the default implementation of a ColumnModel used by the Grid. It defines
4724  * the columns in the grid.
4725  * <br>Usage:<br>
4726  <pre><code>
4727  var colModel = new Roo.grid.ColumnModel([
4728         {header: "Ticker", width: 60, sortable: true, locked: true},
4729         {header: "Company Name", width: 150, sortable: true},
4730         {header: "Market Cap.", width: 100, sortable: true},
4731         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4732         {header: "Employees", width: 100, sortable: true, resizable: false}
4733  ]);
4734  </code></pre>
4735  * <p>
4736  
4737  * The config options listed for this class are options which may appear in each
4738  * individual column definition.
4739  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4740  * @constructor
4741  * @param {Object} config An Array of column config objects. See this class's
4742  * config objects for details.
4743 */
4744 Roo.grid.ColumnModel = function(config){
4745         /**
4746      * The config passed into the constructor
4747      */
4748     this.config = config;
4749     this.lookup = {};
4750
4751     // if no id, create one
4752     // if the column does not have a dataIndex mapping,
4753     // map it to the order it is in the config
4754     for(var i = 0, len = config.length; i < len; i++){
4755         var c = config[i];
4756         if(typeof c.dataIndex == "undefined"){
4757             c.dataIndex = i;
4758         }
4759         if(typeof c.renderer == "string"){
4760             c.renderer = Roo.util.Format[c.renderer];
4761         }
4762         if(typeof c.id == "undefined"){
4763             c.id = Roo.id();
4764         }
4765         if(c.editor && c.editor.xtype){
4766             c.editor  = Roo.factory(c.editor, Roo.grid);
4767         }
4768         if(c.editor && c.editor.isFormField){
4769             c.editor = new Roo.grid.GridEditor(c.editor);
4770         }
4771         this.lookup[c.id] = c;
4772     }
4773
4774     /**
4775      * The width of columns which have no width specified (defaults to 100)
4776      * @type Number
4777      */
4778     this.defaultWidth = 100;
4779
4780     /**
4781      * Default sortable of columns which have no sortable specified (defaults to false)
4782      * @type Boolean
4783      */
4784     this.defaultSortable = false;
4785
4786     this.addEvents({
4787         /**
4788              * @event widthchange
4789              * Fires when the width of a column changes.
4790              * @param {ColumnModel} this
4791              * @param {Number} columnIndex The column index
4792              * @param {Number} newWidth The new width
4793              */
4794             "widthchange": true,
4795         /**
4796              * @event headerchange
4797              * Fires when the text of a header changes.
4798              * @param {ColumnModel} this
4799              * @param {Number} columnIndex The column index
4800              * @param {Number} newText The new header text
4801              */
4802             "headerchange": true,
4803         /**
4804              * @event hiddenchange
4805              * Fires when a column is hidden or "unhidden".
4806              * @param {ColumnModel} this
4807              * @param {Number} columnIndex The column index
4808              * @param {Boolean} hidden true if hidden, false otherwise
4809              */
4810             "hiddenchange": true,
4811             /**
4812          * @event columnmoved
4813          * Fires when a column is moved.
4814          * @param {ColumnModel} this
4815          * @param {Number} oldIndex
4816          * @param {Number} newIndex
4817          */
4818         "columnmoved" : true,
4819         /**
4820          * @event columlockchange
4821          * Fires when a column's locked state is changed
4822          * @param {ColumnModel} this
4823          * @param {Number} colIndex
4824          * @param {Boolean} locked true if locked
4825          */
4826         "columnlockchange" : true
4827     });
4828     Roo.grid.ColumnModel.superclass.constructor.call(this);
4829 };
4830 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4831     /**
4832      * @cfg {String} header The header text to display in the Grid view.
4833      */
4834     /**
4835      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4836      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4837      * specified, the column's index is used as an index into the Record's data Array.
4838      */
4839     /**
4840      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4841      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4842      */
4843     /**
4844      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4845      * Defaults to the value of the {@link #defaultSortable} property.
4846      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4847      */
4848     /**
4849      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4850      */
4851     /**
4852      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4853      */
4854     /**
4855      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4856      */
4857     /**
4858      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4859      */
4860     /**
4861      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4862      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4863      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4864      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4865      */
4866        /**
4867      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4868      */
4869     /**
4870      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4871      */
4872     /**
4873      * @cfg {String} cursor (Optional)
4874      */
4875     /**
4876      * @cfg {String} tooltip (Optional)
4877      */
4878     /**
4879      * Returns the id of the column at the specified index.
4880      * @param {Number} index The column index
4881      * @return {String} the id
4882      */
4883     getColumnId : function(index){
4884         return this.config[index].id;
4885     },
4886
4887     /**
4888      * Returns the column for a specified id.
4889      * @param {String} id The column id
4890      * @return {Object} the column
4891      */
4892     getColumnById : function(id){
4893         return this.lookup[id];
4894     },
4895
4896     
4897     /**
4898      * Returns the column for a specified dataIndex.
4899      * @param {String} dataIndex The column dataIndex
4900      * @return {Object|Boolean} the column or false if not found
4901      */
4902     getColumnByDataIndex: function(dataIndex){
4903         var index = this.findColumnIndex(dataIndex);
4904         return index > -1 ? this.config[index] : false;
4905     },
4906     
4907     /**
4908      * Returns the index for a specified column id.
4909      * @param {String} id The column id
4910      * @return {Number} the index, or -1 if not found
4911      */
4912     getIndexById : function(id){
4913         for(var i = 0, len = this.config.length; i < len; i++){
4914             if(this.config[i].id == id){
4915                 return i;
4916             }
4917         }
4918         return -1;
4919     },
4920     
4921     /**
4922      * Returns the index for a specified column dataIndex.
4923      * @param {String} dataIndex The column dataIndex
4924      * @return {Number} the index, or -1 if not found
4925      */
4926     
4927     findColumnIndex : function(dataIndex){
4928         for(var i = 0, len = this.config.length; i < len; i++){
4929             if(this.config[i].dataIndex == dataIndex){
4930                 return i;
4931             }
4932         }
4933         return -1;
4934     },
4935     
4936     
4937     moveColumn : function(oldIndex, newIndex){
4938         var c = this.config[oldIndex];
4939         this.config.splice(oldIndex, 1);
4940         this.config.splice(newIndex, 0, c);
4941         this.dataMap = null;
4942         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4943     },
4944
4945     isLocked : function(colIndex){
4946         return this.config[colIndex].locked === true;
4947     },
4948
4949     setLocked : function(colIndex, value, suppressEvent){
4950         if(this.isLocked(colIndex) == value){
4951             return;
4952         }
4953         this.config[colIndex].locked = value;
4954         if(!suppressEvent){
4955             this.fireEvent("columnlockchange", this, colIndex, value);
4956         }
4957     },
4958
4959     getTotalLockedWidth : function(){
4960         var totalWidth = 0;
4961         for(var i = 0; i < this.config.length; i++){
4962             if(this.isLocked(i) && !this.isHidden(i)){
4963                 this.totalWidth += this.getColumnWidth(i);
4964             }
4965         }
4966         return totalWidth;
4967     },
4968
4969     getLockedCount : function(){
4970         for(var i = 0, len = this.config.length; i < len; i++){
4971             if(!this.isLocked(i)){
4972                 return i;
4973             }
4974         }
4975     },
4976
4977     /**
4978      * Returns the number of columns.
4979      * @return {Number}
4980      */
4981     getColumnCount : function(visibleOnly){
4982         if(visibleOnly === true){
4983             var c = 0;
4984             for(var i = 0, len = this.config.length; i < len; i++){
4985                 if(!this.isHidden(i)){
4986                     c++;
4987                 }
4988             }
4989             return c;
4990         }
4991         return this.config.length;
4992     },
4993
4994     /**
4995      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4996      * @param {Function} fn
4997      * @param {Object} scope (optional)
4998      * @return {Array} result
4999      */
5000     getColumnsBy : function(fn, scope){
5001         var r = [];
5002         for(var i = 0, len = this.config.length; i < len; i++){
5003             var c = this.config[i];
5004             if(fn.call(scope||this, c, i) === true){
5005                 r[r.length] = c;
5006             }
5007         }
5008         return r;
5009     },
5010
5011     /**
5012      * Returns true if the specified column is sortable.
5013      * @param {Number} col The column index
5014      * @return {Boolean}
5015      */
5016     isSortable : function(col){
5017         if(typeof this.config[col].sortable == "undefined"){
5018             return this.defaultSortable;
5019         }
5020         return this.config[col].sortable;
5021     },
5022
5023     /**
5024      * Returns the rendering (formatting) function defined for the column.
5025      * @param {Number} col The column index.
5026      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5027      */
5028     getRenderer : function(col){
5029         if(!this.config[col].renderer){
5030             return Roo.grid.ColumnModel.defaultRenderer;
5031         }
5032         return this.config[col].renderer;
5033     },
5034
5035     /**
5036      * Sets the rendering (formatting) function for a column.
5037      * @param {Number} col The column index
5038      * @param {Function} fn The function to use to process the cell's raw data
5039      * to return HTML markup for the grid view. The render function is called with
5040      * the following parameters:<ul>
5041      * <li>Data value.</li>
5042      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5043      * <li>css A CSS style string to apply to the table cell.</li>
5044      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5045      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5046      * <li>Row index</li>
5047      * <li>Column index</li>
5048      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5049      */
5050     setRenderer : function(col, fn){
5051         this.config[col].renderer = fn;
5052     },
5053
5054     /**
5055      * Returns the width for the specified column.
5056      * @param {Number} col The column index
5057      * @return {Number}
5058      */
5059     getColumnWidth : function(col){
5060         return this.config[col].width * 1 || this.defaultWidth;
5061     },
5062
5063     /**
5064      * Sets the width for a column.
5065      * @param {Number} col The column index
5066      * @param {Number} width The new width
5067      */
5068     setColumnWidth : function(col, width, suppressEvent){
5069         this.config[col].width = width;
5070         this.totalWidth = null;
5071         if(!suppressEvent){
5072              this.fireEvent("widthchange", this, col, width);
5073         }
5074     },
5075
5076     /**
5077      * Returns the total width of all columns.
5078      * @param {Boolean} includeHidden True to include hidden column widths
5079      * @return {Number}
5080      */
5081     getTotalWidth : function(includeHidden){
5082         if(!this.totalWidth){
5083             this.totalWidth = 0;
5084             for(var i = 0, len = this.config.length; i < len; i++){
5085                 if(includeHidden || !this.isHidden(i)){
5086                     this.totalWidth += this.getColumnWidth(i);
5087                 }
5088             }
5089         }
5090         return this.totalWidth;
5091     },
5092
5093     /**
5094      * Returns the header for the specified column.
5095      * @param {Number} col The column index
5096      * @return {String}
5097      */
5098     getColumnHeader : function(col){
5099         return this.config[col].header;
5100     },
5101
5102     /**
5103      * Sets the header for a column.
5104      * @param {Number} col The column index
5105      * @param {String} header The new header
5106      */
5107     setColumnHeader : function(col, header){
5108         this.config[col].header = header;
5109         this.fireEvent("headerchange", this, col, header);
5110     },
5111
5112     /**
5113      * Returns the tooltip for the specified column.
5114      * @param {Number} col The column index
5115      * @return {String}
5116      */
5117     getColumnTooltip : function(col){
5118             return this.config[col].tooltip;
5119     },
5120     /**
5121      * Sets the tooltip for a column.
5122      * @param {Number} col The column index
5123      * @param {String} tooltip The new tooltip
5124      */
5125     setColumnTooltip : function(col, tooltip){
5126             this.config[col].tooltip = tooltip;
5127     },
5128
5129     /**
5130      * Returns the dataIndex for the specified column.
5131      * @param {Number} col The column index
5132      * @return {Number}
5133      */
5134     getDataIndex : function(col){
5135         return this.config[col].dataIndex;
5136     },
5137
5138     /**
5139      * Sets the dataIndex for a column.
5140      * @param {Number} col The column index
5141      * @param {Number} dataIndex The new dataIndex
5142      */
5143     setDataIndex : function(col, dataIndex){
5144         this.config[col].dataIndex = dataIndex;
5145     },
5146
5147     
5148     
5149     /**
5150      * Returns true if the cell is editable.
5151      * @param {Number} colIndex The column index
5152      * @param {Number} rowIndex The row index
5153      * @return {Boolean}
5154      */
5155     isCellEditable : function(colIndex, rowIndex){
5156         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5157     },
5158
5159     /**
5160      * Returns the editor defined for the cell/column.
5161      * return false or null to disable editing.
5162      * @param {Number} colIndex The column index
5163      * @param {Number} rowIndex The row index
5164      * @return {Object}
5165      */
5166     getCellEditor : function(colIndex, rowIndex){
5167         return this.config[colIndex].editor;
5168     },
5169
5170     /**
5171      * Sets if a column is editable.
5172      * @param {Number} col The column index
5173      * @param {Boolean} editable True if the column is editable
5174      */
5175     setEditable : function(col, editable){
5176         this.config[col].editable = editable;
5177     },
5178
5179
5180     /**
5181      * Returns true if the column is hidden.
5182      * @param {Number} colIndex The column index
5183      * @return {Boolean}
5184      */
5185     isHidden : function(colIndex){
5186         return this.config[colIndex].hidden;
5187     },
5188
5189
5190     /**
5191      * Returns true if the column width cannot be changed
5192      */
5193     isFixed : function(colIndex){
5194         return this.config[colIndex].fixed;
5195     },
5196
5197     /**
5198      * Returns true if the column can be resized
5199      * @return {Boolean}
5200      */
5201     isResizable : function(colIndex){
5202         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5203     },
5204     /**
5205      * Sets if a column is hidden.
5206      * @param {Number} colIndex The column index
5207      * @param {Boolean} hidden True if the column is hidden
5208      */
5209     setHidden : function(colIndex, hidden){
5210         this.config[colIndex].hidden = hidden;
5211         this.totalWidth = null;
5212         this.fireEvent("hiddenchange", this, colIndex, hidden);
5213     },
5214
5215     /**
5216      * Sets the editor for a column.
5217      * @param {Number} col The column index
5218      * @param {Object} editor The editor object
5219      */
5220     setEditor : function(col, editor){
5221         this.config[col].editor = editor;
5222     }
5223 });
5224
5225 Roo.grid.ColumnModel.defaultRenderer = function(value){
5226         if(typeof value == "string" && value.length < 1){
5227             return "&#160;";
5228         }
5229         return value;
5230 };
5231
5232 // Alias for backwards compatibility
5233 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5234 /*
5235  * Based on:
5236  * Ext JS Library 1.1.1
5237  * Copyright(c) 2006-2007, Ext JS, LLC.
5238  *
5239  * Originally Released Under LGPL - original licence link has changed is not relivant.
5240  *
5241  * Fork - LGPL
5242  * <script type="text/javascript">
5243  */
5244  
5245 /**
5246  * @class Roo.LoadMask
5247  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5248  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5249  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5250  * element's UpdateManager load indicator and will be destroyed after the initial load.
5251  * @constructor
5252  * Create a new LoadMask
5253  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5254  * @param {Object} config The config object
5255  */
5256 Roo.LoadMask = function(el, config){
5257     this.el = Roo.get(el);
5258     Roo.apply(this, config);
5259     if(this.store){
5260         this.store.on('beforeload', this.onBeforeLoad, this);
5261         this.store.on('load', this.onLoad, this);
5262         this.store.on('loadexception', this.onLoadException, this);
5263         this.removeMask = false;
5264     }else{
5265         var um = this.el.getUpdateManager();
5266         um.showLoadIndicator = false; // disable the default indicator
5267         um.on('beforeupdate', this.onBeforeLoad, this);
5268         um.on('update', this.onLoad, this);
5269         um.on('failure', this.onLoad, this);
5270         this.removeMask = true;
5271     }
5272 };
5273
5274 Roo.LoadMask.prototype = {
5275     /**
5276      * @cfg {Boolean} removeMask
5277      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5278      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5279      */
5280     /**
5281      * @cfg {String} msg
5282      * The text to display in a centered loading message box (defaults to 'Loading...')
5283      */
5284     msg : 'Loading...',
5285     /**
5286      * @cfg {String} msgCls
5287      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5288      */
5289     msgCls : 'x-mask-loading',
5290
5291     /**
5292      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5293      * @type Boolean
5294      */
5295     disabled: false,
5296
5297     /**
5298      * Disables the mask to prevent it from being displayed
5299      */
5300     disable : function(){
5301        this.disabled = true;
5302     },
5303
5304     /**
5305      * Enables the mask so that it can be displayed
5306      */
5307     enable : function(){
5308         this.disabled = false;
5309     },
5310     
5311     onLoadException : function()
5312     {
5313         Roo.log(arguments);
5314         
5315         if (typeof(arguments[3]) != 'undefined') {
5316             Roo.MessageBox.alert("Error loading",arguments[3]);
5317         } 
5318         /*
5319         try {
5320             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5321                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5322             }   
5323         } catch(e) {
5324             
5325         }
5326         */
5327     
5328         
5329         
5330         this.el.unmask(this.removeMask);
5331     },
5332     // private
5333     onLoad : function()
5334     {
5335         this.el.unmask(this.removeMask);
5336     },
5337
5338     // private
5339     onBeforeLoad : function(){
5340         if(!this.disabled){
5341             this.el.mask(this.msg, this.msgCls);
5342         }
5343     },
5344
5345     // private
5346     destroy : function(){
5347         if(this.store){
5348             this.store.un('beforeload', this.onBeforeLoad, this);
5349             this.store.un('load', this.onLoad, this);
5350             this.store.un('loadexception', this.onLoadException, this);
5351         }else{
5352             var um = this.el.getUpdateManager();
5353             um.un('beforeupdate', this.onBeforeLoad, this);
5354             um.un('update', this.onLoad, this);
5355             um.un('failure', this.onLoad, this);
5356         }
5357     }
5358 };/*
5359  * - LGPL
5360  *
5361  * table
5362  * 
5363  */
5364
5365 /**
5366  * @class Roo.bootstrap.Table
5367  * @extends Roo.bootstrap.Component
5368  * Bootstrap Table class
5369  * @cfg {String} cls table class
5370  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5371  * @cfg {String} bgcolor Specifies the background color for a table
5372  * @cfg {Number} border Specifies whether the table cells should have borders or not
5373  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5374  * @cfg {Number} cellspacing Specifies the space between cells
5375  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5376  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5377  * @cfg {String} sortable Specifies that the table should be sortable
5378  * @cfg {String} summary Specifies a summary of the content of a table
5379  * @cfg {Number} width Specifies the width of a table
5380  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5381  * 
5382  * @cfg {boolean} striped Should the rows be alternative striped
5383  * @cfg {boolean} bordered Add borders to the table
5384  * @cfg {boolean} hover Add hover highlighting
5385  * @cfg {boolean} condensed Format condensed
5386  * @cfg {boolean} responsive Format condensed
5387  * @cfg {Boolean} loadMask (true|false) default false
5388  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5389  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5390  * @cfg {Boolean} rowSelection (true|false) default false
5391  * @cfg {Boolean} cellSelection (true|false) default false
5392  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5393  
5394  * 
5395  * @constructor
5396  * Create a new Table
5397  * @param {Object} config The config object
5398  */
5399
5400 Roo.bootstrap.Table = function(config){
5401     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5402     
5403     // BC...
5404     this.rowSelection = (typeof(config.RowSelection) != 'undefined') ? config.RowSelection : this.rowSelection;
5405     this.cellSelection = (typeof(config.CellSelection) != 'undefined') ? config.CellSelection : this.cellSelection;
5406     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5407     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5408     
5409     
5410     if (this.sm) {
5411         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5412         this.sm = this.selModel;
5413         this.sm.xmodule = this.xmodule || false;
5414     }
5415     if (this.cm && typeof(this.cm.config) == 'undefined') {
5416         this.colModel = new Roo.grid.ColumnModel(this.cm);
5417         this.cm = this.colModel;
5418         this.cm.xmodule = this.xmodule || false;
5419     }
5420     if (this.store) {
5421         this.store= Roo.factory(this.store, Roo.data);
5422         this.ds = this.store;
5423         this.ds.xmodule = this.xmodule || false;
5424          
5425     }
5426     if (this.footer && this.store) {
5427         this.footer.dataSource = this.ds;
5428         this.footer = Roo.factory(this.footer);
5429     }
5430     
5431     /** @private */
5432     this.addEvents({
5433         /**
5434          * @event cellclick
5435          * Fires when a cell is clicked
5436          * @param {Roo.bootstrap.Table} this
5437          * @param {Roo.Element} el
5438          * @param {Number} rowIndex
5439          * @param {Number} columnIndex
5440          * @param {Roo.EventObject} e
5441          */
5442         "cellclick" : true,
5443         /**
5444          * @event celldblclick
5445          * Fires when a cell is double clicked
5446          * @param {Roo.bootstrap.Table} this
5447          * @param {Roo.Element} el
5448          * @param {Number} rowIndex
5449          * @param {Number} columnIndex
5450          * @param {Roo.EventObject} e
5451          */
5452         "celldblclick" : true,
5453         /**
5454          * @event rowclick
5455          * Fires when a row is clicked
5456          * @param {Roo.bootstrap.Table} this
5457          * @param {Roo.Element} el
5458          * @param {Number} rowIndex
5459          * @param {Roo.EventObject} e
5460          */
5461         "rowclick" : true,
5462         /**
5463          * @event rowdblclick
5464          * Fires when a row is double clicked
5465          * @param {Roo.bootstrap.Table} this
5466          * @param {Roo.Element} el
5467          * @param {Number} rowIndex
5468          * @param {Roo.EventObject} e
5469          */
5470         "rowdblclick" : true,
5471         /**
5472          * @event mouseover
5473          * Fires when a mouseover occur
5474          * @param {Roo.bootstrap.Table} this
5475          * @param {Roo.Element} el
5476          * @param {Number} rowIndex
5477          * @param {Number} columnIndex
5478          * @param {Roo.EventObject} e
5479          */
5480         "mouseover" : true,
5481         /**
5482          * @event mouseout
5483          * Fires when a mouseout occur
5484          * @param {Roo.bootstrap.Table} this
5485          * @param {Roo.Element} el
5486          * @param {Number} rowIndex
5487          * @param {Number} columnIndex
5488          * @param {Roo.EventObject} e
5489          */
5490         "mouseout" : true,
5491         /**
5492          * @event rowclass
5493          * Fires when a row is rendered, so you can change add a style to it.
5494          * @param {Roo.bootstrap.Table} this
5495          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5496          */
5497         'rowclass' : true,
5498           /**
5499          * @event rowsrendered
5500          * Fires when all the  rows have been rendered
5501          * @param {Roo.bootstrap.Table} this
5502          */
5503         'rowsrendered' : true
5504         
5505     });
5506 };
5507
5508 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5509     
5510     cls: false,
5511     align: false,
5512     bgcolor: false,
5513     border: false,
5514     cellpadding: false,
5515     cellspacing: false,
5516     frame: false,
5517     rules: false,
5518     sortable: false,
5519     summary: false,
5520     width: false,
5521     striped : false,
5522     bordered: false,
5523     hover:  false,
5524     condensed : false,
5525     responsive : false,
5526     sm : false,
5527     cm : false,
5528     store : false,
5529     loadMask : false,
5530     footerShow : true,
5531     headerShow : true,
5532   
5533     rowSelection : false,
5534     cellSelection : false,
5535     layout : false,
5536     
5537     // Roo.Element - the tbody
5538     mainBody: false, 
5539     
5540     getAutoCreate : function(){
5541         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5542         
5543         cfg = {
5544             tag: 'table',
5545             cls : 'table',
5546             cn : []
5547         }
5548             
5549         if (this.striped) {
5550             cfg.cls += ' table-striped';
5551         }
5552         
5553         if (this.hover) {
5554             cfg.cls += ' table-hover';
5555         }
5556         if (this.bordered) {
5557             cfg.cls += ' table-bordered';
5558         }
5559         if (this.condensed) {
5560             cfg.cls += ' table-condensed';
5561         }
5562         if (this.responsive) {
5563             cfg.cls += ' table-responsive';
5564         }
5565         
5566         if (this.cls) {
5567             cfg.cls+=  ' ' +this.cls;
5568         }
5569         
5570         // this lot should be simplifed...
5571         
5572         if (this.align) {
5573             cfg.align=this.align;
5574         }
5575         if (this.bgcolor) {
5576             cfg.bgcolor=this.bgcolor;
5577         }
5578         if (this.border) {
5579             cfg.border=this.border;
5580         }
5581         if (this.cellpadding) {
5582             cfg.cellpadding=this.cellpadding;
5583         }
5584         if (this.cellspacing) {
5585             cfg.cellspacing=this.cellspacing;
5586         }
5587         if (this.frame) {
5588             cfg.frame=this.frame;
5589         }
5590         if (this.rules) {
5591             cfg.rules=this.rules;
5592         }
5593         if (this.sortable) {
5594             cfg.sortable=this.sortable;
5595         }
5596         if (this.summary) {
5597             cfg.summary=this.summary;
5598         }
5599         if (this.width) {
5600             cfg.width=this.width;
5601         }
5602         if (this.layout) {
5603             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5604         }
5605         
5606         if(this.store || this.cm){
5607             if(this.headerShow){
5608                 cfg.cn.push(this.renderHeader());
5609             }
5610             
5611             cfg.cn.push(this.renderBody());
5612             
5613             if(this.footerShow){
5614                 cfg.cn.push(this.renderFooter());
5615             }
5616             
5617             cfg.cls+=  ' TableGrid';
5618         }
5619         
5620         return { cn : [ cfg ] };
5621     },
5622     
5623     initEvents : function()
5624     {   
5625         if(!this.store || !this.cm){
5626             return;
5627         }
5628         
5629         //Roo.log('initEvents with ds!!!!');
5630         
5631         this.mainBody = this.el.select('tbody', true).first();
5632         
5633         
5634         var _this = this;
5635         
5636         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5637             e.on('click', _this.sort, _this);
5638         });
5639         
5640         this.el.on("click", this.onClick, this);
5641         this.el.on("dblclick", this.onDblClick, this);
5642         
5643         // why is this done????? = it breaks dialogs??
5644         //this.parent().el.setStyle('position', 'relative');
5645         
5646         
5647         if (this.footer) {
5648             this.footer.parentId = this.id;
5649             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5650         }
5651         
5652         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5653         
5654         this.store.on('load', this.onLoad, this);
5655         this.store.on('beforeload', this.onBeforeLoad, this);
5656         this.store.on('update', this.onUpdate, this);
5657         this.store.on('add', this.onAdd, this);
5658         
5659     },
5660     
5661     onMouseover : function(e, el)
5662     {
5663         var cell = Roo.get(el);
5664         
5665         if(!cell){
5666             return;
5667         }
5668         
5669         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5670             cell = cell.findParent('td', false, true);
5671         }
5672         
5673         var row = cell.findParent('tr', false, true);
5674         var cellIndex = cell.dom.cellIndex;
5675         var rowIndex = row.dom.rowIndex - 1; // start from 0
5676         
5677         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5678         
5679     },
5680     
5681     onMouseout : function(e, el)
5682     {
5683         var cell = Roo.get(el);
5684         
5685         if(!cell){
5686             return;
5687         }
5688         
5689         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5690             cell = cell.findParent('td', false, true);
5691         }
5692         
5693         var row = cell.findParent('tr', false, true);
5694         var cellIndex = cell.dom.cellIndex;
5695         var rowIndex = row.dom.rowIndex - 1; // start from 0
5696         
5697         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5698         
5699     },
5700     
5701     onClick : function(e, el)
5702     {
5703         var cell = Roo.get(el);
5704         
5705         if(!cell || (!this.cellSelection && !this.rowSelection)){
5706             return;
5707         }
5708         
5709         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5710             cell = cell.findParent('td', false, true);
5711         }
5712         
5713         if(!cell || typeof(cell) == 'undefined'){
5714             return;
5715         }
5716         
5717         var row = cell.findParent('tr', false, true);
5718         
5719         if(!row || typeof(row) == 'undefined'){
5720             return;
5721         }
5722         
5723         var cellIndex = cell.dom.cellIndex;
5724         var rowIndex = this.getRowIndex(row);
5725         
5726         // why??? - should these not be based on SelectionModel?
5727         if(this.cellSelection){
5728             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5729         }
5730         
5731         if(this.rowSelection){
5732             this.fireEvent('rowclick', this, row, rowIndex, e);
5733         }
5734         
5735         
5736     },
5737     
5738     onDblClick : function(e,el)
5739     {
5740         var cell = Roo.get(el);
5741         
5742         if(!cell || (!this.CellSelection && !this.RowSelection)){
5743             return;
5744         }
5745         
5746         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5747             cell = cell.findParent('td', false, true);
5748         }
5749         
5750         if(!cell || typeof(cell) == 'undefined'){
5751             return;
5752         }
5753         
5754         var row = cell.findParent('tr', false, true);
5755         
5756         if(!row || typeof(row) == 'undefined'){
5757             return;
5758         }
5759         
5760         var cellIndex = cell.dom.cellIndex;
5761         var rowIndex = this.getRowIndex(row);
5762         
5763         if(this.CellSelection){
5764             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5765         }
5766         
5767         if(this.RowSelection){
5768             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5769         }
5770     },
5771     
5772     sort : function(e,el)
5773     {
5774         var col = Roo.get(el);
5775         
5776         if(!col.hasClass('sortable')){
5777             return;
5778         }
5779         
5780         var sort = col.attr('sort');
5781         var dir = 'ASC';
5782         
5783         if(col.hasClass('glyphicon-arrow-up')){
5784             dir = 'DESC';
5785         }
5786         
5787         this.store.sortInfo = {field : sort, direction : dir};
5788         
5789         if (this.footer) {
5790             Roo.log("calling footer first");
5791             this.footer.onClick('first');
5792         } else {
5793         
5794             this.store.load({ params : { start : 0 } });
5795         }
5796     },
5797     
5798     renderHeader : function()
5799     {
5800         var header = {
5801             tag: 'thead',
5802             cn : []
5803         };
5804         
5805         var cm = this.cm;
5806         
5807         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5808             
5809             var config = cm.config[i];
5810             
5811             var c = {
5812                 tag: 'th',
5813                 style : '',
5814                 html: cm.getColumnHeader(i)
5815             };
5816             
5817             var hh = '';
5818             
5819             if(typeof(config.lgHeader) != 'undefined'){
5820                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
5821             }
5822             
5823             if(typeof(config.mdHeader) != 'undefined'){
5824                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
5825             }
5826             
5827             if(typeof(config.smHeader) != 'undefined'){
5828                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
5829             }
5830             
5831             if(typeof(config.xsHeader) != 'undefined'){
5832                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
5833             }
5834             
5835             if(hh.length){
5836                 c.html = hh;
5837             }
5838             
5839             if(typeof(config.tooltip) != 'undefined'){
5840                 c.tooltip = config.tooltip;
5841             }
5842             
5843             if(typeof(config.colspan) != 'undefined'){
5844                 c.colspan = config.colspan;
5845             }
5846             
5847             if(typeof(config.hidden) != 'undefined' && config.hidden){
5848                 c.style += ' display:none;';
5849             }
5850             
5851             if(typeof(config.dataIndex) != 'undefined'){
5852                 c.sort = config.dataIndex;
5853             }
5854             
5855             if(typeof(config.sortable) != 'undefined' && config.sortable){
5856                 c.cls = 'sortable';
5857             }
5858             
5859             if(typeof(config.align) != 'undefined' && config.align.length){
5860                 c.style += ' text-align:' + config.align + ';';
5861             }
5862             
5863             if(typeof(config.width) != 'undefined'){
5864                 c.style += ' width:' + config.width + 'px;';
5865             }
5866             
5867             if(typeof(config.cls) != 'undefined'){
5868                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
5869             }
5870             
5871             header.cn.push(c)
5872         }
5873         
5874         return header;
5875     },
5876     
5877     renderBody : function()
5878     {
5879         var body = {
5880             tag: 'tbody',
5881             cn : [
5882                 {
5883                     tag: 'tr',
5884                     cn : [
5885                         {
5886                             tag : 'td',
5887                             colspan :  this.cm.getColumnCount()
5888                         }
5889                     ]
5890                 }
5891             ]
5892         };
5893         
5894         return body;
5895     },
5896     
5897     renderFooter : function()
5898     {
5899         var footer = {
5900             tag: 'tfoot',
5901             cn : [
5902                 {
5903                     tag: 'tr',
5904                     cn : [
5905                         {
5906                             tag : 'td',
5907                             colspan :  this.cm.getColumnCount()
5908                         }
5909                     ]
5910                 }
5911             ]
5912         };
5913         
5914         return footer;
5915     },
5916     
5917     
5918     
5919     onLoad : function()
5920     {
5921         Roo.log('ds onload');
5922         this.clear();
5923         
5924         var _this = this;
5925         var cm = this.cm;
5926         var ds = this.store;
5927         
5928         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5929             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5930             
5931             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5932                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5933             }
5934             
5935             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5936                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5937             }
5938         });
5939         
5940         var tbody =  this.mainBody;
5941               
5942         if(ds.getCount() > 0){
5943             ds.data.each(function(d,rowIndex){
5944                 var row =  this.renderRow(cm, ds, rowIndex);
5945                 
5946                 tbody.createChild(row);
5947                 
5948                 var _this = this;
5949                 
5950                 if(row.cellObjects.length){
5951                     Roo.each(row.cellObjects, function(r){
5952                         _this.renderCellObject(r);
5953                     })
5954                 }
5955                 
5956             }, this);
5957         }
5958         
5959         Roo.each(this.el.select('tbody td', true).elements, function(e){
5960             e.on('mouseover', _this.onMouseover, _this);
5961         });
5962         
5963         Roo.each(this.el.select('tbody td', true).elements, function(e){
5964             e.on('mouseout', _this.onMouseout, _this);
5965         });
5966         this.fireEvent('rowsrendered', this);
5967         //if(this.loadMask){
5968         //    this.maskEl.hide();
5969         //}
5970     },
5971     
5972     
5973     onUpdate : function(ds,record)
5974     {
5975         this.refreshRow(record);
5976     },
5977     
5978     onRemove : function(ds, record, index, isUpdate){
5979         if(isUpdate !== true){
5980             this.fireEvent("beforerowremoved", this, index, record);
5981         }
5982         var bt = this.mainBody.dom;
5983         
5984         var rows = this.el.select('tbody > tr', true).elements;
5985         
5986         if(typeof(rows[index]) != 'undefined'){
5987             bt.removeChild(rows[index].dom);
5988         }
5989         
5990 //        if(bt.rows[index]){
5991 //            bt.removeChild(bt.rows[index]);
5992 //        }
5993         
5994         if(isUpdate !== true){
5995             //this.stripeRows(index);
5996             //this.syncRowHeights(index, index);
5997             //this.layout();
5998             this.fireEvent("rowremoved", this, index, record);
5999         }
6000     },
6001     
6002     onAdd : function(ds, records, rowIndex)
6003     {
6004         //Roo.log('on Add called');
6005         // - note this does not handle multiple adding very well..
6006         var bt = this.mainBody.dom;
6007         for (var i =0 ; i < records.length;i++) {
6008             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6009             //Roo.log(records[i]);
6010             //Roo.log(this.store.getAt(rowIndex+i));
6011             this.insertRow(this.store, rowIndex + i, false);
6012             return;
6013         }
6014         
6015     },
6016     
6017     
6018     refreshRow : function(record){
6019         var ds = this.store, index;
6020         if(typeof record == 'number'){
6021             index = record;
6022             record = ds.getAt(index);
6023         }else{
6024             index = ds.indexOf(record);
6025         }
6026         this.insertRow(ds, index, true);
6027         this.onRemove(ds, record, index+1, true);
6028         //this.syncRowHeights(index, index);
6029         //this.layout();
6030         this.fireEvent("rowupdated", this, index, record);
6031     },
6032     
6033     insertRow : function(dm, rowIndex, isUpdate){
6034         
6035         if(!isUpdate){
6036             this.fireEvent("beforerowsinserted", this, rowIndex);
6037         }
6038             //var s = this.getScrollState();
6039         var row = this.renderRow(this.cm, this.store, rowIndex);
6040         // insert before rowIndex..
6041         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6042         
6043         var _this = this;
6044                 
6045         if(row.cellObjects.length){
6046             Roo.each(row.cellObjects, function(r){
6047                 _this.renderCellObject(r);
6048             })
6049         }
6050             
6051         if(!isUpdate){
6052             this.fireEvent("rowsinserted", this, rowIndex);
6053             //this.syncRowHeights(firstRow, lastRow);
6054             //this.stripeRows(firstRow);
6055             //this.layout();
6056         }
6057         
6058     },
6059     
6060     
6061     getRowDom : function(rowIndex)
6062     {
6063         var rows = this.el.select('tbody > tr', true).elements;
6064         
6065         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6066         
6067     },
6068     // returns the object tree for a tr..
6069   
6070     
6071     renderRow : function(cm, ds, rowIndex) 
6072     {
6073         
6074         var d = ds.getAt(rowIndex);
6075         
6076         var row = {
6077             tag : 'tr',
6078             cn : []
6079         };
6080             
6081         var cellObjects = [];
6082         
6083         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6084             var config = cm.config[i];
6085             
6086             var renderer = cm.getRenderer(i);
6087             var value = '';
6088             var id = false;
6089             
6090             if(typeof(renderer) !== 'undefined'){
6091                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6092             }
6093             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6094             // and are rendered into the cells after the row is rendered - using the id for the element.
6095             
6096             if(typeof(value) === 'object'){
6097                 id = Roo.id();
6098                 cellObjects.push({
6099                     container : id,
6100                     cfg : value 
6101                 })
6102             }
6103             
6104             var rowcfg = {
6105                 record: d,
6106                 rowIndex : rowIndex,
6107                 colIndex : i,
6108                 rowClass : ''
6109             }
6110
6111             this.fireEvent('rowclass', this, rowcfg);
6112             
6113             var td = {
6114                 tag: 'td',
6115                 cls : rowcfg.rowClass,
6116                 style: '',
6117                 html: (typeof(value) === 'object') ? '' : value
6118             };
6119             
6120             if (id) {
6121                 td.id = id;
6122             }
6123             
6124             if(typeof(config.colspan) != 'undefined'){
6125                 td.colspan = config.colspan;
6126             }
6127             
6128             if(typeof(config.hidden) != 'undefined' && config.hidden){
6129                 td.style += ' display:none;';
6130             }
6131             
6132             if(typeof(config.align) != 'undefined' && config.align.length){
6133                 td.style += ' text-align:' + config.align + ';';
6134             }
6135             
6136             if(typeof(config.width) != 'undefined'){
6137                 td.style += ' width:' +  config.width + 'px;';
6138             }
6139             
6140             if(typeof(config.cursor) != 'undefined'){
6141                 td.style += ' cursor:' +  config.cursor + ';';
6142             }
6143             
6144             if(typeof(config.cls) != 'undefined'){
6145                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6146             }
6147              
6148             row.cn.push(td);
6149            
6150         }
6151         
6152         row.cellObjects = cellObjects;
6153         
6154         return row;
6155           
6156     },
6157     
6158     
6159     
6160     onBeforeLoad : function()
6161     {
6162         //Roo.log('ds onBeforeLoad');
6163         
6164         //this.clear();
6165         
6166         //if(this.loadMask){
6167         //    this.maskEl.show();
6168         //}
6169     },
6170      /**
6171      * Remove all rows
6172      */
6173     clear : function()
6174     {
6175         this.el.select('tbody', true).first().dom.innerHTML = '';
6176     },
6177     /**
6178      * Show or hide a row.
6179      * @param {Number} rowIndex to show or hide
6180      * @param {Boolean} state hide
6181      */
6182     setRowVisibility : function(rowIndex, state)
6183     {
6184         var bt = this.mainBody.dom;
6185         
6186         var rows = this.el.select('tbody > tr', true).elements;
6187         
6188         if(typeof(rows[rowIndex]) == 'undefined'){
6189             return;
6190         }
6191         rows[rowIndex].dom.style.display = state ? '' : 'none';
6192     },
6193     
6194     
6195     getSelectionModel : function(){
6196         if(!this.selModel){
6197             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6198         }
6199         return this.selModel;
6200     },
6201     /*
6202      * Render the Roo.bootstrap object from renderder
6203      */
6204     renderCellObject : function(r)
6205     {
6206         var _this = this;
6207         
6208         var t = r.cfg.render(r.container);
6209         
6210         if(r.cfg.cn){
6211             Roo.each(r.cfg.cn, function(c){
6212                 var child = {
6213                     container: t.getChildContainer(),
6214                     cfg: c
6215                 }
6216                 _this.renderCellObject(child);
6217             })
6218         }
6219     },
6220     
6221     getRowIndex : function(row)
6222     {
6223         var rowIndex = -1;
6224         
6225         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6226             if(el != row){
6227                 return;
6228             }
6229             
6230             rowIndex = index;
6231         });
6232         
6233         return rowIndex;
6234     }
6235    
6236 });
6237
6238  
6239
6240  /*
6241  * - LGPL
6242  *
6243  * table cell
6244  * 
6245  */
6246
6247 /**
6248  * @class Roo.bootstrap.TableCell
6249  * @extends Roo.bootstrap.Component
6250  * Bootstrap TableCell class
6251  * @cfg {String} html cell contain text
6252  * @cfg {String} cls cell class
6253  * @cfg {String} tag cell tag (td|th) default td
6254  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6255  * @cfg {String} align Aligns the content in a cell
6256  * @cfg {String} axis Categorizes cells
6257  * @cfg {String} bgcolor Specifies the background color of a cell
6258  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6259  * @cfg {Number} colspan Specifies the number of columns a cell should span
6260  * @cfg {String} headers Specifies one or more header cells a cell is related to
6261  * @cfg {Number} height Sets the height of a cell
6262  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6263  * @cfg {Number} rowspan Sets the number of rows a cell should span
6264  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6265  * @cfg {String} valign Vertical aligns the content in a cell
6266  * @cfg {Number} width Specifies the width of a cell
6267  * 
6268  * @constructor
6269  * Create a new TableCell
6270  * @param {Object} config The config object
6271  */
6272
6273 Roo.bootstrap.TableCell = function(config){
6274     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6275 };
6276
6277 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6278     
6279     html: false,
6280     cls: false,
6281     tag: false,
6282     abbr: false,
6283     align: false,
6284     axis: false,
6285     bgcolor: false,
6286     charoff: false,
6287     colspan: false,
6288     headers: false,
6289     height: false,
6290     nowrap: false,
6291     rowspan: false,
6292     scope: false,
6293     valign: false,
6294     width: false,
6295     
6296     
6297     getAutoCreate : function(){
6298         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6299         
6300         cfg = {
6301             tag: 'td'
6302         }
6303         
6304         if(this.tag){
6305             cfg.tag = this.tag;
6306         }
6307         
6308         if (this.html) {
6309             cfg.html=this.html
6310         }
6311         if (this.cls) {
6312             cfg.cls=this.cls
6313         }
6314         if (this.abbr) {
6315             cfg.abbr=this.abbr
6316         }
6317         if (this.align) {
6318             cfg.align=this.align
6319         }
6320         if (this.axis) {
6321             cfg.axis=this.axis
6322         }
6323         if (this.bgcolor) {
6324             cfg.bgcolor=this.bgcolor
6325         }
6326         if (this.charoff) {
6327             cfg.charoff=this.charoff
6328         }
6329         if (this.colspan) {
6330             cfg.colspan=this.colspan
6331         }
6332         if (this.headers) {
6333             cfg.headers=this.headers
6334         }
6335         if (this.height) {
6336             cfg.height=this.height
6337         }
6338         if (this.nowrap) {
6339             cfg.nowrap=this.nowrap
6340         }
6341         if (this.rowspan) {
6342             cfg.rowspan=this.rowspan
6343         }
6344         if (this.scope) {
6345             cfg.scope=this.scope
6346         }
6347         if (this.valign) {
6348             cfg.valign=this.valign
6349         }
6350         if (this.width) {
6351             cfg.width=this.width
6352         }
6353         
6354         
6355         return cfg;
6356     }
6357    
6358 });
6359
6360  
6361
6362  /*
6363  * - LGPL
6364  *
6365  * table row
6366  * 
6367  */
6368
6369 /**
6370  * @class Roo.bootstrap.TableRow
6371  * @extends Roo.bootstrap.Component
6372  * Bootstrap TableRow class
6373  * @cfg {String} cls row class
6374  * @cfg {String} align Aligns the content in a table row
6375  * @cfg {String} bgcolor Specifies a background color for a table row
6376  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6377  * @cfg {String} valign Vertical aligns the content in a table row
6378  * 
6379  * @constructor
6380  * Create a new TableRow
6381  * @param {Object} config The config object
6382  */
6383
6384 Roo.bootstrap.TableRow = function(config){
6385     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6386 };
6387
6388 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6389     
6390     cls: false,
6391     align: false,
6392     bgcolor: false,
6393     charoff: false,
6394     valign: false,
6395     
6396     getAutoCreate : function(){
6397         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6398         
6399         cfg = {
6400             tag: 'tr'
6401         }
6402             
6403         if(this.cls){
6404             cfg.cls = this.cls;
6405         }
6406         if(this.align){
6407             cfg.align = this.align;
6408         }
6409         if(this.bgcolor){
6410             cfg.bgcolor = this.bgcolor;
6411         }
6412         if(this.charoff){
6413             cfg.charoff = this.charoff;
6414         }
6415         if(this.valign){
6416             cfg.valign = this.valign;
6417         }
6418         
6419         return cfg;
6420     }
6421    
6422 });
6423
6424  
6425
6426  /*
6427  * - LGPL
6428  *
6429  * table body
6430  * 
6431  */
6432
6433 /**
6434  * @class Roo.bootstrap.TableBody
6435  * @extends Roo.bootstrap.Component
6436  * Bootstrap TableBody class
6437  * @cfg {String} cls element class
6438  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6439  * @cfg {String} align Aligns the content inside the element
6440  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6441  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6442  * 
6443  * @constructor
6444  * Create a new TableBody
6445  * @param {Object} config The config object
6446  */
6447
6448 Roo.bootstrap.TableBody = function(config){
6449     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6450 };
6451
6452 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6453     
6454     cls: false,
6455     tag: false,
6456     align: false,
6457     charoff: false,
6458     valign: false,
6459     
6460     getAutoCreate : function(){
6461         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6462         
6463         cfg = {
6464             tag: 'tbody'
6465         }
6466             
6467         if (this.cls) {
6468             cfg.cls=this.cls
6469         }
6470         if(this.tag){
6471             cfg.tag = this.tag;
6472         }
6473         
6474         if(this.align){
6475             cfg.align = this.align;
6476         }
6477         if(this.charoff){
6478             cfg.charoff = this.charoff;
6479         }
6480         if(this.valign){
6481             cfg.valign = this.valign;
6482         }
6483         
6484         return cfg;
6485     }
6486     
6487     
6488 //    initEvents : function()
6489 //    {
6490 //        
6491 //        if(!this.store){
6492 //            return;
6493 //        }
6494 //        
6495 //        this.store = Roo.factory(this.store, Roo.data);
6496 //        this.store.on('load', this.onLoad, this);
6497 //        
6498 //        this.store.load();
6499 //        
6500 //    },
6501 //    
6502 //    onLoad: function () 
6503 //    {   
6504 //        this.fireEvent('load', this);
6505 //    }
6506 //    
6507 //   
6508 });
6509
6510  
6511
6512  /*
6513  * Based on:
6514  * Ext JS Library 1.1.1
6515  * Copyright(c) 2006-2007, Ext JS, LLC.
6516  *
6517  * Originally Released Under LGPL - original licence link has changed is not relivant.
6518  *
6519  * Fork - LGPL
6520  * <script type="text/javascript">
6521  */
6522
6523 // as we use this in bootstrap.
6524 Roo.namespace('Roo.form');
6525  /**
6526  * @class Roo.form.Action
6527  * Internal Class used to handle form actions
6528  * @constructor
6529  * @param {Roo.form.BasicForm} el The form element or its id
6530  * @param {Object} config Configuration options
6531  */
6532
6533  
6534  
6535 // define the action interface
6536 Roo.form.Action = function(form, options){
6537     this.form = form;
6538     this.options = options || {};
6539 };
6540 /**
6541  * Client Validation Failed
6542  * @const 
6543  */
6544 Roo.form.Action.CLIENT_INVALID = 'client';
6545 /**
6546  * Server Validation Failed
6547  * @const 
6548  */
6549 Roo.form.Action.SERVER_INVALID = 'server';
6550  /**
6551  * Connect to Server Failed
6552  * @const 
6553  */
6554 Roo.form.Action.CONNECT_FAILURE = 'connect';
6555 /**
6556  * Reading Data from Server Failed
6557  * @const 
6558  */
6559 Roo.form.Action.LOAD_FAILURE = 'load';
6560
6561 Roo.form.Action.prototype = {
6562     type : 'default',
6563     failureType : undefined,
6564     response : undefined,
6565     result : undefined,
6566
6567     // interface method
6568     run : function(options){
6569
6570     },
6571
6572     // interface method
6573     success : function(response){
6574
6575     },
6576
6577     // interface method
6578     handleResponse : function(response){
6579
6580     },
6581
6582     // default connection failure
6583     failure : function(response){
6584         
6585         this.response = response;
6586         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6587         this.form.afterAction(this, false);
6588     },
6589
6590     processResponse : function(response){
6591         this.response = response;
6592         if(!response.responseText){
6593             return true;
6594         }
6595         this.result = this.handleResponse(response);
6596         return this.result;
6597     },
6598
6599     // utility functions used internally
6600     getUrl : function(appendParams){
6601         var url = this.options.url || this.form.url || this.form.el.dom.action;
6602         if(appendParams){
6603             var p = this.getParams();
6604             if(p){
6605                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6606             }
6607         }
6608         return url;
6609     },
6610
6611     getMethod : function(){
6612         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6613     },
6614
6615     getParams : function(){
6616         var bp = this.form.baseParams;
6617         var p = this.options.params;
6618         if(p){
6619             if(typeof p == "object"){
6620                 p = Roo.urlEncode(Roo.applyIf(p, bp));
6621             }else if(typeof p == 'string' && bp){
6622                 p += '&' + Roo.urlEncode(bp);
6623             }
6624         }else if(bp){
6625             p = Roo.urlEncode(bp);
6626         }
6627         return p;
6628     },
6629
6630     createCallback : function(){
6631         return {
6632             success: this.success,
6633             failure: this.failure,
6634             scope: this,
6635             timeout: (this.form.timeout*1000),
6636             upload: this.form.fileUpload ? this.success : undefined
6637         };
6638     }
6639 };
6640
6641 Roo.form.Action.Submit = function(form, options){
6642     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6643 };
6644
6645 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6646     type : 'submit',
6647
6648     haveProgress : false,
6649     uploadComplete : false,
6650     
6651     // uploadProgress indicator.
6652     uploadProgress : function()
6653     {
6654         if (!this.form.progressUrl) {
6655             return;
6656         }
6657         
6658         if (!this.haveProgress) {
6659             Roo.MessageBox.progress("Uploading", "Uploading");
6660         }
6661         if (this.uploadComplete) {
6662            Roo.MessageBox.hide();
6663            return;
6664         }
6665         
6666         this.haveProgress = true;
6667    
6668         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6669         
6670         var c = new Roo.data.Connection();
6671         c.request({
6672             url : this.form.progressUrl,
6673             params: {
6674                 id : uid
6675             },
6676             method: 'GET',
6677             success : function(req){
6678                //console.log(data);
6679                 var rdata = false;
6680                 var edata;
6681                 try  {
6682                    rdata = Roo.decode(req.responseText)
6683                 } catch (e) {
6684                     Roo.log("Invalid data from server..");
6685                     Roo.log(edata);
6686                     return;
6687                 }
6688                 if (!rdata || !rdata.success) {
6689                     Roo.log(rdata);
6690                     Roo.MessageBox.alert(Roo.encode(rdata));
6691                     return;
6692                 }
6693                 var data = rdata.data;
6694                 
6695                 if (this.uploadComplete) {
6696                    Roo.MessageBox.hide();
6697                    return;
6698                 }
6699                    
6700                 if (data){
6701                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6702                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6703                     );
6704                 }
6705                 this.uploadProgress.defer(2000,this);
6706             },
6707        
6708             failure: function(data) {
6709                 Roo.log('progress url failed ');
6710                 Roo.log(data);
6711             },
6712             scope : this
6713         });
6714            
6715     },
6716     
6717     
6718     run : function()
6719     {
6720         // run get Values on the form, so it syncs any secondary forms.
6721         this.form.getValues();
6722         
6723         var o = this.options;
6724         var method = this.getMethod();
6725         var isPost = method == 'POST';
6726         if(o.clientValidation === false || this.form.isValid()){
6727             
6728             if (this.form.progressUrl) {
6729                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6730                     (new Date() * 1) + '' + Math.random());
6731                     
6732             } 
6733             
6734             
6735             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6736                 form:this.form.el.dom,
6737                 url:this.getUrl(!isPost),
6738                 method: method,
6739                 params:isPost ? this.getParams() : null,
6740                 isUpload: this.form.fileUpload
6741             }));
6742             
6743             this.uploadProgress();
6744
6745         }else if (o.clientValidation !== false){ // client validation failed
6746             this.failureType = Roo.form.Action.CLIENT_INVALID;
6747             this.form.afterAction(this, false);
6748         }
6749     },
6750
6751     success : function(response)
6752     {
6753         this.uploadComplete= true;
6754         if (this.haveProgress) {
6755             Roo.MessageBox.hide();
6756         }
6757         
6758         
6759         var result = this.processResponse(response);
6760         if(result === true || result.success){
6761             this.form.afterAction(this, true);
6762             return;
6763         }
6764         if(result.errors){
6765             this.form.markInvalid(result.errors);
6766             this.failureType = Roo.form.Action.SERVER_INVALID;
6767         }
6768         this.form.afterAction(this, false);
6769     },
6770     failure : function(response)
6771     {
6772         this.uploadComplete= true;
6773         if (this.haveProgress) {
6774             Roo.MessageBox.hide();
6775         }
6776         
6777         this.response = response;
6778         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6779         this.form.afterAction(this, false);
6780     },
6781     
6782     handleResponse : function(response){
6783         if(this.form.errorReader){
6784             var rs = this.form.errorReader.read(response);
6785             var errors = [];
6786             if(rs.records){
6787                 for(var i = 0, len = rs.records.length; i < len; i++) {
6788                     var r = rs.records[i];
6789                     errors[i] = r.data;
6790                 }
6791             }
6792             if(errors.length < 1){
6793                 errors = null;
6794             }
6795             return {
6796                 success : rs.success,
6797                 errors : errors
6798             };
6799         }
6800         var ret = false;
6801         try {
6802             ret = Roo.decode(response.responseText);
6803         } catch (e) {
6804             ret = {
6805                 success: false,
6806                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6807                 errors : []
6808             };
6809         }
6810         return ret;
6811         
6812     }
6813 });
6814
6815
6816 Roo.form.Action.Load = function(form, options){
6817     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6818     this.reader = this.form.reader;
6819 };
6820
6821 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6822     type : 'load',
6823
6824     run : function(){
6825         
6826         Roo.Ajax.request(Roo.apply(
6827                 this.createCallback(), {
6828                     method:this.getMethod(),
6829                     url:this.getUrl(false),
6830                     params:this.getParams()
6831         }));
6832     },
6833
6834     success : function(response){
6835         
6836         var result = this.processResponse(response);
6837         if(result === true || !result.success || !result.data){
6838             this.failureType = Roo.form.Action.LOAD_FAILURE;
6839             this.form.afterAction(this, false);
6840             return;
6841         }
6842         this.form.clearInvalid();
6843         this.form.setValues(result.data);
6844         this.form.afterAction(this, true);
6845     },
6846
6847     handleResponse : function(response){
6848         if(this.form.reader){
6849             var rs = this.form.reader.read(response);
6850             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6851             return {
6852                 success : rs.success,
6853                 data : data
6854             };
6855         }
6856         return Roo.decode(response.responseText);
6857     }
6858 });
6859
6860 Roo.form.Action.ACTION_TYPES = {
6861     'load' : Roo.form.Action.Load,
6862     'submit' : Roo.form.Action.Submit
6863 };/*
6864  * - LGPL
6865  *
6866  * form
6867  * 
6868  */
6869
6870 /**
6871  * @class Roo.bootstrap.Form
6872  * @extends Roo.bootstrap.Component
6873  * Bootstrap Form class
6874  * @cfg {String} method  GET | POST (default POST)
6875  * @cfg {String} labelAlign top | left (default top)
6876  * @cfg {String} align left  | right - for navbars
6877  * @cfg {Boolean} loadMask load mask when submit (default true)
6878
6879  * 
6880  * @constructor
6881  * Create a new Form
6882  * @param {Object} config The config object
6883  */
6884
6885
6886 Roo.bootstrap.Form = function(config){
6887     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6888     this.addEvents({
6889         /**
6890          * @event clientvalidation
6891          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6892          * @param {Form} this
6893          * @param {Boolean} valid true if the form has passed client-side validation
6894          */
6895         clientvalidation: true,
6896         /**
6897          * @event beforeaction
6898          * Fires before any action is performed. Return false to cancel the action.
6899          * @param {Form} this
6900          * @param {Action} action The action to be performed
6901          */
6902         beforeaction: true,
6903         /**
6904          * @event actionfailed
6905          * Fires when an action fails.
6906          * @param {Form} this
6907          * @param {Action} action The action that failed
6908          */
6909         actionfailed : true,
6910         /**
6911          * @event actioncomplete
6912          * Fires when an action is completed.
6913          * @param {Form} this
6914          * @param {Action} action The action that completed
6915          */
6916         actioncomplete : true
6917     });
6918     
6919 };
6920
6921 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
6922       
6923      /**
6924      * @cfg {String} method
6925      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6926      */
6927     method : 'POST',
6928     /**
6929      * @cfg {String} url
6930      * The URL to use for form actions if one isn't supplied in the action options.
6931      */
6932     /**
6933      * @cfg {Boolean} fileUpload
6934      * Set to true if this form is a file upload.
6935      */
6936      
6937     /**
6938      * @cfg {Object} baseParams
6939      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6940      */
6941       
6942     /**
6943      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6944      */
6945     timeout: 30,
6946     /**
6947      * @cfg {Sting} align (left|right) for navbar forms
6948      */
6949     align : 'left',
6950
6951     // private
6952     activeAction : null,
6953  
6954     /**
6955      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6956      * element by passing it or its id or mask the form itself by passing in true.
6957      * @type Mixed
6958      */
6959     waitMsgTarget : false,
6960     
6961     loadMask : true,
6962     
6963     getAutoCreate : function(){
6964         
6965         var cfg = {
6966             tag: 'form',
6967             method : this.method || 'POST',
6968             id : this.id || Roo.id(),
6969             cls : ''
6970         }
6971         if (this.parent().xtype.match(/^Nav/)) {
6972             cfg.cls = 'navbar-form navbar-' + this.align;
6973             
6974         }
6975         
6976         if (this.labelAlign == 'left' ) {
6977             cfg.cls += ' form-horizontal';
6978         }
6979         
6980         
6981         return cfg;
6982     },
6983     initEvents : function()
6984     {
6985         this.el.on('submit', this.onSubmit, this);
6986         // this was added as random key presses on the form where triggering form submit.
6987         this.el.on('keypress', function(e) {
6988             if (e.getCharCode() != 13) {
6989                 return true;
6990             }
6991             // we might need to allow it for textareas.. and some other items.
6992             // check e.getTarget().
6993             
6994             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6995                 return true;
6996             }
6997         
6998             Roo.log("keypress blocked");
6999             
7000             e.preventDefault();
7001             return false;
7002         });
7003         
7004     },
7005     // private
7006     onSubmit : function(e){
7007         e.stopEvent();
7008     },
7009     
7010      /**
7011      * Returns true if client-side validation on the form is successful.
7012      * @return Boolean
7013      */
7014     isValid : function(){
7015         var items = this.getItems();
7016         var valid = true;
7017         items.each(function(f){
7018            if(!f.validate()){
7019                valid = false;
7020                
7021            }
7022         });
7023         return valid;
7024     },
7025     /**
7026      * Returns true if any fields in this form have changed since their original load.
7027      * @return Boolean
7028      */
7029     isDirty : function(){
7030         var dirty = false;
7031         var items = this.getItems();
7032         items.each(function(f){
7033            if(f.isDirty()){
7034                dirty = true;
7035                return false;
7036            }
7037            return true;
7038         });
7039         return dirty;
7040     },
7041      /**
7042      * Performs a predefined action (submit or load) or custom actions you define on this form.
7043      * @param {String} actionName The name of the action type
7044      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7045      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7046      * accept other config options):
7047      * <pre>
7048 Property          Type             Description
7049 ----------------  ---------------  ----------------------------------------------------------------------------------
7050 url               String           The url for the action (defaults to the form's url)
7051 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7052 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7053 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7054                                    validate the form on the client (defaults to false)
7055      * </pre>
7056      * @return {BasicForm} this
7057      */
7058     doAction : function(action, options){
7059         if(typeof action == 'string'){
7060             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7061         }
7062         if(this.fireEvent('beforeaction', this, action) !== false){
7063             this.beforeAction(action);
7064             action.run.defer(100, action);
7065         }
7066         return this;
7067     },
7068     
7069     // private
7070     beforeAction : function(action){
7071         var o = action.options;
7072         
7073         if(this.loadMask){
7074             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7075         }
7076         // not really supported yet.. ??
7077         
7078         //if(this.waitMsgTarget === true){
7079         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7080         //}else if(this.waitMsgTarget){
7081         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7082         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7083         //}else {
7084         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7085        // }
7086          
7087     },
7088
7089     // private
7090     afterAction : function(action, success){
7091         this.activeAction = null;
7092         var o = action.options;
7093         
7094         //if(this.waitMsgTarget === true){
7095             this.el.unmask();
7096         //}else if(this.waitMsgTarget){
7097         //    this.waitMsgTarget.unmask();
7098         //}else{
7099         //    Roo.MessageBox.updateProgress(1);
7100         //    Roo.MessageBox.hide();
7101        // }
7102         // 
7103         if(success){
7104             if(o.reset){
7105                 this.reset();
7106             }
7107             Roo.callback(o.success, o.scope, [this, action]);
7108             this.fireEvent('actioncomplete', this, action);
7109             
7110         }else{
7111             
7112             // failure condition..
7113             // we have a scenario where updates need confirming.
7114             // eg. if a locking scenario exists..
7115             // we look for { errors : { needs_confirm : true }} in the response.
7116             if (
7117                 (typeof(action.result) != 'undefined')  &&
7118                 (typeof(action.result.errors) != 'undefined')  &&
7119                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7120            ){
7121                 var _t = this;
7122                 Roo.log("not supported yet");
7123                  /*
7124                 
7125                 Roo.MessageBox.confirm(
7126                     "Change requires confirmation",
7127                     action.result.errorMsg,
7128                     function(r) {
7129                         if (r != 'yes') {
7130                             return;
7131                         }
7132                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7133                     }
7134                     
7135                 );
7136                 */
7137                 
7138                 
7139                 return;
7140             }
7141             
7142             Roo.callback(o.failure, o.scope, [this, action]);
7143             // show an error message if no failed handler is set..
7144             if (!this.hasListener('actionfailed')) {
7145                 Roo.log("need to add dialog support");
7146                 /*
7147                 Roo.MessageBox.alert("Error",
7148                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7149                         action.result.errorMsg :
7150                         "Saving Failed, please check your entries or try again"
7151                 );
7152                 */
7153             }
7154             
7155             this.fireEvent('actionfailed', this, action);
7156         }
7157         
7158     },
7159     /**
7160      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7161      * @param {String} id The value to search for
7162      * @return Field
7163      */
7164     findField : function(id){
7165         var items = this.getItems();
7166         var field = items.get(id);
7167         if(!field){
7168              items.each(function(f){
7169                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7170                     field = f;
7171                     return false;
7172                 }
7173                 return true;
7174             });
7175         }
7176         return field || null;
7177     },
7178      /**
7179      * Mark fields in this form invalid in bulk.
7180      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7181      * @return {BasicForm} this
7182      */
7183     markInvalid : function(errors){
7184         if(errors instanceof Array){
7185             for(var i = 0, len = errors.length; i < len; i++){
7186                 var fieldError = errors[i];
7187                 var f = this.findField(fieldError.id);
7188                 if(f){
7189                     f.markInvalid(fieldError.msg);
7190                 }
7191             }
7192         }else{
7193             var field, id;
7194             for(id in errors){
7195                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7196                     field.markInvalid(errors[id]);
7197                 }
7198             }
7199         }
7200         //Roo.each(this.childForms || [], function (f) {
7201         //    f.markInvalid(errors);
7202         //});
7203         
7204         return this;
7205     },
7206
7207     /**
7208      * Set values for fields in this form in bulk.
7209      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7210      * @return {BasicForm} this
7211      */
7212     setValues : function(values){
7213         if(values instanceof Array){ // array of objects
7214             for(var i = 0, len = values.length; i < len; i++){
7215                 var v = values[i];
7216                 var f = this.findField(v.id);
7217                 if(f){
7218                     f.setValue(v.value);
7219                     if(this.trackResetOnLoad){
7220                         f.originalValue = f.getValue();
7221                     }
7222                 }
7223             }
7224         }else{ // object hash
7225             var field, id;
7226             for(id in values){
7227                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7228                     
7229                     if (field.setFromData && 
7230                         field.valueField && 
7231                         field.displayField &&
7232                         // combos' with local stores can 
7233                         // be queried via setValue()
7234                         // to set their value..
7235                         (field.store && !field.store.isLocal)
7236                         ) {
7237                         // it's a combo
7238                         var sd = { };
7239                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7240                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7241                         field.setFromData(sd);
7242                         
7243                     } else {
7244                         field.setValue(values[id]);
7245                     }
7246                     
7247                     
7248                     if(this.trackResetOnLoad){
7249                         field.originalValue = field.getValue();
7250                     }
7251                 }
7252             }
7253         }
7254          
7255         //Roo.each(this.childForms || [], function (f) {
7256         //    f.setValues(values);
7257         //});
7258                 
7259         return this;
7260     },
7261
7262     /**
7263      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7264      * they are returned as an array.
7265      * @param {Boolean} asString
7266      * @return {Object}
7267      */
7268     getValues : function(asString){
7269         //if (this.childForms) {
7270             // copy values from the child forms
7271         //    Roo.each(this.childForms, function (f) {
7272         //        this.setValues(f.getValues());
7273         //    }, this);
7274         //}
7275         
7276         
7277         
7278         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7279         if(asString === true){
7280             return fs;
7281         }
7282         return Roo.urlDecode(fs);
7283     },
7284     
7285     /**
7286      * Returns the fields in this form as an object with key/value pairs. 
7287      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7288      * @return {Object}
7289      */
7290     getFieldValues : function(with_hidden)
7291     {
7292         var items = this.getItems();
7293         var ret = {};
7294         items.each(function(f){
7295             if (!f.getName()) {
7296                 return;
7297             }
7298             var v = f.getValue();
7299             if (f.inputType =='radio') {
7300                 if (typeof(ret[f.getName()]) == 'undefined') {
7301                     ret[f.getName()] = ''; // empty..
7302                 }
7303                 
7304                 if (!f.el.dom.checked) {
7305                     return;
7306                     
7307                 }
7308                 v = f.el.dom.value;
7309                 
7310             }
7311             
7312             // not sure if this supported any more..
7313             if ((typeof(v) == 'object') && f.getRawValue) {
7314                 v = f.getRawValue() ; // dates..
7315             }
7316             // combo boxes where name != hiddenName...
7317             if (f.name != f.getName()) {
7318                 ret[f.name] = f.getRawValue();
7319             }
7320             ret[f.getName()] = v;
7321         });
7322         
7323         return ret;
7324     },
7325
7326     /**
7327      * Clears all invalid messages in this form.
7328      * @return {BasicForm} this
7329      */
7330     clearInvalid : function(){
7331         var items = this.getItems();
7332         
7333         items.each(function(f){
7334            f.clearInvalid();
7335         });
7336         
7337         
7338         
7339         return this;
7340     },
7341
7342     /**
7343      * Resets this form.
7344      * @return {BasicForm} this
7345      */
7346     reset : function(){
7347         var items = this.getItems();
7348         items.each(function(f){
7349             f.reset();
7350         });
7351         
7352         Roo.each(this.childForms || [], function (f) {
7353             f.reset();
7354         });
7355        
7356         
7357         return this;
7358     },
7359     getItems : function()
7360     {
7361         var r=new Roo.util.MixedCollection(false, function(o){
7362             return o.id || (o.id = Roo.id());
7363         });
7364         var iter = function(el) {
7365             if (el.inputEl) {
7366                 r.add(el);
7367             }
7368             if (!el.items) {
7369                 return;
7370             }
7371             Roo.each(el.items,function(e) {
7372                 iter(e);
7373             });
7374             
7375             
7376         };
7377         
7378         iter(this);
7379         return r;
7380         
7381         
7382         
7383         
7384     }
7385     
7386 });
7387
7388  
7389 /*
7390  * Based on:
7391  * Ext JS Library 1.1.1
7392  * Copyright(c) 2006-2007, Ext JS, LLC.
7393  *
7394  * Originally Released Under LGPL - original licence link has changed is not relivant.
7395  *
7396  * Fork - LGPL
7397  * <script type="text/javascript">
7398  */
7399 /**
7400  * @class Roo.form.VTypes
7401  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7402  * @singleton
7403  */
7404 Roo.form.VTypes = function(){
7405     // closure these in so they are only created once.
7406     var alpha = /^[a-zA-Z_]+$/;
7407     var alphanum = /^[a-zA-Z0-9_]+$/;
7408     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
7409     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7410
7411     // All these messages and functions are configurable
7412     return {
7413         /**
7414          * The function used to validate email addresses
7415          * @param {String} value The email address
7416          */
7417         'email' : function(v){
7418             return email.test(v);
7419         },
7420         /**
7421          * The error text to display when the email validation function returns false
7422          * @type String
7423          */
7424         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7425         /**
7426          * The keystroke filter mask to be applied on email input
7427          * @type RegExp
7428          */
7429         'emailMask' : /[a-z0-9_\.\-@]/i,
7430
7431         /**
7432          * The function used to validate URLs
7433          * @param {String} value The URL
7434          */
7435         'url' : function(v){
7436             return url.test(v);
7437         },
7438         /**
7439          * The error text to display when the url validation function returns false
7440          * @type String
7441          */
7442         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7443         
7444         /**
7445          * The function used to validate alpha values
7446          * @param {String} value The value
7447          */
7448         'alpha' : function(v){
7449             return alpha.test(v);
7450         },
7451         /**
7452          * The error text to display when the alpha validation function returns false
7453          * @type String
7454          */
7455         'alphaText' : 'This field should only contain letters and _',
7456         /**
7457          * The keystroke filter mask to be applied on alpha input
7458          * @type RegExp
7459          */
7460         'alphaMask' : /[a-z_]/i,
7461
7462         /**
7463          * The function used to validate alphanumeric values
7464          * @param {String} value The value
7465          */
7466         'alphanum' : function(v){
7467             return alphanum.test(v);
7468         },
7469         /**
7470          * The error text to display when the alphanumeric validation function returns false
7471          * @type String
7472          */
7473         'alphanumText' : 'This field should only contain letters, numbers and _',
7474         /**
7475          * The keystroke filter mask to be applied on alphanumeric input
7476          * @type RegExp
7477          */
7478         'alphanumMask' : /[a-z0-9_]/i
7479     };
7480 }();/*
7481  * - LGPL
7482  *
7483  * Input
7484  * 
7485  */
7486
7487 /**
7488  * @class Roo.bootstrap.Input
7489  * @extends Roo.bootstrap.Component
7490  * Bootstrap Input class
7491  * @cfg {Boolean} disabled is it disabled
7492  * @cfg {String} fieldLabel - the label associated
7493  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7494  * @cfg {String} name name of the input
7495  * @cfg {string} fieldLabel - the label associated
7496  * @cfg {string}  inputType - input / file submit ...
7497  * @cfg {string} placeholder - placeholder to put in text.
7498  * @cfg {string}  before - input group add on before
7499  * @cfg {string} after - input group add on after
7500  * @cfg {string} size - (lg|sm) or leave empty..
7501  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7502  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7503  * @cfg {Number} md colspan out of 12 for computer-sized screens
7504  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7505  * @cfg {string} value default value of the input
7506  * @cfg {Number} labelWidth set the width of label (0-12)
7507  * @cfg {String} labelAlign (top|left)
7508  * @cfg {Boolean} readOnly Specifies that the field should be read-only
7509  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7510
7511  * @cfg {String} align (left|center|right) Default left
7512  * @cfg {Boolean} forceFeedback (true|false) Default false
7513  * 
7514  * 
7515  * 
7516  * 
7517  * @constructor
7518  * Create a new Input
7519  * @param {Object} config The config object
7520  */
7521
7522 Roo.bootstrap.Input = function(config){
7523     Roo.bootstrap.Input.superclass.constructor.call(this, config);
7524    
7525         this.addEvents({
7526             /**
7527              * @event focus
7528              * Fires when this field receives input focus.
7529              * @param {Roo.form.Field} this
7530              */
7531             focus : true,
7532             /**
7533              * @event blur
7534              * Fires when this field loses input focus.
7535              * @param {Roo.form.Field} this
7536              */
7537             blur : true,
7538             /**
7539              * @event specialkey
7540              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
7541              * {@link Roo.EventObject#getKey} to determine which key was pressed.
7542              * @param {Roo.form.Field} this
7543              * @param {Roo.EventObject} e The event object
7544              */
7545             specialkey : true,
7546             /**
7547              * @event change
7548              * Fires just before the field blurs if the field value has changed.
7549              * @param {Roo.form.Field} this
7550              * @param {Mixed} newValue The new value
7551              * @param {Mixed} oldValue The original value
7552              */
7553             change : true,
7554             /**
7555              * @event invalid
7556              * Fires after the field has been marked as invalid.
7557              * @param {Roo.form.Field} this
7558              * @param {String} msg The validation message
7559              */
7560             invalid : true,
7561             /**
7562              * @event valid
7563              * Fires after the field has been validated with no errors.
7564              * @param {Roo.form.Field} this
7565              */
7566             valid : true,
7567              /**
7568              * @event keyup
7569              * Fires after the key up
7570              * @param {Roo.form.Field} this
7571              * @param {Roo.EventObject}  e The event Object
7572              */
7573             keyup : true
7574         });
7575 };
7576
7577 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
7578      /**
7579      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7580       automatic validation (defaults to "keyup").
7581      */
7582     validationEvent : "keyup",
7583      /**
7584      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7585      */
7586     validateOnBlur : true,
7587     /**
7588      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7589      */
7590     validationDelay : 250,
7591      /**
7592      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7593      */
7594     focusClass : "x-form-focus",  // not needed???
7595     
7596        
7597     /**
7598      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7599      */
7600     invalidClass : "has-warning",
7601     
7602     /**
7603      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7604      */
7605     validClass : "has-success",
7606     
7607     /**
7608      * @cfg {Boolean} hasFeedback (true|false) default true
7609      */
7610     hasFeedback : true,
7611     
7612     /**
7613      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7614      */
7615     invalidFeedbackClass : "glyphicon-warning-sign",
7616     
7617     /**
7618      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7619      */
7620     validFeedbackClass : "glyphicon-ok",
7621     
7622     /**
7623      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7624      */
7625     selectOnFocus : false,
7626     
7627      /**
7628      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7629      */
7630     maskRe : null,
7631        /**
7632      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7633      */
7634     vtype : null,
7635     
7636       /**
7637      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7638      */
7639     disableKeyFilter : false,
7640     
7641        /**
7642      * @cfg {Boolean} disabled True to disable the field (defaults to false).
7643      */
7644     disabled : false,
7645      /**
7646      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7647      */
7648     allowBlank : true,
7649     /**
7650      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7651      */
7652     blankText : "This field is required",
7653     
7654      /**
7655      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7656      */
7657     minLength : 0,
7658     /**
7659      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7660      */
7661     maxLength : Number.MAX_VALUE,
7662     /**
7663      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7664      */
7665     minLengthText : "The minimum length for this field is {0}",
7666     /**
7667      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7668      */
7669     maxLengthText : "The maximum length for this field is {0}",
7670   
7671     
7672     /**
7673      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7674      * If available, this function will be called only after the basic validators all return true, and will be passed the
7675      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7676      */
7677     validator : null,
7678     /**
7679      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7680      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7681      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7682      */
7683     regex : null,
7684     /**
7685      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7686      */
7687     regexText : "",
7688     
7689     autocomplete: false,
7690     
7691     
7692     fieldLabel : '',
7693     inputType : 'text',
7694     
7695     name : false,
7696     placeholder: false,
7697     before : false,
7698     after : false,
7699     size : false,
7700     hasFocus : false,
7701     preventMark: false,
7702     isFormField : true,
7703     value : '',
7704     labelWidth : 2,
7705     labelAlign : false,
7706     readOnly : false,
7707     align : false,
7708     formatedValue : false,
7709     forceFeedback : false,
7710     
7711     parentLabelAlign : function()
7712     {
7713         var parent = this;
7714         while (parent.parent()) {
7715             parent = parent.parent();
7716             if (typeof(parent.labelAlign) !='undefined') {
7717                 return parent.labelAlign;
7718             }
7719         }
7720         return 'left';
7721         
7722     },
7723     
7724     getAutoCreate : function(){
7725         
7726         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7727         
7728         var id = Roo.id();
7729         
7730         var cfg = {};
7731         
7732         if(this.inputType != 'hidden'){
7733             cfg.cls = 'form-group' //input-group
7734         }
7735         
7736         var input =  {
7737             tag: 'input',
7738             id : id,
7739             type : this.inputType,
7740             value : this.value,
7741             cls : 'form-control',
7742             placeholder : this.placeholder || '',
7743             autocomplete : this.autocomplete || 'new-password'
7744         };
7745         
7746         
7747         if(this.align){
7748             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7749         }
7750         
7751         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7752             input.maxLength = this.maxLength;
7753         }
7754         
7755         if (this.disabled) {
7756             input.disabled=true;
7757         }
7758         
7759         if (this.readOnly) {
7760             input.readonly=true;
7761         }
7762         
7763         if (this.name) {
7764             input.name = this.name;
7765         }
7766         if (this.size) {
7767             input.cls += ' input-' + this.size;
7768         }
7769         var settings=this;
7770         ['xs','sm','md','lg'].map(function(size){
7771             if (settings[size]) {
7772                 cfg.cls += ' col-' + size + '-' + settings[size];
7773             }
7774         });
7775         
7776         var inputblock = input;
7777         
7778         var feedback = {
7779             tag: 'span',
7780             cls: 'glyphicon form-control-feedback'
7781         };
7782             
7783         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7784             
7785             inputblock = {
7786                 cls : 'has-feedback',
7787                 cn :  [
7788                     input,
7789                     feedback
7790                 ] 
7791             };  
7792         }
7793         
7794         if (this.before || this.after) {
7795             
7796             inputblock = {
7797                 cls : 'input-group',
7798                 cn :  [] 
7799             };
7800             
7801             if (this.before && typeof(this.before) == 'string') {
7802                 
7803                 inputblock.cn.push({
7804                     tag :'span',
7805                     cls : 'roo-input-before input-group-addon',
7806                     html : this.before
7807                 });
7808             }
7809             if (this.before && typeof(this.before) == 'object') {
7810                 this.before = Roo.factory(this.before);
7811                 Roo.log(this.before);
7812                 inputblock.cn.push({
7813                     tag :'span',
7814                     cls : 'roo-input-before input-group-' +
7815                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7816                 });
7817             }
7818             
7819             inputblock.cn.push(input);
7820             
7821             if (this.after && typeof(this.after) == 'string') {
7822                 inputblock.cn.push({
7823                     tag :'span',
7824                     cls : 'roo-input-after input-group-addon',
7825                     html : this.after
7826                 });
7827             }
7828             if (this.after && typeof(this.after) == 'object') {
7829                 this.after = Roo.factory(this.after);
7830                 Roo.log(this.after);
7831                 inputblock.cn.push({
7832                     tag :'span',
7833                     cls : 'roo-input-after input-group-' +
7834                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7835                 });
7836             }
7837             
7838             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7839                 inputblock.cls += ' has-feedback';
7840                 inputblock.cn.push(feedback);
7841             }
7842         };
7843         
7844         if (align ==='left' && this.fieldLabel.length) {
7845                 Roo.log("left and has label");
7846                 cfg.cn = [
7847                     
7848                     {
7849                         tag: 'label',
7850                         'for' :  id,
7851                         cls : 'control-label col-sm-' + this.labelWidth,
7852                         html : this.fieldLabel
7853                         
7854                     },
7855                     {
7856                         cls : "col-sm-" + (12 - this.labelWidth), 
7857                         cn: [
7858                             inputblock
7859                         ]
7860                     }
7861                     
7862                 ];
7863         } else if ( this.fieldLabel.length) {
7864                 Roo.log(" label");
7865                  cfg.cn = [
7866                    
7867                     {
7868                         tag: 'label',
7869                         //cls : 'input-group-addon',
7870                         html : this.fieldLabel
7871                         
7872                     },
7873                     
7874                     inputblock
7875                     
7876                 ];
7877
7878         } else {
7879             
7880                 Roo.log(" no label && no align");
7881                 cfg.cn = [
7882                     
7883                         inputblock
7884                     
7885                 ];
7886                 
7887                 
7888         };
7889         Roo.log('input-parentType: ' + this.parentType);
7890         
7891         if (this.parentType === 'Navbar' &&  this.parent().bar) {
7892            cfg.cls += ' navbar-form';
7893            Roo.log(cfg);
7894         }
7895         
7896         return cfg;
7897         
7898     },
7899     /**
7900      * return the real input element.
7901      */
7902     inputEl: function ()
7903     {
7904         return this.el.select('input.form-control',true).first();
7905     },
7906     
7907     tooltipEl : function()
7908     {
7909         return this.inputEl();
7910     },
7911     
7912     setDisabled : function(v)
7913     {
7914         var i  = this.inputEl().dom;
7915         if (!v) {
7916             i.removeAttribute('disabled');
7917             return;
7918             
7919         }
7920         i.setAttribute('disabled','true');
7921     },
7922     initEvents : function()
7923     {
7924           
7925         this.inputEl().on("keydown" , this.fireKey,  this);
7926         this.inputEl().on("focus", this.onFocus,  this);
7927         this.inputEl().on("blur", this.onBlur,  this);
7928         
7929         this.inputEl().relayEvent('keyup', this);
7930  
7931         // reference to original value for reset
7932         this.originalValue = this.getValue();
7933         //Roo.form.TextField.superclass.initEvents.call(this);
7934         if(this.validationEvent == 'keyup'){
7935             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7936             this.inputEl().on('keyup', this.filterValidation, this);
7937         }
7938         else if(this.validationEvent !== false){
7939             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7940         }
7941         
7942         if(this.selectOnFocus){
7943             this.on("focus", this.preFocus, this);
7944             
7945         }
7946         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7947             this.inputEl().on("keypress", this.filterKeys, this);
7948         }
7949        /* if(this.grow){
7950             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7951             this.el.on("click", this.autoSize,  this);
7952         }
7953         */
7954         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7955             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7956         }
7957         
7958         if (typeof(this.before) == 'object') {
7959             this.before.render(this.el.select('.roo-input-before',true).first());
7960         }
7961         if (typeof(this.after) == 'object') {
7962             this.after.render(this.el.select('.roo-input-after',true).first());
7963         }
7964         
7965         
7966     },
7967     filterValidation : function(e){
7968         if(!e.isNavKeyPress()){
7969             this.validationTask.delay(this.validationDelay);
7970         }
7971     },
7972      /**
7973      * Validates the field value
7974      * @return {Boolean} True if the value is valid, else false
7975      */
7976     validate : function(){
7977         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7978         if(this.disabled || this.validateValue(this.getRawValue())){
7979             this.markValid();
7980             return true;
7981         }
7982         
7983         this.markInvalid();
7984         return false;
7985     },
7986     
7987     
7988     /**
7989      * Validates a value according to the field's validation rules and marks the field as invalid
7990      * if the validation fails
7991      * @param {Mixed} value The value to validate
7992      * @return {Boolean} True if the value is valid, else false
7993      */
7994     validateValue : function(value){
7995         if(value.length < 1)  { // if it's blank
7996             if(this.allowBlank){
7997                 return true;
7998             }
7999             return false;
8000         }
8001         
8002         if(value.length < this.minLength){
8003             return false;
8004         }
8005         if(value.length > this.maxLength){
8006             return false;
8007         }
8008         if(this.vtype){
8009             var vt = Roo.form.VTypes;
8010             if(!vt[this.vtype](value, this)){
8011                 return false;
8012             }
8013         }
8014         if(typeof this.validator == "function"){
8015             var msg = this.validator(value);
8016             if(msg !== true){
8017                 return false;
8018             }
8019         }
8020         
8021         if(this.regex && !this.regex.test(value)){
8022             return false;
8023         }
8024         
8025         return true;
8026     },
8027
8028     
8029     
8030      // private
8031     fireKey : function(e){
8032         //Roo.log('field ' + e.getKey());
8033         if(e.isNavKeyPress()){
8034             this.fireEvent("specialkey", this, e);
8035         }
8036     },
8037     focus : function (selectText){
8038         if(this.rendered){
8039             this.inputEl().focus();
8040             if(selectText === true){
8041                 this.inputEl().dom.select();
8042             }
8043         }
8044         return this;
8045     } ,
8046     
8047     onFocus : function(){
8048         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8049            // this.el.addClass(this.focusClass);
8050         }
8051         if(!this.hasFocus){
8052             this.hasFocus = true;
8053             this.startValue = this.getValue();
8054             this.fireEvent("focus", this);
8055         }
8056     },
8057     
8058     beforeBlur : Roo.emptyFn,
8059
8060     
8061     // private
8062     onBlur : function(){
8063         this.beforeBlur();
8064         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8065             //this.el.removeClass(this.focusClass);
8066         }
8067         this.hasFocus = false;
8068         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8069             this.validate();
8070         }
8071         var v = this.getValue();
8072         if(String(v) !== String(this.startValue)){
8073             this.fireEvent('change', this, v, this.startValue);
8074         }
8075         this.fireEvent("blur", this);
8076     },
8077     
8078     /**
8079      * Resets the current field value to the originally loaded value and clears any validation messages
8080      */
8081     reset : function(){
8082         this.setValue(this.originalValue);
8083         this.validate();
8084     },
8085      /**
8086      * Returns the name of the field
8087      * @return {Mixed} name The name field
8088      */
8089     getName: function(){
8090         return this.name;
8091     },
8092      /**
8093      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8094      * @return {Mixed} value The field value
8095      */
8096     getValue : function(){
8097         
8098         var v = this.inputEl().getValue();
8099         
8100         return v;
8101     },
8102     /**
8103      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8104      * @return {Mixed} value The field value
8105      */
8106     getRawValue : function(){
8107         var v = this.inputEl().getValue();
8108         
8109         return v;
8110     },
8111     
8112     /**
8113      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8114      * @param {Mixed} value The value to set
8115      */
8116     setRawValue : function(v){
8117         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8118     },
8119     
8120     selectText : function(start, end){
8121         var v = this.getRawValue();
8122         if(v.length > 0){
8123             start = start === undefined ? 0 : start;
8124             end = end === undefined ? v.length : end;
8125             var d = this.inputEl().dom;
8126             if(d.setSelectionRange){
8127                 d.setSelectionRange(start, end);
8128             }else if(d.createTextRange){
8129                 var range = d.createTextRange();
8130                 range.moveStart("character", start);
8131                 range.moveEnd("character", v.length-end);
8132                 range.select();
8133             }
8134         }
8135     },
8136     
8137     /**
8138      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8139      * @param {Mixed} value The value to set
8140      */
8141     setValue : function(v){
8142         this.value = v;
8143         if(this.rendered){
8144             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8145             this.validate();
8146         }
8147     },
8148     
8149     /*
8150     processValue : function(value){
8151         if(this.stripCharsRe){
8152             var newValue = value.replace(this.stripCharsRe, '');
8153             if(newValue !== value){
8154                 this.setRawValue(newValue);
8155                 return newValue;
8156             }
8157         }
8158         return value;
8159     },
8160   */
8161     preFocus : function(){
8162         
8163         if(this.selectOnFocus){
8164             this.inputEl().dom.select();
8165         }
8166     },
8167     filterKeys : function(e){
8168         var k = e.getKey();
8169         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8170             return;
8171         }
8172         var c = e.getCharCode(), cc = String.fromCharCode(c);
8173         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8174             return;
8175         }
8176         if(!this.maskRe.test(cc)){
8177             e.stopEvent();
8178         }
8179     },
8180      /**
8181      * Clear any invalid styles/messages for this field
8182      */
8183     clearInvalid : function(){
8184         
8185         if(!this.el || this.preventMark){ // not rendered
8186             return;
8187         }
8188         this.el.removeClass(this.invalidClass);
8189         
8190         this.fireEvent('valid', this);
8191     },
8192     
8193      /**
8194      * Mark this field as valid
8195      */
8196     markValid : function(){
8197         if(!this.el  || this.preventMark){ // not rendered
8198             return;
8199         }
8200         
8201         this.el.removeClass([this.invalidClass, this.validClass]);
8202         
8203         var feedback = this.el.select('.form-control-feedback', true).first();
8204             
8205         if(feedback){
8206             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8207         }
8208
8209         if(this.disabled || this.allowBlank){
8210             return;
8211         }
8212         
8213         this.el.addClass(this.validClass);
8214         
8215         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8216             
8217             var feedback = this.el.select('.form-control-feedback', true).first();
8218             
8219             if(feedback){
8220                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8221                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8222             }
8223             
8224         }
8225         
8226         this.fireEvent('valid', this);
8227     },
8228     
8229      /**
8230      * Mark this field as invalid
8231      * @param {String} msg The validation message
8232      */
8233     markInvalid : function(msg){
8234         if(!this.el  || this.preventMark){ // not rendered
8235             return;
8236         }
8237         
8238         this.el.removeClass([this.invalidClass, this.validClass]);
8239         
8240         var feedback = this.el.select('.form-control-feedback', true).first();
8241             
8242         if(feedback){
8243             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8244         }
8245
8246         if(this.disabled || this.allowBlank){
8247             return;
8248         }
8249         
8250         this.el.addClass(this.invalidClass);
8251         
8252         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8253             
8254             var feedback = this.el.select('.form-control-feedback', true).first();
8255             
8256             if(feedback){
8257                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8258                 
8259                 if(this.getValue().length || this.forceFeedback){
8260                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8261                 }
8262                 
8263             }
8264             
8265         }
8266         
8267         this.fireEvent('invalid', this, msg);
8268     },
8269     // private
8270     SafariOnKeyDown : function(event)
8271     {
8272         // this is a workaround for a password hang bug on chrome/ webkit.
8273         
8274         var isSelectAll = false;
8275         
8276         if(this.inputEl().dom.selectionEnd > 0){
8277             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8278         }
8279         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8280             event.preventDefault();
8281             this.setValue('');
8282             return;
8283         }
8284         
8285         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
8286             
8287             event.preventDefault();
8288             // this is very hacky as keydown always get's upper case.
8289             //
8290             var cc = String.fromCharCode(event.getCharCode());
8291             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8292             
8293         }
8294     },
8295     adjustWidth : function(tag, w){
8296         tag = tag.toLowerCase();
8297         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8298             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8299                 if(tag == 'input'){
8300                     return w + 2;
8301                 }
8302                 if(tag == 'textarea'){
8303                     return w-2;
8304                 }
8305             }else if(Roo.isOpera){
8306                 if(tag == 'input'){
8307                     return w + 2;
8308                 }
8309                 if(tag == 'textarea'){
8310                     return w-2;
8311                 }
8312             }
8313         }
8314         return w;
8315     }
8316     
8317 });
8318
8319  
8320 /*
8321  * - LGPL
8322  *
8323  * Input
8324  * 
8325  */
8326
8327 /**
8328  * @class Roo.bootstrap.TextArea
8329  * @extends Roo.bootstrap.Input
8330  * Bootstrap TextArea class
8331  * @cfg {Number} cols Specifies the visible width of a text area
8332  * @cfg {Number} rows Specifies the visible number of lines in a text area
8333  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8334  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8335  * @cfg {string} html text
8336  * 
8337  * @constructor
8338  * Create a new TextArea
8339  * @param {Object} config The config object
8340  */
8341
8342 Roo.bootstrap.TextArea = function(config){
8343     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8344    
8345 };
8346
8347 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8348      
8349     cols : false,
8350     rows : 5,
8351     readOnly : false,
8352     warp : 'soft',
8353     resize : false,
8354     value: false,
8355     html: false,
8356     
8357     getAutoCreate : function(){
8358         
8359         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8360         
8361         var id = Roo.id();
8362         
8363         var cfg = {};
8364         
8365         var input =  {
8366             tag: 'textarea',
8367             id : id,
8368             warp : this.warp,
8369             rows : this.rows,
8370             value : this.value || '',
8371             html: this.html || '',
8372             cls : 'form-control',
8373             placeholder : this.placeholder || '' 
8374             
8375         };
8376         
8377         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8378             input.maxLength = this.maxLength;
8379         }
8380         
8381         if(this.resize){
8382             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8383         }
8384         
8385         if(this.cols){
8386             input.cols = this.cols;
8387         }
8388         
8389         if (this.readOnly) {
8390             input.readonly = true;
8391         }
8392         
8393         if (this.name) {
8394             input.name = this.name;
8395         }
8396         
8397         if (this.size) {
8398             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8399         }
8400         
8401         var settings=this;
8402         ['xs','sm','md','lg'].map(function(size){
8403             if (settings[size]) {
8404                 cfg.cls += ' col-' + size + '-' + settings[size];
8405             }
8406         });
8407         
8408         var inputblock = input;
8409         
8410         if(this.hasFeedback && !this.allowBlank){
8411             
8412             var feedback = {
8413                 tag: 'span',
8414                 cls: 'glyphicon form-control-feedback'
8415             };
8416
8417             inputblock = {
8418                 cls : 'has-feedback',
8419                 cn :  [
8420                     input,
8421                     feedback
8422                 ] 
8423             };  
8424         }
8425         
8426         
8427         if (this.before || this.after) {
8428             
8429             inputblock = {
8430                 cls : 'input-group',
8431                 cn :  [] 
8432             };
8433             if (this.before) {
8434                 inputblock.cn.push({
8435                     tag :'span',
8436                     cls : 'input-group-addon',
8437                     html : this.before
8438                 });
8439             }
8440             
8441             inputblock.cn.push(input);
8442             
8443             if(this.hasFeedback && !this.allowBlank){
8444                 inputblock.cls += ' has-feedback';
8445                 inputblock.cn.push(feedback);
8446             }
8447             
8448             if (this.after) {
8449                 inputblock.cn.push({
8450                     tag :'span',
8451                     cls : 'input-group-addon',
8452                     html : this.after
8453                 });
8454             }
8455             
8456         }
8457         
8458         if (align ==='left' && this.fieldLabel.length) {
8459                 Roo.log("left and has label");
8460                 cfg.cn = [
8461                     
8462                     {
8463                         tag: 'label',
8464                         'for' :  id,
8465                         cls : 'control-label col-sm-' + this.labelWidth,
8466                         html : this.fieldLabel
8467                         
8468                     },
8469                     {
8470                         cls : "col-sm-" + (12 - this.labelWidth), 
8471                         cn: [
8472                             inputblock
8473                         ]
8474                     }
8475                     
8476                 ];
8477         } else if ( this.fieldLabel.length) {
8478                 Roo.log(" label");
8479                  cfg.cn = [
8480                    
8481                     {
8482                         tag: 'label',
8483                         //cls : 'input-group-addon',
8484                         html : this.fieldLabel
8485                         
8486                     },
8487                     
8488                     inputblock
8489                     
8490                 ];
8491
8492         } else {
8493             
8494                    Roo.log(" no label && no align");
8495                 cfg.cn = [
8496                     
8497                         inputblock
8498                     
8499                 ];
8500                 
8501                 
8502         }
8503         
8504         if (this.disabled) {
8505             input.disabled=true;
8506         }
8507         
8508         return cfg;
8509         
8510     },
8511     /**
8512      * return the real textarea element.
8513      */
8514     inputEl: function ()
8515     {
8516         return this.el.select('textarea.form-control',true).first();
8517     }
8518 });
8519
8520  
8521 /*
8522  * - LGPL
8523  *
8524  * trigger field - base class for combo..
8525  * 
8526  */
8527  
8528 /**
8529  * @class Roo.bootstrap.TriggerField
8530  * @extends Roo.bootstrap.Input
8531  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8532  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8533  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8534  * for which you can provide a custom implementation.  For example:
8535  * <pre><code>
8536 var trigger = new Roo.bootstrap.TriggerField();
8537 trigger.onTriggerClick = myTriggerFn;
8538 trigger.applyTo('my-field');
8539 </code></pre>
8540  *
8541  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8542  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8543  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
8544  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8545  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8546
8547  * @constructor
8548  * Create a new TriggerField.
8549  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8550  * to the base TextField)
8551  */
8552 Roo.bootstrap.TriggerField = function(config){
8553     this.mimicing = false;
8554     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8555 };
8556
8557 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
8558     /**
8559      * @cfg {String} triggerClass A CSS class to apply to the trigger
8560      */
8561      /**
8562      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8563      */
8564     hideTrigger:false,
8565
8566     /**
8567      * @cfg {Boolean} removable (true|false) special filter default false
8568      */
8569     removable : false,
8570     
8571     /** @cfg {Boolean} grow @hide */
8572     /** @cfg {Number} growMin @hide */
8573     /** @cfg {Number} growMax @hide */
8574
8575     /**
8576      * @hide 
8577      * @method
8578      */
8579     autoSize: Roo.emptyFn,
8580     // private
8581     monitorTab : true,
8582     // private
8583     deferHeight : true,
8584
8585     
8586     actionMode : 'wrap',
8587     
8588     caret : false,
8589     
8590     
8591     getAutoCreate : function(){
8592        
8593         var align = this.labelAlign || this.parentLabelAlign();
8594         
8595         var id = Roo.id();
8596         
8597         var cfg = {
8598             cls: 'form-group' //input-group
8599         };
8600         
8601         
8602         var input =  {
8603             tag: 'input',
8604             id : id,
8605             type : this.inputType,
8606             cls : 'form-control',
8607             autocomplete: 'new-password',
8608             placeholder : this.placeholder || '' 
8609             
8610         };
8611         if (this.name) {
8612             input.name = this.name;
8613         }
8614         if (this.size) {
8615             input.cls += ' input-' + this.size;
8616         }
8617         
8618         if (this.disabled) {
8619             input.disabled=true;
8620         }
8621         
8622         var inputblock = input;
8623         
8624         if(this.hasFeedback && !this.allowBlank){
8625             
8626             var feedback = {
8627                 tag: 'span',
8628                 cls: 'glyphicon form-control-feedback'
8629             };
8630             
8631             if(this.removable && !this.editable && !this.tickable){
8632                 inputblock = {
8633                     cls : 'has-feedback',
8634                     cn :  [
8635                         inputblock,
8636                         {
8637                             tag: 'button',
8638                             html : 'x',
8639                             cls : 'roo-combo-removable-btn close'
8640                         },
8641                         feedback
8642                     ] 
8643                 };
8644             } else {
8645                 inputblock = {
8646                     cls : 'has-feedback',
8647                     cn :  [
8648                         inputblock,
8649                         feedback
8650                     ] 
8651                 };
8652             }
8653
8654         } else {
8655             if(this.removable && !this.editable && !this.tickable){
8656                 inputblock = {
8657                     cls : 'roo-removable',
8658                     cn :  [
8659                         inputblock,
8660                         {
8661                             tag: 'button',
8662                             html : 'x',
8663                             cls : 'roo-combo-removable-btn close'
8664                         }
8665                     ] 
8666                 };
8667             }
8668         }
8669         
8670         if (this.before || this.after) {
8671             
8672             inputblock = {
8673                 cls : 'input-group',
8674                 cn :  [] 
8675             };
8676             if (this.before) {
8677                 inputblock.cn.push({
8678                     tag :'span',
8679                     cls : 'input-group-addon',
8680                     html : this.before
8681                 });
8682             }
8683             
8684             inputblock.cn.push(input);
8685             
8686             if(this.hasFeedback && !this.allowBlank){
8687                 inputblock.cls += ' has-feedback';
8688                 inputblock.cn.push(feedback);
8689             }
8690             
8691             if (this.after) {
8692                 inputblock.cn.push({
8693                     tag :'span',
8694                     cls : 'input-group-addon',
8695                     html : this.after
8696                 });
8697             }
8698             
8699         };
8700         
8701         var box = {
8702             tag: 'div',
8703             cn: [
8704                 {
8705                     tag: 'input',
8706                     type : 'hidden',
8707                     cls: 'form-hidden-field'
8708                 },
8709                 inputblock
8710             ]
8711             
8712         };
8713         
8714         if(this.multiple){
8715             Roo.log('multiple');
8716             
8717             box = {
8718                 tag: 'div',
8719                 cn: [
8720                     {
8721                         tag: 'input',
8722                         type : 'hidden',
8723                         cls: 'form-hidden-field'
8724                     },
8725                     {
8726                         tag: 'ul',
8727                         cls: 'select2-choices',
8728                         cn:[
8729                             {
8730                                 tag: 'li',
8731                                 cls: 'select2-search-field',
8732                                 cn: [
8733
8734                                     inputblock
8735                                 ]
8736                             }
8737                         ]
8738                     }
8739                 ]
8740             }
8741         };
8742         
8743         var combobox = {
8744             cls: 'select2-container input-group',
8745             cn: [
8746                 box
8747 //                {
8748 //                    tag: 'ul',
8749 //                    cls: 'typeahead typeahead-long dropdown-menu',
8750 //                    style: 'display:none'
8751 //                }
8752             ]
8753         };
8754         
8755         if(!this.multiple && this.showToggleBtn){
8756             
8757             var caret = {
8758                         tag: 'span',
8759                         cls: 'caret'
8760              };
8761             if (this.caret != false) {
8762                 caret = {
8763                      tag: 'i',
8764                      cls: 'fa fa-' + this.caret
8765                 };
8766                 
8767             }
8768             
8769             combobox.cn.push({
8770                 tag :'span',
8771                 cls : 'input-group-addon btn dropdown-toggle',
8772                 cn : [
8773                     caret,
8774                     {
8775                         tag: 'span',
8776                         cls: 'combobox-clear',
8777                         cn  : [
8778                             {
8779                                 tag : 'i',
8780                                 cls: 'icon-remove'
8781                             }
8782                         ]
8783                     }
8784                 ]
8785
8786             })
8787         }
8788         
8789         if(this.multiple){
8790             combobox.cls += ' select2-container-multi';
8791         }
8792         
8793         if (align ==='left' && this.fieldLabel.length) {
8794             
8795                 Roo.log("left and has label");
8796                 cfg.cn = [
8797                     
8798                     {
8799                         tag: 'label',
8800                         'for' :  id,
8801                         cls : 'control-label col-sm-' + this.labelWidth,
8802                         html : this.fieldLabel
8803                         
8804                     },
8805                     {
8806                         cls : "col-sm-" + (12 - this.labelWidth), 
8807                         cn: [
8808                             combobox
8809                         ]
8810                     }
8811                     
8812                 ];
8813         } else if ( this.fieldLabel.length) {
8814                 Roo.log(" label");
8815                  cfg.cn = [
8816                    
8817                     {
8818                         tag: 'label',
8819                         //cls : 'input-group-addon',
8820                         html : this.fieldLabel
8821                         
8822                     },
8823                     
8824                     combobox
8825                     
8826                 ];
8827
8828         } else {
8829             
8830                 Roo.log(" no label && no align");
8831                 cfg = combobox
8832                      
8833                 
8834         }
8835          
8836         var settings=this;
8837         ['xs','sm','md','lg'].map(function(size){
8838             if (settings[size]) {
8839                 cfg.cls += ' col-' + size + '-' + settings[size];
8840             }
8841         });
8842         Roo.log(cfg);
8843         return cfg;
8844         
8845     },
8846     
8847     
8848     
8849     // private
8850     onResize : function(w, h){
8851 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8852 //        if(typeof w == 'number'){
8853 //            var x = w - this.trigger.getWidth();
8854 //            this.inputEl().setWidth(this.adjustWidth('input', x));
8855 //            this.trigger.setStyle('left', x+'px');
8856 //        }
8857     },
8858
8859     // private
8860     adjustSize : Roo.BoxComponent.prototype.adjustSize,
8861
8862     // private
8863     getResizeEl : function(){
8864         return this.inputEl();
8865     },
8866
8867     // private
8868     getPositionEl : function(){
8869         return this.inputEl();
8870     },
8871
8872     // private
8873     alignErrorIcon : function(){
8874         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8875     },
8876
8877     // private
8878     initEvents : function(){
8879         
8880         this.createList();
8881         
8882         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8883         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8884         if(!this.multiple && this.showToggleBtn){
8885             this.trigger = this.el.select('span.dropdown-toggle',true).first();
8886             if(this.hideTrigger){
8887                 this.trigger.setDisplayed(false);
8888             }
8889             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8890         }
8891         
8892         if(this.multiple){
8893             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8894         }
8895         
8896         if(this.removable && !this.editable && !this.tickable){
8897             var close = this.closeTriggerEl();
8898             
8899             if(close){
8900                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
8901                 close.on('click', this.removeBtnClick, this, close);
8902             }
8903         }
8904         
8905         //this.trigger.addClassOnOver('x-form-trigger-over');
8906         //this.trigger.addClassOnClick('x-form-trigger-click');
8907         
8908         //if(!this.width){
8909         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8910         //}
8911     },
8912     
8913     closeTriggerEl : function()
8914     {
8915         var close = this.el.select('.roo-combo-removable-btn', true).first();
8916         return close ? close : false;
8917     },
8918     
8919     removeBtnClick : function(e, h, el)
8920     {
8921         e.preventDefault();
8922         
8923         if(this.fireEvent("remove", this) !== false){
8924             this.reset();
8925         }
8926     },
8927     
8928     createList : function()
8929     {
8930         this.list = Roo.get(document.body).createChild({
8931             tag: 'ul',
8932             cls: 'typeahead typeahead-long dropdown-menu',
8933             style: 'display:none'
8934         });
8935         
8936         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8937         
8938     },
8939
8940     // private
8941     initTrigger : function(){
8942        
8943     },
8944
8945     // private
8946     onDestroy : function(){
8947         if(this.trigger){
8948             this.trigger.removeAllListeners();
8949           //  this.trigger.remove();
8950         }
8951         //if(this.wrap){
8952         //    this.wrap.remove();
8953         //}
8954         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8955     },
8956
8957     // private
8958     onFocus : function(){
8959         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8960         /*
8961         if(!this.mimicing){
8962             this.wrap.addClass('x-trigger-wrap-focus');
8963             this.mimicing = true;
8964             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8965             if(this.monitorTab){
8966                 this.el.on("keydown", this.checkTab, this);
8967             }
8968         }
8969         */
8970     },
8971
8972     // private
8973     checkTab : function(e){
8974         if(e.getKey() == e.TAB){
8975             this.triggerBlur();
8976         }
8977     },
8978
8979     // private
8980     onBlur : function(){
8981         // do nothing
8982     },
8983
8984     // private
8985     mimicBlur : function(e, t){
8986         /*
8987         if(!this.wrap.contains(t) && this.validateBlur()){
8988             this.triggerBlur();
8989         }
8990         */
8991     },
8992
8993     // private
8994     triggerBlur : function(){
8995         this.mimicing = false;
8996         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8997         if(this.monitorTab){
8998             this.el.un("keydown", this.checkTab, this);
8999         }
9000         //this.wrap.removeClass('x-trigger-wrap-focus');
9001         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9002     },
9003
9004     // private
9005     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9006     validateBlur : function(e, t){
9007         return true;
9008     },
9009
9010     // private
9011     onDisable : function(){
9012         this.inputEl().dom.disabled = true;
9013         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9014         //if(this.wrap){
9015         //    this.wrap.addClass('x-item-disabled');
9016         //}
9017     },
9018
9019     // private
9020     onEnable : function(){
9021         this.inputEl().dom.disabled = false;
9022         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9023         //if(this.wrap){
9024         //    this.el.removeClass('x-item-disabled');
9025         //}
9026     },
9027
9028     // private
9029     onShow : function(){
9030         var ae = this.getActionEl();
9031         
9032         if(ae){
9033             ae.dom.style.display = '';
9034             ae.dom.style.visibility = 'visible';
9035         }
9036     },
9037
9038     // private
9039     
9040     onHide : function(){
9041         var ae = this.getActionEl();
9042         ae.dom.style.display = 'none';
9043     },
9044
9045     /**
9046      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
9047      * by an implementing function.
9048      * @method
9049      * @param {EventObject} e
9050      */
9051     onTriggerClick : Roo.emptyFn
9052 });
9053  /*
9054  * Based on:
9055  * Ext JS Library 1.1.1
9056  * Copyright(c) 2006-2007, Ext JS, LLC.
9057  *
9058  * Originally Released Under LGPL - original licence link has changed is not relivant.
9059  *
9060  * Fork - LGPL
9061  * <script type="text/javascript">
9062  */
9063
9064
9065 /**
9066  * @class Roo.data.SortTypes
9067  * @singleton
9068  * Defines the default sorting (casting?) comparison functions used when sorting data.
9069  */
9070 Roo.data.SortTypes = {
9071     /**
9072      * Default sort that does nothing
9073      * @param {Mixed} s The value being converted
9074      * @return {Mixed} The comparison value
9075      */
9076     none : function(s){
9077         return s;
9078     },
9079     
9080     /**
9081      * The regular expression used to strip tags
9082      * @type {RegExp}
9083      * @property
9084      */
9085     stripTagsRE : /<\/?[^>]+>/gi,
9086     
9087     /**
9088      * Strips all HTML tags to sort on text only
9089      * @param {Mixed} s The value being converted
9090      * @return {String} The comparison value
9091      */
9092     asText : function(s){
9093         return String(s).replace(this.stripTagsRE, "");
9094     },
9095     
9096     /**
9097      * Strips all HTML tags to sort on text only - Case insensitive
9098      * @param {Mixed} s The value being converted
9099      * @return {String} The comparison value
9100      */
9101     asUCText : function(s){
9102         return String(s).toUpperCase().replace(this.stripTagsRE, "");
9103     },
9104     
9105     /**
9106      * Case insensitive string
9107      * @param {Mixed} s The value being converted
9108      * @return {String} The comparison value
9109      */
9110     asUCString : function(s) {
9111         return String(s).toUpperCase();
9112     },
9113     
9114     /**
9115      * Date sorting
9116      * @param {Mixed} s The value being converted
9117      * @return {Number} The comparison value
9118      */
9119     asDate : function(s) {
9120         if(!s){
9121             return 0;
9122         }
9123         if(s instanceof Date){
9124             return s.getTime();
9125         }
9126         return Date.parse(String(s));
9127     },
9128     
9129     /**
9130      * Float sorting
9131      * @param {Mixed} s The value being converted
9132      * @return {Float} The comparison value
9133      */
9134     asFloat : function(s) {
9135         var val = parseFloat(String(s).replace(/,/g, ""));
9136         if(isNaN(val)) val = 0;
9137         return val;
9138     },
9139     
9140     /**
9141      * Integer sorting
9142      * @param {Mixed} s The value being converted
9143      * @return {Number} The comparison value
9144      */
9145     asInt : function(s) {
9146         var val = parseInt(String(s).replace(/,/g, ""));
9147         if(isNaN(val)) val = 0;
9148         return val;
9149     }
9150 };/*
9151  * Based on:
9152  * Ext JS Library 1.1.1
9153  * Copyright(c) 2006-2007, Ext JS, LLC.
9154  *
9155  * Originally Released Under LGPL - original licence link has changed is not relivant.
9156  *
9157  * Fork - LGPL
9158  * <script type="text/javascript">
9159  */
9160
9161 /**
9162 * @class Roo.data.Record
9163  * Instances of this class encapsulate both record <em>definition</em> information, and record
9164  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9165  * to access Records cached in an {@link Roo.data.Store} object.<br>
9166  * <p>
9167  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9168  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9169  * objects.<br>
9170  * <p>
9171  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9172  * @constructor
9173  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9174  * {@link #create}. The parameters are the same.
9175  * @param {Array} data An associative Array of data values keyed by the field name.
9176  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9177  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9178  * not specified an integer id is generated.
9179  */
9180 Roo.data.Record = function(data, id){
9181     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9182     this.data = data;
9183 };
9184
9185 /**
9186  * Generate a constructor for a specific record layout.
9187  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9188  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9189  * Each field definition object may contain the following properties: <ul>
9190  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
9191  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9192  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9193  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9194  * is being used, then this is a string containing the javascript expression to reference the data relative to 
9195  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9196  * to the data item relative to the record element. If the mapping expression is the same as the field name,
9197  * this may be omitted.</p></li>
9198  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9199  * <ul><li>auto (Default, implies no conversion)</li>
9200  * <li>string</li>
9201  * <li>int</li>
9202  * <li>float</li>
9203  * <li>boolean</li>
9204  * <li>date</li></ul></p></li>
9205  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9206  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9207  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9208  * by the Reader into an object that will be stored in the Record. It is passed the
9209  * following parameters:<ul>
9210  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9211  * </ul></p></li>
9212  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9213  * </ul>
9214  * <br>usage:<br><pre><code>
9215 var TopicRecord = Roo.data.Record.create(
9216     {name: 'title', mapping: 'topic_title'},
9217     {name: 'author', mapping: 'username'},
9218     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9219     {name: 'lastPost', mapping: 'post_time', type: 'date'},
9220     {name: 'lastPoster', mapping: 'user2'},
9221     {name: 'excerpt', mapping: 'post_text'}
9222 );
9223
9224 var myNewRecord = new TopicRecord({
9225     title: 'Do my job please',
9226     author: 'noobie',
9227     totalPosts: 1,
9228     lastPost: new Date(),
9229     lastPoster: 'Animal',
9230     excerpt: 'No way dude!'
9231 });
9232 myStore.add(myNewRecord);
9233 </code></pre>
9234  * @method create
9235  * @static
9236  */
9237 Roo.data.Record.create = function(o){
9238     var f = function(){
9239         f.superclass.constructor.apply(this, arguments);
9240     };
9241     Roo.extend(f, Roo.data.Record);
9242     var p = f.prototype;
9243     p.fields = new Roo.util.MixedCollection(false, function(field){
9244         return field.name;
9245     });
9246     for(var i = 0, len = o.length; i < len; i++){
9247         p.fields.add(new Roo.data.Field(o[i]));
9248     }
9249     f.getField = function(name){
9250         return p.fields.get(name);  
9251     };
9252     return f;
9253 };
9254
9255 Roo.data.Record.AUTO_ID = 1000;
9256 Roo.data.Record.EDIT = 'edit';
9257 Roo.data.Record.REJECT = 'reject';
9258 Roo.data.Record.COMMIT = 'commit';
9259
9260 Roo.data.Record.prototype = {
9261     /**
9262      * Readonly flag - true if this record has been modified.
9263      * @type Boolean
9264      */
9265     dirty : false,
9266     editing : false,
9267     error: null,
9268     modified: null,
9269
9270     // private
9271     join : function(store){
9272         this.store = store;
9273     },
9274
9275     /**
9276      * Set the named field to the specified value.
9277      * @param {String} name The name of the field to set.
9278      * @param {Object} value The value to set the field to.
9279      */
9280     set : function(name, value){
9281         if(this.data[name] == value){
9282             return;
9283         }
9284         this.dirty = true;
9285         if(!this.modified){
9286             this.modified = {};
9287         }
9288         if(typeof this.modified[name] == 'undefined'){
9289             this.modified[name] = this.data[name];
9290         }
9291         this.data[name] = value;
9292         if(!this.editing && this.store){
9293             this.store.afterEdit(this);
9294         }       
9295     },
9296
9297     /**
9298      * Get the value of the named field.
9299      * @param {String} name The name of the field to get the value of.
9300      * @return {Object} The value of the field.
9301      */
9302     get : function(name){
9303         return this.data[name]; 
9304     },
9305
9306     // private
9307     beginEdit : function(){
9308         this.editing = true;
9309         this.modified = {}; 
9310     },
9311
9312     // private
9313     cancelEdit : function(){
9314         this.editing = false;
9315         delete this.modified;
9316     },
9317
9318     // private
9319     endEdit : function(){
9320         this.editing = false;
9321         if(this.dirty && this.store){
9322             this.store.afterEdit(this);
9323         }
9324     },
9325
9326     /**
9327      * Usually called by the {@link Roo.data.Store} which owns the Record.
9328      * Rejects all changes made to the Record since either creation, or the last commit operation.
9329      * Modified fields are reverted to their original values.
9330      * <p>
9331      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9332      * of reject operations.
9333      */
9334     reject : function(){
9335         var m = this.modified;
9336         for(var n in m){
9337             if(typeof m[n] != "function"){
9338                 this.data[n] = m[n];
9339             }
9340         }
9341         this.dirty = false;
9342         delete this.modified;
9343         this.editing = false;
9344         if(this.store){
9345             this.store.afterReject(this);
9346         }
9347     },
9348
9349     /**
9350      * Usually called by the {@link Roo.data.Store} which owns the Record.
9351      * Commits all changes made to the Record since either creation, or the last commit operation.
9352      * <p>
9353      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9354      * of commit operations.
9355      */
9356     commit : function(){
9357         this.dirty = false;
9358         delete this.modified;
9359         this.editing = false;
9360         if(this.store){
9361             this.store.afterCommit(this);
9362         }
9363     },
9364
9365     // private
9366     hasError : function(){
9367         return this.error != null;
9368     },
9369
9370     // private
9371     clearError : function(){
9372         this.error = null;
9373     },
9374
9375     /**
9376      * Creates a copy of this record.
9377      * @param {String} id (optional) A new record id if you don't want to use this record's id
9378      * @return {Record}
9379      */
9380     copy : function(newId) {
9381         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9382     }
9383 };/*
9384  * Based on:
9385  * Ext JS Library 1.1.1
9386  * Copyright(c) 2006-2007, Ext JS, LLC.
9387  *
9388  * Originally Released Under LGPL - original licence link has changed is not relivant.
9389  *
9390  * Fork - LGPL
9391  * <script type="text/javascript">
9392  */
9393
9394
9395
9396 /**
9397  * @class Roo.data.Store
9398  * @extends Roo.util.Observable
9399  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9400  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9401  * <p>
9402  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
9403  * has no knowledge of the format of the data returned by the Proxy.<br>
9404  * <p>
9405  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9406  * instances from the data object. These records are cached and made available through accessor functions.
9407  * @constructor
9408  * Creates a new Store.
9409  * @param {Object} config A config object containing the objects needed for the Store to access data,
9410  * and read the data into Records.
9411  */
9412 Roo.data.Store = function(config){
9413     this.data = new Roo.util.MixedCollection(false);
9414     this.data.getKey = function(o){
9415         return o.id;
9416     };
9417     this.baseParams = {};
9418     // private
9419     this.paramNames = {
9420         "start" : "start",
9421         "limit" : "limit",
9422         "sort" : "sort",
9423         "dir" : "dir",
9424         "multisort" : "_multisort"
9425     };
9426
9427     if(config && config.data){
9428         this.inlineData = config.data;
9429         delete config.data;
9430     }
9431
9432     Roo.apply(this, config);
9433     
9434     if(this.reader){ // reader passed
9435         this.reader = Roo.factory(this.reader, Roo.data);
9436         this.reader.xmodule = this.xmodule || false;
9437         if(!this.recordType){
9438             this.recordType = this.reader.recordType;
9439         }
9440         if(this.reader.onMetaChange){
9441             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9442         }
9443     }
9444
9445     if(this.recordType){
9446         this.fields = this.recordType.prototype.fields;
9447     }
9448     this.modified = [];
9449
9450     this.addEvents({
9451         /**
9452          * @event datachanged
9453          * Fires when the data cache has changed, and a widget which is using this Store
9454          * as a Record cache should refresh its view.
9455          * @param {Store} this
9456          */
9457         datachanged : true,
9458         /**
9459          * @event metachange
9460          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9461          * @param {Store} this
9462          * @param {Object} meta The JSON metadata
9463          */
9464         metachange : true,
9465         /**
9466          * @event add
9467          * Fires when Records have been added to the Store
9468          * @param {Store} this
9469          * @param {Roo.data.Record[]} records The array of Records added
9470          * @param {Number} index The index at which the record(s) were added
9471          */
9472         add : true,
9473         /**
9474          * @event remove
9475          * Fires when a Record has been removed from the Store
9476          * @param {Store} this
9477          * @param {Roo.data.Record} record The Record that was removed
9478          * @param {Number} index The index at which the record was removed
9479          */
9480         remove : true,
9481         /**
9482          * @event update
9483          * Fires when a Record has been updated
9484          * @param {Store} this
9485          * @param {Roo.data.Record} record The Record that was updated
9486          * @param {String} operation The update operation being performed.  Value may be one of:
9487          * <pre><code>
9488  Roo.data.Record.EDIT
9489  Roo.data.Record.REJECT
9490  Roo.data.Record.COMMIT
9491          * </code></pre>
9492          */
9493         update : true,
9494         /**
9495          * @event clear
9496          * Fires when the data cache has been cleared.
9497          * @param {Store} this
9498          */
9499         clear : true,
9500         /**
9501          * @event beforeload
9502          * Fires before a request is made for a new data object.  If the beforeload handler returns false
9503          * the load action will be canceled.
9504          * @param {Store} this
9505          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9506          */
9507         beforeload : true,
9508         /**
9509          * @event beforeloadadd
9510          * Fires after a new set of Records has been loaded.
9511          * @param {Store} this
9512          * @param {Roo.data.Record[]} records The Records that were loaded
9513          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9514          */
9515         beforeloadadd : true,
9516         /**
9517          * @event load
9518          * Fires after a new set of Records has been loaded, before they are added to the store.
9519          * @param {Store} this
9520          * @param {Roo.data.Record[]} records The Records that were loaded
9521          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9522          * @params {Object} return from reader
9523          */
9524         load : true,
9525         /**
9526          * @event loadexception
9527          * Fires if an exception occurs in the Proxy during loading.
9528          * Called with the signature of the Proxy's "loadexception" event.
9529          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9530          * 
9531          * @param {Proxy} 
9532          * @param {Object} return from JsonData.reader() - success, totalRecords, records
9533          * @param {Object} load options 
9534          * @param {Object} jsonData from your request (normally this contains the Exception)
9535          */
9536         loadexception : true
9537     });
9538     
9539     if(this.proxy){
9540         this.proxy = Roo.factory(this.proxy, Roo.data);
9541         this.proxy.xmodule = this.xmodule || false;
9542         this.relayEvents(this.proxy,  ["loadexception"]);
9543     }
9544     this.sortToggle = {};
9545     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9546
9547     Roo.data.Store.superclass.constructor.call(this);
9548
9549     if(this.inlineData){
9550         this.loadData(this.inlineData);
9551         delete this.inlineData;
9552     }
9553 };
9554
9555 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9556      /**
9557     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
9558     * without a remote query - used by combo/forms at present.
9559     */
9560     
9561     /**
9562     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9563     */
9564     /**
9565     * @cfg {Array} data Inline data to be loaded when the store is initialized.
9566     */
9567     /**
9568     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9569     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9570     */
9571     /**
9572     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9573     * on any HTTP request
9574     */
9575     /**
9576     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9577     */
9578     /**
9579     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9580     */
9581     multiSort: false,
9582     /**
9583     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9584     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9585     */
9586     remoteSort : false,
9587
9588     /**
9589     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9590      * loaded or when a record is removed. (defaults to false).
9591     */
9592     pruneModifiedRecords : false,
9593
9594     // private
9595     lastOptions : null,
9596
9597     /**
9598      * Add Records to the Store and fires the add event.
9599      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9600      */
9601     add : function(records){
9602         records = [].concat(records);
9603         for(var i = 0, len = records.length; i < len; i++){
9604             records[i].join(this);
9605         }
9606         var index = this.data.length;
9607         this.data.addAll(records);
9608         this.fireEvent("add", this, records, index);
9609     },
9610
9611     /**
9612      * Remove a Record from the Store and fires the remove event.
9613      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9614      */
9615     remove : function(record){
9616         var index = this.data.indexOf(record);
9617         this.data.removeAt(index);
9618         if(this.pruneModifiedRecords){
9619             this.modified.remove(record);
9620         }
9621         this.fireEvent("remove", this, record, index);
9622     },
9623
9624     /**
9625      * Remove all Records from the Store and fires the clear event.
9626      */
9627     removeAll : function(){
9628         this.data.clear();
9629         if(this.pruneModifiedRecords){
9630             this.modified = [];
9631         }
9632         this.fireEvent("clear", this);
9633     },
9634
9635     /**
9636      * Inserts Records to the Store at the given index and fires the add event.
9637      * @param {Number} index The start index at which to insert the passed Records.
9638      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9639      */
9640     insert : function(index, records){
9641         records = [].concat(records);
9642         for(var i = 0, len = records.length; i < len; i++){
9643             this.data.insert(index, records[i]);
9644             records[i].join(this);
9645         }
9646         this.fireEvent("add", this, records, index);
9647     },
9648
9649     /**
9650      * Get the index within the cache of the passed Record.
9651      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9652      * @return {Number} The index of the passed Record. Returns -1 if not found.
9653      */
9654     indexOf : function(record){
9655         return this.data.indexOf(record);
9656     },
9657
9658     /**
9659      * Get the index within the cache of the Record with the passed id.
9660      * @param {String} id The id of the Record to find.
9661      * @return {Number} The index of the Record. Returns -1 if not found.
9662      */
9663     indexOfId : function(id){
9664         return this.data.indexOfKey(id);
9665     },
9666
9667     /**
9668      * Get the Record with the specified id.
9669      * @param {String} id The id of the Record to find.
9670      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
9671      */
9672     getById : function(id){
9673         return this.data.key(id);
9674     },
9675
9676     /**
9677      * Get the Record at the specified index.
9678      * @param {Number} index The index of the Record to find.
9679      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
9680      */
9681     getAt : function(index){
9682         return this.data.itemAt(index);
9683     },
9684
9685     /**
9686      * Returns a range of Records between specified indices.
9687      * @param {Number} startIndex (optional) The starting index (defaults to 0)
9688      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
9689      * @return {Roo.data.Record[]} An array of Records
9690      */
9691     getRange : function(start, end){
9692         return this.data.getRange(start, end);
9693     },
9694
9695     // private
9696     storeOptions : function(o){
9697         o = Roo.apply({}, o);
9698         delete o.callback;
9699         delete o.scope;
9700         this.lastOptions = o;
9701     },
9702
9703     /**
9704      * Loads the Record cache from the configured Proxy using the configured Reader.
9705      * <p>
9706      * If using remote paging, then the first load call must specify the <em>start</em>
9707      * and <em>limit</em> properties in the options.params property to establish the initial
9708      * position within the dataset, and the number of Records to cache on each read from the Proxy.
9709      * <p>
9710      * <strong>It is important to note that for remote data sources, loading is asynchronous,
9711      * and this call will return before the new data has been loaded. Perform any post-processing
9712      * in a callback function, or in a "load" event handler.</strong>
9713      * <p>
9714      * @param {Object} options An object containing properties which control loading options:<ul>
9715      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
9716      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
9717      * passed the following arguments:<ul>
9718      * <li>r : Roo.data.Record[]</li>
9719      * <li>options: Options object from the load call</li>
9720      * <li>success: Boolean success indicator</li></ul></li>
9721      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
9722      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
9723      * </ul>
9724      */
9725     load : function(options){
9726         options = options || {};
9727         if(this.fireEvent("beforeload", this, options) !== false){
9728             this.storeOptions(options);
9729             var p = Roo.apply(options.params || {}, this.baseParams);
9730             // if meta was not loaded from remote source.. try requesting it.
9731             if (!this.reader.metaFromRemote) {
9732                 p._requestMeta = 1;
9733             }
9734             if(this.sortInfo && this.remoteSort){
9735                 var pn = this.paramNames;
9736                 p[pn["sort"]] = this.sortInfo.field;
9737                 p[pn["dir"]] = this.sortInfo.direction;
9738             }
9739             if (this.multiSort) {
9740                 var pn = this.paramNames;
9741                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
9742             }
9743             
9744             this.proxy.load(p, this.reader, this.loadRecords, this, options);
9745         }
9746     },
9747
9748     /**
9749      * Reloads the Record cache from the configured Proxy using the configured Reader and
9750      * the options from the last load operation performed.
9751      * @param {Object} options (optional) An object containing properties which may override the options
9752      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
9753      * the most recently used options are reused).
9754      */
9755     reload : function(options){
9756         this.load(Roo.applyIf(options||{}, this.lastOptions));
9757     },
9758
9759     // private
9760     // Called as a callback by the Reader during a load operation.
9761     loadRecords : function(o, options, success){
9762         if(!o || success === false){
9763             if(success !== false){
9764                 this.fireEvent("load", this, [], options, o);
9765             }
9766             if(options.callback){
9767                 options.callback.call(options.scope || this, [], options, false);
9768             }
9769             return;
9770         }
9771         // if data returned failure - throw an exception.
9772         if (o.success === false) {
9773             // show a message if no listener is registered.
9774             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
9775                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
9776             }
9777             // loadmask wil be hooked into this..
9778             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
9779             return;
9780         }
9781         var r = o.records, t = o.totalRecords || r.length;
9782         
9783         this.fireEvent("beforeloadadd", this, r, options, o);
9784         
9785         if(!options || options.add !== true){
9786             if(this.pruneModifiedRecords){
9787                 this.modified = [];
9788             }
9789             for(var i = 0, len = r.length; i < len; i++){
9790                 r[i].join(this);
9791             }
9792             if(this.snapshot){
9793                 this.data = this.snapshot;
9794                 delete this.snapshot;
9795             }
9796             this.data.clear();
9797             this.data.addAll(r);
9798             this.totalLength = t;
9799             this.applySort();
9800             this.fireEvent("datachanged", this);
9801         }else{
9802             this.totalLength = Math.max(t, this.data.length+r.length);
9803             this.add(r);
9804         }
9805         this.fireEvent("load", this, r, options, o);
9806         if(options.callback){
9807             options.callback.call(options.scope || this, r, options, true);
9808         }
9809     },
9810
9811
9812     /**
9813      * Loads data from a passed data block. A Reader which understands the format of the data
9814      * must have been configured in the constructor.
9815      * @param {Object} data The data block from which to read the Records.  The format of the data expected
9816      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
9817      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
9818      */
9819     loadData : function(o, append){
9820         var r = this.reader.readRecords(o);
9821         this.loadRecords(r, {add: append}, true);
9822     },
9823
9824     /**
9825      * Gets the number of cached records.
9826      * <p>
9827      * <em>If using paging, this may not be the total size of the dataset. If the data object
9828      * used by the Reader contains the dataset size, then the getTotalCount() function returns
9829      * the data set size</em>
9830      */
9831     getCount : function(){
9832         return this.data.length || 0;
9833     },
9834
9835     /**
9836      * Gets the total number of records in the dataset as returned by the server.
9837      * <p>
9838      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9839      * the dataset size</em>
9840      */
9841     getTotalCount : function(){
9842         return this.totalLength || 0;
9843     },
9844
9845     /**
9846      * Returns the sort state of the Store as an object with two properties:
9847      * <pre><code>
9848  field {String} The name of the field by which the Records are sorted
9849  direction {String} The sort order, "ASC" or "DESC"
9850      * </code></pre>
9851      */
9852     getSortState : function(){
9853         return this.sortInfo;
9854     },
9855
9856     // private
9857     applySort : function(){
9858         if(this.sortInfo && !this.remoteSort){
9859             var s = this.sortInfo, f = s.field;
9860             var st = this.fields.get(f).sortType;
9861             var fn = function(r1, r2){
9862                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9863                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9864             };
9865             this.data.sort(s.direction, fn);
9866             if(this.snapshot && this.snapshot != this.data){
9867                 this.snapshot.sort(s.direction, fn);
9868             }
9869         }
9870     },
9871
9872     /**
9873      * Sets the default sort column and order to be used by the next load operation.
9874      * @param {String} fieldName The name of the field to sort by.
9875      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9876      */
9877     setDefaultSort : function(field, dir){
9878         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9879     },
9880
9881     /**
9882      * Sort the Records.
9883      * If remote sorting is used, the sort is performed on the server, and the cache is
9884      * reloaded. If local sorting is used, the cache is sorted internally.
9885      * @param {String} fieldName The name of the field to sort by.
9886      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9887      */
9888     sort : function(fieldName, dir){
9889         var f = this.fields.get(fieldName);
9890         if(!dir){
9891             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9892             
9893             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9894                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9895             }else{
9896                 dir = f.sortDir;
9897             }
9898         }
9899         this.sortToggle[f.name] = dir;
9900         this.sortInfo = {field: f.name, direction: dir};
9901         if(!this.remoteSort){
9902             this.applySort();
9903             this.fireEvent("datachanged", this);
9904         }else{
9905             this.load(this.lastOptions);
9906         }
9907     },
9908
9909     /**
9910      * Calls the specified function for each of the Records in the cache.
9911      * @param {Function} fn The function to call. The Record is passed as the first parameter.
9912      * Returning <em>false</em> aborts and exits the iteration.
9913      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9914      */
9915     each : function(fn, scope){
9916         this.data.each(fn, scope);
9917     },
9918
9919     /**
9920      * Gets all records modified since the last commit.  Modified records are persisted across load operations
9921      * (e.g., during paging).
9922      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9923      */
9924     getModifiedRecords : function(){
9925         return this.modified;
9926     },
9927
9928     // private
9929     createFilterFn : function(property, value, anyMatch){
9930         if(!value.exec){ // not a regex
9931             value = String(value);
9932             if(value.length == 0){
9933                 return false;
9934             }
9935             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9936         }
9937         return function(r){
9938             return value.test(r.data[property]);
9939         };
9940     },
9941
9942     /**
9943      * Sums the value of <i>property</i> for each record between start and end and returns the result.
9944      * @param {String} property A field on your records
9945      * @param {Number} start The record index to start at (defaults to 0)
9946      * @param {Number} end The last record index to include (defaults to length - 1)
9947      * @return {Number} The sum
9948      */
9949     sum : function(property, start, end){
9950         var rs = this.data.items, v = 0;
9951         start = start || 0;
9952         end = (end || end === 0) ? end : rs.length-1;
9953
9954         for(var i = start; i <= end; i++){
9955             v += (rs[i].data[property] || 0);
9956         }
9957         return v;
9958     },
9959
9960     /**
9961      * Filter the records by a specified property.
9962      * @param {String} field A field on your records
9963      * @param {String/RegExp} value Either a string that the field
9964      * should start with or a RegExp to test against the field
9965      * @param {Boolean} anyMatch True to match any part not just the beginning
9966      */
9967     filter : function(property, value, anyMatch){
9968         var fn = this.createFilterFn(property, value, anyMatch);
9969         return fn ? this.filterBy(fn) : this.clearFilter();
9970     },
9971
9972     /**
9973      * Filter by a function. The specified function will be called with each
9974      * record in this data source. If the function returns true the record is included,
9975      * otherwise it is filtered.
9976      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9977      * @param {Object} scope (optional) The scope of the function (defaults to this)
9978      */
9979     filterBy : function(fn, scope){
9980         this.snapshot = this.snapshot || this.data;
9981         this.data = this.queryBy(fn, scope||this);
9982         this.fireEvent("datachanged", this);
9983     },
9984
9985     /**
9986      * Query the records by a specified property.
9987      * @param {String} field A field on your records
9988      * @param {String/RegExp} value Either a string that the field
9989      * should start with or a RegExp to test against the field
9990      * @param {Boolean} anyMatch True to match any part not just the beginning
9991      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9992      */
9993     query : function(property, value, anyMatch){
9994         var fn = this.createFilterFn(property, value, anyMatch);
9995         return fn ? this.queryBy(fn) : this.data.clone();
9996     },
9997
9998     /**
9999      * Query by a function. The specified function will be called with each
10000      * record in this data source. If the function returns true the record is included
10001      * in the results.
10002      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10003      * @param {Object} scope (optional) The scope of the function (defaults to this)
10004       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10005      **/
10006     queryBy : function(fn, scope){
10007         var data = this.snapshot || this.data;
10008         return data.filterBy(fn, scope||this);
10009     },
10010
10011     /**
10012      * Collects unique values for a particular dataIndex from this store.
10013      * @param {String} dataIndex The property to collect
10014      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10015      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10016      * @return {Array} An array of the unique values
10017      **/
10018     collect : function(dataIndex, allowNull, bypassFilter){
10019         var d = (bypassFilter === true && this.snapshot) ?
10020                 this.snapshot.items : this.data.items;
10021         var v, sv, r = [], l = {};
10022         for(var i = 0, len = d.length; i < len; i++){
10023             v = d[i].data[dataIndex];
10024             sv = String(v);
10025             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10026                 l[sv] = true;
10027                 r[r.length] = v;
10028             }
10029         }
10030         return r;
10031     },
10032
10033     /**
10034      * Revert to a view of the Record cache with no filtering applied.
10035      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10036      */
10037     clearFilter : function(suppressEvent){
10038         if(this.snapshot && this.snapshot != this.data){
10039             this.data = this.snapshot;
10040             delete this.snapshot;
10041             if(suppressEvent !== true){
10042                 this.fireEvent("datachanged", this);
10043             }
10044         }
10045     },
10046
10047     // private
10048     afterEdit : function(record){
10049         if(this.modified.indexOf(record) == -1){
10050             this.modified.push(record);
10051         }
10052         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10053     },
10054     
10055     // private
10056     afterReject : function(record){
10057         this.modified.remove(record);
10058         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10059     },
10060
10061     // private
10062     afterCommit : function(record){
10063         this.modified.remove(record);
10064         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10065     },
10066
10067     /**
10068      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10069      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10070      */
10071     commitChanges : function(){
10072         var m = this.modified.slice(0);
10073         this.modified = [];
10074         for(var i = 0, len = m.length; i < len; i++){
10075             m[i].commit();
10076         }
10077     },
10078
10079     /**
10080      * Cancel outstanding changes on all changed records.
10081      */
10082     rejectChanges : function(){
10083         var m = this.modified.slice(0);
10084         this.modified = [];
10085         for(var i = 0, len = m.length; i < len; i++){
10086             m[i].reject();
10087         }
10088     },
10089
10090     onMetaChange : function(meta, rtype, o){
10091         this.recordType = rtype;
10092         this.fields = rtype.prototype.fields;
10093         delete this.snapshot;
10094         this.sortInfo = meta.sortInfo || this.sortInfo;
10095         this.modified = [];
10096         this.fireEvent('metachange', this, this.reader.meta);
10097     },
10098     
10099     moveIndex : function(data, type)
10100     {
10101         var index = this.indexOf(data);
10102         
10103         var newIndex = index + type;
10104         
10105         this.remove(data);
10106         
10107         this.insert(newIndex, data);
10108         
10109     }
10110 });/*
10111  * Based on:
10112  * Ext JS Library 1.1.1
10113  * Copyright(c) 2006-2007, Ext JS, LLC.
10114  *
10115  * Originally Released Under LGPL - original licence link has changed is not relivant.
10116  *
10117  * Fork - LGPL
10118  * <script type="text/javascript">
10119  */
10120
10121 /**
10122  * @class Roo.data.SimpleStore
10123  * @extends Roo.data.Store
10124  * Small helper class to make creating Stores from Array data easier.
10125  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10126  * @cfg {Array} fields An array of field definition objects, or field name strings.
10127  * @cfg {Array} data The multi-dimensional array of data
10128  * @constructor
10129  * @param {Object} config
10130  */
10131 Roo.data.SimpleStore = function(config){
10132     Roo.data.SimpleStore.superclass.constructor.call(this, {
10133         isLocal : true,
10134         reader: new Roo.data.ArrayReader({
10135                 id: config.id
10136             },
10137             Roo.data.Record.create(config.fields)
10138         ),
10139         proxy : new Roo.data.MemoryProxy(config.data)
10140     });
10141     this.load();
10142 };
10143 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10144  * Based on:
10145  * Ext JS Library 1.1.1
10146  * Copyright(c) 2006-2007, Ext JS, LLC.
10147  *
10148  * Originally Released Under LGPL - original licence link has changed is not relivant.
10149  *
10150  * Fork - LGPL
10151  * <script type="text/javascript">
10152  */
10153
10154 /**
10155 /**
10156  * @extends Roo.data.Store
10157  * @class Roo.data.JsonStore
10158  * Small helper class to make creating Stores for JSON data easier. <br/>
10159 <pre><code>
10160 var store = new Roo.data.JsonStore({
10161     url: 'get-images.php',
10162     root: 'images',
10163     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10164 });
10165 </code></pre>
10166  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10167  * JsonReader and HttpProxy (unless inline data is provided).</b>
10168  * @cfg {Array} fields An array of field definition objects, or field name strings.
10169  * @constructor
10170  * @param {Object} config
10171  */
10172 Roo.data.JsonStore = function(c){
10173     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10174         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10175         reader: new Roo.data.JsonReader(c, c.fields)
10176     }));
10177 };
10178 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10179  * Based on:
10180  * Ext JS Library 1.1.1
10181  * Copyright(c) 2006-2007, Ext JS, LLC.
10182  *
10183  * Originally Released Under LGPL - original licence link has changed is not relivant.
10184  *
10185  * Fork - LGPL
10186  * <script type="text/javascript">
10187  */
10188
10189  
10190 Roo.data.Field = function(config){
10191     if(typeof config == "string"){
10192         config = {name: config};
10193     }
10194     Roo.apply(this, config);
10195     
10196     if(!this.type){
10197         this.type = "auto";
10198     }
10199     
10200     var st = Roo.data.SortTypes;
10201     // named sortTypes are supported, here we look them up
10202     if(typeof this.sortType == "string"){
10203         this.sortType = st[this.sortType];
10204     }
10205     
10206     // set default sortType for strings and dates
10207     if(!this.sortType){
10208         switch(this.type){
10209             case "string":
10210                 this.sortType = st.asUCString;
10211                 break;
10212             case "date":
10213                 this.sortType = st.asDate;
10214                 break;
10215             default:
10216                 this.sortType = st.none;
10217         }
10218     }
10219
10220     // define once
10221     var stripRe = /[\$,%]/g;
10222
10223     // prebuilt conversion function for this field, instead of
10224     // switching every time we're reading a value
10225     if(!this.convert){
10226         var cv, dateFormat = this.dateFormat;
10227         switch(this.type){
10228             case "":
10229             case "auto":
10230             case undefined:
10231                 cv = function(v){ return v; };
10232                 break;
10233             case "string":
10234                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10235                 break;
10236             case "int":
10237                 cv = function(v){
10238                     return v !== undefined && v !== null && v !== '' ?
10239                            parseInt(String(v).replace(stripRe, ""), 10) : '';
10240                     };
10241                 break;
10242             case "float":
10243                 cv = function(v){
10244                     return v !== undefined && v !== null && v !== '' ?
10245                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
10246                     };
10247                 break;
10248             case "bool":
10249             case "boolean":
10250                 cv = function(v){ return v === true || v === "true" || v == 1; };
10251                 break;
10252             case "date":
10253                 cv = function(v){
10254                     if(!v){
10255                         return '';
10256                     }
10257                     if(v instanceof Date){
10258                         return v;
10259                     }
10260                     if(dateFormat){
10261                         if(dateFormat == "timestamp"){
10262                             return new Date(v*1000);
10263                         }
10264                         return Date.parseDate(v, dateFormat);
10265                     }
10266                     var parsed = Date.parse(v);
10267                     return parsed ? new Date(parsed) : null;
10268                 };
10269              break;
10270             
10271         }
10272         this.convert = cv;
10273     }
10274 };
10275
10276 Roo.data.Field.prototype = {
10277     dateFormat: null,
10278     defaultValue: "",
10279     mapping: null,
10280     sortType : null,
10281     sortDir : "ASC"
10282 };/*
10283  * Based on:
10284  * Ext JS Library 1.1.1
10285  * Copyright(c) 2006-2007, Ext JS, LLC.
10286  *
10287  * Originally Released Under LGPL - original licence link has changed is not relivant.
10288  *
10289  * Fork - LGPL
10290  * <script type="text/javascript">
10291  */
10292  
10293 // Base class for reading structured data from a data source.  This class is intended to be
10294 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10295
10296 /**
10297  * @class Roo.data.DataReader
10298  * Base class for reading structured data from a data source.  This class is intended to be
10299  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10300  */
10301
10302 Roo.data.DataReader = function(meta, recordType){
10303     
10304     this.meta = meta;
10305     
10306     this.recordType = recordType instanceof Array ? 
10307         Roo.data.Record.create(recordType) : recordType;
10308 };
10309
10310 Roo.data.DataReader.prototype = {
10311      /**
10312      * Create an empty record
10313      * @param {Object} data (optional) - overlay some values
10314      * @return {Roo.data.Record} record created.
10315      */
10316     newRow :  function(d) {
10317         var da =  {};
10318         this.recordType.prototype.fields.each(function(c) {
10319             switch( c.type) {
10320                 case 'int' : da[c.name] = 0; break;
10321                 case 'date' : da[c.name] = new Date(); break;
10322                 case 'float' : da[c.name] = 0.0; break;
10323                 case 'boolean' : da[c.name] = false; break;
10324                 default : da[c.name] = ""; break;
10325             }
10326             
10327         });
10328         return new this.recordType(Roo.apply(da, d));
10329     }
10330     
10331 };/*
10332  * Based on:
10333  * Ext JS Library 1.1.1
10334  * Copyright(c) 2006-2007, Ext JS, LLC.
10335  *
10336  * Originally Released Under LGPL - original licence link has changed is not relivant.
10337  *
10338  * Fork - LGPL
10339  * <script type="text/javascript">
10340  */
10341
10342 /**
10343  * @class Roo.data.DataProxy
10344  * @extends Roo.data.Observable
10345  * This class is an abstract base class for implementations which provide retrieval of
10346  * unformatted data objects.<br>
10347  * <p>
10348  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10349  * (of the appropriate type which knows how to parse the data object) to provide a block of
10350  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10351  * <p>
10352  * Custom implementations must implement the load method as described in
10353  * {@link Roo.data.HttpProxy#load}.
10354  */
10355 Roo.data.DataProxy = function(){
10356     this.addEvents({
10357         /**
10358          * @event beforeload
10359          * Fires before a network request is made to retrieve a data object.
10360          * @param {Object} This DataProxy object.
10361          * @param {Object} params The params parameter to the load function.
10362          */
10363         beforeload : true,
10364         /**
10365          * @event load
10366          * Fires before the load method's callback is called.
10367          * @param {Object} This DataProxy object.
10368          * @param {Object} o The data object.
10369          * @param {Object} arg The callback argument object passed to the load function.
10370          */
10371         load : true,
10372         /**
10373          * @event loadexception
10374          * Fires if an Exception occurs during data retrieval.
10375          * @param {Object} This DataProxy object.
10376          * @param {Object} o The data object.
10377          * @param {Object} arg The callback argument object passed to the load function.
10378          * @param {Object} e The Exception.
10379          */
10380         loadexception : true
10381     });
10382     Roo.data.DataProxy.superclass.constructor.call(this);
10383 };
10384
10385 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10386
10387     /**
10388      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10389      */
10390 /*
10391  * Based on:
10392  * Ext JS Library 1.1.1
10393  * Copyright(c) 2006-2007, Ext JS, LLC.
10394  *
10395  * Originally Released Under LGPL - original licence link has changed is not relivant.
10396  *
10397  * Fork - LGPL
10398  * <script type="text/javascript">
10399  */
10400 /**
10401  * @class Roo.data.MemoryProxy
10402  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10403  * to the Reader when its load method is called.
10404  * @constructor
10405  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10406  */
10407 Roo.data.MemoryProxy = function(data){
10408     if (data.data) {
10409         data = data.data;
10410     }
10411     Roo.data.MemoryProxy.superclass.constructor.call(this);
10412     this.data = data;
10413 };
10414
10415 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10416     /**
10417      * Load data from the requested source (in this case an in-memory
10418      * data object passed to the constructor), read the data object into
10419      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10420      * process that block using the passed callback.
10421      * @param {Object} params This parameter is not used by the MemoryProxy class.
10422      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10423      * object into a block of Roo.data.Records.
10424      * @param {Function} callback The function into which to pass the block of Roo.data.records.
10425      * The function must be passed <ul>
10426      * <li>The Record block object</li>
10427      * <li>The "arg" argument from the load function</li>
10428      * <li>A boolean success indicator</li>
10429      * </ul>
10430      * @param {Object} scope The scope in which to call the callback
10431      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10432      */
10433     load : function(params, reader, callback, scope, arg){
10434         params = params || {};
10435         var result;
10436         try {
10437             result = reader.readRecords(this.data);
10438         }catch(e){
10439             this.fireEvent("loadexception", this, arg, null, e);
10440             callback.call(scope, null, arg, false);
10441             return;
10442         }
10443         callback.call(scope, result, arg, true);
10444     },
10445     
10446     // private
10447     update : function(params, records){
10448         
10449     }
10450 });/*
10451  * Based on:
10452  * Ext JS Library 1.1.1
10453  * Copyright(c) 2006-2007, Ext JS, LLC.
10454  *
10455  * Originally Released Under LGPL - original licence link has changed is not relivant.
10456  *
10457  * Fork - LGPL
10458  * <script type="text/javascript">
10459  */
10460 /**
10461  * @class Roo.data.HttpProxy
10462  * @extends Roo.data.DataProxy
10463  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10464  * configured to reference a certain URL.<br><br>
10465  * <p>
10466  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10467  * from which the running page was served.<br><br>
10468  * <p>
10469  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10470  * <p>
10471  * Be aware that to enable the browser to parse an XML document, the server must set
10472  * the Content-Type header in the HTTP response to "text/xml".
10473  * @constructor
10474  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10475  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
10476  * will be used to make the request.
10477  */
10478 Roo.data.HttpProxy = function(conn){
10479     Roo.data.HttpProxy.superclass.constructor.call(this);
10480     // is conn a conn config or a real conn?
10481     this.conn = conn;
10482     this.useAjax = !conn || !conn.events;
10483   
10484 };
10485
10486 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10487     // thse are take from connection...
10488     
10489     /**
10490      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10491      */
10492     /**
10493      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10494      * extra parameters to each request made by this object. (defaults to undefined)
10495      */
10496     /**
10497      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10498      *  to each request made by this object. (defaults to undefined)
10499      */
10500     /**
10501      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
10502      */
10503     /**
10504      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10505      */
10506      /**
10507      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10508      * @type Boolean
10509      */
10510   
10511
10512     /**
10513      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10514      * @type Boolean
10515      */
10516     /**
10517      * Return the {@link Roo.data.Connection} object being used by this Proxy.
10518      * @return {Connection} The Connection object. This object may be used to subscribe to events on
10519      * a finer-grained basis than the DataProxy events.
10520      */
10521     getConnection : function(){
10522         return this.useAjax ? Roo.Ajax : this.conn;
10523     },
10524
10525     /**
10526      * Load data from the configured {@link Roo.data.Connection}, read the data object into
10527      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10528      * process that block using the passed callback.
10529      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10530      * for the request to the remote server.
10531      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10532      * object into a block of Roo.data.Records.
10533      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10534      * The function must be passed <ul>
10535      * <li>The Record block object</li>
10536      * <li>The "arg" argument from the load function</li>
10537      * <li>A boolean success indicator</li>
10538      * </ul>
10539      * @param {Object} scope The scope in which to call the callback
10540      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10541      */
10542     load : function(params, reader, callback, scope, arg){
10543         if(this.fireEvent("beforeload", this, params) !== false){
10544             var  o = {
10545                 params : params || {},
10546                 request: {
10547                     callback : callback,
10548                     scope : scope,
10549                     arg : arg
10550                 },
10551                 reader: reader,
10552                 callback : this.loadResponse,
10553                 scope: this
10554             };
10555             if(this.useAjax){
10556                 Roo.applyIf(o, this.conn);
10557                 if(this.activeRequest){
10558                     Roo.Ajax.abort(this.activeRequest);
10559                 }
10560                 this.activeRequest = Roo.Ajax.request(o);
10561             }else{
10562                 this.conn.request(o);
10563             }
10564         }else{
10565             callback.call(scope||this, null, arg, false);
10566         }
10567     },
10568
10569     // private
10570     loadResponse : function(o, success, response){
10571         delete this.activeRequest;
10572         if(!success){
10573             this.fireEvent("loadexception", this, o, response);
10574             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10575             return;
10576         }
10577         var result;
10578         try {
10579             result = o.reader.read(response);
10580         }catch(e){
10581             this.fireEvent("loadexception", this, o, response, e);
10582             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10583             return;
10584         }
10585         
10586         this.fireEvent("load", this, o, o.request.arg);
10587         o.request.callback.call(o.request.scope, result, o.request.arg, true);
10588     },
10589
10590     // private
10591     update : function(dataSet){
10592
10593     },
10594
10595     // private
10596     updateResponse : function(dataSet){
10597
10598     }
10599 });/*
10600  * Based on:
10601  * Ext JS Library 1.1.1
10602  * Copyright(c) 2006-2007, Ext JS, LLC.
10603  *
10604  * Originally Released Under LGPL - original licence link has changed is not relivant.
10605  *
10606  * Fork - LGPL
10607  * <script type="text/javascript">
10608  */
10609
10610 /**
10611  * @class Roo.data.ScriptTagProxy
10612  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10613  * other than the originating domain of the running page.<br><br>
10614  * <p>
10615  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
10616  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10617  * <p>
10618  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10619  * source code that is used as the source inside a &lt;script> tag.<br><br>
10620  * <p>
10621  * In order for the browser to process the returned data, the server must wrap the data object
10622  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10623  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10624  * depending on whether the callback name was passed:
10625  * <p>
10626  * <pre><code>
10627 boolean scriptTag = false;
10628 String cb = request.getParameter("callback");
10629 if (cb != null) {
10630     scriptTag = true;
10631     response.setContentType("text/javascript");
10632 } else {
10633     response.setContentType("application/x-json");
10634 }
10635 Writer out = response.getWriter();
10636 if (scriptTag) {
10637     out.write(cb + "(");
10638 }
10639 out.print(dataBlock.toJsonString());
10640 if (scriptTag) {
10641     out.write(");");
10642 }
10643 </pre></code>
10644  *
10645  * @constructor
10646  * @param {Object} config A configuration object.
10647  */
10648 Roo.data.ScriptTagProxy = function(config){
10649     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10650     Roo.apply(this, config);
10651     this.head = document.getElementsByTagName("head")[0];
10652 };
10653
10654 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10655
10656 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10657     /**
10658      * @cfg {String} url The URL from which to request the data object.
10659      */
10660     /**
10661      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
10662      */
10663     timeout : 30000,
10664     /**
10665      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
10666      * the server the name of the callback function set up by the load call to process the returned data object.
10667      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
10668      * javascript output which calls this named function passing the data object as its only parameter.
10669      */
10670     callbackParam : "callback",
10671     /**
10672      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
10673      * name to the request.
10674      */
10675     nocache : true,
10676
10677     /**
10678      * Load data from the configured URL, read the data object into
10679      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10680      * process that block using the passed callback.
10681      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10682      * for the request to the remote server.
10683      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10684      * object into a block of Roo.data.Records.
10685      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10686      * The function must be passed <ul>
10687      * <li>The Record block object</li>
10688      * <li>The "arg" argument from the load function</li>
10689      * <li>A boolean success indicator</li>
10690      * </ul>
10691      * @param {Object} scope The scope in which to call the callback
10692      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10693      */
10694     load : function(params, reader, callback, scope, arg){
10695         if(this.fireEvent("beforeload", this, params) !== false){
10696
10697             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
10698
10699             var url = this.url;
10700             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
10701             if(this.nocache){
10702                 url += "&_dc=" + (new Date().getTime());
10703             }
10704             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
10705             var trans = {
10706                 id : transId,
10707                 cb : "stcCallback"+transId,
10708                 scriptId : "stcScript"+transId,
10709                 params : params,
10710                 arg : arg,
10711                 url : url,
10712                 callback : callback,
10713                 scope : scope,
10714                 reader : reader
10715             };
10716             var conn = this;
10717
10718             window[trans.cb] = function(o){
10719                 conn.handleResponse(o, trans);
10720             };
10721
10722             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
10723
10724             if(this.autoAbort !== false){
10725                 this.abort();
10726             }
10727
10728             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
10729
10730             var script = document.createElement("script");
10731             script.setAttribute("src", url);
10732             script.setAttribute("type", "text/javascript");
10733             script.setAttribute("id", trans.scriptId);
10734             this.head.appendChild(script);
10735
10736             this.trans = trans;
10737         }else{
10738             callback.call(scope||this, null, arg, false);
10739         }
10740     },
10741
10742     // private
10743     isLoading : function(){
10744         return this.trans ? true : false;
10745     },
10746
10747     /**
10748      * Abort the current server request.
10749      */
10750     abort : function(){
10751         if(this.isLoading()){
10752             this.destroyTrans(this.trans);
10753         }
10754     },
10755
10756     // private
10757     destroyTrans : function(trans, isLoaded){
10758         this.head.removeChild(document.getElementById(trans.scriptId));
10759         clearTimeout(trans.timeoutId);
10760         if(isLoaded){
10761             window[trans.cb] = undefined;
10762             try{
10763                 delete window[trans.cb];
10764             }catch(e){}
10765         }else{
10766             // if hasn't been loaded, wait for load to remove it to prevent script error
10767             window[trans.cb] = function(){
10768                 window[trans.cb] = undefined;
10769                 try{
10770                     delete window[trans.cb];
10771                 }catch(e){}
10772             };
10773         }
10774     },
10775
10776     // private
10777     handleResponse : function(o, trans){
10778         this.trans = false;
10779         this.destroyTrans(trans, true);
10780         var result;
10781         try {
10782             result = trans.reader.readRecords(o);
10783         }catch(e){
10784             this.fireEvent("loadexception", this, o, trans.arg, e);
10785             trans.callback.call(trans.scope||window, null, trans.arg, false);
10786             return;
10787         }
10788         this.fireEvent("load", this, o, trans.arg);
10789         trans.callback.call(trans.scope||window, result, trans.arg, true);
10790     },
10791
10792     // private
10793     handleFailure : function(trans){
10794         this.trans = false;
10795         this.destroyTrans(trans, false);
10796         this.fireEvent("loadexception", this, null, trans.arg);
10797         trans.callback.call(trans.scope||window, null, trans.arg, false);
10798     }
10799 });/*
10800  * Based on:
10801  * Ext JS Library 1.1.1
10802  * Copyright(c) 2006-2007, Ext JS, LLC.
10803  *
10804  * Originally Released Under LGPL - original licence link has changed is not relivant.
10805  *
10806  * Fork - LGPL
10807  * <script type="text/javascript">
10808  */
10809
10810 /**
10811  * @class Roo.data.JsonReader
10812  * @extends Roo.data.DataReader
10813  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
10814  * based on mappings in a provided Roo.data.Record constructor.
10815  * 
10816  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
10817  * in the reply previously. 
10818  * 
10819  * <p>
10820  * Example code:
10821  * <pre><code>
10822 var RecordDef = Roo.data.Record.create([
10823     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
10824     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
10825 ]);
10826 var myReader = new Roo.data.JsonReader({
10827     totalProperty: "results",    // The property which contains the total dataset size (optional)
10828     root: "rows",                // The property which contains an Array of row objects
10829     id: "id"                     // The property within each row object that provides an ID for the record (optional)
10830 }, RecordDef);
10831 </code></pre>
10832  * <p>
10833  * This would consume a JSON file like this:
10834  * <pre><code>
10835 { 'results': 2, 'rows': [
10836     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10837     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10838 }
10839 </code></pre>
10840  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10841  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10842  * paged from the remote server.
10843  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10844  * @cfg {String} root name of the property which contains the Array of row objects.
10845  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10846  * @cfg {Array} fields Array of field definition objects
10847  * @constructor
10848  * Create a new JsonReader
10849  * @param {Object} meta Metadata configuration options
10850  * @param {Object} recordType Either an Array of field definition objects,
10851  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10852  */
10853 Roo.data.JsonReader = function(meta, recordType){
10854     
10855     meta = meta || {};
10856     // set some defaults:
10857     Roo.applyIf(meta, {
10858         totalProperty: 'total',
10859         successProperty : 'success',
10860         root : 'data',
10861         id : 'id'
10862     });
10863     
10864     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10865 };
10866 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10867     
10868     /**
10869      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
10870      * Used by Store query builder to append _requestMeta to params.
10871      * 
10872      */
10873     metaFromRemote : false,
10874     /**
10875      * This method is only used by a DataProxy which has retrieved data from a remote server.
10876      * @param {Object} response The XHR object which contains the JSON data in its responseText.
10877      * @return {Object} data A data block which is used by an Roo.data.Store object as
10878      * a cache of Roo.data.Records.
10879      */
10880     read : function(response){
10881         var json = response.responseText;
10882        
10883         var o = /* eval:var:o */ eval("("+json+")");
10884         if(!o) {
10885             throw {message: "JsonReader.read: Json object not found"};
10886         }
10887         
10888         if(o.metaData){
10889             
10890             delete this.ef;
10891             this.metaFromRemote = true;
10892             this.meta = o.metaData;
10893             this.recordType = Roo.data.Record.create(o.metaData.fields);
10894             this.onMetaChange(this.meta, this.recordType, o);
10895         }
10896         return this.readRecords(o);
10897     },
10898
10899     // private function a store will implement
10900     onMetaChange : function(meta, recordType, o){
10901
10902     },
10903
10904     /**
10905          * @ignore
10906          */
10907     simpleAccess: function(obj, subsc) {
10908         return obj[subsc];
10909     },
10910
10911         /**
10912          * @ignore
10913          */
10914     getJsonAccessor: function(){
10915         var re = /[\[\.]/;
10916         return function(expr) {
10917             try {
10918                 return(re.test(expr))
10919                     ? new Function("obj", "return obj." + expr)
10920                     : function(obj){
10921                         return obj[expr];
10922                     };
10923             } catch(e){}
10924             return Roo.emptyFn;
10925         };
10926     }(),
10927
10928     /**
10929      * Create a data block containing Roo.data.Records from an XML document.
10930      * @param {Object} o An object which contains an Array of row objects in the property specified
10931      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10932      * which contains the total size of the dataset.
10933      * @return {Object} data A data block which is used by an Roo.data.Store object as
10934      * a cache of Roo.data.Records.
10935      */
10936     readRecords : function(o){
10937         /**
10938          * After any data loads, the raw JSON data is available for further custom processing.
10939          * @type Object
10940          */
10941         this.o = o;
10942         var s = this.meta, Record = this.recordType,
10943             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
10944
10945 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
10946         if (!this.ef) {
10947             if(s.totalProperty) {
10948                     this.getTotal = this.getJsonAccessor(s.totalProperty);
10949                 }
10950                 if(s.successProperty) {
10951                     this.getSuccess = this.getJsonAccessor(s.successProperty);
10952                 }
10953                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10954                 if (s.id) {
10955                         var g = this.getJsonAccessor(s.id);
10956                         this.getId = function(rec) {
10957                                 var r = g(rec);  
10958                                 return (r === undefined || r === "") ? null : r;
10959                         };
10960                 } else {
10961                         this.getId = function(){return null;};
10962                 }
10963             this.ef = [];
10964             for(var jj = 0; jj < fl; jj++){
10965                 f = fi[jj];
10966                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10967                 this.ef[jj] = this.getJsonAccessor(map);
10968             }
10969         }
10970
10971         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10972         if(s.totalProperty){
10973             var vt = parseInt(this.getTotal(o), 10);
10974             if(!isNaN(vt)){
10975                 totalRecords = vt;
10976             }
10977         }
10978         if(s.successProperty){
10979             var vs = this.getSuccess(o);
10980             if(vs === false || vs === 'false'){
10981                 success = false;
10982             }
10983         }
10984         var records = [];
10985         for(var i = 0; i < c; i++){
10986                 var n = root[i];
10987             var values = {};
10988             var id = this.getId(n);
10989             for(var j = 0; j < fl; j++){
10990                 f = fi[j];
10991             var v = this.ef[j](n);
10992             if (!f.convert) {
10993                 Roo.log('missing convert for ' + f.name);
10994                 Roo.log(f);
10995                 continue;
10996             }
10997             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10998             }
10999             var record = new Record(values, id);
11000             record.json = n;
11001             records[i] = record;
11002         }
11003         return {
11004             raw : o,
11005             success : success,
11006             records : records,
11007             totalRecords : totalRecords
11008         };
11009     }
11010 });/*
11011  * Based on:
11012  * Ext JS Library 1.1.1
11013  * Copyright(c) 2006-2007, Ext JS, LLC.
11014  *
11015  * Originally Released Under LGPL - original licence link has changed is not relivant.
11016  *
11017  * Fork - LGPL
11018  * <script type="text/javascript">
11019  */
11020
11021 /**
11022  * @class Roo.data.ArrayReader
11023  * @extends Roo.data.DataReader
11024  * Data reader class to create an Array of Roo.data.Record objects from an Array.
11025  * Each element of that Array represents a row of data fields. The
11026  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11027  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11028  * <p>
11029  * Example code:.
11030  * <pre><code>
11031 var RecordDef = Roo.data.Record.create([
11032     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
11033     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
11034 ]);
11035 var myReader = new Roo.data.ArrayReader({
11036     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
11037 }, RecordDef);
11038 </code></pre>
11039  * <p>
11040  * This would consume an Array like this:
11041  * <pre><code>
11042 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11043   </code></pre>
11044  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11045  * @constructor
11046  * Create a new JsonReader
11047  * @param {Object} meta Metadata configuration options.
11048  * @param {Object} recordType Either an Array of field definition objects
11049  * as specified to {@link Roo.data.Record#create},
11050  * or an {@link Roo.data.Record} object
11051  * created using {@link Roo.data.Record#create}.
11052  */
11053 Roo.data.ArrayReader = function(meta, recordType){
11054     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11055 };
11056
11057 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11058     /**
11059      * Create a data block containing Roo.data.Records from an XML document.
11060      * @param {Object} o An Array of row objects which represents the dataset.
11061      * @return {Object} data A data block which is used by an Roo.data.Store object as
11062      * a cache of Roo.data.Records.
11063      */
11064     readRecords : function(o){
11065         var sid = this.meta ? this.meta.id : null;
11066         var recordType = this.recordType, fields = recordType.prototype.fields;
11067         var records = [];
11068         var root = o;
11069             for(var i = 0; i < root.length; i++){
11070                     var n = root[i];
11071                 var values = {};
11072                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11073                 for(var j = 0, jlen = fields.length; j < jlen; j++){
11074                 var f = fields.items[j];
11075                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11076                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11077                 v = f.convert(v);
11078                 values[f.name] = v;
11079             }
11080                 var record = new recordType(values, id);
11081                 record.json = n;
11082                 records[records.length] = record;
11083             }
11084             return {
11085                 records : records,
11086                 totalRecords : records.length
11087             };
11088     }
11089 });/*
11090  * - LGPL
11091  * * 
11092  */
11093
11094 /**
11095  * @class Roo.bootstrap.ComboBox
11096  * @extends Roo.bootstrap.TriggerField
11097  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11098  * @cfg {Boolean} append (true|false) default false
11099  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11100  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11101  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11102  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11103  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11104  * @cfg {Boolean} animate default true
11105  * @cfg {Boolean} emptyResultText only for touch device
11106  * @constructor
11107  * Create a new ComboBox.
11108  * @param {Object} config Configuration options
11109  */
11110 Roo.bootstrap.ComboBox = function(config){
11111     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11112     this.addEvents({
11113         /**
11114          * @event expand
11115          * Fires when the dropdown list is expanded
11116              * @param {Roo.bootstrap.ComboBox} combo This combo box
11117              */
11118         'expand' : true,
11119         /**
11120          * @event collapse
11121          * Fires when the dropdown list is collapsed
11122              * @param {Roo.bootstrap.ComboBox} combo This combo box
11123              */
11124         'collapse' : true,
11125         /**
11126          * @event beforeselect
11127          * Fires before a list item is selected. Return false to cancel the selection.
11128              * @param {Roo.bootstrap.ComboBox} combo This combo box
11129              * @param {Roo.data.Record} record The data record returned from the underlying store
11130              * @param {Number} index The index of the selected item in the dropdown list
11131              */
11132         'beforeselect' : true,
11133         /**
11134          * @event select
11135          * Fires when a list item is selected
11136              * @param {Roo.bootstrap.ComboBox} combo This combo box
11137              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11138              * @param {Number} index The index of the selected item in the dropdown list
11139              */
11140         'select' : true,
11141         /**
11142          * @event beforequery
11143          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11144          * The event object passed has these properties:
11145              * @param {Roo.bootstrap.ComboBox} combo This combo box
11146              * @param {String} query The query
11147              * @param {Boolean} forceAll true to force "all" query
11148              * @param {Boolean} cancel true to cancel the query
11149              * @param {Object} e The query event object
11150              */
11151         'beforequery': true,
11152          /**
11153          * @event add
11154          * Fires when the 'add' icon is pressed (add a listener to enable add button)
11155              * @param {Roo.bootstrap.ComboBox} combo This combo box
11156              */
11157         'add' : true,
11158         /**
11159          * @event edit
11160          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11161              * @param {Roo.bootstrap.ComboBox} combo This combo box
11162              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11163              */
11164         'edit' : true,
11165         /**
11166          * @event remove
11167          * Fires when the remove value from the combobox array
11168              * @param {Roo.bootstrap.ComboBox} combo This combo box
11169              */
11170         'remove' : true,
11171         /**
11172          * @event specialfilter
11173          * Fires when specialfilter
11174             * @param {Roo.bootstrap.ComboBox} combo This combo box
11175             */
11176         'specialfilter' : true
11177         
11178     });
11179     
11180     this.item = [];
11181     this.tickItems = [];
11182     
11183     this.selectedIndex = -1;
11184     if(this.mode == 'local'){
11185         if(config.queryDelay === undefined){
11186             this.queryDelay = 10;
11187         }
11188         if(config.minChars === undefined){
11189             this.minChars = 0;
11190         }
11191     }
11192 };
11193
11194 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11195      
11196     /**
11197      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11198      * rendering into an Roo.Editor, defaults to false)
11199      */
11200     /**
11201      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11202      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11203      */
11204     /**
11205      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11206      */
11207     /**
11208      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11209      * the dropdown list (defaults to undefined, with no header element)
11210      */
11211
11212      /**
11213      * @cfg {String/Roo.Template} tpl The template to use to render the output
11214      */
11215      
11216      /**
11217      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11218      */
11219     listWidth: undefined,
11220     /**
11221      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11222      * mode = 'remote' or 'text' if mode = 'local')
11223      */
11224     displayField: undefined,
11225     
11226     /**
11227      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11228      * mode = 'remote' or 'value' if mode = 'local'). 
11229      * Note: use of a valueField requires the user make a selection
11230      * in order for a value to be mapped.
11231      */
11232     valueField: undefined,
11233     
11234     
11235     /**
11236      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11237      * field's data value (defaults to the underlying DOM element's name)
11238      */
11239     hiddenName: undefined,
11240     /**
11241      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11242      */
11243     listClass: '',
11244     /**
11245      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11246      */
11247     selectedClass: 'active',
11248     
11249     /**
11250      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11251      */
11252     shadow:'sides',
11253     /**
11254      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11255      * anchor positions (defaults to 'tl-bl')
11256      */
11257     listAlign: 'tl-bl?',
11258     /**
11259      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11260      */
11261     maxHeight: 300,
11262     /**
11263      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
11264      * query specified by the allQuery config option (defaults to 'query')
11265      */
11266     triggerAction: 'query',
11267     /**
11268      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11269      * (defaults to 4, does not apply if editable = false)
11270      */
11271     minChars : 4,
11272     /**
11273      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11274      * delay (typeAheadDelay) if it matches a known value (defaults to false)
11275      */
11276     typeAhead: false,
11277     /**
11278      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11279      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11280      */
11281     queryDelay: 500,
11282     /**
11283      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11284      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
11285      */
11286     pageSize: 0,
11287     /**
11288      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
11289      * when editable = true (defaults to false)
11290      */
11291     selectOnFocus:false,
11292     /**
11293      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11294      */
11295     queryParam: 'query',
11296     /**
11297      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
11298      * when mode = 'remote' (defaults to 'Loading...')
11299      */
11300     loadingText: 'Loading...',
11301     /**
11302      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11303      */
11304     resizable: false,
11305     /**
11306      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11307      */
11308     handleHeight : 8,
11309     /**
11310      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11311      * traditional select (defaults to true)
11312      */
11313     editable: true,
11314     /**
11315      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11316      */
11317     allQuery: '',
11318     /**
11319      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11320      */
11321     mode: 'remote',
11322     /**
11323      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11324      * listWidth has a higher value)
11325      */
11326     minListWidth : 70,
11327     /**
11328      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11329      * allow the user to set arbitrary text into the field (defaults to false)
11330      */
11331     forceSelection:false,
11332     /**
11333      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11334      * if typeAhead = true (defaults to 250)
11335      */
11336     typeAheadDelay : 250,
11337     /**
11338      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11339      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11340      */
11341     valueNotFoundText : undefined,
11342     /**
11343      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11344      */
11345     blockFocus : false,
11346     
11347     /**
11348      * @cfg {Boolean} disableClear Disable showing of clear button.
11349      */
11350     disableClear : false,
11351     /**
11352      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
11353      */
11354     alwaysQuery : false,
11355     
11356     /**
11357      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
11358      */
11359     multiple : false,
11360     
11361     /**
11362      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11363      */
11364     invalidClass : "has-warning",
11365     
11366     /**
11367      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11368      */
11369     validClass : "has-success",
11370     
11371     /**
11372      * @cfg {Boolean} specialFilter (true|false) special filter default false
11373      */
11374     specialFilter : false,
11375     
11376     /**
11377      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
11378      */
11379     mobileTouchView : true,
11380     
11381     //private
11382     addicon : false,
11383     editicon: false,
11384     
11385     page: 0,
11386     hasQuery: false,
11387     append: false,
11388     loadNext: false,
11389     autoFocus : true,
11390     tickable : false,
11391     btnPosition : 'right',
11392     triggerList : true,
11393     showToggleBtn : true,
11394     animate : true,
11395     emptyResultText: 'Empty',
11396     // element that contains real text value.. (when hidden is used..)
11397     
11398     getAutoCreate : function()
11399     {
11400         var cfg = false;
11401         
11402         /*
11403          * Touch Devices
11404          */
11405         
11406         if(Roo.isTouch && this.mobileTouchView){
11407             cfg = this.getAutoCreateTouchView();
11408             return cfg;;
11409         }
11410         
11411         /*
11412          *  Normal ComboBox
11413          */
11414         if(!this.tickable){
11415             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11416             return cfg;
11417         }
11418         
11419         /*
11420          *  ComboBox with tickable selections
11421          */
11422              
11423         var align = this.labelAlign || this.parentLabelAlign();
11424         
11425         cfg = {
11426             cls : 'form-group roo-combobox-tickable' //input-group
11427         };
11428         
11429         var buttons = {
11430             tag : 'div',
11431             cls : 'tickable-buttons',
11432             cn : [
11433                 {
11434                     tag : 'button',
11435                     type : 'button',
11436                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11437                     html : 'Edit'
11438                 },
11439                 {
11440                     tag : 'button',
11441                     type : 'button',
11442                     name : 'ok',
11443                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11444                     html : 'Done'
11445                 },
11446                 {
11447                     tag : 'button',
11448                     type : 'button',
11449                     name : 'cancel',
11450                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11451                     html : 'Cancel'
11452                 }
11453             ]
11454         };
11455         
11456         if(this.editable){
11457             buttons.cn.unshift({
11458                 tag: 'input',
11459                 cls: 'select2-search-field-input'
11460             });
11461         }
11462         
11463         var _this = this;
11464         
11465         Roo.each(buttons.cn, function(c){
11466             if (_this.size) {
11467                 c.cls += ' btn-' + _this.size;
11468             }
11469
11470             if (_this.disabled) {
11471                 c.disabled = true;
11472             }
11473         });
11474         
11475         var box = {
11476             tag: 'div',
11477             cn: [
11478                 {
11479                     tag: 'input',
11480                     type : 'hidden',
11481                     cls: 'form-hidden-field'
11482                 },
11483                 {
11484                     tag: 'ul',
11485                     cls: 'select2-choices',
11486                     cn:[
11487                         {
11488                             tag: 'li',
11489                             cls: 'select2-search-field',
11490                             cn: [
11491
11492                                 buttons
11493                             ]
11494                         }
11495                     ]
11496                 }
11497             ]
11498         }
11499         
11500         var combobox = {
11501             cls: 'select2-container input-group select2-container-multi',
11502             cn: [
11503                 box
11504 //                {
11505 //                    tag: 'ul',
11506 //                    cls: 'typeahead typeahead-long dropdown-menu',
11507 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
11508 //                }
11509             ]
11510         };
11511         
11512         if(this.hasFeedback && !this.allowBlank){
11513             
11514             var feedback = {
11515                 tag: 'span',
11516                 cls: 'glyphicon form-control-feedback'
11517             };
11518
11519             combobox.cn.push(feedback);
11520         }
11521         
11522         if (align ==='left' && this.fieldLabel.length) {
11523             
11524                 Roo.log("left and has label");
11525                 cfg.cn = [
11526                     
11527                     {
11528                         tag: 'label',
11529                         'for' :  id,
11530                         cls : 'control-label col-sm-' + this.labelWidth,
11531                         html : this.fieldLabel
11532                         
11533                     },
11534                     {
11535                         cls : "col-sm-" + (12 - this.labelWidth), 
11536                         cn: [
11537                             combobox
11538                         ]
11539                     }
11540                     
11541                 ];
11542         } else if ( this.fieldLabel.length) {
11543                 Roo.log(" label");
11544                  cfg.cn = [
11545                    
11546                     {
11547                         tag: 'label',
11548                         //cls : 'input-group-addon',
11549                         html : this.fieldLabel
11550                         
11551                     },
11552                     
11553                     combobox
11554                     
11555                 ];
11556
11557         } else {
11558             
11559                 Roo.log(" no label && no align");
11560                 cfg = combobox
11561                      
11562                 
11563         }
11564          
11565         var settings=this;
11566         ['xs','sm','md','lg'].map(function(size){
11567             if (settings[size]) {
11568                 cfg.cls += ' col-' + size + '-' + settings[size];
11569             }
11570         });
11571         
11572         return cfg;
11573         
11574     },
11575     
11576     // private
11577     initEvents: function()
11578     {
11579         
11580         if (!this.store) {
11581             throw "can not find store for combo";
11582         }
11583         
11584         this.store = Roo.factory(this.store, Roo.data);
11585         
11586         /*
11587          * Touch Devices
11588          */
11589         
11590         if(Roo.isTouch && this.mobileTouchView){
11591             this.initTouchView();
11592             return;
11593         }
11594         
11595         if(this.tickable){
11596             this.initTickableEvents();
11597             return;
11598         }
11599         
11600         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11601         
11602         if(this.hiddenName){
11603             
11604             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11605             
11606             this.hiddenField.dom.value =
11607                 this.hiddenValue !== undefined ? this.hiddenValue :
11608                 this.value !== undefined ? this.value : '';
11609
11610             // prevent input submission
11611             this.el.dom.removeAttribute('name');
11612             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11613              
11614              
11615         }
11616         //if(Roo.isGecko){
11617         //    this.el.dom.setAttribute('autocomplete', 'off');
11618         //}
11619         
11620         var cls = 'x-combo-list';
11621         
11622         //this.list = new Roo.Layer({
11623         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
11624         //});
11625         
11626         var _this = this;
11627         
11628         (function(){
11629             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11630             _this.list.setWidth(lw);
11631         }).defer(100);
11632         
11633         this.list.on('mouseover', this.onViewOver, this);
11634         this.list.on('mousemove', this.onViewMove, this);
11635         
11636         this.list.on('scroll', this.onViewScroll, this);
11637         
11638         /*
11639         this.list.swallowEvent('mousewheel');
11640         this.assetHeight = 0;
11641
11642         if(this.title){
11643             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
11644             this.assetHeight += this.header.getHeight();
11645         }
11646
11647         this.innerList = this.list.createChild({cls:cls+'-inner'});
11648         this.innerList.on('mouseover', this.onViewOver, this);
11649         this.innerList.on('mousemove', this.onViewMove, this);
11650         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11651         
11652         if(this.allowBlank && !this.pageSize && !this.disableClear){
11653             this.footer = this.list.createChild({cls:cls+'-ft'});
11654             this.pageTb = new Roo.Toolbar(this.footer);
11655            
11656         }
11657         if(this.pageSize){
11658             this.footer = this.list.createChild({cls:cls+'-ft'});
11659             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
11660                     {pageSize: this.pageSize});
11661             
11662         }
11663         
11664         if (this.pageTb && this.allowBlank && !this.disableClear) {
11665             var _this = this;
11666             this.pageTb.add(new Roo.Toolbar.Fill(), {
11667                 cls: 'x-btn-icon x-btn-clear',
11668                 text: '&#160;',
11669                 handler: function()
11670                 {
11671                     _this.collapse();
11672                     _this.clearValue();
11673                     _this.onSelect(false, -1);
11674                 }
11675             });
11676         }
11677         if (this.footer) {
11678             this.assetHeight += this.footer.getHeight();
11679         }
11680         */
11681             
11682         if(!this.tpl){
11683             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
11684         }
11685
11686         this.view = new Roo.View(this.list, this.tpl, {
11687             singleSelect:true, store: this.store, selectedClass: this.selectedClass
11688         });
11689         //this.view.wrapEl.setDisplayed(false);
11690         this.view.on('click', this.onViewClick, this);
11691         
11692         
11693         
11694         this.store.on('beforeload', this.onBeforeLoad, this);
11695         this.store.on('load', this.onLoad, this);
11696         this.store.on('loadexception', this.onLoadException, this);
11697         /*
11698         if(this.resizable){
11699             this.resizer = new Roo.Resizable(this.list,  {
11700                pinned:true, handles:'se'
11701             });
11702             this.resizer.on('resize', function(r, w, h){
11703                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
11704                 this.listWidth = w;
11705                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
11706                 this.restrictHeight();
11707             }, this);
11708             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
11709         }
11710         */
11711         if(!this.editable){
11712             this.editable = true;
11713             this.setEditable(false);
11714         }
11715         
11716         /*
11717         
11718         if (typeof(this.events.add.listeners) != 'undefined') {
11719             
11720             this.addicon = this.wrap.createChild(
11721                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
11722        
11723             this.addicon.on('click', function(e) {
11724                 this.fireEvent('add', this);
11725             }, this);
11726         }
11727         if (typeof(this.events.edit.listeners) != 'undefined') {
11728             
11729             this.editicon = this.wrap.createChild(
11730                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
11731             if (this.addicon) {
11732                 this.editicon.setStyle('margin-left', '40px');
11733             }
11734             this.editicon.on('click', function(e) {
11735                 
11736                 // we fire even  if inothing is selected..
11737                 this.fireEvent('edit', this, this.lastData );
11738                 
11739             }, this);
11740         }
11741         */
11742         
11743         this.keyNav = new Roo.KeyNav(this.inputEl(), {
11744             "up" : function(e){
11745                 this.inKeyMode = true;
11746                 this.selectPrev();
11747             },
11748
11749             "down" : function(e){
11750                 if(!this.isExpanded()){
11751                     this.onTriggerClick();
11752                 }else{
11753                     this.inKeyMode = true;
11754                     this.selectNext();
11755                 }
11756             },
11757
11758             "enter" : function(e){
11759 //                this.onViewClick();
11760                 //return true;
11761                 this.collapse();
11762                 
11763                 if(this.fireEvent("specialkey", this, e)){
11764                     this.onViewClick(false);
11765                 }
11766                 
11767                 return true;
11768             },
11769
11770             "esc" : function(e){
11771                 this.collapse();
11772             },
11773
11774             "tab" : function(e){
11775                 this.collapse();
11776                 
11777                 if(this.fireEvent("specialkey", this, e)){
11778                     this.onViewClick(false);
11779                 }
11780                 
11781                 return true;
11782             },
11783
11784             scope : this,
11785
11786             doRelay : function(foo, bar, hname){
11787                 if(hname == 'down' || this.scope.isExpanded()){
11788                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11789                 }
11790                 return true;
11791             },
11792
11793             forceKeyDown: true
11794         });
11795         
11796         
11797         this.queryDelay = Math.max(this.queryDelay || 10,
11798                 this.mode == 'local' ? 10 : 250);
11799         
11800         
11801         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11802         
11803         if(this.typeAhead){
11804             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11805         }
11806         if(this.editable !== false){
11807             this.inputEl().on("keyup", this.onKeyUp, this);
11808         }
11809         if(this.forceSelection){
11810             this.inputEl().on('blur', this.doForce, this);
11811         }
11812         
11813         if(this.multiple){
11814             this.choices = this.el.select('ul.select2-choices', true).first();
11815             this.searchField = this.el.select('ul li.select2-search-field', true).first();
11816         }
11817     },
11818     
11819     initTickableEvents: function()
11820     {   
11821         this.createList();
11822         
11823         if(this.hiddenName){
11824             
11825             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11826             
11827             this.hiddenField.dom.value =
11828                 this.hiddenValue !== undefined ? this.hiddenValue :
11829                 this.value !== undefined ? this.value : '';
11830
11831             // prevent input submission
11832             this.el.dom.removeAttribute('name');
11833             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11834              
11835              
11836         }
11837         
11838 //        this.list = this.el.select('ul.dropdown-menu',true).first();
11839         
11840         this.choices = this.el.select('ul.select2-choices', true).first();
11841         this.searchField = this.el.select('ul li.select2-search-field', true).first();
11842         if(this.triggerList){
11843             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
11844         }
11845          
11846         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
11847         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
11848         
11849         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
11850         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
11851         
11852         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
11853         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
11854         
11855         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
11856         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
11857         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
11858         
11859         this.okBtn.hide();
11860         this.cancelBtn.hide();
11861         
11862         var _this = this;
11863         
11864         (function(){
11865             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11866             _this.list.setWidth(lw);
11867         }).defer(100);
11868         
11869         this.list.on('mouseover', this.onViewOver, this);
11870         this.list.on('mousemove', this.onViewMove, this);
11871         
11872         this.list.on('scroll', this.onViewScroll, this);
11873         
11874         if(!this.tpl){
11875             this.tpl = '<li class="select2-result"><div class="checkbox"><input id="{roo-id}" type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></li>';
11876         }
11877
11878         this.view = new Roo.View(this.list, this.tpl, {
11879             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
11880         });
11881         
11882         //this.view.wrapEl.setDisplayed(false);
11883         this.view.on('click', this.onViewClick, this);
11884         
11885         
11886         
11887         this.store.on('beforeload', this.onBeforeLoad, this);
11888         this.store.on('load', this.onLoad, this);
11889         this.store.on('loadexception', this.onLoadException, this);
11890         
11891         if(this.editable){
11892             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
11893                 "up" : function(e){
11894                     this.inKeyMode = true;
11895                     this.selectPrev();
11896                 },
11897
11898                 "down" : function(e){
11899                     this.inKeyMode = true;
11900                     this.selectNext();
11901                 },
11902
11903                 "enter" : function(e){
11904                     if(this.fireEvent("specialkey", this, e)){
11905                         this.onViewClick(false);
11906                     }
11907                     
11908                     return true;
11909                 },
11910
11911                 "esc" : function(e){
11912                     this.onTickableFooterButtonClick(e, false, false);
11913                 },
11914
11915                 "tab" : function(e){
11916                     this.fireEvent("specialkey", this, e);
11917                     
11918                     this.onTickableFooterButtonClick(e, false, false);
11919                     
11920                     return true;
11921                 },
11922
11923                 scope : this,
11924
11925                 doRelay : function(e, fn, key){
11926                     if(this.scope.isExpanded()){
11927                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11928                     }
11929                     return true;
11930                 },
11931
11932                 forceKeyDown: true
11933             });
11934         }
11935         
11936         this.queryDelay = Math.max(this.queryDelay || 10,
11937                 this.mode == 'local' ? 10 : 250);
11938         
11939         
11940         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11941         
11942         if(this.typeAhead){
11943             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11944         }
11945         
11946         if(this.editable !== false){
11947             this.tickableInputEl().on("keyup", this.onKeyUp, this);
11948         }
11949         
11950     },
11951
11952     onDestroy : function(){
11953         if(this.view){
11954             this.view.setStore(null);
11955             this.view.el.removeAllListeners();
11956             this.view.el.remove();
11957             this.view.purgeListeners();
11958         }
11959         if(this.list){
11960             this.list.dom.innerHTML  = '';
11961         }
11962         
11963         if(this.store){
11964             this.store.un('beforeload', this.onBeforeLoad, this);
11965             this.store.un('load', this.onLoad, this);
11966             this.store.un('loadexception', this.onLoadException, this);
11967         }
11968         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11969     },
11970
11971     // private
11972     fireKey : function(e){
11973         if(e.isNavKeyPress() && !this.list.isVisible()){
11974             this.fireEvent("specialkey", this, e);
11975         }
11976     },
11977
11978     // private
11979     onResize: function(w, h){
11980 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11981 //        
11982 //        if(typeof w != 'number'){
11983 //            // we do not handle it!?!?
11984 //            return;
11985 //        }
11986 //        var tw = this.trigger.getWidth();
11987 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
11988 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
11989 //        var x = w - tw;
11990 //        this.inputEl().setWidth( this.adjustWidth('input', x));
11991 //            
11992 //        //this.trigger.setStyle('left', x+'px');
11993 //        
11994 //        if(this.list && this.listWidth === undefined){
11995 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11996 //            this.list.setWidth(lw);
11997 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11998 //        }
11999         
12000     
12001         
12002     },
12003
12004     /**
12005      * Allow or prevent the user from directly editing the field text.  If false is passed,
12006      * the user will only be able to select from the items defined in the dropdown list.  This method
12007      * is the runtime equivalent of setting the 'editable' config option at config time.
12008      * @param {Boolean} value True to allow the user to directly edit the field text
12009      */
12010     setEditable : function(value){
12011         if(value == this.editable){
12012             return;
12013         }
12014         this.editable = value;
12015         if(!value){
12016             this.inputEl().dom.setAttribute('readOnly', true);
12017             this.inputEl().on('mousedown', this.onTriggerClick,  this);
12018             this.inputEl().addClass('x-combo-noedit');
12019         }else{
12020             this.inputEl().dom.setAttribute('readOnly', false);
12021             this.inputEl().un('mousedown', this.onTriggerClick,  this);
12022             this.inputEl().removeClass('x-combo-noedit');
12023         }
12024     },
12025
12026     // private
12027     
12028     onBeforeLoad : function(combo,opts){
12029         if(!this.hasFocus){
12030             return;
12031         }
12032          if (!opts.add) {
12033             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12034          }
12035         this.restrictHeight();
12036         this.selectedIndex = -1;
12037     },
12038
12039     // private
12040     onLoad : function(){
12041         
12042         this.hasQuery = false;
12043         
12044         if(!this.hasFocus){
12045             return;
12046         }
12047         
12048         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12049             this.loading.hide();
12050         }
12051              
12052         if(this.store.getCount() > 0){
12053             this.expand();
12054             this.restrictHeight();
12055             if(this.lastQuery == this.allQuery){
12056                 if(this.editable && !this.tickable){
12057                     this.inputEl().dom.select();
12058                 }
12059                 
12060                 if(
12061                     !this.selectByValue(this.value, true) &&
12062                     this.autoFocus && 
12063                     (
12064                         !this.store.lastOptions ||
12065                         typeof(this.store.lastOptions.add) == 'undefined' || 
12066                         this.store.lastOptions.add != true
12067                     )
12068                 ){
12069                     this.select(0, true);
12070                 }
12071             }else{
12072                 if(this.autoFocus){
12073                     this.selectNext();
12074                 }
12075                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12076                     this.taTask.delay(this.typeAheadDelay);
12077                 }
12078             }
12079         }else{
12080             this.onEmptyResults();
12081         }
12082         
12083         //this.el.focus();
12084     },
12085     // private
12086     onLoadException : function()
12087     {
12088         this.hasQuery = false;
12089         
12090         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12091             this.loading.hide();
12092         }
12093         
12094         if(this.tickable && this.editable){
12095             return;
12096         }
12097         
12098         this.collapse();
12099         
12100         Roo.log(this.store.reader.jsonData);
12101         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12102             // fixme
12103             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12104         }
12105         
12106         
12107     },
12108     // private
12109     onTypeAhead : function(){
12110         if(this.store.getCount() > 0){
12111             var r = this.store.getAt(0);
12112             var newValue = r.data[this.displayField];
12113             var len = newValue.length;
12114             var selStart = this.getRawValue().length;
12115             
12116             if(selStart != len){
12117                 this.setRawValue(newValue);
12118                 this.selectText(selStart, newValue.length);
12119             }
12120         }
12121     },
12122
12123     // private
12124     onSelect : function(record, index){
12125         
12126         if(this.fireEvent('beforeselect', this, record, index) !== false){
12127         
12128             this.setFromData(index > -1 ? record.data : false);
12129             
12130             this.collapse();
12131             this.fireEvent('select', this, record, index);
12132         }
12133     },
12134
12135     /**
12136      * Returns the currently selected field value or empty string if no value is set.
12137      * @return {String} value The selected value
12138      */
12139     getValue : function(){
12140         
12141         if(this.multiple){
12142             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12143         }
12144         
12145         if(this.valueField){
12146             return typeof this.value != 'undefined' ? this.value : '';
12147         }else{
12148             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12149         }
12150     },
12151
12152     /**
12153      * Clears any text/value currently set in the field
12154      */
12155     clearValue : function(){
12156         if(this.hiddenField){
12157             this.hiddenField.dom.value = '';
12158         }
12159         this.value = '';
12160         this.setRawValue('');
12161         this.lastSelectionText = '';
12162         this.lastData = false;
12163         
12164         var close = this.closeTriggerEl();
12165         
12166         if(close){
12167             close.hide();
12168         }
12169         
12170     },
12171
12172     /**
12173      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
12174      * will be displayed in the field.  If the value does not match the data value of an existing item,
12175      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12176      * Otherwise the field will be blank (although the value will still be set).
12177      * @param {String} value The value to match
12178      */
12179     setValue : function(v){
12180         if(this.multiple){
12181             this.syncValue();
12182             return;
12183         }
12184         
12185         var text = v;
12186         if(this.valueField){
12187             var r = this.findRecord(this.valueField, v);
12188             if(r){
12189                 text = r.data[this.displayField];
12190             }else if(this.valueNotFoundText !== undefined){
12191                 text = this.valueNotFoundText;
12192             }
12193         }
12194         this.lastSelectionText = text;
12195         if(this.hiddenField){
12196             this.hiddenField.dom.value = v;
12197         }
12198         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12199         this.value = v;
12200         
12201         var close = this.closeTriggerEl();
12202         
12203         if(close){
12204             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12205         }
12206     },
12207     /**
12208      * @property {Object} the last set data for the element
12209      */
12210     
12211     lastData : false,
12212     /**
12213      * Sets the value of the field based on a object which is related to the record format for the store.
12214      * @param {Object} value the value to set as. or false on reset?
12215      */
12216     setFromData : function(o){
12217         
12218         if(this.multiple){
12219             this.addItem(o);
12220             return;
12221         }
12222             
12223         var dv = ''; // display value
12224         var vv = ''; // value value..
12225         this.lastData = o;
12226         if (this.displayField) {
12227             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12228         } else {
12229             // this is an error condition!!!
12230             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12231         }
12232         
12233         if(this.valueField){
12234             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12235         }
12236         
12237         var close = this.closeTriggerEl();
12238         
12239         if(close){
12240             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12241         }
12242         
12243         if(this.hiddenField){
12244             this.hiddenField.dom.value = vv;
12245             
12246             this.lastSelectionText = dv;
12247             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12248             this.value = vv;
12249             return;
12250         }
12251         // no hidden field.. - we store the value in 'value', but still display
12252         // display field!!!!
12253         this.lastSelectionText = dv;
12254         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12255         this.value = vv;
12256         
12257         
12258         
12259     },
12260     // private
12261     reset : function(){
12262         // overridden so that last data is reset..
12263         
12264         if(this.multiple){
12265             this.clearItem();
12266             return;
12267         }
12268         
12269         this.setValue(this.originalValue);
12270         this.clearInvalid();
12271         this.lastData = false;
12272         if (this.view) {
12273             this.view.clearSelections();
12274         }
12275     },
12276     // private
12277     findRecord : function(prop, value){
12278         var record;
12279         if(this.store.getCount() > 0){
12280             this.store.each(function(r){
12281                 if(r.data[prop] == value){
12282                     record = r;
12283                     return false;
12284                 }
12285                 return true;
12286             });
12287         }
12288         return record;
12289     },
12290     
12291     getName: function()
12292     {
12293         // returns hidden if it's set..
12294         if (!this.rendered) {return ''};
12295         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
12296         
12297     },
12298     // private
12299     onViewMove : function(e, t){
12300         this.inKeyMode = false;
12301     },
12302
12303     // private
12304     onViewOver : function(e, t){
12305         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12306             return;
12307         }
12308         var item = this.view.findItemFromChild(t);
12309         
12310         if(item){
12311             var index = this.view.indexOf(item);
12312             this.select(index, false);
12313         }
12314     },
12315
12316     // private
12317     onViewClick : function(view, doFocus, el, e)
12318     {
12319         var index = this.view.getSelectedIndexes()[0];
12320         
12321         var r = this.store.getAt(index);
12322         
12323         if(this.tickable){
12324             
12325             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12326                 return;
12327             }
12328             
12329             var rm = false;
12330             var _this = this;
12331             
12332             Roo.each(this.tickItems, function(v,k){
12333                 
12334                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12335                     _this.tickItems.splice(k, 1);
12336                     
12337                     if(typeof(e) == 'undefined' && view == false){
12338                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12339                     }
12340                     
12341                     rm = true;
12342                     return;
12343                 }
12344             });
12345             
12346             if(rm){
12347                 return;
12348             }
12349             
12350             this.tickItems.push(r.data);
12351             
12352             if(typeof(e) == 'undefined' && view == false){
12353                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12354             }
12355                     
12356             return;
12357         }
12358         
12359         if(r){
12360             this.onSelect(r, index);
12361         }
12362         if(doFocus !== false && !this.blockFocus){
12363             this.inputEl().focus();
12364         }
12365     },
12366
12367     // private
12368     restrictHeight : function(){
12369         //this.innerList.dom.style.height = '';
12370         //var inner = this.innerList.dom;
12371         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12372         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12373         //this.list.beginUpdate();
12374         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12375         this.list.alignTo(this.inputEl(), this.listAlign);
12376         this.list.alignTo(this.inputEl(), this.listAlign);
12377         //this.list.endUpdate();
12378     },
12379
12380     // private
12381     onEmptyResults : function(){
12382         
12383         if(this.tickable && this.editable){
12384             this.restrictHeight();
12385             return;
12386         }
12387         
12388         this.collapse();
12389     },
12390
12391     /**
12392      * Returns true if the dropdown list is expanded, else false.
12393      */
12394     isExpanded : function(){
12395         return this.list.isVisible();
12396     },
12397
12398     /**
12399      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12400      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12401      * @param {String} value The data value of the item to select
12402      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12403      * selected item if it is not currently in view (defaults to true)
12404      * @return {Boolean} True if the value matched an item in the list, else false
12405      */
12406     selectByValue : function(v, scrollIntoView){
12407         if(v !== undefined && v !== null){
12408             var r = this.findRecord(this.valueField || this.displayField, v);
12409             if(r){
12410                 this.select(this.store.indexOf(r), scrollIntoView);
12411                 return true;
12412             }
12413         }
12414         return false;
12415     },
12416
12417     /**
12418      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12419      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12420      * @param {Number} index The zero-based index of the list item to select
12421      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12422      * selected item if it is not currently in view (defaults to true)
12423      */
12424     select : function(index, scrollIntoView){
12425         this.selectedIndex = index;
12426         this.view.select(index);
12427         if(scrollIntoView !== false){
12428             var el = this.view.getNode(index);
12429             /*
12430              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12431              */
12432             if(el){
12433                 this.list.scrollChildIntoView(el, false);
12434             }
12435         }
12436     },
12437
12438     // private
12439     selectNext : function(){
12440         var ct = this.store.getCount();
12441         if(ct > 0){
12442             if(this.selectedIndex == -1){
12443                 this.select(0);
12444             }else if(this.selectedIndex < ct-1){
12445                 this.select(this.selectedIndex+1);
12446             }
12447         }
12448     },
12449
12450     // private
12451     selectPrev : function(){
12452         var ct = this.store.getCount();
12453         if(ct > 0){
12454             if(this.selectedIndex == -1){
12455                 this.select(0);
12456             }else if(this.selectedIndex != 0){
12457                 this.select(this.selectedIndex-1);
12458             }
12459         }
12460     },
12461
12462     // private
12463     onKeyUp : function(e){
12464         if(this.editable !== false && !e.isSpecialKey()){
12465             this.lastKey = e.getKey();
12466             this.dqTask.delay(this.queryDelay);
12467         }
12468     },
12469
12470     // private
12471     validateBlur : function(){
12472         return !this.list || !this.list.isVisible();   
12473     },
12474
12475     // private
12476     initQuery : function(){
12477         
12478         var v = this.getRawValue();
12479         
12480         if(this.tickable && this.editable){
12481             v = this.tickableInputEl().getValue();
12482         }
12483         
12484         this.doQuery(v);
12485     },
12486
12487     // private
12488     doForce : function(){
12489         if(this.inputEl().dom.value.length > 0){
12490             this.inputEl().dom.value =
12491                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12492              
12493         }
12494     },
12495
12496     /**
12497      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
12498      * query allowing the query action to be canceled if needed.
12499      * @param {String} query The SQL query to execute
12500      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12501      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
12502      * saved in the current store (defaults to false)
12503      */
12504     doQuery : function(q, forceAll){
12505         
12506         if(q === undefined || q === null){
12507             q = '';
12508         }
12509         var qe = {
12510             query: q,
12511             forceAll: forceAll,
12512             combo: this,
12513             cancel:false
12514         };
12515         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12516             return false;
12517         }
12518         q = qe.query;
12519         
12520         forceAll = qe.forceAll;
12521         if(forceAll === true || (q.length >= this.minChars)){
12522             
12523             this.hasQuery = true;
12524             
12525             if(this.lastQuery != q || this.alwaysQuery){
12526                 this.lastQuery = q;
12527                 if(this.mode == 'local'){
12528                     this.selectedIndex = -1;
12529                     if(forceAll){
12530                         this.store.clearFilter();
12531                     }else{
12532                         
12533                         if(this.specialFilter){
12534                             this.fireEvent('specialfilter', this);
12535                             this.onLoad();
12536                             return;
12537                         }
12538                         
12539                         this.store.filter(this.displayField, q);
12540                     }
12541                     
12542                     this.store.fireEvent("datachanged", this.store);
12543                     
12544                     this.onLoad();
12545                     
12546                     
12547                 }else{
12548                     
12549                     this.store.baseParams[this.queryParam] = q;
12550                     
12551                     var options = {params : this.getParams(q)};
12552                     
12553                     if(this.loadNext){
12554                         options.add = true;
12555                         options.params.start = this.page * this.pageSize;
12556                     }
12557                     
12558                     this.store.load(options);
12559                     
12560                     /*
12561                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
12562                      *  we should expand the list on onLoad
12563                      *  so command out it
12564                      */
12565 //                    this.expand();
12566                 }
12567             }else{
12568                 this.selectedIndex = -1;
12569                 this.onLoad();   
12570             }
12571         }
12572         
12573         this.loadNext = false;
12574     },
12575     
12576     // private
12577     getParams : function(q){
12578         var p = {};
12579         //p[this.queryParam] = q;
12580         
12581         if(this.pageSize){
12582             p.start = 0;
12583             p.limit = this.pageSize;
12584         }
12585         return p;
12586     },
12587
12588     /**
12589      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12590      */
12591     collapse : function(){
12592         if(!this.isExpanded()){
12593             return;
12594         }
12595         
12596         this.list.hide();
12597         
12598         if(this.tickable){
12599             this.hasFocus = false;
12600             this.okBtn.hide();
12601             this.cancelBtn.hide();
12602             this.trigger.show();
12603             
12604             if(this.editable){
12605                 this.tickableInputEl().dom.value = '';
12606                 this.tickableInputEl().blur();
12607             }
12608             
12609         }
12610         
12611         Roo.get(document).un('mousedown', this.collapseIf, this);
12612         Roo.get(document).un('mousewheel', this.collapseIf, this);
12613         if (!this.editable) {
12614             Roo.get(document).un('keydown', this.listKeyPress, this);
12615         }
12616         this.fireEvent('collapse', this);
12617     },
12618
12619     // private
12620     collapseIf : function(e){
12621         var in_combo  = e.within(this.el);
12622         var in_list =  e.within(this.list);
12623         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
12624         
12625         if (in_combo || in_list || is_list) {
12626             //e.stopPropagation();
12627             return;
12628         }
12629         
12630         if(this.tickable){
12631             this.onTickableFooterButtonClick(e, false, false);
12632         }
12633
12634         this.collapse();
12635         
12636     },
12637
12638     /**
12639      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
12640      */
12641     expand : function(){
12642        
12643         if(this.isExpanded() || !this.hasFocus){
12644             return;
12645         }
12646         
12647         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
12648         this.list.setWidth(lw);
12649         
12650         
12651          Roo.log('expand');
12652         
12653         this.list.show();
12654         
12655         this.restrictHeight();
12656         
12657         if(this.tickable){
12658             
12659             this.tickItems = Roo.apply([], this.item);
12660             
12661             this.okBtn.show();
12662             this.cancelBtn.show();
12663             this.trigger.hide();
12664             
12665             if(this.editable){
12666                 this.tickableInputEl().focus();
12667             }
12668             
12669         }
12670         
12671         Roo.get(document).on('mousedown', this.collapseIf, this);
12672         Roo.get(document).on('mousewheel', this.collapseIf, this);
12673         if (!this.editable) {
12674             Roo.get(document).on('keydown', this.listKeyPress, this);
12675         }
12676         
12677         this.fireEvent('expand', this);
12678     },
12679
12680     // private
12681     // Implements the default empty TriggerField.onTriggerClick function
12682     onTriggerClick : function(e)
12683     {
12684         Roo.log('trigger click');
12685         
12686         if(this.disabled || !this.triggerList){
12687             return;
12688         }
12689         
12690         this.page = 0;
12691         this.loadNext = false;
12692         
12693         if(this.isExpanded()){
12694             this.collapse();
12695             if (!this.blockFocus) {
12696                 this.inputEl().focus();
12697             }
12698             
12699         }else {
12700             this.hasFocus = true;
12701             if(this.triggerAction == 'all') {
12702                 this.doQuery(this.allQuery, true);
12703             } else {
12704                 this.doQuery(this.getRawValue());
12705             }
12706             if (!this.blockFocus) {
12707                 this.inputEl().focus();
12708             }
12709         }
12710     },
12711     
12712     onTickableTriggerClick : function(e)
12713     {
12714         if(this.disabled){
12715             return;
12716         }
12717         
12718         this.page = 0;
12719         this.loadNext = false;
12720         this.hasFocus = true;
12721         
12722         if(this.triggerAction == 'all') {
12723             this.doQuery(this.allQuery, true);
12724         } else {
12725             this.doQuery(this.getRawValue());
12726         }
12727     },
12728     
12729     onSearchFieldClick : function(e)
12730     {
12731         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
12732             this.onTickableFooterButtonClick(e, false, false);
12733             return;
12734         }
12735         
12736         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
12737             return;
12738         }
12739         
12740         this.page = 0;
12741         this.loadNext = false;
12742         this.hasFocus = true;
12743         
12744         if(this.triggerAction == 'all') {
12745             this.doQuery(this.allQuery, true);
12746         } else {
12747             this.doQuery(this.getRawValue());
12748         }
12749     },
12750     
12751     listKeyPress : function(e)
12752     {
12753         //Roo.log('listkeypress');
12754         // scroll to first matching element based on key pres..
12755         if (e.isSpecialKey()) {
12756             return false;
12757         }
12758         var k = String.fromCharCode(e.getKey()).toUpperCase();
12759         //Roo.log(k);
12760         var match  = false;
12761         var csel = this.view.getSelectedNodes();
12762         var cselitem = false;
12763         if (csel.length) {
12764             var ix = this.view.indexOf(csel[0]);
12765             cselitem  = this.store.getAt(ix);
12766             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
12767                 cselitem = false;
12768             }
12769             
12770         }
12771         
12772         this.store.each(function(v) { 
12773             if (cselitem) {
12774                 // start at existing selection.
12775                 if (cselitem.id == v.id) {
12776                     cselitem = false;
12777                 }
12778                 return true;
12779             }
12780                 
12781             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
12782                 match = this.store.indexOf(v);
12783                 return false;
12784             }
12785             return true;
12786         }, this);
12787         
12788         if (match === false) {
12789             return true; // no more action?
12790         }
12791         // scroll to?
12792         this.view.select(match);
12793         var sn = Roo.get(this.view.getSelectedNodes()[0])
12794         sn.scrollIntoView(sn.dom.parentNode, false);
12795     },
12796     
12797     onViewScroll : function(e, t){
12798         
12799         if(this.view.el.getScroll().top == 0 ||this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
12800             return;
12801         }
12802         
12803         this.hasQuery = true;
12804         
12805         this.loading = this.list.select('.loading', true).first();
12806         
12807         if(this.loading === null){
12808             this.list.createChild({
12809                 tag: 'div',
12810                 cls: 'loading select2-more-results select2-active',
12811                 html: 'Loading more results...'
12812             })
12813             
12814             this.loading = this.list.select('.loading', true).first();
12815             
12816             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
12817             
12818             this.loading.hide();
12819         }
12820         
12821         this.loading.show();
12822         
12823         var _combo = this;
12824         
12825         this.page++;
12826         this.loadNext = true;
12827         
12828         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
12829         
12830         return;
12831     },
12832     
12833     addItem : function(o)
12834     {   
12835         var dv = ''; // display value
12836         
12837         if (this.displayField) {
12838             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12839         } else {
12840             // this is an error condition!!!
12841             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12842         }
12843         
12844         if(!dv.length){
12845             return;
12846         }
12847         
12848         var choice = this.choices.createChild({
12849             tag: 'li',
12850             cls: 'select2-search-choice',
12851             cn: [
12852                 {
12853                     tag: 'div',
12854                     html: dv
12855                 },
12856                 {
12857                     tag: 'a',
12858                     href: '#',
12859                     cls: 'select2-search-choice-close',
12860                     tabindex: '-1'
12861                 }
12862             ]
12863             
12864         }, this.searchField);
12865         
12866         var close = choice.select('a.select2-search-choice-close', true).first()
12867         
12868         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
12869         
12870         this.item.push(o);
12871         
12872         this.lastData = o;
12873         
12874         this.syncValue();
12875         
12876         this.inputEl().dom.value = '';
12877         
12878         this.validate();
12879     },
12880     
12881     onRemoveItem : function(e, _self, o)
12882     {
12883         e.preventDefault();
12884         
12885         this.lastItem = Roo.apply([], this.item);
12886         
12887         var index = this.item.indexOf(o.data) * 1;
12888         
12889         if( index < 0){
12890             Roo.log('not this item?!');
12891             return;
12892         }
12893         
12894         this.item.splice(index, 1);
12895         o.item.remove();
12896         
12897         this.syncValue();
12898         
12899         this.fireEvent('remove', this, e);
12900         
12901         this.validate();
12902         
12903     },
12904     
12905     syncValue : function()
12906     {
12907         if(!this.item.length){
12908             this.clearValue();
12909             return;
12910         }
12911             
12912         var value = [];
12913         var _this = this;
12914         Roo.each(this.item, function(i){
12915             if(_this.valueField){
12916                 value.push(i[_this.valueField]);
12917                 return;
12918             }
12919
12920             value.push(i);
12921         });
12922
12923         this.value = value.join(',');
12924
12925         if(this.hiddenField){
12926             this.hiddenField.dom.value = this.value;
12927         }
12928         
12929         this.store.fireEvent("datachanged", this.store);
12930     },
12931     
12932     clearItem : function()
12933     {
12934         if(!this.multiple){
12935             return;
12936         }
12937         
12938         this.item = [];
12939         
12940         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
12941            c.remove();
12942         });
12943         
12944         this.syncValue();
12945         
12946         this.validate();
12947         
12948         if(this.tickable && !Roo.isTouch){
12949             this.view.refresh();
12950         }
12951     },
12952     
12953     inputEl: function ()
12954     {
12955         if(Roo.isTouch && this.mobileTouchView){
12956             return this.el.select('input.form-control',true).first();
12957         }
12958         
12959         if(this.tickable){
12960             return this.searchField;
12961         }
12962         
12963         return this.el.select('input.form-control',true).first();
12964     },
12965     
12966     
12967     onTickableFooterButtonClick : function(e, btn, el)
12968     {
12969         e.preventDefault();
12970         
12971         this.lastItem = Roo.apply([], this.item);
12972         
12973         if(btn && btn.name == 'cancel'){
12974             this.tickItems = Roo.apply([], this.item);
12975             this.collapse();
12976             return;
12977         }
12978         
12979         this.clearItem();
12980         
12981         var _this = this;
12982         
12983         Roo.each(this.tickItems, function(o){
12984             _this.addItem(o);
12985         });
12986         
12987         this.collapse();
12988         
12989     },
12990     
12991     validate : function()
12992     {
12993         var v = this.getRawValue();
12994         
12995         if(this.multiple){
12996             v = this.getValue();
12997         }
12998         
12999         if(this.disabled || this.allowBlank || v.length){
13000             this.markValid();
13001             return true;
13002         }
13003         
13004         this.markInvalid();
13005         return false;
13006     },
13007     
13008     tickableInputEl : function()
13009     {
13010         if(!this.tickable || !this.editable){
13011             return this.inputEl();
13012         }
13013         
13014         return this.inputEl().select('.select2-search-field-input', true).first();
13015     },
13016     
13017     
13018     getAutoCreateTouchView : function()
13019     {
13020         var id = Roo.id();
13021         
13022         var cfg = {
13023             cls: 'form-group' //input-group
13024         };
13025         
13026         var input =  {
13027             tag: 'input',
13028             id : id,
13029             type : this.inputType,
13030             cls : 'form-control x-combo-noedit',
13031             autocomplete: 'new-password',
13032             placeholder : this.placeholder || '',
13033             readonly : true
13034         };
13035         
13036         if (this.name) {
13037             input.name = this.name;
13038         }
13039         
13040         if (this.size) {
13041             input.cls += ' input-' + this.size;
13042         }
13043         
13044         if (this.disabled) {
13045             input.disabled = true;
13046         }
13047         
13048         var inputblock = {
13049             cls : '',
13050             cn : [
13051                 input
13052             ]
13053         };
13054         
13055         if(this.before){
13056             inputblock.cls += ' input-group';
13057             
13058             inputblock.cn.unshift({
13059                 tag :'span',
13060                 cls : 'input-group-addon',
13061                 html : this.before
13062             });
13063         }
13064         
13065         if(this.removable && !this.multiple){
13066             inputblock.cls += ' roo-removable';
13067             
13068             inputblock.cn.push({
13069                 tag: 'button',
13070                 html : 'x',
13071                 cls : 'roo-combo-removable-btn close'
13072             });
13073         }
13074
13075         if(this.hasFeedback && !this.allowBlank){
13076             
13077             inputblock.cls += ' has-feedback';
13078             
13079             inputblock.cn.push({
13080                 tag: 'span',
13081                 cls: 'glyphicon form-control-feedback'
13082             });
13083             
13084         }
13085         
13086         if (this.after) {
13087             
13088             inputblock.cls += (this.before) ? '' : ' input-group';
13089             
13090             inputblock.cn.push({
13091                 tag :'span',
13092                 cls : 'input-group-addon',
13093                 html : this.after
13094             });
13095         }
13096
13097         var box = {
13098             tag: 'div',
13099             cn: [
13100                 {
13101                     tag: 'input',
13102                     type : 'hidden',
13103                     cls: 'form-hidden-field'
13104                 },
13105                 inputblock
13106             ]
13107             
13108         };
13109         
13110         if(this.multiple){
13111             box = {
13112                 tag: 'div',
13113                 cn: [
13114                     {
13115                         tag: 'input',
13116                         type : 'hidden',
13117                         cls: 'form-hidden-field'
13118                     },
13119                     {
13120                         tag: 'ul',
13121                         cls: 'select2-choices',
13122                         cn:[
13123                             {
13124                                 tag: 'li',
13125                                 cls: 'select2-search-field',
13126                                 cn: [
13127
13128                                     inputblock
13129                                 ]
13130                             }
13131                         ]
13132                     }
13133                 ]
13134             }
13135         };
13136         
13137         var combobox = {
13138             cls: 'select2-container input-group',
13139             cn: [
13140                 box
13141             ]
13142         };
13143         
13144         if(this.multiple){
13145             combobox.cls += ' select2-container-multi';
13146         }
13147         
13148         var align = this.labelAlign || this.parentLabelAlign();
13149         
13150         cfg.cn = combobox;
13151         
13152         if(this.fieldLabel.length){
13153             
13154             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13155             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13156             
13157             cfg.cn = [
13158                 {
13159                     tag: 'label',
13160                     cls : 'control-label ' + lw,
13161                     html : this.fieldLabel
13162
13163                 },
13164                 {
13165                     cls : cw, 
13166                     cn: [
13167                         combobox
13168                     ]
13169                 }
13170             ];
13171         }
13172         
13173         var settings = this;
13174         
13175         ['xs','sm','md','lg'].map(function(size){
13176             if (settings[size]) {
13177                 cfg.cls += ' col-' + size + '-' + settings[size];
13178             }
13179         });
13180         
13181         return cfg;
13182     },
13183     
13184     initTouchView : function()
13185     {
13186         this.renderTouchView();
13187         
13188         this.touchViewEl.on('scroll', function(){
13189             this.el.dom.scrollTop = 0;
13190         }, this);
13191         
13192         this.inputEl().on("click", this.showTouchView, this);
13193         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13194         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13195         
13196         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13197         
13198         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13199         this.store.on('load', this.onTouchViewLoad, this);
13200         this.store.on('loadexception', this.onTouchViewLoadException, this);
13201         
13202         if(this.hiddenName){
13203             
13204             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13205             
13206             this.hiddenField.dom.value =
13207                 this.hiddenValue !== undefined ? this.hiddenValue :
13208                 this.value !== undefined ? this.value : '';
13209         
13210             this.el.dom.removeAttribute('name');
13211             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13212         }
13213         
13214         if(this.multiple){
13215             this.choices = this.el.select('ul.select2-choices', true).first();
13216             this.searchField = this.el.select('ul li.select2-search-field', true).first();
13217         }
13218         
13219         if(this.removable && !this.multiple){
13220             var close = this.closeTriggerEl();
13221             if(close){
13222                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13223                 close.on('click', this.removeBtnClick, this, close);
13224             }
13225         }
13226         
13227         return;
13228         
13229         
13230     },
13231     
13232     renderTouchView : function()
13233     {
13234         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13235         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13236         
13237         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13238         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13239         
13240         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13241         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13242         this.touchViewBodyEl.setStyle('overflow', 'auto');
13243         
13244         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13245         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13246         
13247         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13248         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13249         
13250     },
13251     
13252     showTouchView : function()
13253     {
13254         this.touchViewHeaderEl.hide();
13255
13256         if(this.fieldLabel.length){
13257             this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13258             this.touchViewHeaderEl.show();
13259         }
13260
13261         this.touchViewEl.show();
13262
13263         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13264         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13265
13266         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13267
13268         if(this.fieldLabel.length){
13269             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13270         }
13271         
13272         this.touchViewBodyEl.setHeight(bodyHeight);
13273
13274         if(this.animate){
13275             var _this = this;
13276             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13277         }else{
13278             this.touchViewEl.addClass('in');
13279         }
13280
13281         this.doTouchViewQuery();
13282         
13283     },
13284     
13285     hideTouchView : function()
13286     {
13287         this.touchViewEl.removeClass('in');
13288
13289         if(this.animate){
13290             var _this = this;
13291             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13292         }else{
13293             this.touchViewEl.setStyle('display', 'none');
13294         }
13295         
13296     },
13297     
13298     setTouchViewValue : function()
13299     {
13300         if(this.multiple){
13301             this.clearItem();
13302         
13303             var _this = this;
13304
13305             Roo.each(this.tickItems, function(o){
13306                 this.addItem(o);
13307             }, this);
13308         }
13309         
13310         this.hideTouchView();
13311     },
13312     
13313     doTouchViewQuery : function()
13314     {
13315         var qe = {
13316             query: '',
13317             forceAll: true,
13318             combo: this,
13319             cancel:false
13320         };
13321         
13322         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13323             return false;
13324         }
13325         
13326         if(!this.alwaysQuery || this.mode == 'local'){
13327             this.onTouchViewLoad();
13328             return;
13329         }
13330         
13331         this.store.load();
13332     },
13333     
13334     onTouchViewBeforeLoad : function(combo,opts)
13335     {
13336         return;
13337     },
13338
13339     // private
13340     onTouchViewLoad : function()
13341     {
13342         if(this.store.getCount() < 1){
13343             this.onTouchViewEmptyResults();
13344             return;
13345         }
13346         
13347         this.clearTouchView();
13348         
13349         var rawValue = this.getRawValue();
13350         
13351         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13352         
13353         this.tickItems = [];
13354         
13355         this.store.data.each(function(d, rowIndex){
13356             var row = this.touchViewListGroup.createChild(template);
13357             
13358             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13359                 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = d.data[this.displayField];
13360             }
13361             
13362             if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
13363                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13364             }
13365             
13366             if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
13367                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13368                 this.tickItems.push(d.data);
13369             }
13370             
13371             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
13372             
13373         }, this);
13374         
13375         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
13376         
13377         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13378
13379         if(this.fieldLabel.length){
13380             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13381         }
13382
13383         var listHeight = this.touchViewListGroup.getHeight();
13384         
13385         var _this = this;
13386         
13387         if(firstChecked && listHeight > bodyHeight){
13388             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
13389         }
13390         
13391     },
13392     
13393     onTouchViewLoadException : function()
13394     {
13395         this.hideTouchView();
13396     },
13397     
13398     onTouchViewEmptyResults : function()
13399     {
13400         this.clearTouchView();
13401         
13402         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
13403         
13404         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
13405         
13406     },
13407     
13408     clearTouchView : function()
13409     {
13410         this.touchViewListGroup.dom.innerHTML = '';
13411     },
13412     
13413     onTouchViewClick : function(e, el, o)
13414     {
13415         e.preventDefault();
13416         
13417         var row = o.row;
13418         var rowIndex = o.rowIndex;
13419         
13420         var r = this.store.getAt(rowIndex);
13421         
13422         if(!this.multiple){
13423             Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
13424                 c.dom.removeAttribute('checked');
13425             }, this);
13426             
13427             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13428         
13429             this.setFromData(r.data);
13430             
13431             var close = this.closeTriggerEl();
13432         
13433             if(close){
13434                 close.show();
13435             }
13436
13437             this.hideTouchView();
13438             
13439             this.fireEvent('select', this, r, rowIndex);
13440             
13441             return;
13442         }
13443         
13444         if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
13445             row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
13446             this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
13447             return;
13448         }
13449         
13450         row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13451         this.addItem(r.data);
13452         this.tickItems.push(r.data);
13453         
13454     }
13455     
13456
13457     /** 
13458     * @cfg {Boolean} grow 
13459     * @hide 
13460     */
13461     /** 
13462     * @cfg {Number} growMin 
13463     * @hide 
13464     */
13465     /** 
13466     * @cfg {Number} growMax 
13467     * @hide 
13468     */
13469     /**
13470      * @hide
13471      * @method autoSize
13472      */
13473 });
13474
13475 Roo.apply(Roo.bootstrap.ComboBox,  {
13476     
13477     header : {
13478         tag: 'div',
13479         cls: 'modal-header',
13480         cn: [
13481             {
13482                 tag: 'h4',
13483                 cls: 'modal-title'
13484             }
13485         ]
13486     },
13487     
13488     body : {
13489         tag: 'div',
13490         cls: 'modal-body',
13491         cn: [
13492             {
13493                 tag: 'ul',
13494                 cls: 'list-group'
13495             }
13496         ]
13497     },
13498     
13499     listItemRadio : {
13500         tag: 'li',
13501         cls: 'list-group-item',
13502         cn: [
13503             {
13504                 tag: 'span',
13505                 cls: 'roo-combobox-list-group-item-value'
13506             },
13507             {
13508                 tag: 'div',
13509                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
13510                 cn: [
13511                     {
13512                         tag: 'input',
13513                         type: 'radio'
13514                     },
13515                     {
13516                         tag: 'label'
13517                     }
13518                 ]
13519             }
13520         ]
13521     },
13522     
13523     listItemCheckbox : {
13524         tag: 'li',
13525         cls: 'list-group-item',
13526         cn: [
13527             {
13528                 tag: 'span',
13529                 cls: 'roo-combobox-list-group-item-value'
13530             },
13531             {
13532                 tag: 'div',
13533                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
13534                 cn: [
13535                     {
13536                         tag: 'input',
13537                         type: 'checkbox'
13538                     },
13539                     {
13540                         tag: 'label'
13541                     }
13542                 ]
13543             }
13544         ]
13545     },
13546     
13547     emptyResult : {
13548         tag: 'div',
13549         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
13550     },
13551     
13552     footer : {
13553         tag: 'div',
13554         cls: 'modal-footer',
13555         cn: [
13556             {
13557                 tag: 'div',
13558                 cls: 'row',
13559                 cn: [
13560                     {
13561                         tag: 'div',
13562                         cls: 'col-xs-6 text-left',
13563                         cn: {
13564                             tag: 'button',
13565                             cls: 'btn btn-danger roo-touch-view-cancel',
13566                             html: 'Cancel'
13567                         }
13568                     },
13569                     {
13570                         tag: 'div',
13571                         cls: 'col-xs-6 text-right',
13572                         cn: {
13573                             tag: 'button',
13574                             cls: 'btn btn-success roo-touch-view-ok',
13575                             html: 'OK'
13576                         }
13577                     }
13578                 ]
13579             }
13580         ]
13581         
13582     }
13583 });
13584
13585 Roo.apply(Roo.bootstrap.ComboBox,  {
13586     
13587     touchViewTemplate : {
13588         tag: 'div',
13589         cls: 'modal fade roo-combobox-touch-view',
13590         cn: [
13591             {
13592                 tag: 'div',
13593                 cls: 'modal-dialog',
13594                 cn: [
13595                     {
13596                         tag: 'div',
13597                         cls: 'modal-content',
13598                         cn: [
13599                             Roo.bootstrap.ComboBox.header,
13600                             Roo.bootstrap.ComboBox.body,
13601                             Roo.bootstrap.ComboBox.footer
13602                         ]
13603                     }
13604                 ]
13605             }
13606         ]
13607     }
13608 });/*
13609  * Based on:
13610  * Ext JS Library 1.1.1
13611  * Copyright(c) 2006-2007, Ext JS, LLC.
13612  *
13613  * Originally Released Under LGPL - original licence link has changed is not relivant.
13614  *
13615  * Fork - LGPL
13616  * <script type="text/javascript">
13617  */
13618
13619 /**
13620  * @class Roo.View
13621  * @extends Roo.util.Observable
13622  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
13623  * This class also supports single and multi selection modes. <br>
13624  * Create a data model bound view:
13625  <pre><code>
13626  var store = new Roo.data.Store(...);
13627
13628  var view = new Roo.View({
13629     el : "my-element",
13630     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
13631  
13632     singleSelect: true,
13633     selectedClass: "ydataview-selected",
13634     store: store
13635  });
13636
13637  // listen for node click?
13638  view.on("click", function(vw, index, node, e){
13639  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
13640  });
13641
13642  // load XML data
13643  dataModel.load("foobar.xml");
13644  </code></pre>
13645  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
13646  * <br><br>
13647  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
13648  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
13649  * 
13650  * Note: old style constructor is still suported (container, template, config)
13651  * 
13652  * @constructor
13653  * Create a new View
13654  * @param {Object} config The config object
13655  * 
13656  */
13657 Roo.View = function(config, depreciated_tpl, depreciated_config){
13658     
13659     this.parent = false;
13660     
13661     if (typeof(depreciated_tpl) == 'undefined') {
13662         // new way.. - universal constructor.
13663         Roo.apply(this, config);
13664         this.el  = Roo.get(this.el);
13665     } else {
13666         // old format..
13667         this.el  = Roo.get(config);
13668         this.tpl = depreciated_tpl;
13669         Roo.apply(this, depreciated_config);
13670     }
13671     this.wrapEl  = this.el.wrap().wrap();
13672     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
13673     
13674     
13675     if(typeof(this.tpl) == "string"){
13676         this.tpl = new Roo.Template(this.tpl);
13677     } else {
13678         // support xtype ctors..
13679         this.tpl = new Roo.factory(this.tpl, Roo);
13680     }
13681     
13682     
13683     this.tpl.compile();
13684     
13685     /** @private */
13686     this.addEvents({
13687         /**
13688          * @event beforeclick
13689          * Fires before a click is processed. Returns false to cancel the default action.
13690          * @param {Roo.View} this
13691          * @param {Number} index The index of the target node
13692          * @param {HTMLElement} node The target node
13693          * @param {Roo.EventObject} e The raw event object
13694          */
13695             "beforeclick" : true,
13696         /**
13697          * @event click
13698          * Fires when a template node is clicked.
13699          * @param {Roo.View} this
13700          * @param {Number} index The index of the target node
13701          * @param {HTMLElement} node The target node
13702          * @param {Roo.EventObject} e The raw event object
13703          */
13704             "click" : true,
13705         /**
13706          * @event dblclick
13707          * Fires when a template node is double clicked.
13708          * @param {Roo.View} this
13709          * @param {Number} index The index of the target node
13710          * @param {HTMLElement} node The target node
13711          * @param {Roo.EventObject} e The raw event object
13712          */
13713             "dblclick" : true,
13714         /**
13715          * @event contextmenu
13716          * Fires when a template node is right clicked.
13717          * @param {Roo.View} this
13718          * @param {Number} index The index of the target node
13719          * @param {HTMLElement} node The target node
13720          * @param {Roo.EventObject} e The raw event object
13721          */
13722             "contextmenu" : true,
13723         /**
13724          * @event selectionchange
13725          * Fires when the selected nodes change.
13726          * @param {Roo.View} this
13727          * @param {Array} selections Array of the selected nodes
13728          */
13729             "selectionchange" : true,
13730     
13731         /**
13732          * @event beforeselect
13733          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
13734          * @param {Roo.View} this
13735          * @param {HTMLElement} node The node to be selected
13736          * @param {Array} selections Array of currently selected nodes
13737          */
13738             "beforeselect" : true,
13739         /**
13740          * @event preparedata
13741          * Fires on every row to render, to allow you to change the data.
13742          * @param {Roo.View} this
13743          * @param {Object} data to be rendered (change this)
13744          */
13745           "preparedata" : true
13746           
13747           
13748         });
13749
13750
13751
13752     this.el.on({
13753         "click": this.onClick,
13754         "dblclick": this.onDblClick,
13755         "contextmenu": this.onContextMenu,
13756         scope:this
13757     });
13758
13759     this.selections = [];
13760     this.nodes = [];
13761     this.cmp = new Roo.CompositeElementLite([]);
13762     if(this.store){
13763         this.store = Roo.factory(this.store, Roo.data);
13764         this.setStore(this.store, true);
13765     }
13766     
13767     if ( this.footer && this.footer.xtype) {
13768            
13769          var fctr = this.wrapEl.appendChild(document.createElement("div"));
13770         
13771         this.footer.dataSource = this.store
13772         this.footer.container = fctr;
13773         this.footer = Roo.factory(this.footer, Roo);
13774         fctr.insertFirst(this.el);
13775         
13776         // this is a bit insane - as the paging toolbar seems to detach the el..
13777 //        dom.parentNode.parentNode.parentNode
13778          // they get detached?
13779     }
13780     
13781     
13782     Roo.View.superclass.constructor.call(this);
13783     
13784     
13785 };
13786
13787 Roo.extend(Roo.View, Roo.util.Observable, {
13788     
13789      /**
13790      * @cfg {Roo.data.Store} store Data store to load data from.
13791      */
13792     store : false,
13793     
13794     /**
13795      * @cfg {String|Roo.Element} el The container element.
13796      */
13797     el : '',
13798     
13799     /**
13800      * @cfg {String|Roo.Template} tpl The template used by this View 
13801      */
13802     tpl : false,
13803     /**
13804      * @cfg {String} dataName the named area of the template to use as the data area
13805      *                          Works with domtemplates roo-name="name"
13806      */
13807     dataName: false,
13808     /**
13809      * @cfg {String} selectedClass The css class to add to selected nodes
13810      */
13811     selectedClass : "x-view-selected",
13812      /**
13813      * @cfg {String} emptyText The empty text to show when nothing is loaded.
13814      */
13815     emptyText : "",
13816     
13817     /**
13818      * @cfg {String} text to display on mask (default Loading)
13819      */
13820     mask : false,
13821     /**
13822      * @cfg {Boolean} multiSelect Allow multiple selection
13823      */
13824     multiSelect : false,
13825     /**
13826      * @cfg {Boolean} singleSelect Allow single selection
13827      */
13828     singleSelect:  false,
13829     
13830     /**
13831      * @cfg {Boolean} toggleSelect - selecting 
13832      */
13833     toggleSelect : false,
13834     
13835     /**
13836      * @cfg {Boolean} tickable - selecting 
13837      */
13838     tickable : false,
13839     
13840     /**
13841      * Returns the element this view is bound to.
13842      * @return {Roo.Element}
13843      */
13844     getEl : function(){
13845         return this.wrapEl;
13846     },
13847     
13848     
13849
13850     /**
13851      * Refreshes the view. - called by datachanged on the store. - do not call directly.
13852      */
13853     refresh : function(){
13854         //Roo.log('refresh');
13855         var t = this.tpl;
13856         
13857         // if we are using something like 'domtemplate', then
13858         // the what gets used is:
13859         // t.applySubtemplate(NAME, data, wrapping data..)
13860         // the outer template then get' applied with
13861         //     the store 'extra data'
13862         // and the body get's added to the
13863         //      roo-name="data" node?
13864         //      <span class='roo-tpl-{name}'></span> ?????
13865         
13866         
13867         
13868         this.clearSelections();
13869         this.el.update("");
13870         var html = [];
13871         var records = this.store.getRange();
13872         if(records.length < 1) {
13873             
13874             // is this valid??  = should it render a template??
13875             
13876             this.el.update(this.emptyText);
13877             return;
13878         }
13879         var el = this.el;
13880         if (this.dataName) {
13881             this.el.update(t.apply(this.store.meta)); //????
13882             el = this.el.child('.roo-tpl-' + this.dataName);
13883         }
13884         
13885         for(var i = 0, len = records.length; i < len; i++){
13886             var data = this.prepareData(records[i].data, i, records[i]);
13887             this.fireEvent("preparedata", this, data, i, records[i]);
13888             
13889             var d = Roo.apply({}, data);
13890             
13891             if(this.tickable){
13892                 Roo.apply(d, {'roo-id' : Roo.id()});
13893                 
13894                 var _this = this;
13895             
13896                 Roo.each(this.parent.item, function(item){
13897                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
13898                         return;
13899                     }
13900                     Roo.apply(d, {'roo-data-checked' : 'checked'});
13901                 });
13902             }
13903             
13904             html[html.length] = Roo.util.Format.trim(
13905                 this.dataName ?
13906                     t.applySubtemplate(this.dataName, d, this.store.meta) :
13907                     t.apply(d)
13908             );
13909         }
13910         
13911         
13912         
13913         el.update(html.join(""));
13914         this.nodes = el.dom.childNodes;
13915         this.updateIndexes(0);
13916     },
13917     
13918
13919     /**
13920      * Function to override to reformat the data that is sent to
13921      * the template for each node.
13922      * DEPRICATED - use the preparedata event handler.
13923      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
13924      * a JSON object for an UpdateManager bound view).
13925      */
13926     prepareData : function(data, index, record)
13927     {
13928         this.fireEvent("preparedata", this, data, index, record);
13929         return data;
13930     },
13931
13932     onUpdate : function(ds, record){
13933         // Roo.log('on update');   
13934         this.clearSelections();
13935         var index = this.store.indexOf(record);
13936         var n = this.nodes[index];
13937         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
13938         n.parentNode.removeChild(n);
13939         this.updateIndexes(index, index);
13940     },
13941
13942     
13943     
13944 // --------- FIXME     
13945     onAdd : function(ds, records, index)
13946     {
13947         //Roo.log(['on Add', ds, records, index] );        
13948         this.clearSelections();
13949         if(this.nodes.length == 0){
13950             this.refresh();
13951             return;
13952         }
13953         var n = this.nodes[index];
13954         for(var i = 0, len = records.length; i < len; i++){
13955             var d = this.prepareData(records[i].data, i, records[i]);
13956             if(n){
13957                 this.tpl.insertBefore(n, d);
13958             }else{
13959                 
13960                 this.tpl.append(this.el, d);
13961             }
13962         }
13963         this.updateIndexes(index);
13964     },
13965
13966     onRemove : function(ds, record, index){
13967        // Roo.log('onRemove');
13968         this.clearSelections();
13969         var el = this.dataName  ?
13970             this.el.child('.roo-tpl-' + this.dataName) :
13971             this.el; 
13972         
13973         el.dom.removeChild(this.nodes[index]);
13974         this.updateIndexes(index);
13975     },
13976
13977     /**
13978      * Refresh an individual node.
13979      * @param {Number} index
13980      */
13981     refreshNode : function(index){
13982         this.onUpdate(this.store, this.store.getAt(index));
13983     },
13984
13985     updateIndexes : function(startIndex, endIndex){
13986         var ns = this.nodes;
13987         startIndex = startIndex || 0;
13988         endIndex = endIndex || ns.length - 1;
13989         for(var i = startIndex; i <= endIndex; i++){
13990             ns[i].nodeIndex = i;
13991         }
13992     },
13993
13994     /**
13995      * Changes the data store this view uses and refresh the view.
13996      * @param {Store} store
13997      */
13998     setStore : function(store, initial){
13999         if(!initial && this.store){
14000             this.store.un("datachanged", this.refresh);
14001             this.store.un("add", this.onAdd);
14002             this.store.un("remove", this.onRemove);
14003             this.store.un("update", this.onUpdate);
14004             this.store.un("clear", this.refresh);
14005             this.store.un("beforeload", this.onBeforeLoad);
14006             this.store.un("load", this.onLoad);
14007             this.store.un("loadexception", this.onLoad);
14008         }
14009         if(store){
14010           
14011             store.on("datachanged", this.refresh, this);
14012             store.on("add", this.onAdd, this);
14013             store.on("remove", this.onRemove, this);
14014             store.on("update", this.onUpdate, this);
14015             store.on("clear", this.refresh, this);
14016             store.on("beforeload", this.onBeforeLoad, this);
14017             store.on("load", this.onLoad, this);
14018             store.on("loadexception", this.onLoad, this);
14019         }
14020         
14021         if(store){
14022             this.refresh();
14023         }
14024     },
14025     /**
14026      * onbeforeLoad - masks the loading area.
14027      *
14028      */
14029     onBeforeLoad : function(store,opts)
14030     {
14031          //Roo.log('onBeforeLoad');   
14032         if (!opts.add) {
14033             this.el.update("");
14034         }
14035         this.el.mask(this.mask ? this.mask : "Loading" ); 
14036     },
14037     onLoad : function ()
14038     {
14039         this.el.unmask();
14040     },
14041     
14042
14043     /**
14044      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14045      * @param {HTMLElement} node
14046      * @return {HTMLElement} The template node
14047      */
14048     findItemFromChild : function(node){
14049         var el = this.dataName  ?
14050             this.el.child('.roo-tpl-' + this.dataName,true) :
14051             this.el.dom; 
14052         
14053         if(!node || node.parentNode == el){
14054                     return node;
14055             }
14056             var p = node.parentNode;
14057             while(p && p != el){
14058             if(p.parentNode == el){
14059                 return p;
14060             }
14061             p = p.parentNode;
14062         }
14063             return null;
14064     },
14065
14066     /** @ignore */
14067     onClick : function(e){
14068         var item = this.findItemFromChild(e.getTarget());
14069         if(item){
14070             var index = this.indexOf(item);
14071             if(this.onItemClick(item, index, e) !== false){
14072                 this.fireEvent("click", this, index, item, e);
14073             }
14074         }else{
14075             this.clearSelections();
14076         }
14077     },
14078
14079     /** @ignore */
14080     onContextMenu : function(e){
14081         var item = this.findItemFromChild(e.getTarget());
14082         if(item){
14083             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14084         }
14085     },
14086
14087     /** @ignore */
14088     onDblClick : function(e){
14089         var item = this.findItemFromChild(e.getTarget());
14090         if(item){
14091             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14092         }
14093     },
14094
14095     onItemClick : function(item, index, e)
14096     {
14097         if(this.fireEvent("beforeclick", this, index, item, e) === false){
14098             return false;
14099         }
14100         if (this.toggleSelect) {
14101             var m = this.isSelected(item) ? 'unselect' : 'select';
14102             //Roo.log(m);
14103             var _t = this;
14104             _t[m](item, true, false);
14105             return true;
14106         }
14107         if(this.multiSelect || this.singleSelect){
14108             if(this.multiSelect && e.shiftKey && this.lastSelection){
14109                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14110             }else{
14111                 this.select(item, this.multiSelect && e.ctrlKey);
14112                 this.lastSelection = item;
14113             }
14114             
14115             if(!this.tickable){
14116                 e.preventDefault();
14117             }
14118             
14119         }
14120         return true;
14121     },
14122
14123     /**
14124      * Get the number of selected nodes.
14125      * @return {Number}
14126      */
14127     getSelectionCount : function(){
14128         return this.selections.length;
14129     },
14130
14131     /**
14132      * Get the currently selected nodes.
14133      * @return {Array} An array of HTMLElements
14134      */
14135     getSelectedNodes : function(){
14136         return this.selections;
14137     },
14138
14139     /**
14140      * Get the indexes of the selected nodes.
14141      * @return {Array}
14142      */
14143     getSelectedIndexes : function(){
14144         var indexes = [], s = this.selections;
14145         for(var i = 0, len = s.length; i < len; i++){
14146             indexes.push(s[i].nodeIndex);
14147         }
14148         return indexes;
14149     },
14150
14151     /**
14152      * Clear all selections
14153      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14154      */
14155     clearSelections : function(suppressEvent){
14156         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14157             this.cmp.elements = this.selections;
14158             this.cmp.removeClass(this.selectedClass);
14159             this.selections = [];
14160             if(!suppressEvent){
14161                 this.fireEvent("selectionchange", this, this.selections);
14162             }
14163         }
14164     },
14165
14166     /**
14167      * Returns true if the passed node is selected
14168      * @param {HTMLElement/Number} node The node or node index
14169      * @return {Boolean}
14170      */
14171     isSelected : function(node){
14172         var s = this.selections;
14173         if(s.length < 1){
14174             return false;
14175         }
14176         node = this.getNode(node);
14177         return s.indexOf(node) !== -1;
14178     },
14179
14180     /**
14181      * Selects nodes.
14182      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
14183      * @param {Boolean} keepExisting (optional) true to keep existing selections
14184      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14185      */
14186     select : function(nodeInfo, keepExisting, suppressEvent){
14187         if(nodeInfo instanceof Array){
14188             if(!keepExisting){
14189                 this.clearSelections(true);
14190             }
14191             for(var i = 0, len = nodeInfo.length; i < len; i++){
14192                 this.select(nodeInfo[i], true, true);
14193             }
14194             return;
14195         } 
14196         var node = this.getNode(nodeInfo);
14197         if(!node || this.isSelected(node)){
14198             return; // already selected.
14199         }
14200         if(!keepExisting){
14201             this.clearSelections(true);
14202         }
14203         
14204         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14205             Roo.fly(node).addClass(this.selectedClass);
14206             this.selections.push(node);
14207             if(!suppressEvent){
14208                 this.fireEvent("selectionchange", this, this.selections);
14209             }
14210         }
14211         
14212         
14213     },
14214       /**
14215      * Unselects nodes.
14216      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
14217      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14218      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14219      */
14220     unselect : function(nodeInfo, keepExisting, suppressEvent)
14221     {
14222         if(nodeInfo instanceof Array){
14223             Roo.each(this.selections, function(s) {
14224                 this.unselect(s, nodeInfo);
14225             }, this);
14226             return;
14227         }
14228         var node = this.getNode(nodeInfo);
14229         if(!node || !this.isSelected(node)){
14230             //Roo.log("not selected");
14231             return; // not selected.
14232         }
14233         // fireevent???
14234         var ns = [];
14235         Roo.each(this.selections, function(s) {
14236             if (s == node ) {
14237                 Roo.fly(node).removeClass(this.selectedClass);
14238
14239                 return;
14240             }
14241             ns.push(s);
14242         },this);
14243         
14244         this.selections= ns;
14245         this.fireEvent("selectionchange", this, this.selections);
14246     },
14247
14248     /**
14249      * Gets a template node.
14250      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14251      * @return {HTMLElement} The node or null if it wasn't found
14252      */
14253     getNode : function(nodeInfo){
14254         if(typeof nodeInfo == "string"){
14255             return document.getElementById(nodeInfo);
14256         }else if(typeof nodeInfo == "number"){
14257             return this.nodes[nodeInfo];
14258         }
14259         return nodeInfo;
14260     },
14261
14262     /**
14263      * Gets a range template nodes.
14264      * @param {Number} startIndex
14265      * @param {Number} endIndex
14266      * @return {Array} An array of nodes
14267      */
14268     getNodes : function(start, end){
14269         var ns = this.nodes;
14270         start = start || 0;
14271         end = typeof end == "undefined" ? ns.length - 1 : end;
14272         var nodes = [];
14273         if(start <= end){
14274             for(var i = start; i <= end; i++){
14275                 nodes.push(ns[i]);
14276             }
14277         } else{
14278             for(var i = start; i >= end; i--){
14279                 nodes.push(ns[i]);
14280             }
14281         }
14282         return nodes;
14283     },
14284
14285     /**
14286      * Finds the index of the passed node
14287      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14288      * @return {Number} The index of the node or -1
14289      */
14290     indexOf : function(node){
14291         node = this.getNode(node);
14292         if(typeof node.nodeIndex == "number"){
14293             return node.nodeIndex;
14294         }
14295         var ns = this.nodes;
14296         for(var i = 0, len = ns.length; i < len; i++){
14297             if(ns[i] == node){
14298                 return i;
14299             }
14300         }
14301         return -1;
14302     }
14303 });
14304 /*
14305  * - LGPL
14306  *
14307  * based on jquery fullcalendar
14308  * 
14309  */
14310
14311 Roo.bootstrap = Roo.bootstrap || {};
14312 /**
14313  * @class Roo.bootstrap.Calendar
14314  * @extends Roo.bootstrap.Component
14315  * Bootstrap Calendar class
14316  * @cfg {Boolean} loadMask (true|false) default false
14317  * @cfg {Object} header generate the user specific header of the calendar, default false
14318
14319  * @constructor
14320  * Create a new Container
14321  * @param {Object} config The config object
14322  */
14323
14324
14325
14326 Roo.bootstrap.Calendar = function(config){
14327     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14328      this.addEvents({
14329         /**
14330              * @event select
14331              * Fires when a date is selected
14332              * @param {DatePicker} this
14333              * @param {Date} date The selected date
14334              */
14335         'select': true,
14336         /**
14337              * @event monthchange
14338              * Fires when the displayed month changes 
14339              * @param {DatePicker} this
14340              * @param {Date} date The selected month
14341              */
14342         'monthchange': true,
14343         /**
14344              * @event evententer
14345              * Fires when mouse over an event
14346              * @param {Calendar} this
14347              * @param {event} Event
14348              */
14349         'evententer': true,
14350         /**
14351              * @event eventleave
14352              * Fires when the mouse leaves an
14353              * @param {Calendar} this
14354              * @param {event}
14355              */
14356         'eventleave': true,
14357         /**
14358              * @event eventclick
14359              * Fires when the mouse click an
14360              * @param {Calendar} this
14361              * @param {event}
14362              */
14363         'eventclick': true
14364         
14365     });
14366
14367 };
14368
14369 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
14370     
14371      /**
14372      * @cfg {Number} startDay
14373      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
14374      */
14375     startDay : 0,
14376     
14377     loadMask : false,
14378     
14379     header : false,
14380       
14381     getAutoCreate : function(){
14382         
14383         
14384         var fc_button = function(name, corner, style, content ) {
14385             return Roo.apply({},{
14386                 tag : 'span',
14387                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
14388                          (corner.length ?
14389                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
14390                             ''
14391                         ),
14392                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
14393                 unselectable: 'on'
14394             });
14395         };
14396         
14397         var header = {};
14398         
14399         if(!this.header){
14400             header = {
14401                 tag : 'table',
14402                 cls : 'fc-header',
14403                 style : 'width:100%',
14404                 cn : [
14405                     {
14406                         tag: 'tr',
14407                         cn : [
14408                             {
14409                                 tag : 'td',
14410                                 cls : 'fc-header-left',
14411                                 cn : [
14412                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
14413                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
14414                                     { tag: 'span', cls: 'fc-header-space' },
14415                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
14416
14417
14418                                 ]
14419                             },
14420
14421                             {
14422                                 tag : 'td',
14423                                 cls : 'fc-header-center',
14424                                 cn : [
14425                                     {
14426                                         tag: 'span',
14427                                         cls: 'fc-header-title',
14428                                         cn : {
14429                                             tag: 'H2',
14430                                             html : 'month / year'
14431                                         }
14432                                     }
14433
14434                                 ]
14435                             },
14436                             {
14437                                 tag : 'td',
14438                                 cls : 'fc-header-right',
14439                                 cn : [
14440                               /*      fc_button('month', 'left', '', 'month' ),
14441                                     fc_button('week', '', '', 'week' ),
14442                                     fc_button('day', 'right', '', 'day' )
14443                                 */    
14444
14445                                 ]
14446                             }
14447
14448                         ]
14449                     }
14450                 ]
14451             };
14452         }
14453         
14454         header = this.header;
14455         
14456        
14457         var cal_heads = function() {
14458             var ret = [];
14459             // fixme - handle this.
14460             
14461             for (var i =0; i < Date.dayNames.length; i++) {
14462                 var d = Date.dayNames[i];
14463                 ret.push({
14464                     tag: 'th',
14465                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
14466                     html : d.substring(0,3)
14467                 });
14468                 
14469             }
14470             ret[0].cls += ' fc-first';
14471             ret[6].cls += ' fc-last';
14472             return ret;
14473         };
14474         var cal_cell = function(n) {
14475             return  {
14476                 tag: 'td',
14477                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
14478                 cn : [
14479                     {
14480                         cn : [
14481                             {
14482                                 cls: 'fc-day-number',
14483                                 html: 'D'
14484                             },
14485                             {
14486                                 cls: 'fc-day-content',
14487                              
14488                                 cn : [
14489                                      {
14490                                         style: 'position: relative;' // height: 17px;
14491                                     }
14492                                 ]
14493                             }
14494                             
14495                             
14496                         ]
14497                     }
14498                 ]
14499                 
14500             }
14501         };
14502         var cal_rows = function() {
14503             
14504             var ret = [];
14505             for (var r = 0; r < 6; r++) {
14506                 var row= {
14507                     tag : 'tr',
14508                     cls : 'fc-week',
14509                     cn : []
14510                 };
14511                 
14512                 for (var i =0; i < Date.dayNames.length; i++) {
14513                     var d = Date.dayNames[i];
14514                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
14515
14516                 }
14517                 row.cn[0].cls+=' fc-first';
14518                 row.cn[0].cn[0].style = 'min-height:90px';
14519                 row.cn[6].cls+=' fc-last';
14520                 ret.push(row);
14521                 
14522             }
14523             ret[0].cls += ' fc-first';
14524             ret[4].cls += ' fc-prev-last';
14525             ret[5].cls += ' fc-last';
14526             return ret;
14527             
14528         };
14529         
14530         var cal_table = {
14531             tag: 'table',
14532             cls: 'fc-border-separate',
14533             style : 'width:100%',
14534             cellspacing  : 0,
14535             cn : [
14536                 { 
14537                     tag: 'thead',
14538                     cn : [
14539                         { 
14540                             tag: 'tr',
14541                             cls : 'fc-first fc-last',
14542                             cn : cal_heads()
14543                         }
14544                     ]
14545                 },
14546                 { 
14547                     tag: 'tbody',
14548                     cn : cal_rows()
14549                 }
14550                   
14551             ]
14552         };
14553          
14554          var cfg = {
14555             cls : 'fc fc-ltr',
14556             cn : [
14557                 header,
14558                 {
14559                     cls : 'fc-content',
14560                     style : "position: relative;",
14561                     cn : [
14562                         {
14563                             cls : 'fc-view fc-view-month fc-grid',
14564                             style : 'position: relative',
14565                             unselectable : 'on',
14566                             cn : [
14567                                 {
14568                                     cls : 'fc-event-container',
14569                                     style : 'position:absolute;z-index:8;top:0;left:0;'
14570                                 },
14571                                 cal_table
14572                             ]
14573                         }
14574                     ]
14575     
14576                 }
14577            ] 
14578             
14579         };
14580         
14581          
14582         
14583         return cfg;
14584     },
14585     
14586     
14587     initEvents : function()
14588     {
14589         if(!this.store){
14590             throw "can not find store for calendar";
14591         }
14592         
14593         var mark = {
14594             tag: "div",
14595             cls:"x-dlg-mask",
14596             style: "text-align:center",
14597             cn: [
14598                 {
14599                     tag: "div",
14600                     style: "background-color:white;width:50%;margin:250 auto",
14601                     cn: [
14602                         {
14603                             tag: "img",
14604                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
14605                         },
14606                         {
14607                             tag: "span",
14608                             html: "Loading"
14609                         }
14610                         
14611                     ]
14612                 }
14613             ]
14614         }
14615         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
14616         
14617         var size = this.el.select('.fc-content', true).first().getSize();
14618         this.maskEl.setSize(size.width, size.height);
14619         this.maskEl.enableDisplayMode("block");
14620         if(!this.loadMask){
14621             this.maskEl.hide();
14622         }
14623         
14624         this.store = Roo.factory(this.store, Roo.data);
14625         this.store.on('load', this.onLoad, this);
14626         this.store.on('beforeload', this.onBeforeLoad, this);
14627         
14628         this.resize();
14629         
14630         this.cells = this.el.select('.fc-day',true);
14631         //Roo.log(this.cells);
14632         this.textNodes = this.el.query('.fc-day-number');
14633         this.cells.addClassOnOver('fc-state-hover');
14634         
14635         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
14636         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
14637         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
14638         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
14639         
14640         this.on('monthchange', this.onMonthChange, this);
14641         
14642         this.update(new Date().clearTime());
14643     },
14644     
14645     resize : function() {
14646         var sz  = this.el.getSize();
14647         
14648         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
14649         this.el.select('.fc-day-content div',true).setHeight(34);
14650     },
14651     
14652     
14653     // private
14654     showPrevMonth : function(e){
14655         this.update(this.activeDate.add("mo", -1));
14656     },
14657     showToday : function(e){
14658         this.update(new Date().clearTime());
14659     },
14660     // private
14661     showNextMonth : function(e){
14662         this.update(this.activeDate.add("mo", 1));
14663     },
14664
14665     // private
14666     showPrevYear : function(){
14667         this.update(this.activeDate.add("y", -1));
14668     },
14669
14670     // private
14671     showNextYear : function(){
14672         this.update(this.activeDate.add("y", 1));
14673     },
14674
14675     
14676    // private
14677     update : function(date)
14678     {
14679         var vd = this.activeDate;
14680         this.activeDate = date;
14681 //        if(vd && this.el){
14682 //            var t = date.getTime();
14683 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
14684 //                Roo.log('using add remove');
14685 //                
14686 //                this.fireEvent('monthchange', this, date);
14687 //                
14688 //                this.cells.removeClass("fc-state-highlight");
14689 //                this.cells.each(function(c){
14690 //                   if(c.dateValue == t){
14691 //                       c.addClass("fc-state-highlight");
14692 //                       setTimeout(function(){
14693 //                            try{c.dom.firstChild.focus();}catch(e){}
14694 //                       }, 50);
14695 //                       return false;
14696 //                   }
14697 //                   return true;
14698 //                });
14699 //                return;
14700 //            }
14701 //        }
14702         
14703         var days = date.getDaysInMonth();
14704         
14705         var firstOfMonth = date.getFirstDateOfMonth();
14706         var startingPos = firstOfMonth.getDay()-this.startDay;
14707         
14708         if(startingPos < this.startDay){
14709             startingPos += 7;
14710         }
14711         
14712         var pm = date.add(Date.MONTH, -1);
14713         var prevStart = pm.getDaysInMonth()-startingPos;
14714 //        
14715         this.cells = this.el.select('.fc-day',true);
14716         this.textNodes = this.el.query('.fc-day-number');
14717         this.cells.addClassOnOver('fc-state-hover');
14718         
14719         var cells = this.cells.elements;
14720         var textEls = this.textNodes;
14721         
14722         Roo.each(cells, function(cell){
14723             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
14724         });
14725         
14726         days += startingPos;
14727
14728         // convert everything to numbers so it's fast
14729         var day = 86400000;
14730         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
14731         //Roo.log(d);
14732         //Roo.log(pm);
14733         //Roo.log(prevStart);
14734         
14735         var today = new Date().clearTime().getTime();
14736         var sel = date.clearTime().getTime();
14737         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
14738         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
14739         var ddMatch = this.disabledDatesRE;
14740         var ddText = this.disabledDatesText;
14741         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
14742         var ddaysText = this.disabledDaysText;
14743         var format = this.format;
14744         
14745         var setCellClass = function(cal, cell){
14746             cell.row = 0;
14747             cell.events = [];
14748             cell.more = [];
14749             //Roo.log('set Cell Class');
14750             cell.title = "";
14751             var t = d.getTime();
14752             
14753             //Roo.log(d);
14754             
14755             cell.dateValue = t;
14756             if(t == today){
14757                 cell.className += " fc-today";
14758                 cell.className += " fc-state-highlight";
14759                 cell.title = cal.todayText;
14760             }
14761             if(t == sel){
14762                 // disable highlight in other month..
14763                 //cell.className += " fc-state-highlight";
14764                 
14765             }
14766             // disabling
14767             if(t < min) {
14768                 cell.className = " fc-state-disabled";
14769                 cell.title = cal.minText;
14770                 return;
14771             }
14772             if(t > max) {
14773                 cell.className = " fc-state-disabled";
14774                 cell.title = cal.maxText;
14775                 return;
14776             }
14777             if(ddays){
14778                 if(ddays.indexOf(d.getDay()) != -1){
14779                     cell.title = ddaysText;
14780                     cell.className = " fc-state-disabled";
14781                 }
14782             }
14783             if(ddMatch && format){
14784                 var fvalue = d.dateFormat(format);
14785                 if(ddMatch.test(fvalue)){
14786                     cell.title = ddText.replace("%0", fvalue);
14787                     cell.className = " fc-state-disabled";
14788                 }
14789             }
14790             
14791             if (!cell.initialClassName) {
14792                 cell.initialClassName = cell.dom.className;
14793             }
14794             
14795             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
14796         };
14797
14798         var i = 0;
14799         
14800         for(; i < startingPos; i++) {
14801             textEls[i].innerHTML = (++prevStart);
14802             d.setDate(d.getDate()+1);
14803             
14804             cells[i].className = "fc-past fc-other-month";
14805             setCellClass(this, cells[i]);
14806         }
14807         
14808         var intDay = 0;
14809         
14810         for(; i < days; i++){
14811             intDay = i - startingPos + 1;
14812             textEls[i].innerHTML = (intDay);
14813             d.setDate(d.getDate()+1);
14814             
14815             cells[i].className = ''; // "x-date-active";
14816             setCellClass(this, cells[i]);
14817         }
14818         var extraDays = 0;
14819         
14820         for(; i < 42; i++) {
14821             textEls[i].innerHTML = (++extraDays);
14822             d.setDate(d.getDate()+1);
14823             
14824             cells[i].className = "fc-future fc-other-month";
14825             setCellClass(this, cells[i]);
14826         }
14827         
14828         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
14829         
14830         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
14831         
14832         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
14833         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
14834         
14835         if(totalRows != 6){
14836             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
14837             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
14838         }
14839         
14840         this.fireEvent('monthchange', this, date);
14841         
14842         
14843         /*
14844         if(!this.internalRender){
14845             var main = this.el.dom.firstChild;
14846             var w = main.offsetWidth;
14847             this.el.setWidth(w + this.el.getBorderWidth("lr"));
14848             Roo.fly(main).setWidth(w);
14849             this.internalRender = true;
14850             // opera does not respect the auto grow header center column
14851             // then, after it gets a width opera refuses to recalculate
14852             // without a second pass
14853             if(Roo.isOpera && !this.secondPass){
14854                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
14855                 this.secondPass = true;
14856                 this.update.defer(10, this, [date]);
14857             }
14858         }
14859         */
14860         
14861     },
14862     
14863     findCell : function(dt) {
14864         dt = dt.clearTime().getTime();
14865         var ret = false;
14866         this.cells.each(function(c){
14867             //Roo.log("check " +c.dateValue + '?=' + dt);
14868             if(c.dateValue == dt){
14869                 ret = c;
14870                 return false;
14871             }
14872             return true;
14873         });
14874         
14875         return ret;
14876     },
14877     
14878     findCells : function(ev) {
14879         var s = ev.start.clone().clearTime().getTime();
14880        // Roo.log(s);
14881         var e= ev.end.clone().clearTime().getTime();
14882        // Roo.log(e);
14883         var ret = [];
14884         this.cells.each(function(c){
14885              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
14886             
14887             if(c.dateValue > e){
14888                 return ;
14889             }
14890             if(c.dateValue < s){
14891                 return ;
14892             }
14893             ret.push(c);
14894         });
14895         
14896         return ret;    
14897     },
14898     
14899 //    findBestRow: function(cells)
14900 //    {
14901 //        var ret = 0;
14902 //        
14903 //        for (var i =0 ; i < cells.length;i++) {
14904 //            ret  = Math.max(cells[i].rows || 0,ret);
14905 //        }
14906 //        return ret;
14907 //        
14908 //    },
14909     
14910     
14911     addItem : function(ev)
14912     {
14913         // look for vertical location slot in
14914         var cells = this.findCells(ev);
14915         
14916 //        ev.row = this.findBestRow(cells);
14917         
14918         // work out the location.
14919         
14920         var crow = false;
14921         var rows = [];
14922         for(var i =0; i < cells.length; i++) {
14923             
14924             cells[i].row = cells[0].row;
14925             
14926             if(i == 0){
14927                 cells[i].row = cells[i].row + 1;
14928             }
14929             
14930             if (!crow) {
14931                 crow = {
14932                     start : cells[i],
14933                     end :  cells[i]
14934                 };
14935                 continue;
14936             }
14937             if (crow.start.getY() == cells[i].getY()) {
14938                 // on same row.
14939                 crow.end = cells[i];
14940                 continue;
14941             }
14942             // different row.
14943             rows.push(crow);
14944             crow = {
14945                 start: cells[i],
14946                 end : cells[i]
14947             };
14948             
14949         }
14950         
14951         rows.push(crow);
14952         ev.els = [];
14953         ev.rows = rows;
14954         ev.cells = cells;
14955         
14956         cells[0].events.push(ev);
14957         
14958         this.calevents.push(ev);
14959     },
14960     
14961     clearEvents: function() {
14962         
14963         if(!this.calevents){
14964             return;
14965         }
14966         
14967         Roo.each(this.cells.elements, function(c){
14968             c.row = 0;
14969             c.events = [];
14970             c.more = [];
14971         });
14972         
14973         Roo.each(this.calevents, function(e) {
14974             Roo.each(e.els, function(el) {
14975                 el.un('mouseenter' ,this.onEventEnter, this);
14976                 el.un('mouseleave' ,this.onEventLeave, this);
14977                 el.remove();
14978             },this);
14979         },this);
14980         
14981         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
14982             e.remove();
14983         });
14984         
14985     },
14986     
14987     renderEvents: function()
14988     {   
14989         var _this = this;
14990         
14991         this.cells.each(function(c) {
14992             
14993             if(c.row < 5){
14994                 return;
14995             }
14996             
14997             var ev = c.events;
14998             
14999             var r = 4;
15000             if(c.row != c.events.length){
15001                 r = 4 - (4 - (c.row - c.events.length));
15002             }
15003             
15004             c.events = ev.slice(0, r);
15005             c.more = ev.slice(r);
15006             
15007             if(c.more.length && c.more.length == 1){
15008                 c.events.push(c.more.pop());
15009             }
15010             
15011             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15012             
15013         });
15014             
15015         this.cells.each(function(c) {
15016             
15017             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15018             
15019             
15020             for (var e = 0; e < c.events.length; e++){
15021                 var ev = c.events[e];
15022                 var rows = ev.rows;
15023                 
15024                 for(var i = 0; i < rows.length; i++) {
15025                 
15026                     // how many rows should it span..
15027
15028                     var  cfg = {
15029                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15030                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15031
15032                         unselectable : "on",
15033                         cn : [
15034                             {
15035                                 cls: 'fc-event-inner',
15036                                 cn : [
15037     //                                {
15038     //                                  tag:'span',
15039     //                                  cls: 'fc-event-time',
15040     //                                  html : cells.length > 1 ? '' : ev.time
15041     //                                },
15042                                     {
15043                                       tag:'span',
15044                                       cls: 'fc-event-title',
15045                                       html : String.format('{0}', ev.title)
15046                                     }
15047
15048
15049                                 ]
15050                             },
15051                             {
15052                                 cls: 'ui-resizable-handle ui-resizable-e',
15053                                 html : '&nbsp;&nbsp;&nbsp'
15054                             }
15055
15056                         ]
15057                     };
15058
15059                     if (i == 0) {
15060                         cfg.cls += ' fc-event-start';
15061                     }
15062                     if ((i+1) == rows.length) {
15063                         cfg.cls += ' fc-event-end';
15064                     }
15065
15066                     var ctr = _this.el.select('.fc-event-container',true).first();
15067                     var cg = ctr.createChild(cfg);
15068
15069                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15070                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15071
15072                     var r = (c.more.length) ? 1 : 0;
15073                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
15074                     cg.setWidth(ebox.right - sbox.x -2);
15075
15076                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15077                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15078                     cg.on('click', _this.onEventClick, _this, ev);
15079
15080                     ev.els.push(cg);
15081                     
15082                 }
15083                 
15084             }
15085             
15086             
15087             if(c.more.length){
15088                 var  cfg = {
15089                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15090                     style : 'position: absolute',
15091                     unselectable : "on",
15092                     cn : [
15093                         {
15094                             cls: 'fc-event-inner',
15095                             cn : [
15096                                 {
15097                                   tag:'span',
15098                                   cls: 'fc-event-title',
15099                                   html : 'More'
15100                                 }
15101
15102
15103                             ]
15104                         },
15105                         {
15106                             cls: 'ui-resizable-handle ui-resizable-e',
15107                             html : '&nbsp;&nbsp;&nbsp'
15108                         }
15109
15110                     ]
15111                 };
15112
15113                 var ctr = _this.el.select('.fc-event-container',true).first();
15114                 var cg = ctr.createChild(cfg);
15115
15116                 var sbox = c.select('.fc-day-content',true).first().getBox();
15117                 var ebox = c.select('.fc-day-content',true).first().getBox();
15118                 //Roo.log(cg);
15119                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
15120                 cg.setWidth(ebox.right - sbox.x -2);
15121
15122                 cg.on('click', _this.onMoreEventClick, _this, c.more);
15123                 
15124             }
15125             
15126         });
15127         
15128         
15129         
15130     },
15131     
15132     onEventEnter: function (e, el,event,d) {
15133         this.fireEvent('evententer', this, el, event);
15134     },
15135     
15136     onEventLeave: function (e, el,event,d) {
15137         this.fireEvent('eventleave', this, el, event);
15138     },
15139     
15140     onEventClick: function (e, el,event,d) {
15141         this.fireEvent('eventclick', this, el, event);
15142     },
15143     
15144     onMonthChange: function () {
15145         this.store.load();
15146     },
15147     
15148     onMoreEventClick: function(e, el, more)
15149     {
15150         var _this = this;
15151         
15152         this.calpopover.placement = 'right';
15153         this.calpopover.setTitle('More');
15154         
15155         this.calpopover.setContent('');
15156         
15157         var ctr = this.calpopover.el.select('.popover-content', true).first();
15158         
15159         Roo.each(more, function(m){
15160             var cfg = {
15161                 cls : 'fc-event-hori fc-event-draggable',
15162                 html : m.title
15163             }
15164             var cg = ctr.createChild(cfg);
15165             
15166             cg.on('click', _this.onEventClick, _this, m);
15167         });
15168         
15169         this.calpopover.show(el);
15170         
15171         
15172     },
15173     
15174     onLoad: function () 
15175     {   
15176         this.calevents = [];
15177         var cal = this;
15178         
15179         if(this.store.getCount() > 0){
15180             this.store.data.each(function(d){
15181                cal.addItem({
15182                     id : d.data.id,
15183                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15184                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15185                     time : d.data.start_time,
15186                     title : d.data.title,
15187                     description : d.data.description,
15188                     venue : d.data.venue
15189                 });
15190             });
15191         }
15192         
15193         this.renderEvents();
15194         
15195         if(this.calevents.length && this.loadMask){
15196             this.maskEl.hide();
15197         }
15198     },
15199     
15200     onBeforeLoad: function()
15201     {
15202         this.clearEvents();
15203         if(this.loadMask){
15204             this.maskEl.show();
15205         }
15206     }
15207 });
15208
15209  
15210  /*
15211  * - LGPL
15212  *
15213  * element
15214  * 
15215  */
15216
15217 /**
15218  * @class Roo.bootstrap.Popover
15219  * @extends Roo.bootstrap.Component
15220  * Bootstrap Popover class
15221  * @cfg {String} html contents of the popover   (or false to use children..)
15222  * @cfg {String} title of popover (or false to hide)
15223  * @cfg {String} placement how it is placed
15224  * @cfg {String} trigger click || hover (or false to trigger manually)
15225  * @cfg {String} over what (parent or false to trigger manually.)
15226  * @cfg {Number} delay - delay before showing
15227  
15228  * @constructor
15229  * Create a new Popover
15230  * @param {Object} config The config object
15231  */
15232
15233 Roo.bootstrap.Popover = function(config){
15234     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15235 };
15236
15237 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
15238     
15239     title: 'Fill in a title',
15240     html: false,
15241     
15242     placement : 'right',
15243     trigger : 'hover', // hover
15244     
15245     delay : 0,
15246     
15247     over: 'parent',
15248     
15249     can_build_overlaid : false,
15250     
15251     getChildContainer : function()
15252     {
15253         return this.el.select('.popover-content',true).first();
15254     },
15255     
15256     getAutoCreate : function(){
15257          Roo.log('make popover?');
15258         var cfg = {
15259            cls : 'popover roo-dynamic',
15260            style: 'display:block',
15261            cn : [
15262                 {
15263                     cls : 'arrow'
15264                 },
15265                 {
15266                     cls : 'popover-inner',
15267                     cn : [
15268                         {
15269                             tag: 'h3',
15270                             cls: 'popover-title',
15271                             html : this.title
15272                         },
15273                         {
15274                             cls : 'popover-content',
15275                             html : this.html
15276                         }
15277                     ]
15278                     
15279                 }
15280            ]
15281         };
15282         
15283         return cfg;
15284     },
15285     setTitle: function(str)
15286     {
15287         this.title = str;
15288         this.el.select('.popover-title',true).first().dom.innerHTML = str;
15289     },
15290     setContent: function(str)
15291     {
15292         this.html = str;
15293         this.el.select('.popover-content',true).first().dom.innerHTML = str;
15294     },
15295     // as it get's added to the bottom of the page.
15296     onRender : function(ct, position)
15297     {
15298         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15299         if(!this.el){
15300             var cfg = Roo.apply({},  this.getAutoCreate());
15301             cfg.id = Roo.id();
15302             
15303             if (this.cls) {
15304                 cfg.cls += ' ' + this.cls;
15305             }
15306             if (this.style) {
15307                 cfg.style = this.style;
15308             }
15309             Roo.log("adding to ")
15310             this.el = Roo.get(document.body).createChild(cfg, position);
15311             Roo.log(this.el);
15312         }
15313         this.initEvents();
15314     },
15315     
15316     initEvents : function()
15317     {
15318         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15319         this.el.enableDisplayMode('block');
15320         this.el.hide();
15321         if (this.over === false) {
15322             return; 
15323         }
15324         if (this.triggers === false) {
15325             return;
15326         }
15327         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15328         var triggers = this.trigger ? this.trigger.split(' ') : [];
15329         Roo.each(triggers, function(trigger) {
15330         
15331             if (trigger == 'click') {
15332                 on_el.on('click', this.toggle, this);
15333             } else if (trigger != 'manual') {
15334                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
15335                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
15336       
15337                 on_el.on(eventIn  ,this.enter, this);
15338                 on_el.on(eventOut, this.leave, this);
15339             }
15340         }, this);
15341         
15342     },
15343     
15344     
15345     // private
15346     timeout : null,
15347     hoverState : null,
15348     
15349     toggle : function () {
15350         this.hoverState == 'in' ? this.leave() : this.enter();
15351     },
15352     
15353     enter : function () {
15354        
15355     
15356         clearTimeout(this.timeout);
15357     
15358         this.hoverState = 'in';
15359     
15360         if (!this.delay || !this.delay.show) {
15361             this.show();
15362             return;
15363         }
15364         var _t = this;
15365         this.timeout = setTimeout(function () {
15366             if (_t.hoverState == 'in') {
15367                 _t.show();
15368             }
15369         }, this.delay.show)
15370     },
15371     leave : function() {
15372         clearTimeout(this.timeout);
15373     
15374         this.hoverState = 'out';
15375     
15376         if (!this.delay || !this.delay.hide) {
15377             this.hide();
15378             return;
15379         }
15380         var _t = this;
15381         this.timeout = setTimeout(function () {
15382             if (_t.hoverState == 'out') {
15383                 _t.hide();
15384             }
15385         }, this.delay.hide)
15386     },
15387     
15388     show : function (on_el)
15389     {
15390         if (!on_el) {
15391             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15392         }
15393         // set content.
15394         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
15395         if (this.html !== false) {
15396             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
15397         }
15398         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
15399         if (!this.title.length) {
15400             this.el.select('.popover-title',true).hide();
15401         }
15402         
15403         var placement = typeof this.placement == 'function' ?
15404             this.placement.call(this, this.el, on_el) :
15405             this.placement;
15406             
15407         var autoToken = /\s?auto?\s?/i;
15408         var autoPlace = autoToken.test(placement);
15409         if (autoPlace) {
15410             placement = placement.replace(autoToken, '') || 'top';
15411         }
15412         
15413         //this.el.detach()
15414         //this.el.setXY([0,0]);
15415         this.el.show();
15416         this.el.dom.style.display='block';
15417         this.el.addClass(placement);
15418         
15419         //this.el.appendTo(on_el);
15420         
15421         var p = this.getPosition();
15422         var box = this.el.getBox();
15423         
15424         if (autoPlace) {
15425             // fixme..
15426         }
15427         var align = Roo.bootstrap.Popover.alignment[placement];
15428         this.el.alignTo(on_el, align[0],align[1]);
15429         //var arrow = this.el.select('.arrow',true).first();
15430         //arrow.set(align[2], 
15431         
15432         this.el.addClass('in');
15433         
15434         
15435         if (this.el.hasClass('fade')) {
15436             // fade it?
15437         }
15438         
15439     },
15440     hide : function()
15441     {
15442         this.el.setXY([0,0]);
15443         this.el.removeClass('in');
15444         this.el.hide();
15445         this.hoverState = null;
15446         
15447     }
15448     
15449 });
15450
15451 Roo.bootstrap.Popover.alignment = {
15452     'left' : ['r-l', [-10,0], 'right'],
15453     'right' : ['l-r', [10,0], 'left'],
15454     'bottom' : ['t-b', [0,10], 'top'],
15455     'top' : [ 'b-t', [0,-10], 'bottom']
15456 };
15457
15458  /*
15459  * - LGPL
15460  *
15461  * Progress
15462  * 
15463  */
15464
15465 /**
15466  * @class Roo.bootstrap.Progress
15467  * @extends Roo.bootstrap.Component
15468  * Bootstrap Progress class
15469  * @cfg {Boolean} striped striped of the progress bar
15470  * @cfg {Boolean} active animated of the progress bar
15471  * 
15472  * 
15473  * @constructor
15474  * Create a new Progress
15475  * @param {Object} config The config object
15476  */
15477
15478 Roo.bootstrap.Progress = function(config){
15479     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
15480 };
15481
15482 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
15483     
15484     striped : false,
15485     active: false,
15486     
15487     getAutoCreate : function(){
15488         var cfg = {
15489             tag: 'div',
15490             cls: 'progress'
15491         };
15492         
15493         
15494         if(this.striped){
15495             cfg.cls += ' progress-striped';
15496         }
15497       
15498         if(this.active){
15499             cfg.cls += ' active';
15500         }
15501         
15502         
15503         return cfg;
15504     }
15505    
15506 });
15507
15508  
15509
15510  /*
15511  * - LGPL
15512  *
15513  * ProgressBar
15514  * 
15515  */
15516
15517 /**
15518  * @class Roo.bootstrap.ProgressBar
15519  * @extends Roo.bootstrap.Component
15520  * Bootstrap ProgressBar class
15521  * @cfg {Number} aria_valuenow aria-value now
15522  * @cfg {Number} aria_valuemin aria-value min
15523  * @cfg {Number} aria_valuemax aria-value max
15524  * @cfg {String} label label for the progress bar
15525  * @cfg {String} panel (success | info | warning | danger )
15526  * @cfg {String} role role of the progress bar
15527  * @cfg {String} sr_only text
15528  * 
15529  * 
15530  * @constructor
15531  * Create a new ProgressBar
15532  * @param {Object} config The config object
15533  */
15534
15535 Roo.bootstrap.ProgressBar = function(config){
15536     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
15537 };
15538
15539 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
15540     
15541     aria_valuenow : 0,
15542     aria_valuemin : 0,
15543     aria_valuemax : 100,
15544     label : false,
15545     panel : false,
15546     role : false,
15547     sr_only: false,
15548     
15549     getAutoCreate : function()
15550     {
15551         
15552         var cfg = {
15553             tag: 'div',
15554             cls: 'progress-bar',
15555             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
15556         };
15557         
15558         if(this.sr_only){
15559             cfg.cn = {
15560                 tag: 'span',
15561                 cls: 'sr-only',
15562                 html: this.sr_only
15563             }
15564         }
15565         
15566         if(this.role){
15567             cfg.role = this.role;
15568         }
15569         
15570         if(this.aria_valuenow){
15571             cfg['aria-valuenow'] = this.aria_valuenow;
15572         }
15573         
15574         if(this.aria_valuemin){
15575             cfg['aria-valuemin'] = this.aria_valuemin;
15576         }
15577         
15578         if(this.aria_valuemax){
15579             cfg['aria-valuemax'] = this.aria_valuemax;
15580         }
15581         
15582         if(this.label && !this.sr_only){
15583             cfg.html = this.label;
15584         }
15585         
15586         if(this.panel){
15587             cfg.cls += ' progress-bar-' + this.panel;
15588         }
15589         
15590         return cfg;
15591     },
15592     
15593     update : function(aria_valuenow)
15594     {
15595         this.aria_valuenow = aria_valuenow;
15596         
15597         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
15598     }
15599    
15600 });
15601
15602  
15603
15604  /*
15605  * - LGPL
15606  *
15607  * column
15608  * 
15609  */
15610
15611 /**
15612  * @class Roo.bootstrap.TabGroup
15613  * @extends Roo.bootstrap.Column
15614  * Bootstrap Column class
15615  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
15616  * @cfg {Boolean} carousel true to make the group behave like a carousel
15617  * @cfg {Number} bullets show the panel pointer.. default 0
15618  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
15619  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
15620  * @cfg {Number} timer auto slide timer .. default 0 millisecond
15621  * 
15622  * @constructor
15623  * Create a new TabGroup
15624  * @param {Object} config The config object
15625  */
15626
15627 Roo.bootstrap.TabGroup = function(config){
15628     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
15629     if (!this.navId) {
15630         this.navId = Roo.id();
15631     }
15632     this.tabs = [];
15633     Roo.bootstrap.TabGroup.register(this);
15634     
15635 };
15636
15637 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
15638     
15639     carousel : false,
15640     transition : false,
15641     bullets : 0,
15642     timer : 0,
15643     autoslide : false,
15644     slideFn : false,
15645     slideOnTouch : false,
15646     
15647     getAutoCreate : function()
15648     {
15649         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
15650         
15651         cfg.cls += ' tab-content';
15652         
15653         Roo.log('get auto create...............');
15654         
15655         if (this.carousel) {
15656             cfg.cls += ' carousel slide';
15657             
15658             cfg.cn = [{
15659                cls : 'carousel-inner'
15660             }];
15661         
15662             if(this.bullets > 0 && !Roo.isTouch){
15663                 
15664                 var bullets = {
15665                     cls : 'carousel-bullets',
15666                     cn : []
15667                 };
15668                 
15669                 if(this.bullets_cls){
15670                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
15671                 }
15672                 
15673                 for (var i = 0; i < this.bullets; i++){
15674                     bullets.cn.push({
15675                         cls : 'bullet bullet-' + i
15676                     });
15677                 }
15678                 
15679                 bullets.cn.push({
15680                     cls : 'clear'
15681                 });
15682                 
15683                 cfg.cn[0].cn = bullets;
15684             }
15685         }
15686         
15687         return cfg;
15688     },
15689     
15690     initEvents:  function()
15691     {
15692         Roo.log('-------- init events on tab group ---------');
15693         
15694         if(this.bullets > 0 && !Roo.isTouch){
15695             this.initBullet();
15696         }
15697         
15698         Roo.log(this);
15699         
15700         if(Roo.isTouch && this.slideOnTouch){
15701             this.el.on("touchstart", this.onTouchStart, this);
15702         }
15703         
15704         if(this.autoslide){
15705             var _this = this;
15706             
15707             this.slideFn = window.setInterval(function() {
15708                 _this.showPanelNext();
15709             }, this.timer);
15710         }
15711         
15712     },
15713     
15714     onTouchStart : function(e, el, o)
15715     {
15716         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
15717             return;
15718         }
15719         
15720         this.showPanelNext();
15721     },
15722     
15723     getChildContainer : function()
15724     {
15725         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
15726     },
15727     
15728     /**
15729     * register a Navigation item
15730     * @param {Roo.bootstrap.NavItem} the navitem to add
15731     */
15732     register : function(item)
15733     {
15734         this.tabs.push( item);
15735         item.navId = this.navId; // not really needed..
15736     
15737     },
15738     
15739     getActivePanel : function()
15740     {
15741         var r = false;
15742         Roo.each(this.tabs, function(t) {
15743             if (t.active) {
15744                 r = t;
15745                 return false;
15746             }
15747             return null;
15748         });
15749         return r;
15750         
15751     },
15752     getPanelByName : function(n)
15753     {
15754         var r = false;
15755         Roo.each(this.tabs, function(t) {
15756             if (t.tabId == n) {
15757                 r = t;
15758                 return false;
15759             }
15760             return null;
15761         });
15762         return r;
15763     },
15764     indexOfPanel : function(p)
15765     {
15766         var r = false;
15767         Roo.each(this.tabs, function(t,i) {
15768             if (t.tabId == p.tabId) {
15769                 r = i;
15770                 return false;
15771             }
15772             return null;
15773         });
15774         return r;
15775     },
15776     /**
15777      * show a specific panel
15778      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
15779      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
15780      */
15781     showPanel : function (pan)
15782     {
15783         if(this.transition){
15784             Roo.log("waiting for the transitionend");
15785             return;
15786         }
15787         
15788         if (typeof(pan) == 'number') {
15789             pan = this.tabs[pan];
15790         }
15791         if (typeof(pan) == 'string') {
15792             pan = this.getPanelByName(pan);
15793         }
15794         if (pan.tabId == this.getActivePanel().tabId) {
15795             return true;
15796         }
15797         var cur = this.getActivePanel();
15798         
15799         if (false === cur.fireEvent('beforedeactivate')) {
15800             return false;
15801         }
15802         
15803         if(this.bullets > 0 && !Roo.isTouch){
15804             this.setActiveBullet(this.indexOfPanel(pan));
15805         }
15806         
15807         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
15808             
15809             this.transition = true;
15810             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
15811             var lr = dir == 'next' ? 'left' : 'right';
15812             pan.el.addClass(dir); // or prev
15813             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
15814             cur.el.addClass(lr); // or right
15815             pan.el.addClass(lr);
15816             
15817             var _this = this;
15818             cur.el.on('transitionend', function() {
15819                 Roo.log("trans end?");
15820                 
15821                 pan.el.removeClass([lr,dir]);
15822                 pan.setActive(true);
15823                 
15824                 cur.el.removeClass([lr]);
15825                 cur.setActive(false);
15826                 
15827                 _this.transition = false;
15828                 
15829             }, this, { single:  true } );
15830             
15831             return true;
15832         }
15833         
15834         cur.setActive(false);
15835         pan.setActive(true);
15836         
15837         return true;
15838         
15839     },
15840     showPanelNext : function()
15841     {
15842         var i = this.indexOfPanel(this.getActivePanel());
15843         
15844         if (i >= this.tabs.length - 1 && !this.autoslide) {
15845             return;
15846         }
15847         
15848         if (i >= this.tabs.length - 1 && this.autoslide) {
15849             i = -1;
15850         }
15851         
15852         this.showPanel(this.tabs[i+1]);
15853     },
15854     
15855     showPanelPrev : function()
15856     {
15857         var i = this.indexOfPanel(this.getActivePanel());
15858         
15859         if (i  < 1 && !this.autoslide) {
15860             return;
15861         }
15862         
15863         if (i < 1 && this.autoslide) {
15864             i = this.tabs.length;
15865         }
15866         
15867         this.showPanel(this.tabs[i-1]);
15868     },
15869     
15870     initBullet : function()
15871     {
15872         if(Roo.isTouch){
15873             return;
15874         }
15875         
15876         var _this = this;
15877         
15878         for (var i = 0; i < this.bullets; i++){
15879             var bullet = this.el.select('.bullet-' + i, true).first();
15880
15881             if(!bullet){
15882                 continue;
15883             }
15884
15885             bullet.on('click', (function(e, el, o, ii, t){
15886
15887                 e.preventDefault();
15888
15889                 _this.showPanel(ii);
15890
15891                 if(_this.autoslide && _this.slideFn){
15892                     clearInterval(_this.slideFn);
15893                     _this.slideFn = window.setInterval(function() {
15894                         _this.showPanelNext();
15895                     }, _this.timer);
15896                 }
15897
15898             }).createDelegate(this, [i, bullet], true));
15899         }
15900     },
15901     
15902     setActiveBullet : function(i)
15903     {
15904         if(Roo.isTouch){
15905             return;
15906         }
15907         
15908         Roo.each(this.el.select('.bullet', true).elements, function(el){
15909             el.removeClass('selected');
15910         });
15911
15912         var bullet = this.el.select('.bullet-' + i, true).first();
15913         
15914         if(!bullet){
15915             return;
15916         }
15917         
15918         bullet.addClass('selected');
15919     }
15920     
15921     
15922   
15923 });
15924
15925  
15926
15927  
15928  
15929 Roo.apply(Roo.bootstrap.TabGroup, {
15930     
15931     groups: {},
15932      /**
15933     * register a Navigation Group
15934     * @param {Roo.bootstrap.NavGroup} the navgroup to add
15935     */
15936     register : function(navgrp)
15937     {
15938         this.groups[navgrp.navId] = navgrp;
15939         
15940     },
15941     /**
15942     * fetch a Navigation Group based on the navigation ID
15943     * if one does not exist , it will get created.
15944     * @param {string} the navgroup to add
15945     * @returns {Roo.bootstrap.NavGroup} the navgroup 
15946     */
15947     get: function(navId) {
15948         if (typeof(this.groups[navId]) == 'undefined') {
15949             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
15950         }
15951         return this.groups[navId] ;
15952     }
15953     
15954     
15955     
15956 });
15957
15958  /*
15959  * - LGPL
15960  *
15961  * TabPanel
15962  * 
15963  */
15964
15965 /**
15966  * @class Roo.bootstrap.TabPanel
15967  * @extends Roo.bootstrap.Component
15968  * Bootstrap TabPanel class
15969  * @cfg {Boolean} active panel active
15970  * @cfg {String} html panel content
15971  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
15972  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
15973  * 
15974  * 
15975  * @constructor
15976  * Create a new TabPanel
15977  * @param {Object} config The config object
15978  */
15979
15980 Roo.bootstrap.TabPanel = function(config){
15981     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
15982     this.addEvents({
15983         /**
15984              * @event changed
15985              * Fires when the active status changes
15986              * @param {Roo.bootstrap.TabPanel} this
15987              * @param {Boolean} state the new state
15988             
15989          */
15990         'changed': true,
15991         /**
15992              * @event beforedeactivate
15993              * Fires before a tab is de-activated - can be used to do validation on a form.
15994              * @param {Roo.bootstrap.TabPanel} this
15995              * @return {Boolean} false if there is an error
15996             
15997          */
15998         'beforedeactivate': true
15999      });
16000     
16001     this.tabId = this.tabId || Roo.id();
16002   
16003 };
16004
16005 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
16006     
16007     active: false,
16008     html: false,
16009     tabId: false,
16010     navId : false,
16011     
16012     getAutoCreate : function(){
16013         var cfg = {
16014             tag: 'div',
16015             // item is needed for carousel - not sure if it has any effect otherwise
16016             cls: 'tab-pane item',
16017             html: this.html || ''
16018         };
16019         
16020         if(this.active){
16021             cfg.cls += ' active';
16022         }
16023         
16024         if(this.tabId){
16025             cfg.tabId = this.tabId;
16026         }
16027         
16028         
16029         return cfg;
16030     },
16031     
16032     initEvents:  function()
16033     {
16034         Roo.log('-------- init events on tab panel ---------');
16035         
16036         var p = this.parent();
16037         this.navId = this.navId || p.navId;
16038         
16039         if (typeof(this.navId) != 'undefined') {
16040             // not really needed.. but just in case.. parent should be a NavGroup.
16041             var tg = Roo.bootstrap.TabGroup.get(this.navId);
16042             Roo.log(['register', tg, this]);
16043             tg.register(this);
16044             
16045             var i = tg.tabs.length - 1;
16046             
16047             if(this.active && tg.bullets > 0 && i < tg.bullets){
16048                 tg.setActiveBullet(i);
16049             }
16050         }
16051         
16052     },
16053     
16054     
16055     onRender : function(ct, position)
16056     {
16057        // Roo.log("Call onRender: " + this.xtype);
16058         
16059         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16060         
16061         
16062         
16063         
16064         
16065     },
16066     
16067     setActive: function(state)
16068     {
16069         Roo.log("panel - set active " + this.tabId + "=" + state);
16070         
16071         this.active = state;
16072         if (!state) {
16073             this.el.removeClass('active');
16074             
16075         } else  if (!this.el.hasClass('active')) {
16076             this.el.addClass('active');
16077         }
16078         
16079         this.fireEvent('changed', this, state);
16080     }
16081     
16082     
16083 });
16084  
16085
16086  
16087
16088  /*
16089  * - LGPL
16090  *
16091  * DateField
16092  * 
16093  */
16094
16095 /**
16096  * @class Roo.bootstrap.DateField
16097  * @extends Roo.bootstrap.Input
16098  * Bootstrap DateField class
16099  * @cfg {Number} weekStart default 0
16100  * @cfg {String} viewMode default empty, (months|years)
16101  * @cfg {String} minViewMode default empty, (months|years)
16102  * @cfg {Number} startDate default -Infinity
16103  * @cfg {Number} endDate default Infinity
16104  * @cfg {Boolean} todayHighlight default false
16105  * @cfg {Boolean} todayBtn default false
16106  * @cfg {Boolean} calendarWeeks default false
16107  * @cfg {Object} daysOfWeekDisabled default empty
16108  * @cfg {Boolean} singleMode default false (true | false)
16109  * 
16110  * @cfg {Boolean} keyboardNavigation default true
16111  * @cfg {String} language default en
16112  * 
16113  * @constructor
16114  * Create a new DateField
16115  * @param {Object} config The config object
16116  */
16117
16118 Roo.bootstrap.DateField = function(config){
16119     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16120      this.addEvents({
16121             /**
16122              * @event show
16123              * Fires when this field show.
16124              * @param {Roo.bootstrap.DateField} this
16125              * @param {Mixed} date The date value
16126              */
16127             show : true,
16128             /**
16129              * @event show
16130              * Fires when this field hide.
16131              * @param {Roo.bootstrap.DateField} this
16132              * @param {Mixed} date The date value
16133              */
16134             hide : true,
16135             /**
16136              * @event select
16137              * Fires when select a date.
16138              * @param {Roo.bootstrap.DateField} this
16139              * @param {Mixed} date The date value
16140              */
16141             select : true
16142         });
16143 };
16144
16145 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
16146     
16147     /**
16148      * @cfg {String} format
16149      * The default date format string which can be overriden for localization support.  The format must be
16150      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16151      */
16152     format : "m/d/y",
16153     /**
16154      * @cfg {String} altFormats
16155      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16156      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16157      */
16158     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16159     
16160     weekStart : 0,
16161     
16162     viewMode : '',
16163     
16164     minViewMode : '',
16165     
16166     todayHighlight : false,
16167     
16168     todayBtn: false,
16169     
16170     language: 'en',
16171     
16172     keyboardNavigation: true,
16173     
16174     calendarWeeks: false,
16175     
16176     startDate: -Infinity,
16177     
16178     endDate: Infinity,
16179     
16180     daysOfWeekDisabled: [],
16181     
16182     _events: [],
16183     
16184     singleMode : false,
16185     
16186     UTCDate: function()
16187     {
16188         return new Date(Date.UTC.apply(Date, arguments));
16189     },
16190     
16191     UTCToday: function()
16192     {
16193         var today = new Date();
16194         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16195     },
16196     
16197     getDate: function() {
16198             var d = this.getUTCDate();
16199             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16200     },
16201     
16202     getUTCDate: function() {
16203             return this.date;
16204     },
16205     
16206     setDate: function(d) {
16207             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16208     },
16209     
16210     setUTCDate: function(d) {
16211             this.date = d;
16212             this.setValue(this.formatDate(this.date));
16213     },
16214         
16215     onRender: function(ct, position)
16216     {
16217         
16218         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16219         
16220         this.language = this.language || 'en';
16221         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16222         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16223         
16224         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16225         this.format = this.format || 'm/d/y';
16226         this.isInline = false;
16227         this.isInput = true;
16228         this.component = this.el.select('.add-on', true).first() || false;
16229         this.component = (this.component && this.component.length === 0) ? false : this.component;
16230         this.hasInput = this.component && this.inputEL().length;
16231         
16232         if (typeof(this.minViewMode === 'string')) {
16233             switch (this.minViewMode) {
16234                 case 'months':
16235                     this.minViewMode = 1;
16236                     break;
16237                 case 'years':
16238                     this.minViewMode = 2;
16239                     break;
16240                 default:
16241                     this.minViewMode = 0;
16242                     break;
16243             }
16244         }
16245         
16246         if (typeof(this.viewMode === 'string')) {
16247             switch (this.viewMode) {
16248                 case 'months':
16249                     this.viewMode = 1;
16250                     break;
16251                 case 'years':
16252                     this.viewMode = 2;
16253                     break;
16254                 default:
16255                     this.viewMode = 0;
16256                     break;
16257             }
16258         }
16259                 
16260         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16261         
16262 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16263         
16264         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16265         
16266         this.picker().on('mousedown', this.onMousedown, this);
16267         this.picker().on('click', this.onClick, this);
16268         
16269         this.picker().addClass('datepicker-dropdown');
16270         
16271         this.startViewMode = this.viewMode;
16272         
16273         if(this.singleMode){
16274             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16275                 v.setVisibilityMode(Roo.Element.DISPLAY)
16276                 v.hide();
16277             });
16278             
16279             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16280                 v.setStyle('width', '189px');
16281             });
16282         }
16283         
16284         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16285             if(!this.calendarWeeks){
16286                 v.remove();
16287                 return;
16288             }
16289             
16290             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16291             v.attr('colspan', function(i, val){
16292                 return parseInt(val) + 1;
16293             });
16294         })
16295                         
16296         
16297         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16298         
16299         this.setStartDate(this.startDate);
16300         this.setEndDate(this.endDate);
16301         
16302         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16303         
16304         this.fillDow();
16305         this.fillMonths();
16306         this.update();
16307         this.showMode();
16308         
16309         if(this.isInline) {
16310             this.show();
16311         }
16312     },
16313     
16314     picker : function()
16315     {
16316         return this.pickerEl;
16317 //        return this.el.select('.datepicker', true).first();
16318     },
16319     
16320     fillDow: function()
16321     {
16322         var dowCnt = this.weekStart;
16323         
16324         var dow = {
16325             tag: 'tr',
16326             cn: [
16327                 
16328             ]
16329         };
16330         
16331         if(this.calendarWeeks){
16332             dow.cn.push({
16333                 tag: 'th',
16334                 cls: 'cw',
16335                 html: '&nbsp;'
16336             })
16337         }
16338         
16339         while (dowCnt < this.weekStart + 7) {
16340             dow.cn.push({
16341                 tag: 'th',
16342                 cls: 'dow',
16343                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16344             });
16345         }
16346         
16347         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16348     },
16349     
16350     fillMonths: function()
16351     {    
16352         var i = 0;
16353         var months = this.picker().select('>.datepicker-months td', true).first();
16354         
16355         months.dom.innerHTML = '';
16356         
16357         while (i < 12) {
16358             var month = {
16359                 tag: 'span',
16360                 cls: 'month',
16361                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16362             }
16363             
16364             months.createChild(month);
16365         }
16366         
16367     },
16368     
16369     update: function()
16370     {
16371         this.date = (typeof(this.date) === 'undefined' || ((typeof(this.date) === 'string') && !this.date.length)) ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
16372         
16373         if (this.date < this.startDate) {
16374             this.viewDate = new Date(this.startDate);
16375         } else if (this.date > this.endDate) {
16376             this.viewDate = new Date(this.endDate);
16377         } else {
16378             this.viewDate = new Date(this.date);
16379         }
16380         
16381         this.fill();
16382     },
16383     
16384     fill: function() 
16385     {
16386         var d = new Date(this.viewDate),
16387                 year = d.getUTCFullYear(),
16388                 month = d.getUTCMonth(),
16389                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16390                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16391                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16392                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16393                 currentDate = this.date && this.date.valueOf(),
16394                 today = this.UTCToday();
16395         
16396         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16397         
16398 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16399         
16400 //        this.picker.select('>tfoot th.today').
16401 //                                              .text(dates[this.language].today)
16402 //                                              .toggle(this.todayBtn !== false);
16403     
16404         this.updateNavArrows();
16405         this.fillMonths();
16406                                                 
16407         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16408         
16409         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16410          
16411         prevMonth.setUTCDate(day);
16412         
16413         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16414         
16415         var nextMonth = new Date(prevMonth);
16416         
16417         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16418         
16419         nextMonth = nextMonth.valueOf();
16420         
16421         var fillMonths = false;
16422         
16423         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16424         
16425         while(prevMonth.valueOf() < nextMonth) {
16426             var clsName = '';
16427             
16428             if (prevMonth.getUTCDay() === this.weekStart) {
16429                 if(fillMonths){
16430                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16431                 }
16432                     
16433                 fillMonths = {
16434                     tag: 'tr',
16435                     cn: []
16436                 };
16437                 
16438                 if(this.calendarWeeks){
16439                     // ISO 8601: First week contains first thursday.
16440                     // ISO also states week starts on Monday, but we can be more abstract here.
16441                     var
16442                     // Start of current week: based on weekstart/current date
16443                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16444                     // Thursday of this week
16445                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16446                     // First Thursday of year, year from thursday
16447                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16448                     // Calendar week: ms between thursdays, div ms per day, div 7 days
16449                     calWeek =  (th - yth) / 864e5 / 7 + 1;
16450                     
16451                     fillMonths.cn.push({
16452                         tag: 'td',
16453                         cls: 'cw',
16454                         html: calWeek
16455                     });
16456                 }
16457             }
16458             
16459             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16460                 clsName += ' old';
16461             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16462                 clsName += ' new';
16463             }
16464             if (this.todayHighlight &&
16465                 prevMonth.getUTCFullYear() == today.getFullYear() &&
16466                 prevMonth.getUTCMonth() == today.getMonth() &&
16467                 prevMonth.getUTCDate() == today.getDate()) {
16468                 clsName += ' today';
16469             }
16470             
16471             if (currentDate && prevMonth.valueOf() === currentDate) {
16472                 clsName += ' active';
16473             }
16474             
16475             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
16476                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
16477                     clsName += ' disabled';
16478             }
16479             
16480             fillMonths.cn.push({
16481                 tag: 'td',
16482                 cls: 'day ' + clsName,
16483                 html: prevMonth.getDate()
16484             })
16485             
16486             prevMonth.setDate(prevMonth.getDate()+1);
16487         }
16488           
16489         var currentYear = this.date && this.date.getUTCFullYear();
16490         var currentMonth = this.date && this.date.getUTCMonth();
16491         
16492         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
16493         
16494         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
16495             v.removeClass('active');
16496             
16497             if(currentYear === year && k === currentMonth){
16498                 v.addClass('active');
16499             }
16500             
16501             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
16502                 v.addClass('disabled');
16503             }
16504             
16505         });
16506         
16507         
16508         year = parseInt(year/10, 10) * 10;
16509         
16510         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
16511         
16512         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
16513         
16514         year -= 1;
16515         for (var i = -1; i < 11; i++) {
16516             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
16517                 tag: 'span',
16518                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
16519                 html: year
16520             })
16521             
16522             year += 1;
16523         }
16524     },
16525     
16526     showMode: function(dir) 
16527     {
16528         if (dir) {
16529             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
16530         }
16531         
16532         Roo.each(this.picker().select('>div',true).elements, function(v){
16533             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16534             v.hide();
16535         });
16536         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
16537     },
16538     
16539     place: function()
16540     {
16541         if(this.isInline) return;
16542         
16543         this.picker().removeClass(['bottom', 'top']);
16544         
16545         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16546             /*
16547              * place to the top of element!
16548              *
16549              */
16550             
16551             this.picker().addClass('top');
16552             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16553             
16554             return;
16555         }
16556         
16557         this.picker().addClass('bottom');
16558         
16559         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16560     },
16561     
16562     parseDate : function(value)
16563     {
16564         if(!value || value instanceof Date){
16565             return value;
16566         }
16567         var v = Date.parseDate(value, this.format);
16568         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
16569             v = Date.parseDate(value, 'Y-m-d');
16570         }
16571         if(!v && this.altFormats){
16572             if(!this.altFormatsArray){
16573                 this.altFormatsArray = this.altFormats.split("|");
16574             }
16575             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
16576                 v = Date.parseDate(value, this.altFormatsArray[i]);
16577             }
16578         }
16579         return v;
16580     },
16581     
16582     formatDate : function(date, fmt)
16583     {   
16584         return (!date || !(date instanceof Date)) ?
16585         date : date.dateFormat(fmt || this.format);
16586     },
16587     
16588     onFocus : function()
16589     {
16590         Roo.bootstrap.DateField.superclass.onFocus.call(this);
16591         this.show();
16592     },
16593     
16594     onBlur : function()
16595     {
16596         Roo.bootstrap.DateField.superclass.onBlur.call(this);
16597         
16598         var d = this.inputEl().getValue();
16599         
16600         this.setValue(d);
16601                 
16602         this.hide();
16603     },
16604     
16605     show : function()
16606     {
16607         this.picker().show();
16608         this.update();
16609         this.place();
16610         
16611         this.fireEvent('show', this, this.date);
16612     },
16613     
16614     hide : function()
16615     {
16616         if(this.isInline) return;
16617         this.picker().hide();
16618         this.viewMode = this.startViewMode;
16619         this.showMode();
16620         
16621         this.fireEvent('hide', this, this.date);
16622         
16623     },
16624     
16625     onMousedown: function(e)
16626     {
16627         e.stopPropagation();
16628         e.preventDefault();
16629     },
16630     
16631     keyup: function(e)
16632     {
16633         Roo.bootstrap.DateField.superclass.keyup.call(this);
16634         this.update();
16635     },
16636
16637     setValue: function(v)
16638     {
16639         
16640         // v can be a string or a date..
16641         
16642         
16643         var d = new Date(this.parseDate(v) ).clearTime();
16644         
16645         if(isNaN(d.getTime())){
16646             this.date = this.viewDate = '';
16647             Roo.bootstrap.DateField.superclass.setValue.call(this, '');
16648             return;
16649         }
16650         
16651         v = this.formatDate(d);
16652         
16653         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
16654         
16655         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
16656      
16657         this.update();
16658
16659         this.fireEvent('select', this, this.date);
16660         
16661     },
16662     
16663     getValue: function()
16664     {
16665         return this.formatDate(this.date);
16666     },
16667     
16668     fireKey: function(e)
16669     {
16670         if (!this.picker().isVisible()){
16671             if (e.keyCode == 27) // allow escape to hide and re-show picker
16672                 this.show();
16673             return;
16674         }
16675         
16676         var dateChanged = false,
16677         dir, day, month,
16678         newDate, newViewDate;
16679         
16680         switch(e.keyCode){
16681             case 27: // escape
16682                 this.hide();
16683                 e.preventDefault();
16684                 break;
16685             case 37: // left
16686             case 39: // right
16687                 if (!this.keyboardNavigation) break;
16688                 dir = e.keyCode == 37 ? -1 : 1;
16689                 
16690                 if (e.ctrlKey){
16691                     newDate = this.moveYear(this.date, dir);
16692                     newViewDate = this.moveYear(this.viewDate, dir);
16693                 } else if (e.shiftKey){
16694                     newDate = this.moveMonth(this.date, dir);
16695                     newViewDate = this.moveMonth(this.viewDate, dir);
16696                 } else {
16697                     newDate = new Date(this.date);
16698                     newDate.setUTCDate(this.date.getUTCDate() + dir);
16699                     newViewDate = new Date(this.viewDate);
16700                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
16701                 }
16702                 if (this.dateWithinRange(newDate)){
16703                     this.date = newDate;
16704                     this.viewDate = newViewDate;
16705                     this.setValue(this.formatDate(this.date));
16706 //                    this.update();
16707                     e.preventDefault();
16708                     dateChanged = true;
16709                 }
16710                 break;
16711             case 38: // up
16712             case 40: // down
16713                 if (!this.keyboardNavigation) break;
16714                 dir = e.keyCode == 38 ? -1 : 1;
16715                 if (e.ctrlKey){
16716                     newDate = this.moveYear(this.date, dir);
16717                     newViewDate = this.moveYear(this.viewDate, dir);
16718                 } else if (e.shiftKey){
16719                     newDate = this.moveMonth(this.date, dir);
16720                     newViewDate = this.moveMonth(this.viewDate, dir);
16721                 } else {
16722                     newDate = new Date(this.date);
16723                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
16724                     newViewDate = new Date(this.viewDate);
16725                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
16726                 }
16727                 if (this.dateWithinRange(newDate)){
16728                     this.date = newDate;
16729                     this.viewDate = newViewDate;
16730                     this.setValue(this.formatDate(this.date));
16731 //                    this.update();
16732                     e.preventDefault();
16733                     dateChanged = true;
16734                 }
16735                 break;
16736             case 13: // enter
16737                 this.setValue(this.formatDate(this.date));
16738                 this.hide();
16739                 e.preventDefault();
16740                 break;
16741             case 9: // tab
16742                 this.setValue(this.formatDate(this.date));
16743                 this.hide();
16744                 break;
16745             case 16: // shift
16746             case 17: // ctrl
16747             case 18: // alt
16748                 break;
16749             default :
16750                 this.hide();
16751                 
16752         }
16753     },
16754     
16755     
16756     onClick: function(e) 
16757     {
16758         e.stopPropagation();
16759         e.preventDefault();
16760         
16761         var target = e.getTarget();
16762         
16763         if(target.nodeName.toLowerCase() === 'i'){
16764             target = Roo.get(target).dom.parentNode;
16765         }
16766         
16767         var nodeName = target.nodeName;
16768         var className = target.className;
16769         var html = target.innerHTML;
16770         //Roo.log(nodeName);
16771         
16772         switch(nodeName.toLowerCase()) {
16773             case 'th':
16774                 switch(className) {
16775                     case 'switch':
16776                         this.showMode(1);
16777                         break;
16778                     case 'prev':
16779                     case 'next':
16780                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
16781                         switch(this.viewMode){
16782                                 case 0:
16783                                         this.viewDate = this.moveMonth(this.viewDate, dir);
16784                                         break;
16785                                 case 1:
16786                                 case 2:
16787                                         this.viewDate = this.moveYear(this.viewDate, dir);
16788                                         break;
16789                         }
16790                         this.fill();
16791                         break;
16792                     case 'today':
16793                         var date = new Date();
16794                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
16795 //                        this.fill()
16796                         this.setValue(this.formatDate(this.date));
16797                         
16798                         this.hide();
16799                         break;
16800                 }
16801                 break;
16802             case 'span':
16803                 if (className.indexOf('disabled') < 0) {
16804                     this.viewDate.setUTCDate(1);
16805                     if (className.indexOf('month') > -1) {
16806                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
16807                     } else {
16808                         var year = parseInt(html, 10) || 0;
16809                         this.viewDate.setUTCFullYear(year);
16810                         
16811                     }
16812                     
16813                     if(this.singleMode){
16814                         this.setValue(this.formatDate(this.viewDate));
16815                         this.hide();
16816                         return;
16817                     }
16818                     
16819                     this.showMode(-1);
16820                     this.fill();
16821                 }
16822                 break;
16823                 
16824             case 'td':
16825                 //Roo.log(className);
16826                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
16827                     var day = parseInt(html, 10) || 1;
16828                     var year = this.viewDate.getUTCFullYear(),
16829                         month = this.viewDate.getUTCMonth();
16830
16831                     if (className.indexOf('old') > -1) {
16832                         if(month === 0 ){
16833                             month = 11;
16834                             year -= 1;
16835                         }else{
16836                             month -= 1;
16837                         }
16838                     } else if (className.indexOf('new') > -1) {
16839                         if (month == 11) {
16840                             month = 0;
16841                             year += 1;
16842                         } else {
16843                             month += 1;
16844                         }
16845                     }
16846                     //Roo.log([year,month,day]);
16847                     this.date = this.UTCDate(year, month, day,0,0,0,0);
16848                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
16849 //                    this.fill();
16850                     //Roo.log(this.formatDate(this.date));
16851                     this.setValue(this.formatDate(this.date));
16852                     this.hide();
16853                 }
16854                 break;
16855         }
16856     },
16857     
16858     setStartDate: function(startDate)
16859     {
16860         this.startDate = startDate || -Infinity;
16861         if (this.startDate !== -Infinity) {
16862             this.startDate = this.parseDate(this.startDate);
16863         }
16864         this.update();
16865         this.updateNavArrows();
16866     },
16867
16868     setEndDate: function(endDate)
16869     {
16870         this.endDate = endDate || Infinity;
16871         if (this.endDate !== Infinity) {
16872             this.endDate = this.parseDate(this.endDate);
16873         }
16874         this.update();
16875         this.updateNavArrows();
16876     },
16877     
16878     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
16879     {
16880         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
16881         if (typeof(this.daysOfWeekDisabled) !== 'object') {
16882             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
16883         }
16884         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
16885             return parseInt(d, 10);
16886         });
16887         this.update();
16888         this.updateNavArrows();
16889     },
16890     
16891     updateNavArrows: function() 
16892     {
16893         if(this.singleMode){
16894             return;
16895         }
16896         
16897         var d = new Date(this.viewDate),
16898         year = d.getUTCFullYear(),
16899         month = d.getUTCMonth();
16900         
16901         Roo.each(this.picker().select('.prev', true).elements, function(v){
16902             v.show();
16903             switch (this.viewMode) {
16904                 case 0:
16905
16906                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
16907                         v.hide();
16908                     }
16909                     break;
16910                 case 1:
16911                 case 2:
16912                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
16913                         v.hide();
16914                     }
16915                     break;
16916             }
16917         });
16918         
16919         Roo.each(this.picker().select('.next', true).elements, function(v){
16920             v.show();
16921             switch (this.viewMode) {
16922                 case 0:
16923
16924                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
16925                         v.hide();
16926                     }
16927                     break;
16928                 case 1:
16929                 case 2:
16930                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
16931                         v.hide();
16932                     }
16933                     break;
16934             }
16935         })
16936     },
16937     
16938     moveMonth: function(date, dir)
16939     {
16940         if (!dir) return date;
16941         var new_date = new Date(date.valueOf()),
16942         day = new_date.getUTCDate(),
16943         month = new_date.getUTCMonth(),
16944         mag = Math.abs(dir),
16945         new_month, test;
16946         dir = dir > 0 ? 1 : -1;
16947         if (mag == 1){
16948             test = dir == -1
16949             // If going back one month, make sure month is not current month
16950             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
16951             ? function(){
16952                 return new_date.getUTCMonth() == month;
16953             }
16954             // If going forward one month, make sure month is as expected
16955             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
16956             : function(){
16957                 return new_date.getUTCMonth() != new_month;
16958             };
16959             new_month = month + dir;
16960             new_date.setUTCMonth(new_month);
16961             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
16962             if (new_month < 0 || new_month > 11)
16963                 new_month = (new_month + 12) % 12;
16964         } else {
16965             // For magnitudes >1, move one month at a time...
16966             for (var i=0; i<mag; i++)
16967                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
16968                 new_date = this.moveMonth(new_date, dir);
16969             // ...then reset the day, keeping it in the new month
16970             new_month = new_date.getUTCMonth();
16971             new_date.setUTCDate(day);
16972             test = function(){
16973                 return new_month != new_date.getUTCMonth();
16974             };
16975         }
16976         // Common date-resetting loop -- if date is beyond end of month, make it
16977         // end of month
16978         while (test()){
16979             new_date.setUTCDate(--day);
16980             new_date.setUTCMonth(new_month);
16981         }
16982         return new_date;
16983     },
16984
16985     moveYear: function(date, dir)
16986     {
16987         return this.moveMonth(date, dir*12);
16988     },
16989
16990     dateWithinRange: function(date)
16991     {
16992         return date >= this.startDate && date <= this.endDate;
16993     },
16994
16995     
16996     remove: function() 
16997     {
16998         this.picker().remove();
16999     }
17000    
17001 });
17002
17003 Roo.apply(Roo.bootstrap.DateField,  {
17004     
17005     head : {
17006         tag: 'thead',
17007         cn: [
17008         {
17009             tag: 'tr',
17010             cn: [
17011             {
17012                 tag: 'th',
17013                 cls: 'prev',
17014                 html: '<i class="fa fa-arrow-left"/>'
17015             },
17016             {
17017                 tag: 'th',
17018                 cls: 'switch',
17019                 colspan: '5'
17020             },
17021             {
17022                 tag: 'th',
17023                 cls: 'next',
17024                 html: '<i class="fa fa-arrow-right"/>'
17025             }
17026
17027             ]
17028         }
17029         ]
17030     },
17031     
17032     content : {
17033         tag: 'tbody',
17034         cn: [
17035         {
17036             tag: 'tr',
17037             cn: [
17038             {
17039                 tag: 'td',
17040                 colspan: '7'
17041             }
17042             ]
17043         }
17044         ]
17045     },
17046     
17047     footer : {
17048         tag: 'tfoot',
17049         cn: [
17050         {
17051             tag: 'tr',
17052             cn: [
17053             {
17054                 tag: 'th',
17055                 colspan: '7',
17056                 cls: 'today'
17057             }
17058                     
17059             ]
17060         }
17061         ]
17062     },
17063     
17064     dates:{
17065         en: {
17066             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17067             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17068             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17069             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17070             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17071             today: "Today"
17072         }
17073     },
17074     
17075     modes: [
17076     {
17077         clsName: 'days',
17078         navFnc: 'Month',
17079         navStep: 1
17080     },
17081     {
17082         clsName: 'months',
17083         navFnc: 'FullYear',
17084         navStep: 1
17085     },
17086     {
17087         clsName: 'years',
17088         navFnc: 'FullYear',
17089         navStep: 10
17090     }]
17091 });
17092
17093 Roo.apply(Roo.bootstrap.DateField,  {
17094   
17095     template : {
17096         tag: 'div',
17097         cls: 'datepicker dropdown-menu roo-dynamic',
17098         cn: [
17099         {
17100             tag: 'div',
17101             cls: 'datepicker-days',
17102             cn: [
17103             {
17104                 tag: 'table',
17105                 cls: 'table-condensed',
17106                 cn:[
17107                 Roo.bootstrap.DateField.head,
17108                 {
17109                     tag: 'tbody'
17110                 },
17111                 Roo.bootstrap.DateField.footer
17112                 ]
17113             }
17114             ]
17115         },
17116         {
17117             tag: 'div',
17118             cls: 'datepicker-months',
17119             cn: [
17120             {
17121                 tag: 'table',
17122                 cls: 'table-condensed',
17123                 cn:[
17124                 Roo.bootstrap.DateField.head,
17125                 Roo.bootstrap.DateField.content,
17126                 Roo.bootstrap.DateField.footer
17127                 ]
17128             }
17129             ]
17130         },
17131         {
17132             tag: 'div',
17133             cls: 'datepicker-years',
17134             cn: [
17135             {
17136                 tag: 'table',
17137                 cls: 'table-condensed',
17138                 cn:[
17139                 Roo.bootstrap.DateField.head,
17140                 Roo.bootstrap.DateField.content,
17141                 Roo.bootstrap.DateField.footer
17142                 ]
17143             }
17144             ]
17145         }
17146         ]
17147     }
17148 });
17149
17150  
17151
17152  /*
17153  * - LGPL
17154  *
17155  * TimeField
17156  * 
17157  */
17158
17159 /**
17160  * @class Roo.bootstrap.TimeField
17161  * @extends Roo.bootstrap.Input
17162  * Bootstrap DateField class
17163  * 
17164  * 
17165  * @constructor
17166  * Create a new TimeField
17167  * @param {Object} config The config object
17168  */
17169
17170 Roo.bootstrap.TimeField = function(config){
17171     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17172     this.addEvents({
17173             /**
17174              * @event show
17175              * Fires when this field show.
17176              * @param {Roo.bootstrap.DateField} thisthis
17177              * @param {Mixed} date The date value
17178              */
17179             show : true,
17180             /**
17181              * @event show
17182              * Fires when this field hide.
17183              * @param {Roo.bootstrap.DateField} this
17184              * @param {Mixed} date The date value
17185              */
17186             hide : true,
17187             /**
17188              * @event select
17189              * Fires when select a date.
17190              * @param {Roo.bootstrap.DateField} this
17191              * @param {Mixed} date The date value
17192              */
17193             select : true
17194         });
17195 };
17196
17197 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
17198     
17199     /**
17200      * @cfg {String} format
17201      * The default time format string which can be overriden for localization support.  The format must be
17202      * valid according to {@link Date#parseDate} (defaults to 'H:i').
17203      */
17204     format : "H:i",
17205        
17206     onRender: function(ct, position)
17207     {
17208         
17209         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17210                 
17211         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17212         
17213         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17214         
17215         this.pop = this.picker().select('>.datepicker-time',true).first();
17216         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17217         
17218         this.picker().on('mousedown', this.onMousedown, this);
17219         this.picker().on('click', this.onClick, this);
17220         
17221         this.picker().addClass('datepicker-dropdown');
17222     
17223         this.fillTime();
17224         this.update();
17225             
17226         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17227         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17228         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17229         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17230         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17231         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17232
17233     },
17234     
17235     fireKey: function(e){
17236         if (!this.picker().isVisible()){
17237             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17238                 this.show();
17239             }
17240             return;
17241         }
17242
17243         e.preventDefault();
17244         
17245         switch(e.keyCode){
17246             case 27: // escape
17247                 this.hide();
17248                 break;
17249             case 37: // left
17250             case 39: // right
17251                 this.onTogglePeriod();
17252                 break;
17253             case 38: // up
17254                 this.onIncrementMinutes();
17255                 break;
17256             case 40: // down
17257                 this.onDecrementMinutes();
17258                 break;
17259             case 13: // enter
17260             case 9: // tab
17261                 this.setTime();
17262                 break;
17263         }
17264     },
17265     
17266     onClick: function(e) {
17267         e.stopPropagation();
17268         e.preventDefault();
17269     },
17270     
17271     picker : function()
17272     {
17273         return this.el.select('.datepicker', true).first();
17274     },
17275     
17276     fillTime: function()
17277     {    
17278         var time = this.pop.select('tbody', true).first();
17279         
17280         time.dom.innerHTML = '';
17281         
17282         time.createChild({
17283             tag: 'tr',
17284             cn: [
17285                 {
17286                     tag: 'td',
17287                     cn: [
17288                         {
17289                             tag: 'a',
17290                             href: '#',
17291                             cls: 'btn',
17292                             cn: [
17293                                 {
17294                                     tag: 'span',
17295                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
17296                                 }
17297                             ]
17298                         } 
17299                     ]
17300                 },
17301                 {
17302                     tag: 'td',
17303                     cls: 'separator'
17304                 },
17305                 {
17306                     tag: 'td',
17307                     cn: [
17308                         {
17309                             tag: 'a',
17310                             href: '#',
17311                             cls: 'btn',
17312                             cn: [
17313                                 {
17314                                     tag: 'span',
17315                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
17316                                 }
17317                             ]
17318                         }
17319                     ]
17320                 },
17321                 {
17322                     tag: 'td',
17323                     cls: 'separator'
17324                 }
17325             ]
17326         });
17327         
17328         time.createChild({
17329             tag: 'tr',
17330             cn: [
17331                 {
17332                     tag: 'td',
17333                     cn: [
17334                         {
17335                             tag: 'span',
17336                             cls: 'timepicker-hour',
17337                             html: '00'
17338                         }  
17339                     ]
17340                 },
17341                 {
17342                     tag: 'td',
17343                     cls: 'separator',
17344                     html: ':'
17345                 },
17346                 {
17347                     tag: 'td',
17348                     cn: [
17349                         {
17350                             tag: 'span',
17351                             cls: 'timepicker-minute',
17352                             html: '00'
17353                         }  
17354                     ]
17355                 },
17356                 {
17357                     tag: 'td',
17358                     cls: 'separator'
17359                 },
17360                 {
17361                     tag: 'td',
17362                     cn: [
17363                         {
17364                             tag: 'button',
17365                             type: 'button',
17366                             cls: 'btn btn-primary period',
17367                             html: 'AM'
17368                             
17369                         }
17370                     ]
17371                 }
17372             ]
17373         });
17374         
17375         time.createChild({
17376             tag: 'tr',
17377             cn: [
17378                 {
17379                     tag: 'td',
17380                     cn: [
17381                         {
17382                             tag: 'a',
17383                             href: '#',
17384                             cls: 'btn',
17385                             cn: [
17386                                 {
17387                                     tag: 'span',
17388                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
17389                                 }
17390                             ]
17391                         }
17392                     ]
17393                 },
17394                 {
17395                     tag: 'td',
17396                     cls: 'separator'
17397                 },
17398                 {
17399                     tag: 'td',
17400                     cn: [
17401                         {
17402                             tag: 'a',
17403                             href: '#',
17404                             cls: 'btn',
17405                             cn: [
17406                                 {
17407                                     tag: 'span',
17408                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
17409                                 }
17410                             ]
17411                         }
17412                     ]
17413                 },
17414                 {
17415                     tag: 'td',
17416                     cls: 'separator'
17417                 }
17418             ]
17419         });
17420         
17421     },
17422     
17423     update: function()
17424     {
17425         
17426         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17427         
17428         this.fill();
17429     },
17430     
17431     fill: function() 
17432     {
17433         var hours = this.time.getHours();
17434         var minutes = this.time.getMinutes();
17435         var period = 'AM';
17436         
17437         if(hours > 11){
17438             period = 'PM';
17439         }
17440         
17441         if(hours == 0){
17442             hours = 12;
17443         }
17444         
17445         
17446         if(hours > 12){
17447             hours = hours - 12;
17448         }
17449         
17450         if(hours < 10){
17451             hours = '0' + hours;
17452         }
17453         
17454         if(minutes < 10){
17455             minutes = '0' + minutes;
17456         }
17457         
17458         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
17459         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
17460         this.pop.select('button', true).first().dom.innerHTML = period;
17461         
17462     },
17463     
17464     place: function()
17465     {   
17466         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
17467         
17468         var cls = ['bottom'];
17469         
17470         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
17471             cls.pop();
17472             cls.push('top');
17473         }
17474         
17475         cls.push('right');
17476         
17477         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
17478             cls.pop();
17479             cls.push('left');
17480         }
17481         
17482         this.picker().addClass(cls.join('-'));
17483         
17484         var _this = this;
17485         
17486         Roo.each(cls, function(c){
17487             if(c == 'bottom'){
17488                 _this.picker().setTop(_this.inputEl().getHeight());
17489                 return;
17490             }
17491             if(c == 'top'){
17492                 _this.picker().setTop(0 - _this.picker().getHeight());
17493                 return;
17494             }
17495             
17496             if(c == 'left'){
17497                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
17498                 return;
17499             }
17500             if(c == 'right'){
17501                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
17502                 return;
17503             }
17504         });
17505         
17506     },
17507   
17508     onFocus : function()
17509     {
17510         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
17511         this.show();
17512     },
17513     
17514     onBlur : function()
17515     {
17516         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
17517         this.hide();
17518     },
17519     
17520     show : function()
17521     {
17522         this.picker().show();
17523         this.pop.show();
17524         this.update();
17525         this.place();
17526         
17527         this.fireEvent('show', this, this.date);
17528     },
17529     
17530     hide : function()
17531     {
17532         this.picker().hide();
17533         this.pop.hide();
17534         
17535         this.fireEvent('hide', this, this.date);
17536     },
17537     
17538     setTime : function()
17539     {
17540         this.hide();
17541         this.setValue(this.time.format(this.format));
17542         
17543         this.fireEvent('select', this, this.date);
17544         
17545         
17546     },
17547     
17548     onMousedown: function(e){
17549         e.stopPropagation();
17550         e.preventDefault();
17551     },
17552     
17553     onIncrementHours: function()
17554     {
17555         Roo.log('onIncrementHours');
17556         this.time = this.time.add(Date.HOUR, 1);
17557         this.update();
17558         
17559     },
17560     
17561     onDecrementHours: function()
17562     {
17563         Roo.log('onDecrementHours');
17564         this.time = this.time.add(Date.HOUR, -1);
17565         this.update();
17566     },
17567     
17568     onIncrementMinutes: function()
17569     {
17570         Roo.log('onIncrementMinutes');
17571         this.time = this.time.add(Date.MINUTE, 1);
17572         this.update();
17573     },
17574     
17575     onDecrementMinutes: function()
17576     {
17577         Roo.log('onDecrementMinutes');
17578         this.time = this.time.add(Date.MINUTE, -1);
17579         this.update();
17580     },
17581     
17582     onTogglePeriod: function()
17583     {
17584         Roo.log('onTogglePeriod');
17585         this.time = this.time.add(Date.HOUR, 12);
17586         this.update();
17587     }
17588     
17589    
17590 });
17591
17592 Roo.apply(Roo.bootstrap.TimeField,  {
17593     
17594     content : {
17595         tag: 'tbody',
17596         cn: [
17597             {
17598                 tag: 'tr',
17599                 cn: [
17600                 {
17601                     tag: 'td',
17602                     colspan: '7'
17603                 }
17604                 ]
17605             }
17606         ]
17607     },
17608     
17609     footer : {
17610         tag: 'tfoot',
17611         cn: [
17612             {
17613                 tag: 'tr',
17614                 cn: [
17615                 {
17616                     tag: 'th',
17617                     colspan: '7',
17618                     cls: '',
17619                     cn: [
17620                         {
17621                             tag: 'button',
17622                             cls: 'btn btn-info ok',
17623                             html: 'OK'
17624                         }
17625                     ]
17626                 }
17627
17628                 ]
17629             }
17630         ]
17631     }
17632 });
17633
17634 Roo.apply(Roo.bootstrap.TimeField,  {
17635   
17636     template : {
17637         tag: 'div',
17638         cls: 'datepicker dropdown-menu',
17639         cn: [
17640             {
17641                 tag: 'div',
17642                 cls: 'datepicker-time',
17643                 cn: [
17644                 {
17645                     tag: 'table',
17646                     cls: 'table-condensed',
17647                     cn:[
17648                     Roo.bootstrap.TimeField.content,
17649                     Roo.bootstrap.TimeField.footer
17650                     ]
17651                 }
17652                 ]
17653             }
17654         ]
17655     }
17656 });
17657
17658  
17659
17660  /*
17661  * - LGPL
17662  *
17663  * MonthField
17664  * 
17665  */
17666
17667 /**
17668  * @class Roo.bootstrap.MonthField
17669  * @extends Roo.bootstrap.Input
17670  * Bootstrap MonthField class
17671  * 
17672  * @cfg {String} language default en
17673  * 
17674  * @constructor
17675  * Create a new MonthField
17676  * @param {Object} config The config object
17677  */
17678
17679 Roo.bootstrap.MonthField = function(config){
17680     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
17681     
17682     this.addEvents({
17683         /**
17684          * @event show
17685          * Fires when this field show.
17686          * @param {Roo.bootstrap.MonthField} this
17687          * @param {Mixed} date The date value
17688          */
17689         show : true,
17690         /**
17691          * @event show
17692          * Fires when this field hide.
17693          * @param {Roo.bootstrap.MonthField} this
17694          * @param {Mixed} date The date value
17695          */
17696         hide : true,
17697         /**
17698          * @event select
17699          * Fires when select a date.
17700          * @param {Roo.bootstrap.MonthField} this
17701          * @param {String} oldvalue The old value
17702          * @param {String} newvalue The new value
17703          */
17704         select : true
17705     });
17706 };
17707
17708 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
17709     
17710     onRender: function(ct, position)
17711     {
17712         
17713         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
17714         
17715         this.language = this.language || 'en';
17716         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
17717         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
17718         
17719         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
17720         this.isInline = false;
17721         this.isInput = true;
17722         this.component = this.el.select('.add-on', true).first() || false;
17723         this.component = (this.component && this.component.length === 0) ? false : this.component;
17724         this.hasInput = this.component && this.inputEL().length;
17725         
17726         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
17727         
17728         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17729         
17730         this.picker().on('mousedown', this.onMousedown, this);
17731         this.picker().on('click', this.onClick, this);
17732         
17733         this.picker().addClass('datepicker-dropdown');
17734         
17735         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17736             v.setStyle('width', '189px');
17737         });
17738         
17739         this.fillMonths();
17740         
17741         this.update();
17742         
17743         if(this.isInline) {
17744             this.show();
17745         }
17746         
17747     },
17748     
17749     setValue: function(v, suppressEvent)
17750     {   
17751         var o = this.getValue();
17752         
17753         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
17754         
17755         this.update();
17756
17757         if(suppressEvent !== true){
17758             this.fireEvent('select', this, o, v);
17759         }
17760         
17761     },
17762     
17763     getValue: function()
17764     {
17765         return this.value;
17766     },
17767     
17768     onClick: function(e) 
17769     {
17770         e.stopPropagation();
17771         e.preventDefault();
17772         
17773         var target = e.getTarget();
17774         
17775         if(target.nodeName.toLowerCase() === 'i'){
17776             target = Roo.get(target).dom.parentNode;
17777         }
17778         
17779         var nodeName = target.nodeName;
17780         var className = target.className;
17781         var html = target.innerHTML;
17782         
17783         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
17784             return;
17785         }
17786         
17787         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
17788         
17789         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17790         
17791         this.hide();
17792                         
17793     },
17794     
17795     picker : function()
17796     {
17797         return this.pickerEl;
17798     },
17799     
17800     fillMonths: function()
17801     {    
17802         var i = 0;
17803         var months = this.picker().select('>.datepicker-months td', true).first();
17804         
17805         months.dom.innerHTML = '';
17806         
17807         while (i < 12) {
17808             var month = {
17809                 tag: 'span',
17810                 cls: 'month',
17811                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
17812             }
17813             
17814             months.createChild(month);
17815         }
17816         
17817     },
17818     
17819     update: function()
17820     {
17821         var _this = this;
17822         
17823         if(typeof(this.vIndex) == 'undefined' && this.value.length){
17824             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
17825         }
17826         
17827         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
17828             e.removeClass('active');
17829             
17830             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
17831                 e.addClass('active');
17832             }
17833         })
17834     },
17835     
17836     place: function()
17837     {
17838         if(this.isInline) return;
17839         
17840         this.picker().removeClass(['bottom', 'top']);
17841         
17842         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17843             /*
17844              * place to the top of element!
17845              *
17846              */
17847             
17848             this.picker().addClass('top');
17849             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17850             
17851             return;
17852         }
17853         
17854         this.picker().addClass('bottom');
17855         
17856         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17857     },
17858     
17859     onFocus : function()
17860     {
17861         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
17862         this.show();
17863     },
17864     
17865     onBlur : function()
17866     {
17867         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
17868         
17869         var d = this.inputEl().getValue();
17870         
17871         this.setValue(d);
17872                 
17873         this.hide();
17874     },
17875     
17876     show : function()
17877     {
17878         this.picker().show();
17879         this.picker().select('>.datepicker-months', true).first().show();
17880         this.update();
17881         this.place();
17882         
17883         this.fireEvent('show', this, this.date);
17884     },
17885     
17886     hide : function()
17887     {
17888         if(this.isInline) return;
17889         this.picker().hide();
17890         this.fireEvent('hide', this, this.date);
17891         
17892     },
17893     
17894     onMousedown: function(e)
17895     {
17896         e.stopPropagation();
17897         e.preventDefault();
17898     },
17899     
17900     keyup: function(e)
17901     {
17902         Roo.bootstrap.MonthField.superclass.keyup.call(this);
17903         this.update();
17904     },
17905
17906     fireKey: function(e)
17907     {
17908         if (!this.picker().isVisible()){
17909             if (e.keyCode == 27) // allow escape to hide and re-show picker
17910                 this.show();
17911             return;
17912         }
17913         
17914         var dir;
17915         
17916         switch(e.keyCode){
17917             case 27: // escape
17918                 this.hide();
17919                 e.preventDefault();
17920                 break;
17921             case 37: // left
17922             case 39: // right
17923                 dir = e.keyCode == 37 ? -1 : 1;
17924                 
17925                 this.vIndex = this.vIndex + dir;
17926                 
17927                 if(this.vIndex < 0){
17928                     this.vIndex = 0;
17929                 }
17930                 
17931                 if(this.vIndex > 11){
17932                     this.vIndex = 11;
17933                 }
17934                 
17935                 if(isNaN(this.vIndex)){
17936                     this.vIndex = 0;
17937                 }
17938                 
17939                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17940                 
17941                 break;
17942             case 38: // up
17943             case 40: // down
17944                 
17945                 dir = e.keyCode == 38 ? -1 : 1;
17946                 
17947                 this.vIndex = this.vIndex + dir * 4;
17948                 
17949                 if(this.vIndex < 0){
17950                     this.vIndex = 0;
17951                 }
17952                 
17953                 if(this.vIndex > 11){
17954                     this.vIndex = 11;
17955                 }
17956                 
17957                 if(isNaN(this.vIndex)){
17958                     this.vIndex = 0;
17959                 }
17960                 
17961                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17962                 break;
17963                 
17964             case 13: // enter
17965                 
17966                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
17967                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17968                 }
17969                 
17970                 this.hide();
17971                 e.preventDefault();
17972                 break;
17973             case 9: // tab
17974                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
17975                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17976                 }
17977                 this.hide();
17978                 break;
17979             case 16: // shift
17980             case 17: // ctrl
17981             case 18: // alt
17982                 break;
17983             default :
17984                 this.hide();
17985                 
17986         }
17987     },
17988     
17989     remove: function() 
17990     {
17991         this.picker().remove();
17992     }
17993    
17994 });
17995
17996 Roo.apply(Roo.bootstrap.MonthField,  {
17997     
17998     content : {
17999         tag: 'tbody',
18000         cn: [
18001         {
18002             tag: 'tr',
18003             cn: [
18004             {
18005                 tag: 'td',
18006                 colspan: '7'
18007             }
18008             ]
18009         }
18010         ]
18011     },
18012     
18013     dates:{
18014         en: {
18015             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18016             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18017         }
18018     }
18019 });
18020
18021 Roo.apply(Roo.bootstrap.MonthField,  {
18022   
18023     template : {
18024         tag: 'div',
18025         cls: 'datepicker dropdown-menu roo-dynamic',
18026         cn: [
18027             {
18028                 tag: 'div',
18029                 cls: 'datepicker-months',
18030                 cn: [
18031                 {
18032                     tag: 'table',
18033                     cls: 'table-condensed',
18034                     cn:[
18035                         Roo.bootstrap.DateField.content
18036                     ]
18037                 }
18038                 ]
18039             }
18040         ]
18041     }
18042 });
18043
18044  
18045
18046  
18047  /*
18048  * - LGPL
18049  *
18050  * CheckBox
18051  * 
18052  */
18053
18054 /**
18055  * @class Roo.bootstrap.CheckBox
18056  * @extends Roo.bootstrap.Input
18057  * Bootstrap CheckBox class
18058  * 
18059  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18060  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18061  * @cfg {String} boxLabel The text that appears beside the checkbox
18062  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18063  * @cfg {Boolean} checked initnal the element
18064  * @cfg {Boolean} inline inline the element (default false)
18065  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18066  * 
18067  * @constructor
18068  * Create a new CheckBox
18069  * @param {Object} config The config object
18070  */
18071
18072 Roo.bootstrap.CheckBox = function(config){
18073     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18074    
18075     this.addEvents({
18076         /**
18077         * @event check
18078         * Fires when the element is checked or unchecked.
18079         * @param {Roo.bootstrap.CheckBox} this This input
18080         * @param {Boolean} checked The new checked value
18081         */
18082        check : true
18083     });
18084     
18085 };
18086
18087 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
18088   
18089     inputType: 'checkbox',
18090     inputValue: 1,
18091     valueOff: 0,
18092     boxLabel: false,
18093     checked: false,
18094     weight : false,
18095     inline: false,
18096     
18097     getAutoCreate : function()
18098     {
18099         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18100         
18101         var id = Roo.id();
18102         
18103         var cfg = {};
18104         
18105         cfg.cls = 'form-group ' + this.inputType; //input-group
18106         
18107         if(this.inline){
18108             cfg.cls += ' ' + this.inputType + '-inline';
18109         }
18110         
18111         var input =  {
18112             tag: 'input',
18113             id : id,
18114             type : this.inputType,
18115             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18116             cls : 'roo-' + this.inputType, //'form-box',
18117             placeholder : this.placeholder || ''
18118             
18119         };
18120         
18121         if (this.weight) { // Validity check?
18122             cfg.cls += " " + this.inputType + "-" + this.weight;
18123         }
18124         
18125         if (this.disabled) {
18126             input.disabled=true;
18127         }
18128         
18129         if(this.checked){
18130             input.checked = this.checked;
18131         }
18132         
18133         if (this.name) {
18134             input.name = this.name;
18135         }
18136         
18137         if (this.size) {
18138             input.cls += ' input-' + this.size;
18139         }
18140         
18141         var settings=this;
18142         
18143         ['xs','sm','md','lg'].map(function(size){
18144             if (settings[size]) {
18145                 cfg.cls += ' col-' + size + '-' + settings[size];
18146             }
18147         });
18148         
18149         var inputblock = input;
18150          
18151         if (this.before || this.after) {
18152             
18153             inputblock = {
18154                 cls : 'input-group',
18155                 cn :  [] 
18156             };
18157             
18158             if (this.before) {
18159                 inputblock.cn.push({
18160                     tag :'span',
18161                     cls : 'input-group-addon',
18162                     html : this.before
18163                 });
18164             }
18165             
18166             inputblock.cn.push(input);
18167             
18168             if (this.after) {
18169                 inputblock.cn.push({
18170                     tag :'span',
18171                     cls : 'input-group-addon',
18172                     html : this.after
18173                 });
18174             }
18175             
18176         }
18177         
18178         if (align ==='left' && this.fieldLabel.length) {
18179                 Roo.log("left and has label");
18180                 cfg.cn = [
18181                     
18182                     {
18183                         tag: 'label',
18184                         'for' :  id,
18185                         cls : 'control-label col-md-' + this.labelWidth,
18186                         html : this.fieldLabel
18187                         
18188                     },
18189                     {
18190                         cls : "col-md-" + (12 - this.labelWidth), 
18191                         cn: [
18192                             inputblock
18193                         ]
18194                     }
18195                     
18196                 ];
18197         } else if ( this.fieldLabel.length) {
18198                 Roo.log(" label");
18199                 cfg.cn = [
18200                    
18201                     {
18202                         tag: this.boxLabel ? 'span' : 'label',
18203                         'for': id,
18204                         cls: 'control-label box-input-label',
18205                         //cls : 'input-group-addon',
18206                         html : this.fieldLabel
18207                         
18208                     },
18209                     
18210                     inputblock
18211                     
18212                 ];
18213
18214         } else {
18215             
18216                 Roo.log(" no label && no align");
18217                 cfg.cn = [  inputblock ] ;
18218                 
18219                 
18220         }
18221         if(this.boxLabel){
18222              var boxLabelCfg = {
18223                 tag: 'label',
18224                 //'for': id, // box label is handled by onclick - so no for...
18225                 cls: 'box-label',
18226                 html: this.boxLabel
18227             }
18228             
18229             if(this.tooltip){
18230                 boxLabelCfg.tooltip = this.tooltip;
18231             }
18232              
18233             cfg.cn.push(boxLabelCfg);
18234         }
18235         
18236         
18237        
18238         return cfg;
18239         
18240     },
18241     
18242     /**
18243      * return the real input element.
18244      */
18245     inputEl: function ()
18246     {
18247         return this.el.select('input.roo-' + this.inputType,true).first();
18248     },
18249     
18250     labelEl: function()
18251     {
18252         return this.el.select('label.control-label',true).first();
18253     },
18254     /* depricated... */
18255     
18256     label: function()
18257     {
18258         return this.labelEl();
18259     },
18260     
18261     initEvents : function()
18262     {
18263 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18264         
18265         this.inputEl().on('click', this.onClick,  this);
18266         
18267         if (this.boxLabel) { 
18268             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
18269         }
18270         
18271         this.startValue = this.getValue();
18272         
18273         if(this.groupId){
18274             Roo.bootstrap.CheckBox.register(this);
18275         }
18276     },
18277     
18278     onClick : function()
18279     {   
18280         this.setChecked(!this.checked);
18281     },
18282     
18283     setChecked : function(state,suppressEvent)
18284     {
18285         this.startValue = this.getValue();
18286         
18287         if(this.inputType == 'radio'){
18288             
18289             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18290                 e.dom.checked = false;
18291             });
18292             
18293             this.inputEl().dom.checked = true;
18294             
18295             this.inputEl().dom.value = this.inputValue;
18296             
18297             if(suppressEvent !== true){
18298                 this.fireEvent('check', this, true);
18299             }
18300             
18301             this.validate();
18302             
18303             return;
18304         }
18305         
18306         this.checked = state;
18307         
18308         this.inputEl().dom.checked = state;
18309         
18310         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18311         
18312         if(suppressEvent !== true){
18313             this.fireEvent('check', this, state);
18314         }
18315         
18316         this.validate();
18317     },
18318     
18319     getValue : function()
18320     {
18321         if(this.inputType == 'radio'){
18322             return this.getGroupValue();
18323         }
18324         
18325         return this.inputEl().getValue();
18326         
18327     },
18328     
18329     getGroupValue : function()
18330     {
18331         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18332             return '';
18333         }
18334         
18335         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18336     },
18337     
18338     setValue : function(v,suppressEvent)
18339     {
18340         if(this.inputType == 'radio'){
18341             this.setGroupValue(v, suppressEvent);
18342             return;
18343         }
18344         
18345         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18346         
18347         this.validate();
18348     },
18349     
18350     setGroupValue : function(v, suppressEvent)
18351     {
18352         this.startValue = this.getValue();
18353         
18354         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18355             e.dom.checked = false;
18356             
18357             if(e.dom.value == v){
18358                 e.dom.checked = true;
18359             }
18360         });
18361         
18362         if(suppressEvent !== true){
18363             this.fireEvent('check', this, true);
18364         }
18365
18366         this.validate();
18367         
18368         return;
18369     },
18370     
18371     validate : function()
18372     {
18373         if(
18374                 this.disabled || 
18375                 (this.inputType == 'radio' && this.validateRadio()) ||
18376                 (this.inputType == 'checkbox' && this.validateCheckbox())
18377         ){
18378             this.markValid();
18379             return true;
18380         }
18381         
18382         this.markInvalid();
18383         return false;
18384     },
18385     
18386     validateRadio : function()
18387     {
18388         var valid = false;
18389         
18390         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18391             if(!e.dom.checked){
18392                 return;
18393             }
18394             
18395             valid = true;
18396             
18397             return false;
18398         });
18399         
18400         return valid;
18401     },
18402     
18403     validateCheckbox : function()
18404     {
18405         if(!this.groupId){
18406             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18407         }
18408         
18409         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18410         
18411         if(!group){
18412             return false;
18413         }
18414         
18415         var r = false;
18416         
18417         for(var i in group){
18418             if(r){
18419                 break;
18420             }
18421             
18422             r = (group[i].getValue() == group[i].inputValue) ? true : false;
18423         }
18424         
18425         return r;
18426     },
18427     
18428     /**
18429      * Mark this field as valid
18430      */
18431     markValid : function()
18432     {
18433         if(this.allowBlank){
18434             return;
18435         }
18436         
18437         var _this = this;
18438         
18439         this.fireEvent('valid', this);
18440         
18441         if(this.inputType == 'radio'){
18442             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18443                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18444                 e.findParent('.form-group', false, true).addClass(_this.validClass);
18445             });
18446             
18447             return;
18448         }
18449         
18450         if(!this.groupId){
18451             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18452             this.el.findParent('.form-group', false, true).addClass(this.validClass);
18453             return;
18454         }
18455         
18456         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18457             
18458         if(!group){
18459             return;
18460         }
18461         
18462         for(var i in group){
18463             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18464             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
18465         }
18466     },
18467     
18468      /**
18469      * Mark this field as invalid
18470      * @param {String} msg The validation message
18471      */
18472     markInvalid : function(msg)
18473     {
18474         if(this.allowBlank){
18475             return;
18476         }
18477         
18478         var _this = this;
18479         
18480         this.fireEvent('invalid', this, msg);
18481         
18482         if(this.inputType == 'radio'){
18483             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18484                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18485                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
18486             });
18487             
18488             return;
18489         }
18490         
18491         if(!this.groupId){
18492             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18493             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
18494             return;
18495         }
18496         
18497         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18498             
18499         if(!group){
18500             return;
18501         }
18502         
18503         for(var i in group){
18504             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18505             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
18506         }
18507         
18508     }
18509     
18510 });
18511
18512 Roo.apply(Roo.bootstrap.CheckBox, {
18513     
18514     groups: {},
18515     
18516      /**
18517     * register a CheckBox Group
18518     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
18519     */
18520     register : function(checkbox)
18521     {
18522         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
18523             this.groups[checkbox.groupId] = {};
18524         }
18525         
18526         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
18527             return;
18528         }
18529         
18530         this.groups[checkbox.groupId][checkbox.name] = checkbox;
18531         
18532     },
18533     /**
18534     * fetch a CheckBox Group based on the group ID
18535     * @param {string} the group ID
18536     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
18537     */
18538     get: function(groupId) {
18539         if (typeof(this.groups[groupId]) == 'undefined') {
18540             return false;
18541         }
18542         
18543         return this.groups[groupId] ;
18544     }
18545     
18546     
18547 });
18548 /*
18549  * - LGPL
18550  *
18551  * Radio
18552  *
18553  *
18554  * not inline
18555  *<div class="radio">
18556   <label>
18557     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
18558     Option one is this and that&mdash;be sure to include why it's great
18559   </label>
18560 </div>
18561  *
18562  *
18563  *inline
18564  *<span>
18565  *<label class="radio-inline">fieldLabel</label>
18566  *<label class="radio-inline">
18567   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
18568 </label>
18569 <span>
18570  * 
18571  * 
18572  */
18573
18574 /**
18575  * @class Roo.bootstrap.Radio
18576  * @extends Roo.bootstrap.CheckBox
18577  * Bootstrap Radio class
18578
18579  * @constructor
18580  * Create a new Radio
18581  * @param {Object} config The config object
18582  */
18583
18584 Roo.bootstrap.Radio = function(config){
18585     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
18586    
18587 };
18588
18589 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
18590     
18591     inputType: 'radio',
18592     inputValue: '',
18593     valueOff: '',
18594     
18595     getAutoCreate : function()
18596     {
18597         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18598         align = align || 'left'; // default...
18599         
18600         
18601         
18602         var id = Roo.id();
18603         
18604         var cfg = {
18605                 tag : this.inline ? 'span' : 'div',
18606                 cls : '',
18607                 cn : []
18608         };
18609         
18610         var inline = this.inline ? ' radio-inline' : '';
18611         
18612         var lbl = {
18613                 tag: 'label' ,
18614                 // does not need for, as we wrap the input with it..
18615                 'for' : id,
18616                 cls : 'control-label box-label' + inline,
18617                 cn : []
18618         };
18619         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
18620         
18621         var fieldLabel = {
18622             tag: 'label' ,
18623             //cls : 'control-label' + inline,
18624             html : this.fieldLabel,
18625             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
18626         };
18627         
18628  
18629         
18630         
18631         var input =  {
18632             tag: 'input',
18633             id : id,
18634             type : this.inputType,
18635             //value : (!this.checked) ? this.valueOff : this.inputValue,
18636             value : this.inputValue,
18637             cls : 'roo-radio',
18638             placeholder : this.placeholder || '' // ?? needed????
18639             
18640         };
18641         if (this.weight) { // Validity check?
18642             input.cls += " radio-" + this.weight;
18643         }
18644         if (this.disabled) {
18645             input.disabled=true;
18646         }
18647         
18648         if(this.checked){
18649             input.checked = this.checked;
18650         }
18651         
18652         if (this.name) {
18653             input.name = this.name;
18654         }
18655         
18656         if (this.size) {
18657             input.cls += ' input-' + this.size;
18658         }
18659         
18660         //?? can span's inline have a width??
18661         
18662         var settings=this;
18663         ['xs','sm','md','lg'].map(function(size){
18664             if (settings[size]) {
18665                 cfg.cls += ' col-' + size + '-' + settings[size];
18666             }
18667         });
18668         
18669         var inputblock = input;
18670         
18671         if (this.before || this.after) {
18672             
18673             inputblock = {
18674                 cls : 'input-group',
18675                 tag : 'span',
18676                 cn :  [] 
18677             };
18678             if (this.before) {
18679                 inputblock.cn.push({
18680                     tag :'span',
18681                     cls : 'input-group-addon',
18682                     html : this.before
18683                 });
18684             }
18685             inputblock.cn.push(input);
18686             if (this.after) {
18687                 inputblock.cn.push({
18688                     tag :'span',
18689                     cls : 'input-group-addon',
18690                     html : this.after
18691                 });
18692             }
18693             
18694         };
18695         
18696         
18697         if (this.fieldLabel && this.fieldLabel.length) {
18698             cfg.cn.push(fieldLabel);
18699         }
18700        
18701         // normal bootstrap puts the input inside the label.
18702         // however with our styled version - it has to go after the input.
18703        
18704         //lbl.cn.push(inputblock);
18705         
18706         var lblwrap =  {
18707             tag: 'span',
18708             cls: 'radio' + inline,
18709             cn: [
18710                 inputblock,
18711                 lbl
18712             ]
18713         };
18714         
18715         cfg.cn.push( lblwrap);
18716         
18717         if(this.boxLabel){
18718             lbl.cn.push({
18719                 tag: 'span',
18720                 html: this.boxLabel
18721             })
18722         }
18723          
18724         
18725         return cfg;
18726         
18727     },
18728     
18729     initEvents : function()
18730     {
18731 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18732         
18733         this.inputEl().on('click', this.onClick,  this);
18734         if (this.boxLabel) {
18735             Roo.log('find label')
18736             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
18737         }
18738         
18739     },
18740     
18741     inputEl: function ()
18742     {
18743         return this.el.select('input.roo-radio',true).first();
18744     },
18745     onClick : function()
18746     {   
18747         Roo.log("click");
18748         this.setChecked(true);
18749     },
18750     
18751     setChecked : function(state,suppressEvent)
18752     {
18753         if(state){
18754             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18755                 v.dom.checked = false;
18756             });
18757         }
18758         Roo.log(this.inputEl().dom);
18759         this.checked = state;
18760         this.inputEl().dom.checked = state;
18761         
18762         if(suppressEvent !== true){
18763             this.fireEvent('check', this, state);
18764         }
18765         
18766         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18767         
18768     },
18769     
18770     getGroupValue : function()
18771     {
18772         var value = '';
18773         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18774             if(v.dom.checked == true){
18775                 value = v.dom.value;
18776             }
18777         });
18778         
18779         return value;
18780     },
18781     
18782     /**
18783      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
18784      * @return {Mixed} value The field value
18785      */
18786     getValue : function(){
18787         return this.getGroupValue();
18788     }
18789     
18790 });
18791
18792  
18793 //<script type="text/javascript">
18794
18795 /*
18796  * Based  Ext JS Library 1.1.1
18797  * Copyright(c) 2006-2007, Ext JS, LLC.
18798  * LGPL
18799  *
18800  */
18801  
18802 /**
18803  * @class Roo.HtmlEditorCore
18804  * @extends Roo.Component
18805  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
18806  *
18807  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
18808  */
18809
18810 Roo.HtmlEditorCore = function(config){
18811     
18812     
18813     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
18814     
18815     
18816     this.addEvents({
18817         /**
18818          * @event initialize
18819          * Fires when the editor is fully initialized (including the iframe)
18820          * @param {Roo.HtmlEditorCore} this
18821          */
18822         initialize: true,
18823         /**
18824          * @event activate
18825          * Fires when the editor is first receives the focus. Any insertion must wait
18826          * until after this event.
18827          * @param {Roo.HtmlEditorCore} this
18828          */
18829         activate: true,
18830          /**
18831          * @event beforesync
18832          * Fires before the textarea is updated with content from the editor iframe. Return false
18833          * to cancel the sync.
18834          * @param {Roo.HtmlEditorCore} this
18835          * @param {String} html
18836          */
18837         beforesync: true,
18838          /**
18839          * @event beforepush
18840          * Fires before the iframe editor is updated with content from the textarea. Return false
18841          * to cancel the push.
18842          * @param {Roo.HtmlEditorCore} this
18843          * @param {String} html
18844          */
18845         beforepush: true,
18846          /**
18847          * @event sync
18848          * Fires when the textarea is updated with content from the editor iframe.
18849          * @param {Roo.HtmlEditorCore} this
18850          * @param {String} html
18851          */
18852         sync: true,
18853          /**
18854          * @event push
18855          * Fires when the iframe editor is updated with content from the textarea.
18856          * @param {Roo.HtmlEditorCore} this
18857          * @param {String} html
18858          */
18859         push: true,
18860         
18861         /**
18862          * @event editorevent
18863          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
18864          * @param {Roo.HtmlEditorCore} this
18865          */
18866         editorevent: true
18867         
18868     });
18869     
18870     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
18871     
18872     // defaults : white / black...
18873     this.applyBlacklists();
18874     
18875     
18876     
18877 };
18878
18879
18880 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
18881
18882
18883      /**
18884      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
18885      */
18886     
18887     owner : false,
18888     
18889      /**
18890      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
18891      *                        Roo.resizable.
18892      */
18893     resizable : false,
18894      /**
18895      * @cfg {Number} height (in pixels)
18896      */   
18897     height: 300,
18898    /**
18899      * @cfg {Number} width (in pixels)
18900      */   
18901     width: 500,
18902     
18903     /**
18904      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
18905      * 
18906      */
18907     stylesheets: false,
18908     
18909     // id of frame..
18910     frameId: false,
18911     
18912     // private properties
18913     validationEvent : false,
18914     deferHeight: true,
18915     initialized : false,
18916     activated : false,
18917     sourceEditMode : false,
18918     onFocus : Roo.emptyFn,
18919     iframePad:3,
18920     hideMode:'offsets',
18921     
18922     clearUp: true,
18923     
18924     // blacklist + whitelisted elements..
18925     black: false,
18926     white: false,
18927      
18928     
18929
18930     /**
18931      * Protected method that will not generally be called directly. It
18932      * is called when the editor initializes the iframe with HTML contents. Override this method if you
18933      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
18934      */
18935     getDocMarkup : function(){
18936         // body styles..
18937         var st = '';
18938         
18939         // inherit styels from page...?? 
18940         if (this.stylesheets === false) {
18941             
18942             Roo.get(document.head).select('style').each(function(node) {
18943                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
18944             });
18945             
18946             Roo.get(document.head).select('link').each(function(node) { 
18947                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
18948             });
18949             
18950         } else if (!this.stylesheets.length) {
18951                 // simple..
18952                 st = '<style type="text/css">' +
18953                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
18954                    '</style>';
18955         } else { 
18956             
18957         }
18958         
18959         st +=  '<style type="text/css">' +
18960             'IMG { cursor: pointer } ' +
18961         '</style>';
18962
18963         
18964         return '<html><head>' + st  +
18965             //<style type="text/css">' +
18966             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
18967             //'</style>' +
18968             ' </head><body class="roo-htmleditor-body"></body></html>';
18969     },
18970
18971     // private
18972     onRender : function(ct, position)
18973     {
18974         var _t = this;
18975         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
18976         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
18977         
18978         
18979         this.el.dom.style.border = '0 none';
18980         this.el.dom.setAttribute('tabIndex', -1);
18981         this.el.addClass('x-hidden hide');
18982         
18983         
18984         
18985         if(Roo.isIE){ // fix IE 1px bogus margin
18986             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
18987         }
18988        
18989         
18990         this.frameId = Roo.id();
18991         
18992          
18993         
18994         var iframe = this.owner.wrap.createChild({
18995             tag: 'iframe',
18996             cls: 'form-control', // bootstrap..
18997             id: this.frameId,
18998             name: this.frameId,
18999             frameBorder : 'no',
19000             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
19001         }, this.el
19002         );
19003         
19004         
19005         this.iframe = iframe.dom;
19006
19007          this.assignDocWin();
19008         
19009         this.doc.designMode = 'on';
19010        
19011         this.doc.open();
19012         this.doc.write(this.getDocMarkup());
19013         this.doc.close();
19014
19015         
19016         var task = { // must defer to wait for browser to be ready
19017             run : function(){
19018                 //console.log("run task?" + this.doc.readyState);
19019                 this.assignDocWin();
19020                 if(this.doc.body || this.doc.readyState == 'complete'){
19021                     try {
19022                         this.doc.designMode="on";
19023                     } catch (e) {
19024                         return;
19025                     }
19026                     Roo.TaskMgr.stop(task);
19027                     this.initEditor.defer(10, this);
19028                 }
19029             },
19030             interval : 10,
19031             duration: 10000,
19032             scope: this
19033         };
19034         Roo.TaskMgr.start(task);
19035
19036     },
19037
19038     // private
19039     onResize : function(w, h)
19040     {
19041          Roo.log('resize: ' +w + ',' + h );
19042         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19043         if(!this.iframe){
19044             return;
19045         }
19046         if(typeof w == 'number'){
19047             
19048             this.iframe.style.width = w + 'px';
19049         }
19050         if(typeof h == 'number'){
19051             
19052             this.iframe.style.height = h + 'px';
19053             if(this.doc){
19054                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19055             }
19056         }
19057         
19058     },
19059
19060     /**
19061      * Toggles the editor between standard and source edit mode.
19062      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19063      */
19064     toggleSourceEdit : function(sourceEditMode){
19065         
19066         this.sourceEditMode = sourceEditMode === true;
19067         
19068         if(this.sourceEditMode){
19069  
19070             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
19071             
19072         }else{
19073             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19074             //this.iframe.className = '';
19075             this.deferFocus();
19076         }
19077         //this.setSize(this.owner.wrap.getSize());
19078         //this.fireEvent('editmodechange', this, this.sourceEditMode);
19079     },
19080
19081     
19082   
19083
19084     /**
19085      * Protected method that will not generally be called directly. If you need/want
19086      * custom HTML cleanup, this is the method you should override.
19087      * @param {String} html The HTML to be cleaned
19088      * return {String} The cleaned HTML
19089      */
19090     cleanHtml : function(html){
19091         html = String(html);
19092         if(html.length > 5){
19093             if(Roo.isSafari){ // strip safari nonsense
19094                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19095             }
19096         }
19097         if(html == '&nbsp;'){
19098             html = '';
19099         }
19100         return html;
19101     },
19102
19103     /**
19104      * HTML Editor -> Textarea
19105      * Protected method that will not generally be called directly. Syncs the contents
19106      * of the editor iframe with the textarea.
19107      */
19108     syncValue : function(){
19109         if(this.initialized){
19110             var bd = (this.doc.body || this.doc.documentElement);
19111             //this.cleanUpPaste(); -- this is done else where and causes havoc..
19112             var html = bd.innerHTML;
19113             if(Roo.isSafari){
19114                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19115                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19116                 if(m && m[1]){
19117                     html = '<div style="'+m[0]+'">' + html + '</div>';
19118                 }
19119             }
19120             html = this.cleanHtml(html);
19121             // fix up the special chars.. normaly like back quotes in word...
19122             // however we do not want to do this with chinese..
19123             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19124                 var cc = b.charCodeAt();
19125                 if (
19126                     (cc >= 0x4E00 && cc < 0xA000 ) ||
19127                     (cc >= 0x3400 && cc < 0x4E00 ) ||
19128                     (cc >= 0xf900 && cc < 0xfb00 )
19129                 ) {
19130                         return b;
19131                 }
19132                 return "&#"+cc+";" 
19133             });
19134             if(this.owner.fireEvent('beforesync', this, html) !== false){
19135                 this.el.dom.value = html;
19136                 this.owner.fireEvent('sync', this, html);
19137             }
19138         }
19139     },
19140
19141     /**
19142      * Protected method that will not generally be called directly. Pushes the value of the textarea
19143      * into the iframe editor.
19144      */
19145     pushValue : function(){
19146         if(this.initialized){
19147             var v = this.el.dom.value.trim();
19148             
19149 //            if(v.length < 1){
19150 //                v = '&#160;';
19151 //            }
19152             
19153             if(this.owner.fireEvent('beforepush', this, v) !== false){
19154                 var d = (this.doc.body || this.doc.documentElement);
19155                 d.innerHTML = v;
19156                 this.cleanUpPaste();
19157                 this.el.dom.value = d.innerHTML;
19158                 this.owner.fireEvent('push', this, v);
19159             }
19160         }
19161     },
19162
19163     // private
19164     deferFocus : function(){
19165         this.focus.defer(10, this);
19166     },
19167
19168     // doc'ed in Field
19169     focus : function(){
19170         if(this.win && !this.sourceEditMode){
19171             this.win.focus();
19172         }else{
19173             this.el.focus();
19174         }
19175     },
19176     
19177     assignDocWin: function()
19178     {
19179         var iframe = this.iframe;
19180         
19181          if(Roo.isIE){
19182             this.doc = iframe.contentWindow.document;
19183             this.win = iframe.contentWindow;
19184         } else {
19185 //            if (!Roo.get(this.frameId)) {
19186 //                return;
19187 //            }
19188 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19189 //            this.win = Roo.get(this.frameId).dom.contentWindow;
19190             
19191             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19192                 return;
19193             }
19194             
19195             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19196             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19197         }
19198     },
19199     
19200     // private
19201     initEditor : function(){
19202         //console.log("INIT EDITOR");
19203         this.assignDocWin();
19204         
19205         
19206         
19207         this.doc.designMode="on";
19208         this.doc.open();
19209         this.doc.write(this.getDocMarkup());
19210         this.doc.close();
19211         
19212         var dbody = (this.doc.body || this.doc.documentElement);
19213         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19214         // this copies styles from the containing element into thsi one..
19215         // not sure why we need all of this..
19216         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19217         
19218         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19219         //ss['background-attachment'] = 'fixed'; // w3c
19220         dbody.bgProperties = 'fixed'; // ie
19221         //Roo.DomHelper.applyStyles(dbody, ss);
19222         Roo.EventManager.on(this.doc, {
19223             //'mousedown': this.onEditorEvent,
19224             'mouseup': this.onEditorEvent,
19225             'dblclick': this.onEditorEvent,
19226             'click': this.onEditorEvent,
19227             'keyup': this.onEditorEvent,
19228             buffer:100,
19229             scope: this
19230         });
19231         if(Roo.isGecko){
19232             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19233         }
19234         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19235             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19236         }
19237         this.initialized = true;
19238
19239         this.owner.fireEvent('initialize', this);
19240         this.pushValue();
19241     },
19242
19243     // private
19244     onDestroy : function(){
19245         
19246         
19247         
19248         if(this.rendered){
19249             
19250             //for (var i =0; i < this.toolbars.length;i++) {
19251             //    // fixme - ask toolbars for heights?
19252             //    this.toolbars[i].onDestroy();
19253            // }
19254             
19255             //this.wrap.dom.innerHTML = '';
19256             //this.wrap.remove();
19257         }
19258     },
19259
19260     // private
19261     onFirstFocus : function(){
19262         
19263         this.assignDocWin();
19264         
19265         
19266         this.activated = true;
19267          
19268     
19269         if(Roo.isGecko){ // prevent silly gecko errors
19270             this.win.focus();
19271             var s = this.win.getSelection();
19272             if(!s.focusNode || s.focusNode.nodeType != 3){
19273                 var r = s.getRangeAt(0);
19274                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19275                 r.collapse(true);
19276                 this.deferFocus();
19277             }
19278             try{
19279                 this.execCmd('useCSS', true);
19280                 this.execCmd('styleWithCSS', false);
19281             }catch(e){}
19282         }
19283         this.owner.fireEvent('activate', this);
19284     },
19285
19286     // private
19287     adjustFont: function(btn){
19288         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19289         //if(Roo.isSafari){ // safari
19290         //    adjust *= 2;
19291        // }
19292         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19293         if(Roo.isSafari){ // safari
19294             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19295             v =  (v < 10) ? 10 : v;
19296             v =  (v > 48) ? 48 : v;
19297             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19298             
19299         }
19300         
19301         
19302         v = Math.max(1, v+adjust);
19303         
19304         this.execCmd('FontSize', v  );
19305     },
19306
19307     onEditorEvent : function(e)
19308     {
19309         this.owner.fireEvent('editorevent', this, e);
19310       //  this.updateToolbar();
19311         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19312     },
19313
19314     insertTag : function(tg)
19315     {
19316         // could be a bit smarter... -> wrap the current selected tRoo..
19317         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19318             
19319             range = this.createRange(this.getSelection());
19320             var wrappingNode = this.doc.createElement(tg.toLowerCase());
19321             wrappingNode.appendChild(range.extractContents());
19322             range.insertNode(wrappingNode);
19323
19324             return;
19325             
19326             
19327             
19328         }
19329         this.execCmd("formatblock",   tg);
19330         
19331     },
19332     
19333     insertText : function(txt)
19334     {
19335         
19336         
19337         var range = this.createRange();
19338         range.deleteContents();
19339                //alert(Sender.getAttribute('label'));
19340                
19341         range.insertNode(this.doc.createTextNode(txt));
19342     } ,
19343     
19344      
19345
19346     /**
19347      * Executes a Midas editor command on the editor document and performs necessary focus and
19348      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19349      * @param {String} cmd The Midas command
19350      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19351      */
19352     relayCmd : function(cmd, value){
19353         this.win.focus();
19354         this.execCmd(cmd, value);
19355         this.owner.fireEvent('editorevent', this);
19356         //this.updateToolbar();
19357         this.owner.deferFocus();
19358     },
19359
19360     /**
19361      * Executes a Midas editor command directly on the editor document.
19362      * For visual commands, you should use {@link #relayCmd} instead.
19363      * <b>This should only be called after the editor is initialized.</b>
19364      * @param {String} cmd The Midas command
19365      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19366      */
19367     execCmd : function(cmd, value){
19368         this.doc.execCommand(cmd, false, value === undefined ? null : value);
19369         this.syncValue();
19370     },
19371  
19372  
19373    
19374     /**
19375      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19376      * to insert tRoo.
19377      * @param {String} text | dom node.. 
19378      */
19379     insertAtCursor : function(text)
19380     {
19381         
19382         
19383         
19384         if(!this.activated){
19385             return;
19386         }
19387         /*
19388         if(Roo.isIE){
19389             this.win.focus();
19390             var r = this.doc.selection.createRange();
19391             if(r){
19392                 r.collapse(true);
19393                 r.pasteHTML(text);
19394                 this.syncValue();
19395                 this.deferFocus();
19396             
19397             }
19398             return;
19399         }
19400         */
19401         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19402             this.win.focus();
19403             
19404             
19405             // from jquery ui (MIT licenced)
19406             var range, node;
19407             var win = this.win;
19408             
19409             if (win.getSelection && win.getSelection().getRangeAt) {
19410                 range = win.getSelection().getRangeAt(0);
19411                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19412                 range.insertNode(node);
19413             } else if (win.document.selection && win.document.selection.createRange) {
19414                 // no firefox support
19415                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19416                 win.document.selection.createRange().pasteHTML(txt);
19417             } else {
19418                 // no firefox support
19419                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19420                 this.execCmd('InsertHTML', txt);
19421             } 
19422             
19423             this.syncValue();
19424             
19425             this.deferFocus();
19426         }
19427     },
19428  // private
19429     mozKeyPress : function(e){
19430         if(e.ctrlKey){
19431             var c = e.getCharCode(), cmd;
19432           
19433             if(c > 0){
19434                 c = String.fromCharCode(c).toLowerCase();
19435                 switch(c){
19436                     case 'b':
19437                         cmd = 'bold';
19438                         break;
19439                     case 'i':
19440                         cmd = 'italic';
19441                         break;
19442                     
19443                     case 'u':
19444                         cmd = 'underline';
19445                         break;
19446                     
19447                     case 'v':
19448                         this.cleanUpPaste.defer(100, this);
19449                         return;
19450                         
19451                 }
19452                 if(cmd){
19453                     this.win.focus();
19454                     this.execCmd(cmd);
19455                     this.deferFocus();
19456                     e.preventDefault();
19457                 }
19458                 
19459             }
19460         }
19461     },
19462
19463     // private
19464     fixKeys : function(){ // load time branching for fastest keydown performance
19465         if(Roo.isIE){
19466             return function(e){
19467                 var k = e.getKey(), r;
19468                 if(k == e.TAB){
19469                     e.stopEvent();
19470                     r = this.doc.selection.createRange();
19471                     if(r){
19472                         r.collapse(true);
19473                         r.pasteHTML('&#160;&#160;&#160;&#160;');
19474                         this.deferFocus();
19475                     }
19476                     return;
19477                 }
19478                 
19479                 if(k == e.ENTER){
19480                     r = this.doc.selection.createRange();
19481                     if(r){
19482                         var target = r.parentElement();
19483                         if(!target || target.tagName.toLowerCase() != 'li'){
19484                             e.stopEvent();
19485                             r.pasteHTML('<br />');
19486                             r.collapse(false);
19487                             r.select();
19488                         }
19489                     }
19490                 }
19491                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19492                     this.cleanUpPaste.defer(100, this);
19493                     return;
19494                 }
19495                 
19496                 
19497             };
19498         }else if(Roo.isOpera){
19499             return function(e){
19500                 var k = e.getKey();
19501                 if(k == e.TAB){
19502                     e.stopEvent();
19503                     this.win.focus();
19504                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
19505                     this.deferFocus();
19506                 }
19507                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19508                     this.cleanUpPaste.defer(100, this);
19509                     return;
19510                 }
19511                 
19512             };
19513         }else if(Roo.isSafari){
19514             return function(e){
19515                 var k = e.getKey();
19516                 
19517                 if(k == e.TAB){
19518                     e.stopEvent();
19519                     this.execCmd('InsertText','\t');
19520                     this.deferFocus();
19521                     return;
19522                 }
19523                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19524                     this.cleanUpPaste.defer(100, this);
19525                     return;
19526                 }
19527                 
19528              };
19529         }
19530     }(),
19531     
19532     getAllAncestors: function()
19533     {
19534         var p = this.getSelectedNode();
19535         var a = [];
19536         if (!p) {
19537             a.push(p); // push blank onto stack..
19538             p = this.getParentElement();
19539         }
19540         
19541         
19542         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
19543             a.push(p);
19544             p = p.parentNode;
19545         }
19546         a.push(this.doc.body);
19547         return a;
19548     },
19549     lastSel : false,
19550     lastSelNode : false,
19551     
19552     
19553     getSelection : function() 
19554     {
19555         this.assignDocWin();
19556         return Roo.isIE ? this.doc.selection : this.win.getSelection();
19557     },
19558     
19559     getSelectedNode: function() 
19560     {
19561         // this may only work on Gecko!!!
19562         
19563         // should we cache this!!!!
19564         
19565         
19566         
19567          
19568         var range = this.createRange(this.getSelection()).cloneRange();
19569         
19570         if (Roo.isIE) {
19571             var parent = range.parentElement();
19572             while (true) {
19573                 var testRange = range.duplicate();
19574                 testRange.moveToElementText(parent);
19575                 if (testRange.inRange(range)) {
19576                     break;
19577                 }
19578                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
19579                     break;
19580                 }
19581                 parent = parent.parentElement;
19582             }
19583             return parent;
19584         }
19585         
19586         // is ancestor a text element.
19587         var ac =  range.commonAncestorContainer;
19588         if (ac.nodeType == 3) {
19589             ac = ac.parentNode;
19590         }
19591         
19592         var ar = ac.childNodes;
19593          
19594         var nodes = [];
19595         var other_nodes = [];
19596         var has_other_nodes = false;
19597         for (var i=0;i<ar.length;i++) {
19598             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
19599                 continue;
19600             }
19601             // fullly contained node.
19602             
19603             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
19604                 nodes.push(ar[i]);
19605                 continue;
19606             }
19607             
19608             // probably selected..
19609             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
19610                 other_nodes.push(ar[i]);
19611                 continue;
19612             }
19613             // outer..
19614             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
19615                 continue;
19616             }
19617             
19618             
19619             has_other_nodes = true;
19620         }
19621         if (!nodes.length && other_nodes.length) {
19622             nodes= other_nodes;
19623         }
19624         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
19625             return false;
19626         }
19627         
19628         return nodes[0];
19629     },
19630     createRange: function(sel)
19631     {
19632         // this has strange effects when using with 
19633         // top toolbar - not sure if it's a great idea.
19634         //this.editor.contentWindow.focus();
19635         if (typeof sel != "undefined") {
19636             try {
19637                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
19638             } catch(e) {
19639                 return this.doc.createRange();
19640             }
19641         } else {
19642             return this.doc.createRange();
19643         }
19644     },
19645     getParentElement: function()
19646     {
19647         
19648         this.assignDocWin();
19649         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
19650         
19651         var range = this.createRange(sel);
19652          
19653         try {
19654             var p = range.commonAncestorContainer;
19655             while (p.nodeType == 3) { // text node
19656                 p = p.parentNode;
19657             }
19658             return p;
19659         } catch (e) {
19660             return null;
19661         }
19662     
19663     },
19664     /***
19665      *
19666      * Range intersection.. the hard stuff...
19667      *  '-1' = before
19668      *  '0' = hits..
19669      *  '1' = after.
19670      *         [ -- selected range --- ]
19671      *   [fail]                        [fail]
19672      *
19673      *    basically..
19674      *      if end is before start or  hits it. fail.
19675      *      if start is after end or hits it fail.
19676      *
19677      *   if either hits (but other is outside. - then it's not 
19678      *   
19679      *    
19680      **/
19681     
19682     
19683     // @see http://www.thismuchiknow.co.uk/?p=64.
19684     rangeIntersectsNode : function(range, node)
19685     {
19686         var nodeRange = node.ownerDocument.createRange();
19687         try {
19688             nodeRange.selectNode(node);
19689         } catch (e) {
19690             nodeRange.selectNodeContents(node);
19691         }
19692     
19693         var rangeStartRange = range.cloneRange();
19694         rangeStartRange.collapse(true);
19695     
19696         var rangeEndRange = range.cloneRange();
19697         rangeEndRange.collapse(false);
19698     
19699         var nodeStartRange = nodeRange.cloneRange();
19700         nodeStartRange.collapse(true);
19701     
19702         var nodeEndRange = nodeRange.cloneRange();
19703         nodeEndRange.collapse(false);
19704     
19705         return rangeStartRange.compareBoundaryPoints(
19706                  Range.START_TO_START, nodeEndRange) == -1 &&
19707                rangeEndRange.compareBoundaryPoints(
19708                  Range.START_TO_START, nodeStartRange) == 1;
19709         
19710          
19711     },
19712     rangeCompareNode : function(range, node)
19713     {
19714         var nodeRange = node.ownerDocument.createRange();
19715         try {
19716             nodeRange.selectNode(node);
19717         } catch (e) {
19718             nodeRange.selectNodeContents(node);
19719         }
19720         
19721         
19722         range.collapse(true);
19723     
19724         nodeRange.collapse(true);
19725      
19726         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
19727         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
19728          
19729         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
19730         
19731         var nodeIsBefore   =  ss == 1;
19732         var nodeIsAfter    = ee == -1;
19733         
19734         if (nodeIsBefore && nodeIsAfter)
19735             return 0; // outer
19736         if (!nodeIsBefore && nodeIsAfter)
19737             return 1; //right trailed.
19738         
19739         if (nodeIsBefore && !nodeIsAfter)
19740             return 2;  // left trailed.
19741         // fully contined.
19742         return 3;
19743     },
19744
19745     // private? - in a new class?
19746     cleanUpPaste :  function()
19747     {
19748         // cleans up the whole document..
19749         Roo.log('cleanuppaste');
19750         
19751         this.cleanUpChildren(this.doc.body);
19752         var clean = this.cleanWordChars(this.doc.body.innerHTML);
19753         if (clean != this.doc.body.innerHTML) {
19754             this.doc.body.innerHTML = clean;
19755         }
19756         
19757     },
19758     
19759     cleanWordChars : function(input) {// change the chars to hex code
19760         var he = Roo.HtmlEditorCore;
19761         
19762         var output = input;
19763         Roo.each(he.swapCodes, function(sw) { 
19764             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
19765             
19766             output = output.replace(swapper, sw[1]);
19767         });
19768         
19769         return output;
19770     },
19771     
19772     
19773     cleanUpChildren : function (n)
19774     {
19775         if (!n.childNodes.length) {
19776             return;
19777         }
19778         for (var i = n.childNodes.length-1; i > -1 ; i--) {
19779            this.cleanUpChild(n.childNodes[i]);
19780         }
19781     },
19782     
19783     
19784         
19785     
19786     cleanUpChild : function (node)
19787     {
19788         var ed = this;
19789         //console.log(node);
19790         if (node.nodeName == "#text") {
19791             // clean up silly Windows -- stuff?
19792             return; 
19793         }
19794         if (node.nodeName == "#comment") {
19795             node.parentNode.removeChild(node);
19796             // clean up silly Windows -- stuff?
19797             return; 
19798         }
19799         var lcname = node.tagName.toLowerCase();
19800         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
19801         // whitelist of tags..
19802         
19803         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
19804             // remove node.
19805             node.parentNode.removeChild(node);
19806             return;
19807             
19808         }
19809         
19810         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
19811         
19812         // remove <a name=....> as rendering on yahoo mailer is borked with this.
19813         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
19814         
19815         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
19816         //    remove_keep_children = true;
19817         //}
19818         
19819         if (remove_keep_children) {
19820             this.cleanUpChildren(node);
19821             // inserts everything just before this node...
19822             while (node.childNodes.length) {
19823                 var cn = node.childNodes[0];
19824                 node.removeChild(cn);
19825                 node.parentNode.insertBefore(cn, node);
19826             }
19827             node.parentNode.removeChild(node);
19828             return;
19829         }
19830         
19831         if (!node.attributes || !node.attributes.length) {
19832             this.cleanUpChildren(node);
19833             return;
19834         }
19835         
19836         function cleanAttr(n,v)
19837         {
19838             
19839             if (v.match(/^\./) || v.match(/^\//)) {
19840                 return;
19841             }
19842             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
19843                 return;
19844             }
19845             if (v.match(/^#/)) {
19846                 return;
19847             }
19848 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
19849             node.removeAttribute(n);
19850             
19851         }
19852         
19853         var cwhite = this.cwhite;
19854         var cblack = this.cblack;
19855             
19856         function cleanStyle(n,v)
19857         {
19858             if (v.match(/expression/)) { //XSS?? should we even bother..
19859                 node.removeAttribute(n);
19860                 return;
19861             }
19862             
19863             var parts = v.split(/;/);
19864             var clean = [];
19865             
19866             Roo.each(parts, function(p) {
19867                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
19868                 if (!p.length) {
19869                     return true;
19870                 }
19871                 var l = p.split(':').shift().replace(/\s+/g,'');
19872                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
19873                 
19874                 if ( cwhite.length && cblack.indexOf(l) > -1) {
19875 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
19876                     //node.removeAttribute(n);
19877                     return true;
19878                 }
19879                 //Roo.log()
19880                 // only allow 'c whitelisted system attributes'
19881                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
19882 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
19883                     //node.removeAttribute(n);
19884                     return true;
19885                 }
19886                 
19887                 
19888                  
19889                 
19890                 clean.push(p);
19891                 return true;
19892             });
19893             if (clean.length) { 
19894                 node.setAttribute(n, clean.join(';'));
19895             } else {
19896                 node.removeAttribute(n);
19897             }
19898             
19899         }
19900         
19901         
19902         for (var i = node.attributes.length-1; i > -1 ; i--) {
19903             var a = node.attributes[i];
19904             //console.log(a);
19905             
19906             if (a.name.toLowerCase().substr(0,2)=='on')  {
19907                 node.removeAttribute(a.name);
19908                 continue;
19909             }
19910             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
19911                 node.removeAttribute(a.name);
19912                 continue;
19913             }
19914             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
19915                 cleanAttr(a.name,a.value); // fixme..
19916                 continue;
19917             }
19918             if (a.name == 'style') {
19919                 cleanStyle(a.name,a.value);
19920                 continue;
19921             }
19922             /// clean up MS crap..
19923             // tecnically this should be a list of valid class'es..
19924             
19925             
19926             if (a.name == 'class') {
19927                 if (a.value.match(/^Mso/)) {
19928                     node.className = '';
19929                 }
19930                 
19931                 if (a.value.match(/body/)) {
19932                     node.className = '';
19933                 }
19934                 continue;
19935             }
19936             
19937             // style cleanup!?
19938             // class cleanup?
19939             
19940         }
19941         
19942         
19943         this.cleanUpChildren(node);
19944         
19945         
19946     },
19947     
19948     /**
19949      * Clean up MS wordisms...
19950      */
19951     cleanWord : function(node)
19952     {
19953         
19954         
19955         if (!node) {
19956             this.cleanWord(this.doc.body);
19957             return;
19958         }
19959         if (node.nodeName == "#text") {
19960             // clean up silly Windows -- stuff?
19961             return; 
19962         }
19963         if (node.nodeName == "#comment") {
19964             node.parentNode.removeChild(node);
19965             // clean up silly Windows -- stuff?
19966             return; 
19967         }
19968         
19969         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
19970             node.parentNode.removeChild(node);
19971             return;
19972         }
19973         
19974         // remove - but keep children..
19975         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
19976             while (node.childNodes.length) {
19977                 var cn = node.childNodes[0];
19978                 node.removeChild(cn);
19979                 node.parentNode.insertBefore(cn, node);
19980             }
19981             node.parentNode.removeChild(node);
19982             this.iterateChildren(node, this.cleanWord);
19983             return;
19984         }
19985         // clean styles
19986         if (node.className.length) {
19987             
19988             var cn = node.className.split(/\W+/);
19989             var cna = [];
19990             Roo.each(cn, function(cls) {
19991                 if (cls.match(/Mso[a-zA-Z]+/)) {
19992                     return;
19993                 }
19994                 cna.push(cls);
19995             });
19996             node.className = cna.length ? cna.join(' ') : '';
19997             if (!cna.length) {
19998                 node.removeAttribute("class");
19999             }
20000         }
20001         
20002         if (node.hasAttribute("lang")) {
20003             node.removeAttribute("lang");
20004         }
20005         
20006         if (node.hasAttribute("style")) {
20007             
20008             var styles = node.getAttribute("style").split(";");
20009             var nstyle = [];
20010             Roo.each(styles, function(s) {
20011                 if (!s.match(/:/)) {
20012                     return;
20013                 }
20014                 var kv = s.split(":");
20015                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20016                     return;
20017                 }
20018                 // what ever is left... we allow.
20019                 nstyle.push(s);
20020             });
20021             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20022             if (!nstyle.length) {
20023                 node.removeAttribute('style');
20024             }
20025         }
20026         this.iterateChildren(node, this.cleanWord);
20027         
20028         
20029         
20030     },
20031     /**
20032      * iterateChildren of a Node, calling fn each time, using this as the scole..
20033      * @param {DomNode} node node to iterate children of.
20034      * @param {Function} fn method of this class to call on each item.
20035      */
20036     iterateChildren : function(node, fn)
20037     {
20038         if (!node.childNodes.length) {
20039                 return;
20040         }
20041         for (var i = node.childNodes.length-1; i > -1 ; i--) {
20042            fn.call(this, node.childNodes[i])
20043         }
20044     },
20045     
20046     
20047     /**
20048      * cleanTableWidths.
20049      *
20050      * Quite often pasting from word etc.. results in tables with column and widths.
20051      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20052      *
20053      */
20054     cleanTableWidths : function(node)
20055     {
20056          
20057          
20058         if (!node) {
20059             this.cleanTableWidths(this.doc.body);
20060             return;
20061         }
20062         
20063         // ignore list...
20064         if (node.nodeName == "#text" || node.nodeName == "#comment") {
20065             return; 
20066         }
20067         Roo.log(node.tagName);
20068         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20069             this.iterateChildren(node, this.cleanTableWidths);
20070             return;
20071         }
20072         if (node.hasAttribute('width')) {
20073             node.removeAttribute('width');
20074         }
20075         
20076          
20077         if (node.hasAttribute("style")) {
20078             // pretty basic...
20079             
20080             var styles = node.getAttribute("style").split(";");
20081             var nstyle = [];
20082             Roo.each(styles, function(s) {
20083                 if (!s.match(/:/)) {
20084                     return;
20085                 }
20086                 var kv = s.split(":");
20087                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20088                     return;
20089                 }
20090                 // what ever is left... we allow.
20091                 nstyle.push(s);
20092             });
20093             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20094             if (!nstyle.length) {
20095                 node.removeAttribute('style');
20096             }
20097         }
20098         
20099         this.iterateChildren(node, this.cleanTableWidths);
20100         
20101         
20102     },
20103     
20104     
20105     
20106     
20107     domToHTML : function(currentElement, depth, nopadtext) {
20108         
20109         depth = depth || 0;
20110         nopadtext = nopadtext || false;
20111     
20112         if (!currentElement) {
20113             return this.domToHTML(this.doc.body);
20114         }
20115         
20116         //Roo.log(currentElement);
20117         var j;
20118         var allText = false;
20119         var nodeName = currentElement.nodeName;
20120         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20121         
20122         if  (nodeName == '#text') {
20123             
20124             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20125         }
20126         
20127         
20128         var ret = '';
20129         if (nodeName != 'BODY') {
20130              
20131             var i = 0;
20132             // Prints the node tagName, such as <A>, <IMG>, etc
20133             if (tagName) {
20134                 var attr = [];
20135                 for(i = 0; i < currentElement.attributes.length;i++) {
20136                     // quoting?
20137                     var aname = currentElement.attributes.item(i).name;
20138                     if (!currentElement.attributes.item(i).value.length) {
20139                         continue;
20140                     }
20141                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20142                 }
20143                 
20144                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20145             } 
20146             else {
20147                 
20148                 // eack
20149             }
20150         } else {
20151             tagName = false;
20152         }
20153         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20154             return ret;
20155         }
20156         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20157             nopadtext = true;
20158         }
20159         
20160         
20161         // Traverse the tree
20162         i = 0;
20163         var currentElementChild = currentElement.childNodes.item(i);
20164         var allText = true;
20165         var innerHTML  = '';
20166         lastnode = '';
20167         while (currentElementChild) {
20168             // Formatting code (indent the tree so it looks nice on the screen)
20169             var nopad = nopadtext;
20170             if (lastnode == 'SPAN') {
20171                 nopad  = true;
20172             }
20173             // text
20174             if  (currentElementChild.nodeName == '#text') {
20175                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20176                 toadd = nopadtext ? toadd : toadd.trim();
20177                 if (!nopad && toadd.length > 80) {
20178                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
20179                 }
20180                 innerHTML  += toadd;
20181                 
20182                 i++;
20183                 currentElementChild = currentElement.childNodes.item(i);
20184                 lastNode = '';
20185                 continue;
20186             }
20187             allText = false;
20188             
20189             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
20190                 
20191             // Recursively traverse the tree structure of the child node
20192             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
20193             lastnode = currentElementChild.nodeName;
20194             i++;
20195             currentElementChild=currentElement.childNodes.item(i);
20196         }
20197         
20198         ret += innerHTML;
20199         
20200         if (!allText) {
20201                 // The remaining code is mostly for formatting the tree
20202             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
20203         }
20204         
20205         
20206         if (tagName) {
20207             ret+= "</"+tagName+">";
20208         }
20209         return ret;
20210         
20211     },
20212         
20213     applyBlacklists : function()
20214     {
20215         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
20216         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
20217         
20218         this.white = [];
20219         this.black = [];
20220         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20221             if (b.indexOf(tag) > -1) {
20222                 return;
20223             }
20224             this.white.push(tag);
20225             
20226         }, this);
20227         
20228         Roo.each(w, function(tag) {
20229             if (b.indexOf(tag) > -1) {
20230                 return;
20231             }
20232             if (this.white.indexOf(tag) > -1) {
20233                 return;
20234             }
20235             this.white.push(tag);
20236             
20237         }, this);
20238         
20239         
20240         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20241             if (w.indexOf(tag) > -1) {
20242                 return;
20243             }
20244             this.black.push(tag);
20245             
20246         }, this);
20247         
20248         Roo.each(b, function(tag) {
20249             if (w.indexOf(tag) > -1) {
20250                 return;
20251             }
20252             if (this.black.indexOf(tag) > -1) {
20253                 return;
20254             }
20255             this.black.push(tag);
20256             
20257         }, this);
20258         
20259         
20260         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
20261         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
20262         
20263         this.cwhite = [];
20264         this.cblack = [];
20265         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20266             if (b.indexOf(tag) > -1) {
20267                 return;
20268             }
20269             this.cwhite.push(tag);
20270             
20271         }, this);
20272         
20273         Roo.each(w, function(tag) {
20274             if (b.indexOf(tag) > -1) {
20275                 return;
20276             }
20277             if (this.cwhite.indexOf(tag) > -1) {
20278                 return;
20279             }
20280             this.cwhite.push(tag);
20281             
20282         }, this);
20283         
20284         
20285         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20286             if (w.indexOf(tag) > -1) {
20287                 return;
20288             }
20289             this.cblack.push(tag);
20290             
20291         }, this);
20292         
20293         Roo.each(b, function(tag) {
20294             if (w.indexOf(tag) > -1) {
20295                 return;
20296             }
20297             if (this.cblack.indexOf(tag) > -1) {
20298                 return;
20299             }
20300             this.cblack.push(tag);
20301             
20302         }, this);
20303     },
20304     
20305     setStylesheets : function(stylesheets)
20306     {
20307         if(typeof(stylesheets) == 'string'){
20308             Roo.get(this.iframe.contentDocument.head).createChild({
20309                 tag : 'link',
20310                 rel : 'stylesheet',
20311                 type : 'text/css',
20312                 href : stylesheets
20313             });
20314             
20315             return;
20316         }
20317         var _this = this;
20318      
20319         Roo.each(stylesheets, function(s) {
20320             if(!s.length){
20321                 return;
20322             }
20323             
20324             Roo.get(_this.iframe.contentDocument.head).createChild({
20325                 tag : 'link',
20326                 rel : 'stylesheet',
20327                 type : 'text/css',
20328                 href : s
20329             });
20330         });
20331
20332         
20333     },
20334     
20335     removeStylesheets : function()
20336     {
20337         var _this = this;
20338         
20339         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20340             s.remove();
20341         });
20342     }
20343     
20344     // hide stuff that is not compatible
20345     /**
20346      * @event blur
20347      * @hide
20348      */
20349     /**
20350      * @event change
20351      * @hide
20352      */
20353     /**
20354      * @event focus
20355      * @hide
20356      */
20357     /**
20358      * @event specialkey
20359      * @hide
20360      */
20361     /**
20362      * @cfg {String} fieldClass @hide
20363      */
20364     /**
20365      * @cfg {String} focusClass @hide
20366      */
20367     /**
20368      * @cfg {String} autoCreate @hide
20369      */
20370     /**
20371      * @cfg {String} inputType @hide
20372      */
20373     /**
20374      * @cfg {String} invalidClass @hide
20375      */
20376     /**
20377      * @cfg {String} invalidText @hide
20378      */
20379     /**
20380      * @cfg {String} msgFx @hide
20381      */
20382     /**
20383      * @cfg {String} validateOnBlur @hide
20384      */
20385 });
20386
20387 Roo.HtmlEditorCore.white = [
20388         'area', 'br', 'img', 'input', 'hr', 'wbr',
20389         
20390        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
20391        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
20392        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
20393        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
20394        'table',   'ul',         'xmp', 
20395        
20396        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
20397       'thead',   'tr', 
20398      
20399       'dir', 'menu', 'ol', 'ul', 'dl',
20400        
20401       'embed',  'object'
20402 ];
20403
20404
20405 Roo.HtmlEditorCore.black = [
20406     //    'embed',  'object', // enable - backend responsiblity to clean thiese
20407         'applet', // 
20408         'base',   'basefont', 'bgsound', 'blink',  'body', 
20409         'frame',  'frameset', 'head',    'html',   'ilayer', 
20410         'iframe', 'layer',  'link',     'meta',    'object',   
20411         'script', 'style' ,'title',  'xml' // clean later..
20412 ];
20413 Roo.HtmlEditorCore.clean = [
20414     'script', 'style', 'title', 'xml'
20415 ];
20416 Roo.HtmlEditorCore.remove = [
20417     'font'
20418 ];
20419 // attributes..
20420
20421 Roo.HtmlEditorCore.ablack = [
20422     'on'
20423 ];
20424     
20425 Roo.HtmlEditorCore.aclean = [ 
20426     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
20427 ];
20428
20429 // protocols..
20430 Roo.HtmlEditorCore.pwhite= [
20431         'http',  'https',  'mailto'
20432 ];
20433
20434 // white listed style attributes.
20435 Roo.HtmlEditorCore.cwhite= [
20436       //  'text-align', /// default is to allow most things..
20437       
20438          
20439 //        'font-size'//??
20440 ];
20441
20442 // black listed style attributes.
20443 Roo.HtmlEditorCore.cblack= [
20444       //  'font-size' -- this can be set by the project 
20445 ];
20446
20447
20448 Roo.HtmlEditorCore.swapCodes   =[ 
20449     [    8211, "--" ], 
20450     [    8212, "--" ], 
20451     [    8216,  "'" ],  
20452     [    8217, "'" ],  
20453     [    8220, '"' ],  
20454     [    8221, '"' ],  
20455     [    8226, "*" ],  
20456     [    8230, "..." ]
20457 ]; 
20458
20459     /*
20460  * - LGPL
20461  *
20462  * HtmlEditor
20463  * 
20464  */
20465
20466 /**
20467  * @class Roo.bootstrap.HtmlEditor
20468  * @extends Roo.bootstrap.TextArea
20469  * Bootstrap HtmlEditor class
20470
20471  * @constructor
20472  * Create a new HtmlEditor
20473  * @param {Object} config The config object
20474  */
20475
20476 Roo.bootstrap.HtmlEditor = function(config){
20477     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
20478     if (!this.toolbars) {
20479         this.toolbars = [];
20480     }
20481     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
20482     this.addEvents({
20483             /**
20484              * @event initialize
20485              * Fires when the editor is fully initialized (including the iframe)
20486              * @param {HtmlEditor} this
20487              */
20488             initialize: true,
20489             /**
20490              * @event activate
20491              * Fires when the editor is first receives the focus. Any insertion must wait
20492              * until after this event.
20493              * @param {HtmlEditor} this
20494              */
20495             activate: true,
20496              /**
20497              * @event beforesync
20498              * Fires before the textarea is updated with content from the editor iframe. Return false
20499              * to cancel the sync.
20500              * @param {HtmlEditor} this
20501              * @param {String} html
20502              */
20503             beforesync: true,
20504              /**
20505              * @event beforepush
20506              * Fires before the iframe editor is updated with content from the textarea. Return false
20507              * to cancel the push.
20508              * @param {HtmlEditor} this
20509              * @param {String} html
20510              */
20511             beforepush: true,
20512              /**
20513              * @event sync
20514              * Fires when the textarea is updated with content from the editor iframe.
20515              * @param {HtmlEditor} this
20516              * @param {String} html
20517              */
20518             sync: true,
20519              /**
20520              * @event push
20521              * Fires when the iframe editor is updated with content from the textarea.
20522              * @param {HtmlEditor} this
20523              * @param {String} html
20524              */
20525             push: true,
20526              /**
20527              * @event editmodechange
20528              * Fires when the editor switches edit modes
20529              * @param {HtmlEditor} this
20530              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
20531              */
20532             editmodechange: true,
20533             /**
20534              * @event editorevent
20535              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20536              * @param {HtmlEditor} this
20537              */
20538             editorevent: true,
20539             /**
20540              * @event firstfocus
20541              * Fires when on first focus - needed by toolbars..
20542              * @param {HtmlEditor} this
20543              */
20544             firstfocus: true,
20545             /**
20546              * @event autosave
20547              * Auto save the htmlEditor value as a file into Events
20548              * @param {HtmlEditor} this
20549              */
20550             autosave: true,
20551             /**
20552              * @event savedpreview
20553              * preview the saved version of htmlEditor
20554              * @param {HtmlEditor} this
20555              */
20556             savedpreview: true
20557         });
20558 };
20559
20560
20561 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
20562     
20563     
20564       /**
20565      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
20566      */
20567     toolbars : false,
20568    
20569      /**
20570      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20571      *                        Roo.resizable.
20572      */
20573     resizable : false,
20574      /**
20575      * @cfg {Number} height (in pixels)
20576      */   
20577     height: 300,
20578    /**
20579      * @cfg {Number} width (in pixels)
20580      */   
20581     width: false,
20582     
20583     /**
20584      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20585      * 
20586      */
20587     stylesheets: false,
20588     
20589     // id of frame..
20590     frameId: false,
20591     
20592     // private properties
20593     validationEvent : false,
20594     deferHeight: true,
20595     initialized : false,
20596     activated : false,
20597     
20598     onFocus : Roo.emptyFn,
20599     iframePad:3,
20600     hideMode:'offsets',
20601     
20602     
20603     tbContainer : false,
20604     
20605     toolbarContainer :function() {
20606         return this.wrap.select('.x-html-editor-tb',true).first();
20607     },
20608
20609     /**
20610      * Protected method that will not generally be called directly. It
20611      * is called when the editor creates its toolbar. Override this method if you need to
20612      * add custom toolbar buttons.
20613      * @param {HtmlEditor} editor
20614      */
20615     createToolbar : function(){
20616         
20617         Roo.log("create toolbars");
20618         
20619         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
20620         this.toolbars[0].render(this.toolbarContainer());
20621         
20622         return;
20623         
20624 //        if (!editor.toolbars || !editor.toolbars.length) {
20625 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
20626 //        }
20627 //        
20628 //        for (var i =0 ; i < editor.toolbars.length;i++) {
20629 //            editor.toolbars[i] = Roo.factory(
20630 //                    typeof(editor.toolbars[i]) == 'string' ?
20631 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
20632 //                Roo.bootstrap.HtmlEditor);
20633 //            editor.toolbars[i].init(editor);
20634 //        }
20635     },
20636
20637      
20638     // private
20639     onRender : function(ct, position)
20640     {
20641        // Roo.log("Call onRender: " + this.xtype);
20642         var _t = this;
20643         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
20644       
20645         this.wrap = this.inputEl().wrap({
20646             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
20647         });
20648         
20649         this.editorcore.onRender(ct, position);
20650          
20651         if (this.resizable) {
20652             this.resizeEl = new Roo.Resizable(this.wrap, {
20653                 pinned : true,
20654                 wrap: true,
20655                 dynamic : true,
20656                 minHeight : this.height,
20657                 height: this.height,
20658                 handles : this.resizable,
20659                 width: this.width,
20660                 listeners : {
20661                     resize : function(r, w, h) {
20662                         _t.onResize(w,h); // -something
20663                     }
20664                 }
20665             });
20666             
20667         }
20668         this.createToolbar(this);
20669        
20670         
20671         if(!this.width && this.resizable){
20672             this.setSize(this.wrap.getSize());
20673         }
20674         if (this.resizeEl) {
20675             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
20676             // should trigger onReize..
20677         }
20678         
20679     },
20680
20681     // private
20682     onResize : function(w, h)
20683     {
20684         Roo.log('resize: ' +w + ',' + h );
20685         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
20686         var ew = false;
20687         var eh = false;
20688         
20689         if(this.inputEl() ){
20690             if(typeof w == 'number'){
20691                 var aw = w - this.wrap.getFrameWidth('lr');
20692                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
20693                 ew = aw;
20694             }
20695             if(typeof h == 'number'){
20696                  var tbh = -11;  // fixme it needs to tool bar size!
20697                 for (var i =0; i < this.toolbars.length;i++) {
20698                     // fixme - ask toolbars for heights?
20699                     tbh += this.toolbars[i].el.getHeight();
20700                     //if (this.toolbars[i].footer) {
20701                     //    tbh += this.toolbars[i].footer.el.getHeight();
20702                     //}
20703                 }
20704               
20705                 
20706                 
20707                 
20708                 
20709                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
20710                 ah -= 5; // knock a few pixes off for look..
20711                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
20712                 var eh = ah;
20713             }
20714         }
20715         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
20716         this.editorcore.onResize(ew,eh);
20717         
20718     },
20719
20720     /**
20721      * Toggles the editor between standard and source edit mode.
20722      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20723      */
20724     toggleSourceEdit : function(sourceEditMode)
20725     {
20726         this.editorcore.toggleSourceEdit(sourceEditMode);
20727         
20728         if(this.editorcore.sourceEditMode){
20729             Roo.log('editor - showing textarea');
20730             
20731 //            Roo.log('in');
20732 //            Roo.log(this.syncValue());
20733             this.syncValue();
20734             this.inputEl().removeClass(['hide', 'x-hidden']);
20735             this.inputEl().dom.removeAttribute('tabIndex');
20736             this.inputEl().focus();
20737         }else{
20738             Roo.log('editor - hiding textarea');
20739 //            Roo.log('out')
20740 //            Roo.log(this.pushValue()); 
20741             this.pushValue();
20742             
20743             this.inputEl().addClass(['hide', 'x-hidden']);
20744             this.inputEl().dom.setAttribute('tabIndex', -1);
20745             //this.deferFocus();
20746         }
20747          
20748         if(this.resizable){
20749             this.setSize(this.wrap.getSize());
20750         }
20751         
20752         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
20753     },
20754  
20755     // private (for BoxComponent)
20756     adjustSize : Roo.BoxComponent.prototype.adjustSize,
20757
20758     // private (for BoxComponent)
20759     getResizeEl : function(){
20760         return this.wrap;
20761     },
20762
20763     // private (for BoxComponent)
20764     getPositionEl : function(){
20765         return this.wrap;
20766     },
20767
20768     // private
20769     initEvents : function(){
20770         this.originalValue = this.getValue();
20771     },
20772
20773 //    /**
20774 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20775 //     * @method
20776 //     */
20777 //    markInvalid : Roo.emptyFn,
20778 //    /**
20779 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20780 //     * @method
20781 //     */
20782 //    clearInvalid : Roo.emptyFn,
20783
20784     setValue : function(v){
20785         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
20786         this.editorcore.pushValue();
20787     },
20788
20789      
20790     // private
20791     deferFocus : function(){
20792         this.focus.defer(10, this);
20793     },
20794
20795     // doc'ed in Field
20796     focus : function(){
20797         this.editorcore.focus();
20798         
20799     },
20800       
20801
20802     // private
20803     onDestroy : function(){
20804         
20805         
20806         
20807         if(this.rendered){
20808             
20809             for (var i =0; i < this.toolbars.length;i++) {
20810                 // fixme - ask toolbars for heights?
20811                 this.toolbars[i].onDestroy();
20812             }
20813             
20814             this.wrap.dom.innerHTML = '';
20815             this.wrap.remove();
20816         }
20817     },
20818
20819     // private
20820     onFirstFocus : function(){
20821         //Roo.log("onFirstFocus");
20822         this.editorcore.onFirstFocus();
20823          for (var i =0; i < this.toolbars.length;i++) {
20824             this.toolbars[i].onFirstFocus();
20825         }
20826         
20827     },
20828     
20829     // private
20830     syncValue : function()
20831     {   
20832         this.editorcore.syncValue();
20833     },
20834     
20835     pushValue : function()
20836     {   
20837         this.editorcore.pushValue();
20838     }
20839      
20840     
20841     // hide stuff that is not compatible
20842     /**
20843      * @event blur
20844      * @hide
20845      */
20846     /**
20847      * @event change
20848      * @hide
20849      */
20850     /**
20851      * @event focus
20852      * @hide
20853      */
20854     /**
20855      * @event specialkey
20856      * @hide
20857      */
20858     /**
20859      * @cfg {String} fieldClass @hide
20860      */
20861     /**
20862      * @cfg {String} focusClass @hide
20863      */
20864     /**
20865      * @cfg {String} autoCreate @hide
20866      */
20867     /**
20868      * @cfg {String} inputType @hide
20869      */
20870     /**
20871      * @cfg {String} invalidClass @hide
20872      */
20873     /**
20874      * @cfg {String} invalidText @hide
20875      */
20876     /**
20877      * @cfg {String} msgFx @hide
20878      */
20879     /**
20880      * @cfg {String} validateOnBlur @hide
20881      */
20882 });
20883  
20884     
20885    
20886    
20887    
20888       
20889 Roo.namespace('Roo.bootstrap.htmleditor');
20890 /**
20891  * @class Roo.bootstrap.HtmlEditorToolbar1
20892  * Basic Toolbar
20893  * 
20894  * Usage:
20895  *
20896  new Roo.bootstrap.HtmlEditor({
20897     ....
20898     toolbars : [
20899         new Roo.bootstrap.HtmlEditorToolbar1({
20900             disable : { fonts: 1 , format: 1, ..., ... , ...],
20901             btns : [ .... ]
20902         })
20903     }
20904      
20905  * 
20906  * @cfg {Object} disable List of elements to disable..
20907  * @cfg {Array} btns List of additional buttons.
20908  * 
20909  * 
20910  * NEEDS Extra CSS? 
20911  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
20912  */
20913  
20914 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
20915 {
20916     
20917     Roo.apply(this, config);
20918     
20919     // default disabled, based on 'good practice'..
20920     this.disable = this.disable || {};
20921     Roo.applyIf(this.disable, {
20922         fontSize : true,
20923         colors : true,
20924         specialElements : true
20925     });
20926     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
20927     
20928     this.editor = config.editor;
20929     this.editorcore = config.editor.editorcore;
20930     
20931     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
20932     
20933     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
20934     // dont call parent... till later.
20935 }
20936 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
20937      
20938     bar : true,
20939     
20940     editor : false,
20941     editorcore : false,
20942     
20943     
20944     formats : [
20945         "p" ,  
20946         "h1","h2","h3","h4","h5","h6", 
20947         "pre", "code", 
20948         "abbr", "acronym", "address", "cite", "samp", "var",
20949         'div','span'
20950     ],
20951     
20952     onRender : function(ct, position)
20953     {
20954        // Roo.log("Call onRender: " + this.xtype);
20955         
20956        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
20957        Roo.log(this.el);
20958        this.el.dom.style.marginBottom = '0';
20959        var _this = this;
20960        var editorcore = this.editorcore;
20961        var editor= this.editor;
20962        
20963        var children = [];
20964        var btn = function(id,cmd , toggle, handler){
20965        
20966             var  event = toggle ? 'toggle' : 'click';
20967        
20968             var a = {
20969                 size : 'sm',
20970                 xtype: 'Button',
20971                 xns: Roo.bootstrap,
20972                 glyphicon : id,
20973                 cmd : id || cmd,
20974                 enableToggle:toggle !== false,
20975                 //html : 'submit'
20976                 pressed : toggle ? false : null,
20977                 listeners : {}
20978             };
20979             a.listeners[toggle ? 'toggle' : 'click'] = function() {
20980                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
20981             };
20982             children.push(a);
20983             return a;
20984        }
20985         
20986         var style = {
20987                 xtype: 'Button',
20988                 size : 'sm',
20989                 xns: Roo.bootstrap,
20990                 glyphicon : 'font',
20991                 //html : 'submit'
20992                 menu : {
20993                     xtype: 'Menu',
20994                     xns: Roo.bootstrap,
20995                     items:  []
20996                 }
20997         };
20998         Roo.each(this.formats, function(f) {
20999             style.menu.items.push({
21000                 xtype :'MenuItem',
21001                 xns: Roo.bootstrap,
21002                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21003                 tagname : f,
21004                 listeners : {
21005                     click : function()
21006                     {
21007                         editorcore.insertTag(this.tagname);
21008                         editor.focus();
21009                     }
21010                 }
21011                 
21012             });
21013         });
21014          children.push(style);   
21015             
21016             
21017         btn('bold',false,true);
21018         btn('italic',false,true);
21019         btn('align-left', 'justifyleft',true);
21020         btn('align-center', 'justifycenter',true);
21021         btn('align-right' , 'justifyright',true);
21022         btn('link', false, false, function(btn) {
21023             //Roo.log("create link?");
21024             var url = prompt(this.createLinkText, this.defaultLinkValue);
21025             if(url && url != 'http:/'+'/'){
21026                 this.editorcore.relayCmd('createlink', url);
21027             }
21028         }),
21029         btn('list','insertunorderedlist',true);
21030         btn('pencil', false,true, function(btn){
21031                 Roo.log(this);
21032                 
21033                 this.toggleSourceEdit(btn.pressed);
21034         });
21035         /*
21036         var cog = {
21037                 xtype: 'Button',
21038                 size : 'sm',
21039                 xns: Roo.bootstrap,
21040                 glyphicon : 'cog',
21041                 //html : 'submit'
21042                 menu : {
21043                     xtype: 'Menu',
21044                     xns: Roo.bootstrap,
21045                     items:  []
21046                 }
21047         };
21048         
21049         cog.menu.items.push({
21050             xtype :'MenuItem',
21051             xns: Roo.bootstrap,
21052             html : Clean styles,
21053             tagname : f,
21054             listeners : {
21055                 click : function()
21056                 {
21057                     editorcore.insertTag(this.tagname);
21058                     editor.focus();
21059                 }
21060             }
21061             
21062         });
21063        */
21064         
21065          
21066        this.xtype = 'NavSimplebar';
21067         
21068         for(var i=0;i< children.length;i++) {
21069             
21070             this.buttons.add(this.addxtypeChild(children[i]));
21071             
21072         }
21073         
21074         editor.on('editorevent', this.updateToolbar, this);
21075     },
21076     onBtnClick : function(id)
21077     {
21078        this.editorcore.relayCmd(id);
21079        this.editorcore.focus();
21080     },
21081     
21082     /**
21083      * Protected method that will not generally be called directly. It triggers
21084      * a toolbar update by reading the markup state of the current selection in the editor.
21085      */
21086     updateToolbar: function(){
21087
21088         if(!this.editorcore.activated){
21089             this.editor.onFirstFocus(); // is this neeed?
21090             return;
21091         }
21092
21093         var btns = this.buttons; 
21094         var doc = this.editorcore.doc;
21095         btns.get('bold').setActive(doc.queryCommandState('bold'));
21096         btns.get('italic').setActive(doc.queryCommandState('italic'));
21097         //btns.get('underline').setActive(doc.queryCommandState('underline'));
21098         
21099         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21100         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21101         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21102         
21103         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21104         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21105          /*
21106         
21107         var ans = this.editorcore.getAllAncestors();
21108         if (this.formatCombo) {
21109             
21110             
21111             var store = this.formatCombo.store;
21112             this.formatCombo.setValue("");
21113             for (var i =0; i < ans.length;i++) {
21114                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21115                     // select it..
21116                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21117                     break;
21118                 }
21119             }
21120         }
21121         
21122         
21123         
21124         // hides menus... - so this cant be on a menu...
21125         Roo.bootstrap.MenuMgr.hideAll();
21126         */
21127         Roo.bootstrap.MenuMgr.hideAll();
21128         //this.editorsyncValue();
21129     },
21130     onFirstFocus: function() {
21131         this.buttons.each(function(item){
21132            item.enable();
21133         });
21134     },
21135     toggleSourceEdit : function(sourceEditMode){
21136         
21137           
21138         if(sourceEditMode){
21139             Roo.log("disabling buttons");
21140            this.buttons.each( function(item){
21141                 if(item.cmd != 'pencil'){
21142                     item.disable();
21143                 }
21144             });
21145           
21146         }else{
21147             Roo.log("enabling buttons");
21148             if(this.editorcore.initialized){
21149                 this.buttons.each( function(item){
21150                     item.enable();
21151                 });
21152             }
21153             
21154         }
21155         Roo.log("calling toggole on editor");
21156         // tell the editor that it's been pressed..
21157         this.editor.toggleSourceEdit(sourceEditMode);
21158        
21159     }
21160 });
21161
21162
21163
21164
21165
21166 /**
21167  * @class Roo.bootstrap.Table.AbstractSelectionModel
21168  * @extends Roo.util.Observable
21169  * Abstract base class for grid SelectionModels.  It provides the interface that should be
21170  * implemented by descendant classes.  This class should not be directly instantiated.
21171  * @constructor
21172  */
21173 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21174     this.locked = false;
21175     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21176 };
21177
21178
21179 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
21180     /** @ignore Called by the grid automatically. Do not call directly. */
21181     init : function(grid){
21182         this.grid = grid;
21183         this.initEvents();
21184     },
21185
21186     /**
21187      * Locks the selections.
21188      */
21189     lock : function(){
21190         this.locked = true;
21191     },
21192
21193     /**
21194      * Unlocks the selections.
21195      */
21196     unlock : function(){
21197         this.locked = false;
21198     },
21199
21200     /**
21201      * Returns true if the selections are locked.
21202      * @return {Boolean}
21203      */
21204     isLocked : function(){
21205         return this.locked;
21206     }
21207 });
21208 /**
21209  * @extends Roo.bootstrap.Table.AbstractSelectionModel
21210  * @class Roo.bootstrap.Table.RowSelectionModel
21211  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21212  * It supports multiple selections and keyboard selection/navigation. 
21213  * @constructor
21214  * @param {Object} config
21215  */
21216
21217 Roo.bootstrap.Table.RowSelectionModel = function(config){
21218     Roo.apply(this, config);
21219     this.selections = new Roo.util.MixedCollection(false, function(o){
21220         return o.id;
21221     });
21222
21223     this.last = false;
21224     this.lastActive = false;
21225
21226     this.addEvents({
21227         /**
21228              * @event selectionchange
21229              * Fires when the selection changes
21230              * @param {SelectionModel} this
21231              */
21232             "selectionchange" : true,
21233         /**
21234              * @event afterselectionchange
21235              * Fires after the selection changes (eg. by key press or clicking)
21236              * @param {SelectionModel} this
21237              */
21238             "afterselectionchange" : true,
21239         /**
21240              * @event beforerowselect
21241              * Fires when a row is selected being selected, return false to cancel.
21242              * @param {SelectionModel} this
21243              * @param {Number} rowIndex The selected index
21244              * @param {Boolean} keepExisting False if other selections will be cleared
21245              */
21246             "beforerowselect" : true,
21247         /**
21248              * @event rowselect
21249              * Fires when a row is selected.
21250              * @param {SelectionModel} this
21251              * @param {Number} rowIndex The selected index
21252              * @param {Roo.data.Record} r The record
21253              */
21254             "rowselect" : true,
21255         /**
21256              * @event rowdeselect
21257              * Fires when a row is deselected.
21258              * @param {SelectionModel} this
21259              * @param {Number} rowIndex The selected index
21260              */
21261         "rowdeselect" : true
21262     });
21263     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21264     this.locked = false;
21265 };
21266
21267 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
21268     /**
21269      * @cfg {Boolean} singleSelect
21270      * True to allow selection of only one row at a time (defaults to false)
21271      */
21272     singleSelect : false,
21273
21274     // private
21275     initEvents : function(){
21276
21277         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21278             this.grid.on("mousedown", this.handleMouseDown, this);
21279         }else{ // allow click to work like normal
21280             this.grid.on("rowclick", this.handleDragableRowClick, this);
21281         }
21282
21283         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21284             "up" : function(e){
21285                 if(!e.shiftKey){
21286                     this.selectPrevious(e.shiftKey);
21287                 }else if(this.last !== false && this.lastActive !== false){
21288                     var last = this.last;
21289                     this.selectRange(this.last,  this.lastActive-1);
21290                     this.grid.getView().focusRow(this.lastActive);
21291                     if(last !== false){
21292                         this.last = last;
21293                     }
21294                 }else{
21295                     this.selectFirstRow();
21296                 }
21297                 this.fireEvent("afterselectionchange", this);
21298             },
21299             "down" : function(e){
21300                 if(!e.shiftKey){
21301                     this.selectNext(e.shiftKey);
21302                 }else if(this.last !== false && this.lastActive !== false){
21303                     var last = this.last;
21304                     this.selectRange(this.last,  this.lastActive+1);
21305                     this.grid.getView().focusRow(this.lastActive);
21306                     if(last !== false){
21307                         this.last = last;
21308                     }
21309                 }else{
21310                     this.selectFirstRow();
21311                 }
21312                 this.fireEvent("afterselectionchange", this);
21313             },
21314             scope: this
21315         });
21316
21317         var view = this.grid.view;
21318         view.on("refresh", this.onRefresh, this);
21319         view.on("rowupdated", this.onRowUpdated, this);
21320         view.on("rowremoved", this.onRemove, this);
21321     },
21322
21323     // private
21324     onRefresh : function(){
21325         var ds = this.grid.dataSource, i, v = this.grid.view;
21326         var s = this.selections;
21327         s.each(function(r){
21328             if((i = ds.indexOfId(r.id)) != -1){
21329                 v.onRowSelect(i);
21330             }else{
21331                 s.remove(r);
21332             }
21333         });
21334     },
21335
21336     // private
21337     onRemove : function(v, index, r){
21338         this.selections.remove(r);
21339     },
21340
21341     // private
21342     onRowUpdated : function(v, index, r){
21343         if(this.isSelected(r)){
21344             v.onRowSelect(index);
21345         }
21346     },
21347
21348     /**
21349      * Select records.
21350      * @param {Array} records The records to select
21351      * @param {Boolean} keepExisting (optional) True to keep existing selections
21352      */
21353     selectRecords : function(records, keepExisting){
21354         if(!keepExisting){
21355             this.clearSelections();
21356         }
21357         var ds = this.grid.dataSource;
21358         for(var i = 0, len = records.length; i < len; i++){
21359             this.selectRow(ds.indexOf(records[i]), true);
21360         }
21361     },
21362
21363     /**
21364      * Gets the number of selected rows.
21365      * @return {Number}
21366      */
21367     getCount : function(){
21368         return this.selections.length;
21369     },
21370
21371     /**
21372      * Selects the first row in the grid.
21373      */
21374     selectFirstRow : function(){
21375         this.selectRow(0);
21376     },
21377
21378     /**
21379      * Select the last row.
21380      * @param {Boolean} keepExisting (optional) True to keep existing selections
21381      */
21382     selectLastRow : function(keepExisting){
21383         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21384     },
21385
21386     /**
21387      * Selects the row immediately following the last selected row.
21388      * @param {Boolean} keepExisting (optional) True to keep existing selections
21389      */
21390     selectNext : function(keepExisting){
21391         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21392             this.selectRow(this.last+1, keepExisting);
21393             this.grid.getView().focusRow(this.last);
21394         }
21395     },
21396
21397     /**
21398      * Selects the row that precedes the last selected row.
21399      * @param {Boolean} keepExisting (optional) True to keep existing selections
21400      */
21401     selectPrevious : function(keepExisting){
21402         if(this.last){
21403             this.selectRow(this.last-1, keepExisting);
21404             this.grid.getView().focusRow(this.last);
21405         }
21406     },
21407
21408     /**
21409      * Returns the selected records
21410      * @return {Array} Array of selected records
21411      */
21412     getSelections : function(){
21413         return [].concat(this.selections.items);
21414     },
21415
21416     /**
21417      * Returns the first selected record.
21418      * @return {Record}
21419      */
21420     getSelected : function(){
21421         return this.selections.itemAt(0);
21422     },
21423
21424
21425     /**
21426      * Clears all selections.
21427      */
21428     clearSelections : function(fast){
21429         if(this.locked) return;
21430         if(fast !== true){
21431             var ds = this.grid.dataSource;
21432             var s = this.selections;
21433             s.each(function(r){
21434                 this.deselectRow(ds.indexOfId(r.id));
21435             }, this);
21436             s.clear();
21437         }else{
21438             this.selections.clear();
21439         }
21440         this.last = false;
21441     },
21442
21443
21444     /**
21445      * Selects all rows.
21446      */
21447     selectAll : function(){
21448         if(this.locked) return;
21449         this.selections.clear();
21450         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
21451             this.selectRow(i, true);
21452         }
21453     },
21454
21455     /**
21456      * Returns True if there is a selection.
21457      * @return {Boolean}
21458      */
21459     hasSelection : function(){
21460         return this.selections.length > 0;
21461     },
21462
21463     /**
21464      * Returns True if the specified row is selected.
21465      * @param {Number/Record} record The record or index of the record to check
21466      * @return {Boolean}
21467      */
21468     isSelected : function(index){
21469         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
21470         return (r && this.selections.key(r.id) ? true : false);
21471     },
21472
21473     /**
21474      * Returns True if the specified record id is selected.
21475      * @param {String} id The id of record to check
21476      * @return {Boolean}
21477      */
21478     isIdSelected : function(id){
21479         return (this.selections.key(id) ? true : false);
21480     },
21481
21482     // private
21483     handleMouseDown : function(e, t){
21484         var view = this.grid.getView(), rowIndex;
21485         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
21486             return;
21487         };
21488         if(e.shiftKey && this.last !== false){
21489             var last = this.last;
21490             this.selectRange(last, rowIndex, e.ctrlKey);
21491             this.last = last; // reset the last
21492             view.focusRow(rowIndex);
21493         }else{
21494             var isSelected = this.isSelected(rowIndex);
21495             if(e.button !== 0 && isSelected){
21496                 view.focusRow(rowIndex);
21497             }else if(e.ctrlKey && isSelected){
21498                 this.deselectRow(rowIndex);
21499             }else if(!isSelected){
21500                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
21501                 view.focusRow(rowIndex);
21502             }
21503         }
21504         this.fireEvent("afterselectionchange", this);
21505     },
21506     // private
21507     handleDragableRowClick :  function(grid, rowIndex, e) 
21508     {
21509         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
21510             this.selectRow(rowIndex, false);
21511             grid.view.focusRow(rowIndex);
21512              this.fireEvent("afterselectionchange", this);
21513         }
21514     },
21515     
21516     /**
21517      * Selects multiple rows.
21518      * @param {Array} rows Array of the indexes of the row to select
21519      * @param {Boolean} keepExisting (optional) True to keep existing selections
21520      */
21521     selectRows : function(rows, keepExisting){
21522         if(!keepExisting){
21523             this.clearSelections();
21524         }
21525         for(var i = 0, len = rows.length; i < len; i++){
21526             this.selectRow(rows[i], true);
21527         }
21528     },
21529
21530     /**
21531      * Selects a range of rows. All rows in between startRow and endRow are also selected.
21532      * @param {Number} startRow The index of the first row in the range
21533      * @param {Number} endRow The index of the last row in the range
21534      * @param {Boolean} keepExisting (optional) True to retain existing selections
21535      */
21536     selectRange : function(startRow, endRow, keepExisting){
21537         if(this.locked) return;
21538         if(!keepExisting){
21539             this.clearSelections();
21540         }
21541         if(startRow <= endRow){
21542             for(var i = startRow; i <= endRow; i++){
21543                 this.selectRow(i, true);
21544             }
21545         }else{
21546             for(var i = startRow; i >= endRow; i--){
21547                 this.selectRow(i, true);
21548             }
21549         }
21550     },
21551
21552     /**
21553      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
21554      * @param {Number} startRow The index of the first row in the range
21555      * @param {Number} endRow The index of the last row in the range
21556      */
21557     deselectRange : function(startRow, endRow, preventViewNotify){
21558         if(this.locked) return;
21559         for(var i = startRow; i <= endRow; i++){
21560             this.deselectRow(i, preventViewNotify);
21561         }
21562     },
21563
21564     /**
21565      * Selects a row.
21566      * @param {Number} row The index of the row to select
21567      * @param {Boolean} keepExisting (optional) True to keep existing selections
21568      */
21569     selectRow : function(index, keepExisting, preventViewNotify){
21570         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
21571         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
21572             if(!keepExisting || this.singleSelect){
21573                 this.clearSelections();
21574             }
21575             var r = this.grid.dataSource.getAt(index);
21576             this.selections.add(r);
21577             this.last = this.lastActive = index;
21578             if(!preventViewNotify){
21579                 this.grid.getView().onRowSelect(index);
21580             }
21581             this.fireEvent("rowselect", this, index, r);
21582             this.fireEvent("selectionchange", this);
21583         }
21584     },
21585
21586     /**
21587      * Deselects a row.
21588      * @param {Number} row The index of the row to deselect
21589      */
21590     deselectRow : function(index, preventViewNotify){
21591         if(this.locked) return;
21592         if(this.last == index){
21593             this.last = false;
21594         }
21595         if(this.lastActive == index){
21596             this.lastActive = false;
21597         }
21598         var r = this.grid.dataSource.getAt(index);
21599         this.selections.remove(r);
21600         if(!preventViewNotify){
21601             this.grid.getView().onRowDeselect(index);
21602         }
21603         this.fireEvent("rowdeselect", this, index);
21604         this.fireEvent("selectionchange", this);
21605     },
21606
21607     // private
21608     restoreLast : function(){
21609         if(this._last){
21610             this.last = this._last;
21611         }
21612     },
21613
21614     // private
21615     acceptsNav : function(row, col, cm){
21616         return !cm.isHidden(col) && cm.isCellEditable(col, row);
21617     },
21618
21619     // private
21620     onEditorKey : function(field, e){
21621         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
21622         if(k == e.TAB){
21623             e.stopEvent();
21624             ed.completeEdit();
21625             if(e.shiftKey){
21626                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
21627             }else{
21628                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
21629             }
21630         }else if(k == e.ENTER && !e.ctrlKey){
21631             e.stopEvent();
21632             ed.completeEdit();
21633             if(e.shiftKey){
21634                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
21635             }else{
21636                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
21637             }
21638         }else if(k == e.ESC){
21639             ed.cancelEdit();
21640         }
21641         if(newCell){
21642             g.startEditing(newCell[0], newCell[1]);
21643         }
21644     }
21645 });/*
21646  * Based on:
21647  * Ext JS Library 1.1.1
21648  * Copyright(c) 2006-2007, Ext JS, LLC.
21649  *
21650  * Originally Released Under LGPL - original licence link has changed is not relivant.
21651  *
21652  * Fork - LGPL
21653  * <script type="text/javascript">
21654  */
21655  
21656 /**
21657  * @class Roo.bootstrap.PagingToolbar
21658  * @extends Roo.bootstrap.NavSimplebar
21659  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
21660  * @constructor
21661  * Create a new PagingToolbar
21662  * @param {Object} config The config object
21663  * {Roo.data.Store} store
21664  */
21665 Roo.bootstrap.PagingToolbar = function(config)
21666 {
21667     // old args format still supported... - xtype is prefered..
21668         // created from xtype...
21669         
21670     var ds = config.dataSource;
21671     
21672     Roo.log(ds);
21673     Roo.log(this.store);
21674     
21675     if (this.store && !ds) {
21676         Roo.log('run??');
21677         this.store= Roo.factory(this.store, Roo.data);
21678         ds = this.store;
21679         ds.xmodule = this.xmodule || false;
21680     }
21681     
21682     this.toolbarItems = [];
21683     if (config.items) {
21684         this.toolbarItems = config.items;
21685 //        config.items = [];
21686     }
21687     
21688     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
21689     this.ds = ds;
21690     this.cursor = 0;
21691     if (ds) { 
21692         this.bind(ds);
21693     }
21694     Roo.log('ds??');
21695     Roo.log(this.ds);
21696     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
21697     
21698 };
21699
21700 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
21701     /**
21702      * @cfg {Roo.data.Store} dataSource
21703      * The underlying data store providing the paged data
21704      */
21705     /**
21706      * @cfg {String/HTMLElement/Element} container
21707      * container The id or element that will contain the toolbar
21708      */
21709     /**
21710      * @cfg {Boolean} displayInfo
21711      * True to display the displayMsg (defaults to false)
21712      */
21713     /**
21714      * @cfg {Number} pageSize
21715      * The number of records to display per page (defaults to 20)
21716      */
21717     pageSize: 20,
21718     /**
21719      * @cfg {String} displayMsg
21720      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
21721      */
21722     displayMsg : 'Displaying {0} - {1} of {2}',
21723     /**
21724      * @cfg {String} emptyMsg
21725      * The message to display when no records are found (defaults to "No data to display")
21726      */
21727     emptyMsg : 'No data to display',
21728     /**
21729      * Customizable piece of the default paging text (defaults to "Page")
21730      * @type String
21731      */
21732     beforePageText : "Page",
21733     /**
21734      * Customizable piece of the default paging text (defaults to "of %0")
21735      * @type String
21736      */
21737     afterPageText : "of {0}",
21738     /**
21739      * Customizable piece of the default paging text (defaults to "First Page")
21740      * @type String
21741      */
21742     firstText : "First Page",
21743     /**
21744      * Customizable piece of the default paging text (defaults to "Previous Page")
21745      * @type String
21746      */
21747     prevText : "Previous Page",
21748     /**
21749      * Customizable piece of the default paging text (defaults to "Next Page")
21750      * @type String
21751      */
21752     nextText : "Next Page",
21753     /**
21754      * Customizable piece of the default paging text (defaults to "Last Page")
21755      * @type String
21756      */
21757     lastText : "Last Page",
21758     /**
21759      * Customizable piece of the default paging text (defaults to "Refresh")
21760      * @type String
21761      */
21762     refreshText : "Refresh",
21763
21764     buttons : false,
21765     // private
21766     onRender : function(ct, position) 
21767     {
21768         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
21769         this.navgroup.parentId = this.id;
21770         this.navgroup.onRender(this.el, null);
21771         // add the buttons to the navgroup
21772         
21773         if(this.displayInfo){
21774             Roo.log(this.el.select('ul.navbar-nav',true).first());
21775             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
21776             this.displayEl = this.el.select('.x-paging-info', true).first();
21777 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
21778 //            this.displayEl = navel.el.select('span',true).first();
21779         }
21780         
21781         var _this = this;
21782         
21783         if(this.buttons){
21784             Roo.each(_this.buttons, function(e){
21785                Roo.factory(e).onRender(_this.el, null);
21786             });
21787         }
21788             
21789         Roo.each(_this.toolbarItems, function(e) {
21790             _this.navgroup.addItem(e);
21791         });
21792         
21793         
21794         this.first = this.navgroup.addItem({
21795             tooltip: this.firstText,
21796             cls: "prev",
21797             icon : 'fa fa-backward',
21798             disabled: true,
21799             preventDefault: true,
21800             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
21801         });
21802         
21803         this.prev =  this.navgroup.addItem({
21804             tooltip: this.prevText,
21805             cls: "prev",
21806             icon : 'fa fa-step-backward',
21807             disabled: true,
21808             preventDefault: true,
21809             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
21810         });
21811     //this.addSeparator();
21812         
21813         
21814         var field = this.navgroup.addItem( {
21815             tagtype : 'span',
21816             cls : 'x-paging-position',
21817             
21818             html : this.beforePageText  +
21819                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
21820                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
21821          } ); //?? escaped?
21822         
21823         this.field = field.el.select('input', true).first();
21824         this.field.on("keydown", this.onPagingKeydown, this);
21825         this.field.on("focus", function(){this.dom.select();});
21826     
21827     
21828         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
21829         //this.field.setHeight(18);
21830         //this.addSeparator();
21831         this.next = this.navgroup.addItem({
21832             tooltip: this.nextText,
21833             cls: "next",
21834             html : ' <i class="fa fa-step-forward">',
21835             disabled: true,
21836             preventDefault: true,
21837             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
21838         });
21839         this.last = this.navgroup.addItem({
21840             tooltip: this.lastText,
21841             icon : 'fa fa-forward',
21842             cls: "next",
21843             disabled: true,
21844             preventDefault: true,
21845             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
21846         });
21847     //this.addSeparator();
21848         this.loading = this.navgroup.addItem({
21849             tooltip: this.refreshText,
21850             icon: 'fa fa-refresh',
21851             preventDefault: true,
21852             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
21853         });
21854         
21855     },
21856
21857     // private
21858     updateInfo : function(){
21859         if(this.displayEl){
21860             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
21861             var msg = count == 0 ?
21862                 this.emptyMsg :
21863                 String.format(
21864                     this.displayMsg,
21865                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
21866                 );
21867             this.displayEl.update(msg);
21868         }
21869     },
21870
21871     // private
21872     onLoad : function(ds, r, o){
21873        this.cursor = o.params ? o.params.start : 0;
21874        var d = this.getPageData(),
21875             ap = d.activePage,
21876             ps = d.pages;
21877         
21878        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
21879        this.field.dom.value = ap;
21880        this.first.setDisabled(ap == 1);
21881        this.prev.setDisabled(ap == 1);
21882        this.next.setDisabled(ap == ps);
21883        this.last.setDisabled(ap == ps);
21884        this.loading.enable();
21885        this.updateInfo();
21886     },
21887
21888     // private
21889     getPageData : function(){
21890         var total = this.ds.getTotalCount();
21891         return {
21892             total : total,
21893             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
21894             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
21895         };
21896     },
21897
21898     // private
21899     onLoadError : function(){
21900         this.loading.enable();
21901     },
21902
21903     // private
21904     onPagingKeydown : function(e){
21905         var k = e.getKey();
21906         var d = this.getPageData();
21907         if(k == e.RETURN){
21908             var v = this.field.dom.value, pageNum;
21909             if(!v || isNaN(pageNum = parseInt(v, 10))){
21910                 this.field.dom.value = d.activePage;
21911                 return;
21912             }
21913             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
21914             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
21915             e.stopEvent();
21916         }
21917         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))
21918         {
21919           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
21920           this.field.dom.value = pageNum;
21921           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
21922           e.stopEvent();
21923         }
21924         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
21925         {
21926           var v = this.field.dom.value, pageNum; 
21927           var increment = (e.shiftKey) ? 10 : 1;
21928           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
21929             increment *= -1;
21930           if(!v || isNaN(pageNum = parseInt(v, 10))) {
21931             this.field.dom.value = d.activePage;
21932             return;
21933           }
21934           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
21935           {
21936             this.field.dom.value = parseInt(v, 10) + increment;
21937             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
21938             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
21939           }
21940           e.stopEvent();
21941         }
21942     },
21943
21944     // private
21945     beforeLoad : function(){
21946         if(this.loading){
21947             this.loading.disable();
21948         }
21949     },
21950
21951     // private
21952     onClick : function(which){
21953         
21954         var ds = this.ds;
21955         if (!ds) {
21956             return;
21957         }
21958         
21959         switch(which){
21960             case "first":
21961                 ds.load({params:{start: 0, limit: this.pageSize}});
21962             break;
21963             case "prev":
21964                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
21965             break;
21966             case "next":
21967                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
21968             break;
21969             case "last":
21970                 var total = ds.getTotalCount();
21971                 var extra = total % this.pageSize;
21972                 var lastStart = extra ? (total - extra) : total-this.pageSize;
21973                 ds.load({params:{start: lastStart, limit: this.pageSize}});
21974             break;
21975             case "refresh":
21976                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
21977             break;
21978         }
21979     },
21980
21981     /**
21982      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
21983      * @param {Roo.data.Store} store The data store to unbind
21984      */
21985     unbind : function(ds){
21986         ds.un("beforeload", this.beforeLoad, this);
21987         ds.un("load", this.onLoad, this);
21988         ds.un("loadexception", this.onLoadError, this);
21989         ds.un("remove", this.updateInfo, this);
21990         ds.un("add", this.updateInfo, this);
21991         this.ds = undefined;
21992     },
21993
21994     /**
21995      * Binds the paging toolbar to the specified {@link Roo.data.Store}
21996      * @param {Roo.data.Store} store The data store to bind
21997      */
21998     bind : function(ds){
21999         ds.on("beforeload", this.beforeLoad, this);
22000         ds.on("load", this.onLoad, this);
22001         ds.on("loadexception", this.onLoadError, this);
22002         ds.on("remove", this.updateInfo, this);
22003         ds.on("add", this.updateInfo, this);
22004         this.ds = ds;
22005     }
22006 });/*
22007  * - LGPL
22008  *
22009  * element
22010  * 
22011  */
22012
22013 /**
22014  * @class Roo.bootstrap.MessageBar
22015  * @extends Roo.bootstrap.Component
22016  * Bootstrap MessageBar class
22017  * @cfg {String} html contents of the MessageBar
22018  * @cfg {String} weight (info | success | warning | danger) default info
22019  * @cfg {String} beforeClass insert the bar before the given class
22020  * @cfg {Boolean} closable (true | false) default false
22021  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22022  * 
22023  * @constructor
22024  * Create a new Element
22025  * @param {Object} config The config object
22026  */
22027
22028 Roo.bootstrap.MessageBar = function(config){
22029     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22030 };
22031
22032 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
22033     
22034     html: '',
22035     weight: 'info',
22036     closable: false,
22037     fixed: false,
22038     beforeClass: 'bootstrap-sticky-wrap',
22039     
22040     getAutoCreate : function(){
22041         
22042         var cfg = {
22043             tag: 'div',
22044             cls: 'alert alert-dismissable alert-' + this.weight,
22045             cn: [
22046                 {
22047                     tag: 'span',
22048                     cls: 'message',
22049                     html: this.html || ''
22050                 }
22051             ]
22052         }
22053         
22054         if(this.fixed){
22055             cfg.cls += ' alert-messages-fixed';
22056         }
22057         
22058         if(this.closable){
22059             cfg.cn.push({
22060                 tag: 'button',
22061                 cls: 'close',
22062                 html: 'x'
22063             });
22064         }
22065         
22066         return cfg;
22067     },
22068     
22069     onRender : function(ct, position)
22070     {
22071         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22072         
22073         if(!this.el){
22074             var cfg = Roo.apply({},  this.getAutoCreate());
22075             cfg.id = Roo.id();
22076             
22077             if (this.cls) {
22078                 cfg.cls += ' ' + this.cls;
22079             }
22080             if (this.style) {
22081                 cfg.style = this.style;
22082             }
22083             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22084             
22085             this.el.setVisibilityMode(Roo.Element.DISPLAY);
22086         }
22087         
22088         this.el.select('>button.close').on('click', this.hide, this);
22089         
22090     },
22091     
22092     show : function()
22093     {
22094         if (!this.rendered) {
22095             this.render();
22096         }
22097         
22098         this.el.show();
22099         
22100         this.fireEvent('show', this);
22101         
22102     },
22103     
22104     hide : function()
22105     {
22106         if (!this.rendered) {
22107             this.render();
22108         }
22109         
22110         this.el.hide();
22111         
22112         this.fireEvent('hide', this);
22113     },
22114     
22115     update : function()
22116     {
22117 //        var e = this.el.dom.firstChild;
22118 //        
22119 //        if(this.closable){
22120 //            e = e.nextSibling;
22121 //        }
22122 //        
22123 //        e.data = this.html || '';
22124
22125         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22126     }
22127    
22128 });
22129
22130  
22131
22132      /*
22133  * - LGPL
22134  *
22135  * Graph
22136  * 
22137  */
22138
22139
22140 /**
22141  * @class Roo.bootstrap.Graph
22142  * @extends Roo.bootstrap.Component
22143  * Bootstrap Graph class
22144 > Prameters
22145  -sm {number} sm 4
22146  -md {number} md 5
22147  @cfg {String} graphtype  bar | vbar | pie
22148  @cfg {number} g_x coodinator | centre x (pie)
22149  @cfg {number} g_y coodinator | centre y (pie)
22150  @cfg {number} g_r radius (pie)
22151  @cfg {number} g_height height of the chart (respected by all elements in the set)
22152  @cfg {number} g_width width of the chart (respected by all elements in the set)
22153  @cfg {Object} title The title of the chart
22154     
22155  -{Array}  values
22156  -opts (object) options for the chart 
22157      o {
22158      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22159      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22160      o vgutter (number)
22161      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.
22162      o stacked (boolean) whether or not to tread values as in a stacked bar chart
22163      o to
22164      o stretch (boolean)
22165      o }
22166  -opts (object) options for the pie
22167      o{
22168      o cut
22169      o startAngle (number)
22170      o endAngle (number)
22171      } 
22172  *
22173  * @constructor
22174  * Create a new Input
22175  * @param {Object} config The config object
22176  */
22177
22178 Roo.bootstrap.Graph = function(config){
22179     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22180     
22181     this.addEvents({
22182         // img events
22183         /**
22184          * @event click
22185          * The img click event for the img.
22186          * @param {Roo.EventObject} e
22187          */
22188         "click" : true
22189     });
22190 };
22191
22192 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
22193     
22194     sm: 4,
22195     md: 5,
22196     graphtype: 'bar',
22197     g_height: 250,
22198     g_width: 400,
22199     g_x: 50,
22200     g_y: 50,
22201     g_r: 30,
22202     opts:{
22203         //g_colors: this.colors,
22204         g_type: 'soft',
22205         g_gutter: '20%'
22206
22207     },
22208     title : false,
22209
22210     getAutoCreate : function(){
22211         
22212         var cfg = {
22213             tag: 'div',
22214             html : null
22215         }
22216         
22217         
22218         return  cfg;
22219     },
22220
22221     onRender : function(ct,position){
22222         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22223         this.raphael = Raphael(this.el.dom);
22224         
22225                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22226                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22227                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22228                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22229                 /*
22230                 r.text(160, 10, "Single Series Chart").attr(txtattr);
22231                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22232                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22233                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22234                 
22235                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22236                 r.barchart(330, 10, 300, 220, data1);
22237                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22238                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22239                 */
22240                 
22241                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22242                 // r.barchart(30, 30, 560, 250,  xdata, {
22243                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22244                 //     axis : "0 0 1 1",
22245                 //     axisxlabels :  xdata
22246                 //     //yvalues : cols,
22247                    
22248                 // });
22249 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22250 //        
22251 //        this.load(null,xdata,{
22252 //                axis : "0 0 1 1",
22253 //                axisxlabels :  xdata
22254 //                });
22255
22256     },
22257
22258     load : function(graphtype,xdata,opts){
22259         this.raphael.clear();
22260         if(!graphtype) {
22261             graphtype = this.graphtype;
22262         }
22263         if(!opts){
22264             opts = this.opts;
22265         }
22266         var r = this.raphael,
22267             fin = function () {
22268                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22269             },
22270             fout = function () {
22271                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22272             },
22273             pfin = function() {
22274                 this.sector.stop();
22275                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22276
22277                 if (this.label) {
22278                     this.label[0].stop();
22279                     this.label[0].attr({ r: 7.5 });
22280                     this.label[1].attr({ "font-weight": 800 });
22281                 }
22282             },
22283             pfout = function() {
22284                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22285
22286                 if (this.label) {
22287                     this.label[0].animate({ r: 5 }, 500, "bounce");
22288                     this.label[1].attr({ "font-weight": 400 });
22289                 }
22290             };
22291
22292         switch(graphtype){
22293             case 'bar':
22294                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22295                 break;
22296             case 'hbar':
22297                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22298                 break;
22299             case 'pie':
22300 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
22301 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22302 //            
22303                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22304                 
22305                 break;
22306
22307         }
22308         
22309         if(this.title){
22310             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22311         }
22312         
22313     },
22314     
22315     setTitle: function(o)
22316     {
22317         this.title = o;
22318     },
22319     
22320     initEvents: function() {
22321         
22322         if(!this.href){
22323             this.el.on('click', this.onClick, this);
22324         }
22325     },
22326     
22327     onClick : function(e)
22328     {
22329         Roo.log('img onclick');
22330         this.fireEvent('click', this, e);
22331     }
22332    
22333 });
22334
22335  
22336 /*
22337  * - LGPL
22338  *
22339  * numberBox
22340  * 
22341  */
22342 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22343
22344 /**
22345  * @class Roo.bootstrap.dash.NumberBox
22346  * @extends Roo.bootstrap.Component
22347  * Bootstrap NumberBox class
22348  * @cfg {String} headline Box headline
22349  * @cfg {String} content Box content
22350  * @cfg {String} icon Box icon
22351  * @cfg {String} footer Footer text
22352  * @cfg {String} fhref Footer href
22353  * 
22354  * @constructor
22355  * Create a new NumberBox
22356  * @param {Object} config The config object
22357  */
22358
22359
22360 Roo.bootstrap.dash.NumberBox = function(config){
22361     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22362     
22363 };
22364
22365 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
22366     
22367     headline : '',
22368     content : '',
22369     icon : '',
22370     footer : '',
22371     fhref : '',
22372     ficon : '',
22373     
22374     getAutoCreate : function(){
22375         
22376         var cfg = {
22377             tag : 'div',
22378             cls : 'small-box ',
22379             cn : [
22380                 {
22381                     tag : 'div',
22382                     cls : 'inner',
22383                     cn :[
22384                         {
22385                             tag : 'h3',
22386                             cls : 'roo-headline',
22387                             html : this.headline
22388                         },
22389                         {
22390                             tag : 'p',
22391                             cls : 'roo-content',
22392                             html : this.content
22393                         }
22394                     ]
22395                 }
22396             ]
22397         }
22398         
22399         if(this.icon){
22400             cfg.cn.push({
22401                 tag : 'div',
22402                 cls : 'icon',
22403                 cn :[
22404                     {
22405                         tag : 'i',
22406                         cls : 'ion ' + this.icon
22407                     }
22408                 ]
22409             });
22410         }
22411         
22412         if(this.footer){
22413             var footer = {
22414                 tag : 'a',
22415                 cls : 'small-box-footer',
22416                 href : this.fhref || '#',
22417                 html : this.footer
22418             };
22419             
22420             cfg.cn.push(footer);
22421             
22422         }
22423         
22424         return  cfg;
22425     },
22426
22427     onRender : function(ct,position){
22428         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
22429
22430
22431        
22432                 
22433     },
22434
22435     setHeadline: function (value)
22436     {
22437         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
22438     },
22439     
22440     setFooter: function (value, href)
22441     {
22442         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
22443         
22444         if(href){
22445             this.el.select('a.small-box-footer',true).first().attr('href', href);
22446         }
22447         
22448     },
22449
22450     setContent: function (value)
22451     {
22452         this.el.select('.roo-content',true).first().dom.innerHTML = value;
22453     },
22454
22455     initEvents: function() 
22456     {   
22457         
22458     }
22459     
22460 });
22461
22462  
22463 /*
22464  * - LGPL
22465  *
22466  * TabBox
22467  * 
22468  */
22469 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22470
22471 /**
22472  * @class Roo.bootstrap.dash.TabBox
22473  * @extends Roo.bootstrap.Component
22474  * Bootstrap TabBox class
22475  * @cfg {String} title Title of the TabBox
22476  * @cfg {String} icon Icon of the TabBox
22477  * @cfg {Boolean} showtabs (true|false) show the tabs default true
22478  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
22479  * 
22480  * @constructor
22481  * Create a new TabBox
22482  * @param {Object} config The config object
22483  */
22484
22485
22486 Roo.bootstrap.dash.TabBox = function(config){
22487     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
22488     this.addEvents({
22489         // raw events
22490         /**
22491          * @event addpane
22492          * When a pane is added
22493          * @param {Roo.bootstrap.dash.TabPane} pane
22494          */
22495         "addpane" : true,
22496         /**
22497          * @event activatepane
22498          * When a pane is activated
22499          * @param {Roo.bootstrap.dash.TabPane} pane
22500          */
22501         "activatepane" : true
22502         
22503          
22504     });
22505     
22506     this.panes = [];
22507 };
22508
22509 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
22510
22511     title : '',
22512     icon : false,
22513     showtabs : true,
22514     tabScrollable : false,
22515     
22516     getChildContainer : function()
22517     {
22518         return this.el.select('.tab-content', true).first();
22519     },
22520     
22521     getAutoCreate : function(){
22522         
22523         var header = {
22524             tag: 'li',
22525             cls: 'pull-left header',
22526             html: this.title,
22527             cn : []
22528         };
22529         
22530         if(this.icon){
22531             header.cn.push({
22532                 tag: 'i',
22533                 cls: 'fa ' + this.icon
22534             });
22535         }
22536         
22537         var h = {
22538             tag: 'ul',
22539             cls: 'nav nav-tabs pull-right',
22540             cn: [
22541                 header
22542             ]
22543         };
22544         
22545         if(this.tabScrollable){
22546             h = {
22547                 tag: 'div',
22548                 cls: 'tab-header',
22549                 cn: [
22550                     {
22551                         tag: 'ul',
22552                         cls: 'nav nav-tabs pull-right',
22553                         cn: [
22554                             header
22555                         ]
22556                     }
22557                 ]
22558             }
22559         }
22560         
22561         var cfg = {
22562             tag: 'div',
22563             cls: 'nav-tabs-custom',
22564             cn: [
22565                 h,
22566                 {
22567                     tag: 'div',
22568                     cls: 'tab-content no-padding',
22569                     cn: []
22570                 }
22571             ]
22572         }
22573
22574         return  cfg;
22575     },
22576     initEvents : function()
22577     {
22578         //Roo.log('add add pane handler');
22579         this.on('addpane', this.onAddPane, this);
22580     },
22581      /**
22582      * Updates the box title
22583      * @param {String} html to set the title to.
22584      */
22585     setTitle : function(value)
22586     {
22587         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
22588     },
22589     onAddPane : function(pane)
22590     {
22591         this.panes.push(pane);
22592         //Roo.log('addpane');
22593         //Roo.log(pane);
22594         // tabs are rendere left to right..
22595         if(!this.showtabs){
22596             return;
22597         }
22598         
22599         var ctr = this.el.select('.nav-tabs', true).first();
22600          
22601          
22602         var existing = ctr.select('.nav-tab',true);
22603         var qty = existing.getCount();;
22604         
22605         
22606         var tab = ctr.createChild({
22607             tag : 'li',
22608             cls : 'nav-tab' + (qty ? '' : ' active'),
22609             cn : [
22610                 {
22611                     tag : 'a',
22612                     href:'#',
22613                     html : pane.title
22614                 }
22615             ]
22616         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
22617         pane.tab = tab;
22618         
22619         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
22620         if (!qty) {
22621             pane.el.addClass('active');
22622         }
22623         
22624                 
22625     },
22626     onTabClick : function(ev,un,ob,pane)
22627     {
22628         //Roo.log('tab - prev default');
22629         ev.preventDefault();
22630         
22631         
22632         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
22633         pane.tab.addClass('active');
22634         //Roo.log(pane.title);
22635         this.getChildContainer().select('.tab-pane',true).removeClass('active');
22636         // technically we should have a deactivate event.. but maybe add later.
22637         // and it should not de-activate the selected tab...
22638         this.fireEvent('activatepane', pane);
22639         pane.el.addClass('active');
22640         pane.fireEvent('activate');
22641         
22642         
22643     },
22644     
22645     getActivePane : function()
22646     {
22647         var r = false;
22648         Roo.each(this.panes, function(p) {
22649             if(p.el.hasClass('active')){
22650                 r = p;
22651                 return false;
22652             }
22653             
22654             return;
22655         });
22656         
22657         return r;
22658     }
22659     
22660     
22661 });
22662
22663  
22664 /*
22665  * - LGPL
22666  *
22667  * Tab pane
22668  * 
22669  */
22670 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22671 /**
22672  * @class Roo.bootstrap.TabPane
22673  * @extends Roo.bootstrap.Component
22674  * Bootstrap TabPane class
22675  * @cfg {Boolean} active (false | true) Default false
22676  * @cfg {String} title title of panel
22677
22678  * 
22679  * @constructor
22680  * Create a new TabPane
22681  * @param {Object} config The config object
22682  */
22683
22684 Roo.bootstrap.dash.TabPane = function(config){
22685     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
22686     
22687     this.addEvents({
22688         // raw events
22689         /**
22690          * @event activate
22691          * When a pane is activated
22692          * @param {Roo.bootstrap.dash.TabPane} pane
22693          */
22694         "activate" : true
22695          
22696     });
22697 };
22698
22699 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
22700     
22701     active : false,
22702     title : '',
22703     
22704     // the tabBox that this is attached to.
22705     tab : false,
22706      
22707     getAutoCreate : function() 
22708     {
22709         var cfg = {
22710             tag: 'div',
22711             cls: 'tab-pane'
22712         }
22713         
22714         if(this.active){
22715             cfg.cls += ' active';
22716         }
22717         
22718         return cfg;
22719     },
22720     initEvents  : function()
22721     {
22722         //Roo.log('trigger add pane handler');
22723         this.parent().fireEvent('addpane', this)
22724     },
22725     
22726      /**
22727      * Updates the tab title 
22728      * @param {String} html to set the title to.
22729      */
22730     setTitle: function(str)
22731     {
22732         if (!this.tab) {
22733             return;
22734         }
22735         this.title = str;
22736         this.tab.select('a', true).first().dom.innerHTML = str;
22737         
22738     }
22739     
22740     
22741     
22742 });
22743
22744  
22745
22746
22747  /*
22748  * - LGPL
22749  *
22750  * menu
22751  * 
22752  */
22753 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22754
22755 /**
22756  * @class Roo.bootstrap.menu.Menu
22757  * @extends Roo.bootstrap.Component
22758  * Bootstrap Menu class - container for Menu
22759  * @cfg {String} html Text of the menu
22760  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
22761  * @cfg {String} icon Font awesome icon
22762  * @cfg {String} pos Menu align to (top | bottom) default bottom
22763  * 
22764  * 
22765  * @constructor
22766  * Create a new Menu
22767  * @param {Object} config The config object
22768  */
22769
22770
22771 Roo.bootstrap.menu.Menu = function(config){
22772     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
22773     
22774     this.addEvents({
22775         /**
22776          * @event beforeshow
22777          * Fires before this menu is displayed
22778          * @param {Roo.bootstrap.menu.Menu} this
22779          */
22780         beforeshow : true,
22781         /**
22782          * @event beforehide
22783          * Fires before this menu is hidden
22784          * @param {Roo.bootstrap.menu.Menu} this
22785          */
22786         beforehide : true,
22787         /**
22788          * @event show
22789          * Fires after this menu is displayed
22790          * @param {Roo.bootstrap.menu.Menu} this
22791          */
22792         show : true,
22793         /**
22794          * @event hide
22795          * Fires after this menu is hidden
22796          * @param {Roo.bootstrap.menu.Menu} this
22797          */
22798         hide : true,
22799         /**
22800          * @event click
22801          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
22802          * @param {Roo.bootstrap.menu.Menu} this
22803          * @param {Roo.EventObject} e
22804          */
22805         click : true
22806     });
22807     
22808 };
22809
22810 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
22811     
22812     submenu : false,
22813     html : '',
22814     weight : 'default',
22815     icon : false,
22816     pos : 'bottom',
22817     
22818     
22819     getChildContainer : function() {
22820         if(this.isSubMenu){
22821             return this.el;
22822         }
22823         
22824         return this.el.select('ul.dropdown-menu', true).first();  
22825     },
22826     
22827     getAutoCreate : function()
22828     {
22829         var text = [
22830             {
22831                 tag : 'span',
22832                 cls : 'roo-menu-text',
22833                 html : this.html
22834             }
22835         ];
22836         
22837         if(this.icon){
22838             text.unshift({
22839                 tag : 'i',
22840                 cls : 'fa ' + this.icon
22841             })
22842         }
22843         
22844         
22845         var cfg = {
22846             tag : 'div',
22847             cls : 'btn-group',
22848             cn : [
22849                 {
22850                     tag : 'button',
22851                     cls : 'dropdown-button btn btn-' + this.weight,
22852                     cn : text
22853                 },
22854                 {
22855                     tag : 'button',
22856                     cls : 'dropdown-toggle btn btn-' + this.weight,
22857                     cn : [
22858                         {
22859                             tag : 'span',
22860                             cls : 'caret'
22861                         }
22862                     ]
22863                 },
22864                 {
22865                     tag : 'ul',
22866                     cls : 'dropdown-menu'
22867                 }
22868             ]
22869             
22870         };
22871         
22872         if(this.pos == 'top'){
22873             cfg.cls += ' dropup';
22874         }
22875         
22876         if(this.isSubMenu){
22877             cfg = {
22878                 tag : 'ul',
22879                 cls : 'dropdown-menu'
22880             }
22881         }
22882         
22883         return cfg;
22884     },
22885     
22886     onRender : function(ct, position)
22887     {
22888         this.isSubMenu = ct.hasClass('dropdown-submenu');
22889         
22890         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
22891     },
22892     
22893     initEvents : function() 
22894     {
22895         if(this.isSubMenu){
22896             return;
22897         }
22898         
22899         this.hidden = true;
22900         
22901         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
22902         this.triggerEl.on('click', this.onTriggerPress, this);
22903         
22904         this.buttonEl = this.el.select('button.dropdown-button', true).first();
22905         this.buttonEl.on('click', this.onClick, this);
22906         
22907     },
22908     
22909     list : function()
22910     {
22911         if(this.isSubMenu){
22912             return this.el;
22913         }
22914         
22915         return this.el.select('ul.dropdown-menu', true).first();
22916     },
22917     
22918     onClick : function(e)
22919     {
22920         this.fireEvent("click", this, e);
22921     },
22922     
22923     onTriggerPress  : function(e)
22924     {   
22925         if (this.isVisible()) {
22926             this.hide();
22927         } else {
22928             this.show();
22929         }
22930     },
22931     
22932     isVisible : function(){
22933         return !this.hidden;
22934     },
22935     
22936     show : function()
22937     {
22938         this.fireEvent("beforeshow", this);
22939         
22940         this.hidden = false;
22941         this.el.addClass('open');
22942         
22943         Roo.get(document).on("mouseup", this.onMouseUp, this);
22944         
22945         this.fireEvent("show", this);
22946         
22947         
22948     },
22949     
22950     hide : function()
22951     {
22952         this.fireEvent("beforehide", this);
22953         
22954         this.hidden = true;
22955         this.el.removeClass('open');
22956         
22957         Roo.get(document).un("mouseup", this.onMouseUp);
22958         
22959         this.fireEvent("hide", this);
22960     },
22961     
22962     onMouseUp : function()
22963     {
22964         this.hide();
22965     }
22966     
22967 });
22968
22969  
22970  /*
22971  * - LGPL
22972  *
22973  * menu item
22974  * 
22975  */
22976 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22977
22978 /**
22979  * @class Roo.bootstrap.menu.Item
22980  * @extends Roo.bootstrap.Component
22981  * Bootstrap MenuItem class
22982  * @cfg {Boolean} submenu (true | false) default false
22983  * @cfg {String} html text of the item
22984  * @cfg {String} href the link
22985  * @cfg {Boolean} disable (true | false) default false
22986  * @cfg {Boolean} preventDefault (true | false) default true
22987  * @cfg {String} icon Font awesome icon
22988  * @cfg {String} pos Submenu align to (left | right) default right 
22989  * 
22990  * 
22991  * @constructor
22992  * Create a new Item
22993  * @param {Object} config The config object
22994  */
22995
22996
22997 Roo.bootstrap.menu.Item = function(config){
22998     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
22999     this.addEvents({
23000         /**
23001          * @event mouseover
23002          * Fires when the mouse is hovering over this menu
23003          * @param {Roo.bootstrap.menu.Item} this
23004          * @param {Roo.EventObject} e
23005          */
23006         mouseover : true,
23007         /**
23008          * @event mouseout
23009          * Fires when the mouse exits this menu
23010          * @param {Roo.bootstrap.menu.Item} this
23011          * @param {Roo.EventObject} e
23012          */
23013         mouseout : true,
23014         // raw events
23015         /**
23016          * @event click
23017          * The raw click event for the entire grid.
23018          * @param {Roo.EventObject} e
23019          */
23020         click : true
23021     });
23022 };
23023
23024 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
23025     
23026     submenu : false,
23027     href : '',
23028     html : '',
23029     preventDefault: true,
23030     disable : false,
23031     icon : false,
23032     pos : 'right',
23033     
23034     getAutoCreate : function()
23035     {
23036         var text = [
23037             {
23038                 tag : 'span',
23039                 cls : 'roo-menu-item-text',
23040                 html : this.html
23041             }
23042         ];
23043         
23044         if(this.icon){
23045             text.unshift({
23046                 tag : 'i',
23047                 cls : 'fa ' + this.icon
23048             })
23049         }
23050         
23051         var cfg = {
23052             tag : 'li',
23053             cn : [
23054                 {
23055                     tag : 'a',
23056                     href : this.href || '#',
23057                     cn : text
23058                 }
23059             ]
23060         };
23061         
23062         if(this.disable){
23063             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23064         }
23065         
23066         if(this.submenu){
23067             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23068             
23069             if(this.pos == 'left'){
23070                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23071             }
23072         }
23073         
23074         return cfg;
23075     },
23076     
23077     initEvents : function() 
23078     {
23079         this.el.on('mouseover', this.onMouseOver, this);
23080         this.el.on('mouseout', this.onMouseOut, this);
23081         
23082         this.el.select('a', true).first().on('click', this.onClick, this);
23083         
23084     },
23085     
23086     onClick : function(e)
23087     {
23088         if(this.preventDefault){
23089             e.preventDefault();
23090         }
23091         
23092         this.fireEvent("click", this, e);
23093     },
23094     
23095     onMouseOver : function(e)
23096     {
23097         if(this.submenu && this.pos == 'left'){
23098             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23099         }
23100         
23101         this.fireEvent("mouseover", this, e);
23102     },
23103     
23104     onMouseOut : function(e)
23105     {
23106         this.fireEvent("mouseout", this, e);
23107     }
23108 });
23109
23110  
23111
23112  /*
23113  * - LGPL
23114  *
23115  * menu separator
23116  * 
23117  */
23118 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23119
23120 /**
23121  * @class Roo.bootstrap.menu.Separator
23122  * @extends Roo.bootstrap.Component
23123  * Bootstrap Separator class
23124  * 
23125  * @constructor
23126  * Create a new Separator
23127  * @param {Object} config The config object
23128  */
23129
23130
23131 Roo.bootstrap.menu.Separator = function(config){
23132     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23133 };
23134
23135 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
23136     
23137     getAutoCreate : function(){
23138         var cfg = {
23139             tag : 'li',
23140             cls: 'divider'
23141         };
23142         
23143         return cfg;
23144     }
23145    
23146 });
23147
23148  
23149
23150  /*
23151  * - LGPL
23152  *
23153  * Tooltip
23154  * 
23155  */
23156
23157 /**
23158  * @class Roo.bootstrap.Tooltip
23159  * Bootstrap Tooltip class
23160  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23161  * to determine which dom element triggers the tooltip.
23162  * 
23163  * It needs to add support for additional attributes like tooltip-position
23164  * 
23165  * @constructor
23166  * Create a new Toolti
23167  * @param {Object} config The config object
23168  */
23169
23170 Roo.bootstrap.Tooltip = function(config){
23171     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23172 };
23173
23174 Roo.apply(Roo.bootstrap.Tooltip, {
23175     /**
23176      * @function init initialize tooltip monitoring.
23177      * @static
23178      */
23179     currentEl : false,
23180     currentTip : false,
23181     currentRegion : false,
23182     
23183     //  init : delay?
23184     
23185     init : function()
23186     {
23187         Roo.get(document).on('mouseover', this.enter ,this);
23188         Roo.get(document).on('mouseout', this.leave, this);
23189          
23190         
23191         this.currentTip = new Roo.bootstrap.Tooltip();
23192     },
23193     
23194     enter : function(ev)
23195     {
23196         var dom = ev.getTarget();
23197         
23198         //Roo.log(['enter',dom]);
23199         var el = Roo.fly(dom);
23200         if (this.currentEl) {
23201             //Roo.log(dom);
23202             //Roo.log(this.currentEl);
23203             //Roo.log(this.currentEl.contains(dom));
23204             if (this.currentEl == el) {
23205                 return;
23206             }
23207             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23208                 return;
23209             }
23210
23211         }
23212         
23213         
23214         
23215         if (this.currentTip.el) {
23216             this.currentTip.el.hide(); // force hiding...
23217         }    
23218         //Roo.log(ev);
23219         var bindEl = el;
23220         
23221         // you can not look for children, as if el is the body.. then everythign is the child..
23222         if (!el.attr('tooltip')) { //
23223             if (!el.select("[tooltip]").elements.length) {
23224                 return;
23225             }
23226             // is the mouse over this child...?
23227             bindEl = el.select("[tooltip]").first();
23228             var xy = ev.getXY();
23229             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23230                 //Roo.log("not in region.");
23231                 return;
23232             }
23233             //Roo.log("child element over..");
23234             
23235         }
23236         this.currentEl = bindEl;
23237         this.currentTip.bind(bindEl);
23238         this.currentRegion = Roo.lib.Region.getRegion(dom);
23239         this.currentTip.enter();
23240         
23241     },
23242     leave : function(ev)
23243     {
23244         var dom = ev.getTarget();
23245         //Roo.log(['leave',dom]);
23246         if (!this.currentEl) {
23247             return;
23248         }
23249         
23250         
23251         if (dom != this.currentEl.dom) {
23252             return;
23253         }
23254         var xy = ev.getXY();
23255         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
23256             return;
23257         }
23258         // only activate leave if mouse cursor is outside... bounding box..
23259         
23260         
23261         
23262         
23263         if (this.currentTip) {
23264             this.currentTip.leave();
23265         }
23266         //Roo.log('clear currentEl');
23267         this.currentEl = false;
23268         
23269         
23270     },
23271     alignment : {
23272         'left' : ['r-l', [-2,0], 'right'],
23273         'right' : ['l-r', [2,0], 'left'],
23274         'bottom' : ['t-b', [0,2], 'top'],
23275         'top' : [ 'b-t', [0,-2], 'bottom']
23276     }
23277     
23278 });
23279
23280
23281 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
23282     
23283     
23284     bindEl : false,
23285     
23286     delay : null, // can be { show : 300 , hide: 500}
23287     
23288     timeout : null,
23289     
23290     hoverState : null, //???
23291     
23292     placement : 'bottom', 
23293     
23294     getAutoCreate : function(){
23295     
23296         var cfg = {
23297            cls : 'tooltip',
23298            role : 'tooltip',
23299            cn : [
23300                 {
23301                     cls : 'tooltip-arrow'
23302                 },
23303                 {
23304                     cls : 'tooltip-inner'
23305                 }
23306            ]
23307         };
23308         
23309         return cfg;
23310     },
23311     bind : function(el)
23312     {
23313         this.bindEl = el;
23314     },
23315       
23316     
23317     enter : function () {
23318        
23319         if (this.timeout != null) {
23320             clearTimeout(this.timeout);
23321         }
23322         
23323         this.hoverState = 'in';
23324          //Roo.log("enter - show");
23325         if (!this.delay || !this.delay.show) {
23326             this.show();
23327             return;
23328         }
23329         var _t = this;
23330         this.timeout = setTimeout(function () {
23331             if (_t.hoverState == 'in') {
23332                 _t.show();
23333             }
23334         }, this.delay.show);
23335     },
23336     leave : function()
23337     {
23338         clearTimeout(this.timeout);
23339     
23340         this.hoverState = 'out';
23341          if (!this.delay || !this.delay.hide) {
23342             this.hide();
23343             return;
23344         }
23345        
23346         var _t = this;
23347         this.timeout = setTimeout(function () {
23348             //Roo.log("leave - timeout");
23349             
23350             if (_t.hoverState == 'out') {
23351                 _t.hide();
23352                 Roo.bootstrap.Tooltip.currentEl = false;
23353             }
23354         }, delay);
23355     },
23356     
23357     show : function ()
23358     {
23359         if (!this.el) {
23360             this.render(document.body);
23361         }
23362         // set content.
23363         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23364         
23365         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23366         
23367         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23368         
23369         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23370         
23371         var placement = typeof this.placement == 'function' ?
23372             this.placement.call(this, this.el, on_el) :
23373             this.placement;
23374             
23375         var autoToken = /\s?auto?\s?/i;
23376         var autoPlace = autoToken.test(placement);
23377         if (autoPlace) {
23378             placement = placement.replace(autoToken, '') || 'top';
23379         }
23380         
23381         //this.el.detach()
23382         //this.el.setXY([0,0]);
23383         this.el.show();
23384         //this.el.dom.style.display='block';
23385         
23386         //this.el.appendTo(on_el);
23387         
23388         var p = this.getPosition();
23389         var box = this.el.getBox();
23390         
23391         if (autoPlace) {
23392             // fixme..
23393         }
23394         
23395         var align = Roo.bootstrap.Tooltip.alignment[placement];
23396         
23397         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
23398         
23399         if(placement == 'top' || placement == 'bottom'){
23400             if(xy[0] < 0){
23401                 placement = 'right';
23402             }
23403             
23404             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
23405                 placement = 'left';
23406             }
23407         }
23408         
23409         align = Roo.bootstrap.Tooltip.alignment[placement];
23410         
23411         this.el.alignTo(this.bindEl, align[0],align[1]);
23412         //var arrow = this.el.select('.arrow',true).first();
23413         //arrow.set(align[2], 
23414         
23415         this.el.addClass(placement);
23416         
23417         this.el.addClass('in fade');
23418         
23419         this.hoverState = null;
23420         
23421         if (this.el.hasClass('fade')) {
23422             // fade it?
23423         }
23424         
23425     },
23426     hide : function()
23427     {
23428          
23429         if (!this.el) {
23430             return;
23431         }
23432         //this.el.setXY([0,0]);
23433         this.el.removeClass('in');
23434         //this.el.hide();
23435         
23436     }
23437     
23438 });
23439  
23440
23441  /*
23442  * - LGPL
23443  *
23444  * Location Picker
23445  * 
23446  */
23447
23448 /**
23449  * @class Roo.bootstrap.LocationPicker
23450  * @extends Roo.bootstrap.Component
23451  * Bootstrap LocationPicker class
23452  * @cfg {Number} latitude Position when init default 0
23453  * @cfg {Number} longitude Position when init default 0
23454  * @cfg {Number} zoom default 15
23455  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
23456  * @cfg {Boolean} mapTypeControl default false
23457  * @cfg {Boolean} disableDoubleClickZoom default false
23458  * @cfg {Boolean} scrollwheel default true
23459  * @cfg {Boolean} streetViewControl default false
23460  * @cfg {Number} radius default 0
23461  * @cfg {String} locationName
23462  * @cfg {Boolean} draggable default true
23463  * @cfg {Boolean} enableAutocomplete default false
23464  * @cfg {Boolean} enableReverseGeocode default true
23465  * @cfg {String} markerTitle
23466  * 
23467  * @constructor
23468  * Create a new LocationPicker
23469  * @param {Object} config The config object
23470  */
23471
23472
23473 Roo.bootstrap.LocationPicker = function(config){
23474     
23475     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
23476     
23477     this.addEvents({
23478         /**
23479          * @event initial
23480          * Fires when the picker initialized.
23481          * @param {Roo.bootstrap.LocationPicker} this
23482          * @param {Google Location} location
23483          */
23484         initial : true,
23485         /**
23486          * @event positionchanged
23487          * Fires when the picker position changed.
23488          * @param {Roo.bootstrap.LocationPicker} this
23489          * @param {Google Location} location
23490          */
23491         positionchanged : true,
23492         /**
23493          * @event resize
23494          * Fires when the map resize.
23495          * @param {Roo.bootstrap.LocationPicker} this
23496          */
23497         resize : true,
23498         /**
23499          * @event show
23500          * Fires when the map show.
23501          * @param {Roo.bootstrap.LocationPicker} this
23502          */
23503         show : true,
23504         /**
23505          * @event hide
23506          * Fires when the map hide.
23507          * @param {Roo.bootstrap.LocationPicker} this
23508          */
23509         hide : true,
23510         /**
23511          * @event mapClick
23512          * Fires when click the map.
23513          * @param {Roo.bootstrap.LocationPicker} this
23514          * @param {Map event} e
23515          */
23516         mapClick : true,
23517         /**
23518          * @event mapRightClick
23519          * Fires when right click the map.
23520          * @param {Roo.bootstrap.LocationPicker} this
23521          * @param {Map event} e
23522          */
23523         mapRightClick : true,
23524         /**
23525          * @event markerClick
23526          * Fires when click the marker.
23527          * @param {Roo.bootstrap.LocationPicker} this
23528          * @param {Map event} e
23529          */
23530         markerClick : true,
23531         /**
23532          * @event markerRightClick
23533          * Fires when right click the marker.
23534          * @param {Roo.bootstrap.LocationPicker} this
23535          * @param {Map event} e
23536          */
23537         markerRightClick : true,
23538         /**
23539          * @event OverlayViewDraw
23540          * Fires when OverlayView Draw
23541          * @param {Roo.bootstrap.LocationPicker} this
23542          */
23543         OverlayViewDraw : true,
23544         /**
23545          * @event OverlayViewOnAdd
23546          * Fires when OverlayView Draw
23547          * @param {Roo.bootstrap.LocationPicker} this
23548          */
23549         OverlayViewOnAdd : true,
23550         /**
23551          * @event OverlayViewOnRemove
23552          * Fires when OverlayView Draw
23553          * @param {Roo.bootstrap.LocationPicker} this
23554          */
23555         OverlayViewOnRemove : true,
23556         /**
23557          * @event OverlayViewShow
23558          * Fires when OverlayView Draw
23559          * @param {Roo.bootstrap.LocationPicker} this
23560          * @param {Pixel} cpx
23561          */
23562         OverlayViewShow : true,
23563         /**
23564          * @event OverlayViewHide
23565          * Fires when OverlayView Draw
23566          * @param {Roo.bootstrap.LocationPicker} this
23567          */
23568         OverlayViewHide : true
23569     });
23570         
23571 };
23572
23573 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
23574     
23575     gMapContext: false,
23576     
23577     latitude: 0,
23578     longitude: 0,
23579     zoom: 15,
23580     mapTypeId: false,
23581     mapTypeControl: false,
23582     disableDoubleClickZoom: false,
23583     scrollwheel: true,
23584     streetViewControl: false,
23585     radius: 0,
23586     locationName: '',
23587     draggable: true,
23588     enableAutocomplete: false,
23589     enableReverseGeocode: true,
23590     markerTitle: '',
23591     
23592     getAutoCreate: function()
23593     {
23594
23595         var cfg = {
23596             tag: 'div',
23597             cls: 'roo-location-picker'
23598         };
23599         
23600         return cfg
23601     },
23602     
23603     initEvents: function(ct, position)
23604     {       
23605         if(!this.el.getWidth() || this.isApplied()){
23606             return;
23607         }
23608         
23609         this.el.setVisibilityMode(Roo.Element.DISPLAY);
23610         
23611         this.initial();
23612     },
23613     
23614     initial: function()
23615     {
23616         if(!this.mapTypeId){
23617             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
23618         }
23619         
23620         this.gMapContext = this.GMapContext();
23621         
23622         this.initOverlayView();
23623         
23624         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
23625         
23626         var _this = this;
23627                 
23628         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
23629             _this.setPosition(_this.gMapContext.marker.position);
23630         });
23631         
23632         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
23633             _this.fireEvent('mapClick', this, event);
23634             
23635         });
23636
23637         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
23638             _this.fireEvent('mapRightClick', this, event);
23639             
23640         });
23641         
23642         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
23643             _this.fireEvent('markerClick', this, event);
23644             
23645         });
23646
23647         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
23648             _this.fireEvent('markerRightClick', this, event);
23649             
23650         });
23651         
23652         this.setPosition(this.gMapContext.location);
23653         
23654         this.fireEvent('initial', this, this.gMapContext.location);
23655     },
23656     
23657     initOverlayView: function()
23658     {
23659         var _this = this;
23660         
23661         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
23662             
23663             draw: function()
23664             {
23665                 _this.fireEvent('OverlayViewDraw', _this);
23666             },
23667             
23668             onAdd: function()
23669             {
23670                 _this.fireEvent('OverlayViewOnAdd', _this);
23671             },
23672             
23673             onRemove: function()
23674             {
23675                 _this.fireEvent('OverlayViewOnRemove', _this);
23676             },
23677             
23678             show: function(cpx)
23679             {
23680                 _this.fireEvent('OverlayViewShow', _this, cpx);
23681             },
23682             
23683             hide: function()
23684             {
23685                 _this.fireEvent('OverlayViewHide', _this);
23686             }
23687             
23688         });
23689     },
23690     
23691     fromLatLngToContainerPixel: function(event)
23692     {
23693         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
23694     },
23695     
23696     isApplied: function() 
23697     {
23698         return this.getGmapContext() == false ? false : true;
23699     },
23700     
23701     getGmapContext: function() 
23702     {
23703         return this.gMapContext
23704     },
23705     
23706     GMapContext: function() 
23707     {
23708         var position = new google.maps.LatLng(this.latitude, this.longitude);
23709         
23710         var _map = new google.maps.Map(this.el.dom, {
23711             center: position,
23712             zoom: this.zoom,
23713             mapTypeId: this.mapTypeId,
23714             mapTypeControl: this.mapTypeControl,
23715             disableDoubleClickZoom: this.disableDoubleClickZoom,
23716             scrollwheel: this.scrollwheel,
23717             streetViewControl: this.streetViewControl,
23718             locationName: this.locationName,
23719             draggable: this.draggable,
23720             enableAutocomplete: this.enableAutocomplete,
23721             enableReverseGeocode: this.enableReverseGeocode
23722         });
23723         
23724         var _marker = new google.maps.Marker({
23725             position: position,
23726             map: _map,
23727             title: this.markerTitle,
23728             draggable: this.draggable
23729         });
23730         
23731         return {
23732             map: _map,
23733             marker: _marker,
23734             circle: null,
23735             location: position,
23736             radius: this.radius,
23737             locationName: this.locationName,
23738             addressComponents: {
23739                 formatted_address: null,
23740                 addressLine1: null,
23741                 addressLine2: null,
23742                 streetName: null,
23743                 streetNumber: null,
23744                 city: null,
23745                 district: null,
23746                 state: null,
23747                 stateOrProvince: null
23748             },
23749             settings: this,
23750             domContainer: this.el.dom,
23751             geodecoder: new google.maps.Geocoder()
23752         };
23753     },
23754     
23755     drawCircle: function(center, radius, options) 
23756     {
23757         if (this.gMapContext.circle != null) {
23758             this.gMapContext.circle.setMap(null);
23759         }
23760         if (radius > 0) {
23761             radius *= 1;
23762             options = Roo.apply({}, options, {
23763                 strokeColor: "#0000FF",
23764                 strokeOpacity: .35,
23765                 strokeWeight: 2,
23766                 fillColor: "#0000FF",
23767                 fillOpacity: .2
23768             });
23769             
23770             options.map = this.gMapContext.map;
23771             options.radius = radius;
23772             options.center = center;
23773             this.gMapContext.circle = new google.maps.Circle(options);
23774             return this.gMapContext.circle;
23775         }
23776         
23777         return null;
23778     },
23779     
23780     setPosition: function(location) 
23781     {
23782         this.gMapContext.location = location;
23783         this.gMapContext.marker.setPosition(location);
23784         this.gMapContext.map.panTo(location);
23785         this.drawCircle(location, this.gMapContext.radius, {});
23786         
23787         var _this = this;
23788         
23789         if (this.gMapContext.settings.enableReverseGeocode) {
23790             this.gMapContext.geodecoder.geocode({
23791                 latLng: this.gMapContext.location
23792             }, function(results, status) {
23793                 
23794                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
23795                     _this.gMapContext.locationName = results[0].formatted_address;
23796                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
23797                     
23798                     _this.fireEvent('positionchanged', this, location);
23799                 }
23800             });
23801             
23802             return;
23803         }
23804         
23805         this.fireEvent('positionchanged', this, location);
23806     },
23807     
23808     resize: function()
23809     {
23810         google.maps.event.trigger(this.gMapContext.map, "resize");
23811         
23812         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
23813         
23814         this.fireEvent('resize', this);
23815     },
23816     
23817     setPositionByLatLng: function(latitude, longitude)
23818     {
23819         this.setPosition(new google.maps.LatLng(latitude, longitude));
23820     },
23821     
23822     getCurrentPosition: function() 
23823     {
23824         return {
23825             latitude: this.gMapContext.location.lat(),
23826             longitude: this.gMapContext.location.lng()
23827         };
23828     },
23829     
23830     getAddressName: function() 
23831     {
23832         return this.gMapContext.locationName;
23833     },
23834     
23835     getAddressComponents: function() 
23836     {
23837         return this.gMapContext.addressComponents;
23838     },
23839     
23840     address_component_from_google_geocode: function(address_components) 
23841     {
23842         var result = {};
23843         
23844         for (var i = 0; i < address_components.length; i++) {
23845             var component = address_components[i];
23846             if (component.types.indexOf("postal_code") >= 0) {
23847                 result.postalCode = component.short_name;
23848             } else if (component.types.indexOf("street_number") >= 0) {
23849                 result.streetNumber = component.short_name;
23850             } else if (component.types.indexOf("route") >= 0) {
23851                 result.streetName = component.short_name;
23852             } else if (component.types.indexOf("neighborhood") >= 0) {
23853                 result.city = component.short_name;
23854             } else if (component.types.indexOf("locality") >= 0) {
23855                 result.city = component.short_name;
23856             } else if (component.types.indexOf("sublocality") >= 0) {
23857                 result.district = component.short_name;
23858             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
23859                 result.stateOrProvince = component.short_name;
23860             } else if (component.types.indexOf("country") >= 0) {
23861                 result.country = component.short_name;
23862             }
23863         }
23864         
23865         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
23866         result.addressLine2 = "";
23867         return result;
23868     },
23869     
23870     setZoomLevel: function(zoom)
23871     {
23872         this.gMapContext.map.setZoom(zoom);
23873     },
23874     
23875     show: function()
23876     {
23877         if(!this.el){
23878             return;
23879         }
23880         
23881         this.el.show();
23882         
23883         this.resize();
23884         
23885         this.fireEvent('show', this);
23886     },
23887     
23888     hide: function()
23889     {
23890         if(!this.el){
23891             return;
23892         }
23893         
23894         this.el.hide();
23895         
23896         this.fireEvent('hide', this);
23897     }
23898     
23899 });
23900
23901 Roo.apply(Roo.bootstrap.LocationPicker, {
23902     
23903     OverlayView : function(map, options)
23904     {
23905         options = options || {};
23906         
23907         this.setMap(map);
23908     }
23909     
23910     
23911 });/*
23912  * - LGPL
23913  *
23914  * Alert
23915  * 
23916  */
23917
23918 /**
23919  * @class Roo.bootstrap.Alert
23920  * @extends Roo.bootstrap.Component
23921  * Bootstrap Alert class
23922  * @cfg {String} title The title of alert
23923  * @cfg {String} html The content of alert
23924  * @cfg {String} weight (  success | info | warning | danger )
23925  * @cfg {String} faicon font-awesomeicon
23926  * 
23927  * @constructor
23928  * Create a new alert
23929  * @param {Object} config The config object
23930  */
23931
23932
23933 Roo.bootstrap.Alert = function(config){
23934     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
23935     
23936 };
23937
23938 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
23939     
23940     title: '',
23941     html: '',
23942     weight: false,
23943     faicon: false,
23944     
23945     getAutoCreate : function()
23946     {
23947         
23948         var cfg = {
23949             tag : 'div',
23950             cls : 'alert',
23951             cn : [
23952                 {
23953                     tag : 'i',
23954                     cls : 'roo-alert-icon'
23955                     
23956                 },
23957                 {
23958                     tag : 'b',
23959                     cls : 'roo-alert-title',
23960                     html : this.title
23961                 },
23962                 {
23963                     tag : 'span',
23964                     cls : 'roo-alert-text',
23965                     html : this.html
23966                 }
23967             ]
23968         };
23969         
23970         if(this.faicon){
23971             cfg.cn[0].cls += ' fa ' + this.faicon;
23972         }
23973         
23974         if(this.weight){
23975             cfg.cls += ' alert-' + this.weight;
23976         }
23977         
23978         return cfg;
23979     },
23980     
23981     initEvents: function() 
23982     {
23983         this.el.setVisibilityMode(Roo.Element.DISPLAY);
23984     },
23985     
23986     setTitle : function(str)
23987     {
23988         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
23989     },
23990     
23991     setText : function(str)
23992     {
23993         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
23994     },
23995     
23996     setWeight : function(weight)
23997     {
23998         if(this.weight){
23999             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24000         }
24001         
24002         this.weight = weight;
24003         
24004         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24005     },
24006     
24007     setIcon : function(icon)
24008     {
24009         if(this.faicon){
24010             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24011         }
24012         
24013         this.faicon = icon
24014         
24015         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24016     },
24017     
24018     hide: function() 
24019     {
24020         this.el.hide();   
24021     },
24022     
24023     show: function() 
24024     {  
24025         this.el.show();   
24026     }
24027     
24028 });
24029
24030  
24031 /*
24032 * Licence: LGPL
24033 */
24034
24035 /**
24036  * @class Roo.bootstrap.UploadCropbox
24037  * @extends Roo.bootstrap.Component
24038  * Bootstrap UploadCropbox class
24039  * @cfg {String} emptyText show when image has been loaded
24040  * @cfg {String} rotateNotify show when image too small to rotate
24041  * @cfg {Number} errorTimeout default 3000
24042  * @cfg {Number} minWidth default 300
24043  * @cfg {Number} minHeight default 300
24044  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24045  * @cfg {Boolean} isDocument (true|false) default false
24046  * 
24047  * @constructor
24048  * Create a new UploadCropbox
24049  * @param {Object} config The config object
24050  */
24051
24052 Roo.bootstrap.UploadCropbox = function(config){
24053     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
24054     
24055     this.addEvents({
24056         /**
24057          * @event beforeselectfile
24058          * Fire before select file
24059          * @param {Roo.bootstrap.UploadCropbox} this
24060          */
24061         "beforeselectfile" : true,
24062         /**
24063          * @event initial
24064          * Fire after initEvent
24065          * @param {Roo.bootstrap.UploadCropbox} this
24066          */
24067         "initial" : true,
24068         /**
24069          * @event crop
24070          * Fire after initEvent
24071          * @param {Roo.bootstrap.UploadCropbox} this
24072          * @param {String} data
24073          */
24074         "crop" : true,
24075         /**
24076          * @event prepare
24077          * Fire when preparing the file data
24078          * @param {Roo.bootstrap.UploadCropbox} this
24079          * @param {Object} file
24080          */
24081         "prepare" : true,
24082         /**
24083          * @event exception
24084          * Fire when get exception
24085          * @param {Roo.bootstrap.UploadCropbox} this
24086          * @param {Object} options
24087          */
24088         "exception" : true,
24089         /**
24090          * @event beforeloadcanvas
24091          * Fire before load the canvas
24092          * @param {Roo.bootstrap.UploadCropbox} this
24093          * @param {String} src
24094          */
24095         "beforeloadcanvas" : true,
24096         /**
24097          * @event trash
24098          * Fire when trash image
24099          * @param {Roo.bootstrap.UploadCropbox} this
24100          */
24101         "trash" : true,
24102         /**
24103          * @event download
24104          * Fire when download the image
24105          * @param {Roo.bootstrap.UploadCropbox} this
24106          */
24107         "download" : true,
24108         /**
24109          * @event footerbuttonclick
24110          * Fire when footerbuttonclick
24111          * @param {Roo.bootstrap.UploadCropbox} this
24112          * @param {String} type
24113          */
24114         "footerbuttonclick" : true,
24115         /**
24116          * @event resize
24117          * Fire when resize
24118          * @param {Roo.bootstrap.UploadCropbox} this
24119          */
24120         "resize" : true,
24121         /**
24122          * @event rotate
24123          * Fire when rotate the image
24124          * @param {Roo.bootstrap.UploadCropbox} this
24125          * @param {String} pos
24126          */
24127         "rotate" : true
24128     });
24129     
24130     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24131 };
24132
24133 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
24134     
24135     emptyText : 'Click to upload image',
24136     rotateNotify : 'Image is too small to rotate',
24137     errorTimeout : 3000,
24138     scale : 0,
24139     baseScale : 1,
24140     rotate : 0,
24141     dragable : false,
24142     pinching : false,
24143     mouseX : 0,
24144     mouseY : 0,
24145     cropData : false,
24146     minWidth : 300,
24147     minHeight : 300,
24148     file : false,
24149     exif : {},
24150     baseRotate : 1,
24151     cropType : 'image/jpeg',
24152     buttons : false,
24153     canvasLoaded : false,
24154     isDocument : false,
24155     
24156     getAutoCreate : function()
24157     {
24158         var cfg = {
24159             tag : 'div',
24160             cls : 'roo-upload-cropbox',
24161             cn : [
24162                 {
24163                     tag : 'div',
24164                     cls : 'roo-upload-cropbox-body',
24165                     style : 'cursor:pointer',
24166                     cn : [
24167                         {
24168                             tag : 'div',
24169                             cls : 'roo-upload-cropbox-preview'
24170                         },
24171                         {
24172                             tag : 'div',
24173                             cls : 'roo-upload-cropbox-thumb'
24174                         },
24175                         {
24176                             tag : 'div',
24177                             cls : 'roo-upload-cropbox-empty-notify',
24178                             html : this.emptyText
24179                         },
24180                         {
24181                             tag : 'div',
24182                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
24183                             html : this.rotateNotify
24184                         }
24185                     ]
24186                 },
24187                 {
24188                     tag : 'div',
24189                     cls : 'roo-upload-cropbox-footer',
24190                     cn : {
24191                         tag : 'div',
24192                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24193                         cn : []
24194                     }
24195                 }
24196             ]
24197         };
24198         
24199         return cfg;
24200     },
24201     
24202     onRender : function(ct, position)
24203     {
24204         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
24205         
24206         if (this.buttons.length) {
24207             
24208             Roo.each(this.buttons, function(bb) {
24209                 
24210                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
24211                 
24212                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
24213                 
24214             }, this);
24215         }
24216     },
24217     
24218     initEvents : function()
24219     {
24220         this.urlAPI = (window.createObjectURL && window) || 
24221                                 (window.URL && URL.revokeObjectURL && URL) || 
24222                                 (window.webkitURL && webkitURL);
24223                         
24224         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
24225         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24226         
24227         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
24228         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24229         
24230         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
24231         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24232         this.thumbEl.hide();
24233         
24234         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
24235         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24236         
24237         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
24238         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24239         this.errorEl.hide();
24240         
24241         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
24242         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24243         this.footerEl.hide();
24244         
24245         this.setThumbBoxSize();
24246         
24247         this.bind();
24248         
24249         this.resize();
24250         
24251         this.fireEvent('initial', this);
24252     },
24253
24254     bind : function()
24255     {
24256         var _this = this;
24257         
24258         window.addEventListener("resize", function() { _this.resize(); } );
24259         
24260         this.bodyEl.on('click', this.beforeSelectFile, this);
24261         
24262         if(Roo.isTouch){
24263             this.bodyEl.on('touchstart', this.onTouchStart, this);
24264             this.bodyEl.on('touchmove', this.onTouchMove, this);
24265             this.bodyEl.on('touchend', this.onTouchEnd, this);
24266         }
24267         
24268         if(!Roo.isTouch){
24269             this.bodyEl.on('mousedown', this.onMouseDown, this);
24270             this.bodyEl.on('mousemove', this.onMouseMove, this);
24271             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
24272             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
24273             Roo.get(document).on('mouseup', this.onMouseUp, this);
24274         }
24275     },
24276     
24277     reset : function()
24278     {    
24279         this.scale = 0;
24280         this.baseScale = 1;
24281         this.rotate = 0;
24282         this.baseRotate = 1;
24283         this.dragable = false;
24284         this.pinching = false;
24285         this.mouseX = 0;
24286         this.mouseY = 0;
24287         this.cropData = false;
24288         this.notifyEl.dom.innerHTML = this.emptyText;
24289         
24290     },
24291     
24292     resize : function()
24293     {
24294         if(this.fireEvent('resize', this) != false){
24295             this.setThumbBoxPosition();
24296             this.setCanvasPosition();
24297         }
24298     },
24299     
24300     onFooterButtonClick : function(e, el, o, type)
24301     {
24302         switch (type) {
24303             case 'rotate-left' :
24304                 this.onRotateLeft(e);
24305                 break;
24306             case 'rotate-right' :
24307                 this.onRotateRight(e);
24308                 break;
24309             case 'picture' :
24310                 this.beforeSelectFile(e);
24311                 break;
24312             case 'trash' :
24313                 this.trash(e);
24314                 break;
24315             case 'crop' :
24316                 this.crop(e);
24317                 break;
24318             case 'download' :
24319                 this.download(e);
24320                 break;
24321             default :
24322                 break;
24323         }
24324         
24325         this.fireEvent('footerbuttonclick', this, type);
24326     },
24327     
24328     beforeSelectFile : function(e)
24329     {
24330         this.fireEvent('beforeselectfile', this);
24331     },
24332     
24333     trash : function(e)
24334     {
24335         this.fireEvent('trash', this);
24336     },
24337     
24338     download : function(e)
24339     {
24340         this.fireEvent('download', this);
24341     },
24342     
24343     loadCanvas : function(src)
24344     {   
24345         if(this.fireEvent('beforeloadcanvas', this, src) != false){
24346             
24347             this.reset();
24348             
24349             this.imageEl = document.createElement('img');
24350             
24351             var _this = this;
24352             
24353             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
24354             
24355             this.imageEl.src = src;
24356         }
24357     },
24358     
24359     onLoadCanvas : function()
24360     {   
24361         this.bodyEl.un('click', this.beforeSelectFile, this);
24362         
24363         this.notifyEl.hide();
24364         this.thumbEl.show();
24365         this.footerEl.show();
24366         
24367         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
24368         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
24369         
24370         this.baseRotateLevel();
24371         
24372         if(this.isDocument){
24373             this.setThumbBoxSize();
24374         }
24375         
24376         this.setThumbBoxPosition();
24377         
24378         this.baseScaleLevel();
24379         
24380         this.draw();
24381         
24382         this.resize();
24383         
24384         this.canvasLoaded = true;
24385         
24386     },
24387     
24388     setCanvasPosition : function()
24389     {   
24390         if(!this.canvasEl){
24391             return;
24392         }
24393         
24394         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
24395         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
24396         
24397         this.previewEl.setLeft(pw);
24398         this.previewEl.setTop(ph);
24399         
24400     },
24401     
24402     onMouseDown : function(e)
24403     {   
24404         e.stopEvent();
24405         
24406         this.dragable = true;
24407         this.pinching = false;
24408         
24409         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
24410             this.dragable = false;
24411             return;
24412         }
24413         
24414         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24415         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24416         
24417     },
24418     
24419     onMouseMove : function(e)
24420     {   
24421         e.stopEvent();
24422         
24423         if(!this.canvasLoaded){
24424             return;
24425         }
24426         
24427         if (!this.dragable){
24428             return;
24429         }
24430         
24431         var minX = Math.ceil(this.thumbEl.getLeft(true));
24432         var minY = Math.ceil(this.thumbEl.getTop(true));
24433         
24434         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
24435         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
24436         
24437         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24438         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24439         
24440         x = x - this.mouseX;
24441         y = y - this.mouseY;
24442         
24443         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
24444         var bgY = Math.ceil(y + this.previewEl.getTop(true));
24445         
24446         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
24447         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
24448         
24449         this.previewEl.setLeft(bgX);
24450         this.previewEl.setTop(bgY);
24451         
24452         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24453         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24454     },
24455     
24456     onMouseUp : function(e)
24457     {   
24458         e.stopEvent();
24459         
24460         this.dragable = false;
24461     },
24462     
24463     onMouseWheel : function(e)
24464     {   
24465         e.stopEvent();
24466         
24467         this.startScale = this.scale;
24468         
24469         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
24470         
24471         if(!this.zoomable()){
24472             this.scale = this.startScale;
24473             return;
24474         }
24475         
24476         this.draw();
24477         
24478         return;
24479     },
24480     
24481     zoomable : function()
24482     {
24483         var minScale = this.thumbEl.getWidth() / this.minWidth;
24484         
24485         if(this.minWidth < this.minHeight){
24486             minScale = this.thumbEl.getHeight() / this.minHeight;
24487         }
24488         
24489         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
24490         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
24491         
24492         if(
24493                 this.isDocument &&
24494                 (this.rotate == 0 || this.rotate == 180) && 
24495                 (
24496                     width > this.imageEl.OriginWidth || 
24497                     height > this.imageEl.OriginHeight ||
24498                     (width < this.minWidth && height < this.minHeight)
24499                 )
24500         ){
24501             return false;
24502         }
24503         
24504         if(
24505                 this.isDocument &&
24506                 (this.rotate == 90 || this.rotate == 270) && 
24507                 (
24508                     width > this.imageEl.OriginWidth || 
24509                     height > this.imageEl.OriginHeight ||
24510                     (width < this.minHeight && height < this.minWidth)
24511                 )
24512         ){
24513             return false;
24514         }
24515         
24516         if(
24517                 !this.isDocument &&
24518                 (this.rotate == 0 || this.rotate == 180) && 
24519                 (
24520                     width < this.minWidth || 
24521                     width > this.imageEl.OriginWidth || 
24522                     height < this.minHeight || 
24523                     height > this.imageEl.OriginHeight
24524                 )
24525         ){
24526             return false;
24527         }
24528         
24529         if(
24530                 !this.isDocument &&
24531                 (this.rotate == 90 || this.rotate == 270) && 
24532                 (
24533                     width < this.minHeight || 
24534                     width > this.imageEl.OriginWidth || 
24535                     height < this.minWidth || 
24536                     height > this.imageEl.OriginHeight
24537                 )
24538         ){
24539             return false;
24540         }
24541         
24542         return true;
24543         
24544     },
24545     
24546     onRotateLeft : function(e)
24547     {   
24548         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
24549             
24550             var minScale = this.thumbEl.getWidth() / this.minWidth;
24551             
24552             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
24553             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
24554             
24555             this.startScale = this.scale;
24556             
24557             while (this.getScaleLevel() < minScale){
24558             
24559                 this.scale = this.scale + 1;
24560                 
24561                 if(!this.zoomable()){
24562                     break;
24563                 }
24564                 
24565                 if(
24566                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
24567                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
24568                 ){
24569                     continue;
24570                 }
24571                 
24572                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
24573
24574                 this.draw();
24575                 
24576                 return;
24577             }
24578             
24579             this.scale = this.startScale;
24580             
24581             this.onRotateFail();
24582             
24583             return false;
24584         }
24585         
24586         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
24587
24588         if(this.isDocument){
24589             this.setThumbBoxSize();
24590             this.setThumbBoxPosition();
24591             this.setCanvasPosition();
24592         }
24593         
24594         this.draw();
24595         
24596         this.fireEvent('rotate', this, 'left');
24597         
24598     },
24599     
24600     onRotateRight : function(e)
24601     {
24602         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
24603             
24604             var minScale = this.thumbEl.getWidth() / this.minWidth;
24605         
24606             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
24607             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
24608             
24609             this.startScale = this.scale;
24610             
24611             while (this.getScaleLevel() < minScale){
24612             
24613                 this.scale = this.scale + 1;
24614                 
24615                 if(!this.zoomable()){
24616                     break;
24617                 }
24618                 
24619                 if(
24620                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
24621                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
24622                 ){
24623                     continue;
24624                 }
24625                 
24626                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
24627
24628                 this.draw();
24629                 
24630                 return;
24631             }
24632             
24633             this.scale = this.startScale;
24634             
24635             this.onRotateFail();
24636             
24637             return false;
24638         }
24639         
24640         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
24641
24642         if(this.isDocument){
24643             this.setThumbBoxSize();
24644             this.setThumbBoxPosition();
24645             this.setCanvasPosition();
24646         }
24647         
24648         this.draw();
24649         
24650         this.fireEvent('rotate', this, 'right');
24651     },
24652     
24653     onRotateFail : function()
24654     {
24655         this.errorEl.show(true);
24656         
24657         var _this = this;
24658         
24659         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
24660     },
24661     
24662     draw : function()
24663     {
24664         this.previewEl.dom.innerHTML = '';
24665         
24666         var canvasEl = document.createElement("canvas");
24667         
24668         var contextEl = canvasEl.getContext("2d");
24669         
24670         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24671         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24672         var center = this.imageEl.OriginWidth / 2;
24673         
24674         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
24675             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24676             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24677             center = this.imageEl.OriginHeight / 2;
24678         }
24679         
24680         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
24681         
24682         contextEl.translate(center, center);
24683         contextEl.rotate(this.rotate * Math.PI / 180);
24684
24685         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
24686         
24687         this.canvasEl = document.createElement("canvas");
24688         
24689         this.contextEl = this.canvasEl.getContext("2d");
24690         
24691         switch (this.rotate) {
24692             case 0 :
24693                 
24694                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24695                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24696                 
24697                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24698                 
24699                 break;
24700             case 90 : 
24701                 
24702                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24703                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24704                 
24705                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24706                     this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24707                     break;
24708                 }
24709                 
24710                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24711                 
24712                 break;
24713             case 180 :
24714                 
24715                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24716                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24717                 
24718                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24719                     this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24720                     break;
24721                 }
24722                 
24723                 this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24724                 
24725                 break;
24726             case 270 :
24727                 
24728                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24729                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24730         
24731                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24732                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24733                     break;
24734                 }
24735                 
24736                 this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24737                 
24738                 break;
24739             default : 
24740                 break;
24741         }
24742         
24743         this.previewEl.appendChild(this.canvasEl);
24744         
24745         this.setCanvasPosition();
24746     },
24747     
24748     crop : function()
24749     {
24750         if(!this.canvasLoaded){
24751             return;
24752         }
24753         
24754         var imageCanvas = document.createElement("canvas");
24755         
24756         var imageContext = imageCanvas.getContext("2d");
24757         
24758         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
24759         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
24760         
24761         var center = imageCanvas.width / 2;
24762         
24763         imageContext.translate(center, center);
24764         
24765         imageContext.rotate(this.rotate * Math.PI / 180);
24766         
24767         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
24768         
24769         var canvas = document.createElement("canvas");
24770         
24771         var context = canvas.getContext("2d");
24772                 
24773         canvas.width = this.minWidth;
24774         canvas.height = this.minHeight;
24775
24776         switch (this.rotate) {
24777             case 0 :
24778                 
24779                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
24780                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
24781                 
24782                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
24783                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
24784                 
24785                 var targetWidth = this.minWidth - 2 * x;
24786                 var targetHeight = this.minHeight - 2 * y;
24787                 
24788                 var scale = 1;
24789                 
24790                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
24791                     scale = targetWidth / width;
24792                 }
24793                 
24794                 if(x > 0 && y == 0){
24795                     scale = targetHeight / height;
24796                 }
24797                 
24798                 if(x > 0 && y > 0){
24799                     scale = targetWidth / width;
24800                     
24801                     if(width < height){
24802                         scale = targetHeight / height;
24803                     }
24804                 }
24805                 
24806                 context.scale(scale, scale);
24807                 
24808                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
24809                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
24810
24811                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
24812                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
24813
24814                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
24815                 
24816                 break;
24817             case 90 : 
24818                 
24819                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
24820                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
24821                 
24822                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
24823                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
24824                 
24825                 var targetWidth = this.minWidth - 2 * x;
24826                 var targetHeight = this.minHeight - 2 * y;
24827                 
24828                 var scale = 1;
24829                 
24830                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
24831                     scale = targetWidth / width;
24832                 }
24833                 
24834                 if(x > 0 && y == 0){
24835                     scale = targetHeight / height;
24836                 }
24837                 
24838                 if(x > 0 && y > 0){
24839                     scale = targetWidth / width;
24840                     
24841                     if(width < height){
24842                         scale = targetHeight / height;
24843                     }
24844                 }
24845                 
24846                 context.scale(scale, scale);
24847                 
24848                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
24849                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
24850
24851                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
24852                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
24853                 
24854                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
24855                 
24856                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
24857                 
24858                 break;
24859             case 180 :
24860                 
24861                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
24862                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
24863                 
24864                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
24865                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
24866                 
24867                 var targetWidth = this.minWidth - 2 * x;
24868                 var targetHeight = this.minHeight - 2 * y;
24869                 
24870                 var scale = 1;
24871                 
24872                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
24873                     scale = targetWidth / width;
24874                 }
24875                 
24876                 if(x > 0 && y == 0){
24877                     scale = targetHeight / height;
24878                 }
24879                 
24880                 if(x > 0 && y > 0){
24881                     scale = targetWidth / width;
24882                     
24883                     if(width < height){
24884                         scale = targetHeight / height;
24885                     }
24886                 }
24887                 
24888                 context.scale(scale, scale);
24889                 
24890                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
24891                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
24892
24893                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
24894                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
24895
24896                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
24897                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
24898                 
24899                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
24900                 
24901                 break;
24902             case 270 :
24903                 
24904                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
24905                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
24906                 
24907                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
24908                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
24909                 
24910                 var targetWidth = this.minWidth - 2 * x;
24911                 var targetHeight = this.minHeight - 2 * y;
24912                 
24913                 var scale = 1;
24914                 
24915                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
24916                     scale = targetWidth / width;
24917                 }
24918                 
24919                 if(x > 0 && y == 0){
24920                     scale = targetHeight / height;
24921                 }
24922                 
24923                 if(x > 0 && y > 0){
24924                     scale = targetWidth / width;
24925                     
24926                     if(width < height){
24927                         scale = targetHeight / height;
24928                     }
24929                 }
24930                 
24931                 context.scale(scale, scale);
24932                 
24933                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
24934                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
24935
24936                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
24937                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
24938                 
24939                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
24940                 
24941                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
24942                 
24943                 break;
24944             default : 
24945                 break;
24946         }
24947         
24948         this.cropData = canvas.toDataURL(this.cropType);
24949         
24950         this.fireEvent('crop', this, this.cropData);
24951         
24952     },
24953     
24954     setThumbBoxSize : function()
24955     {
24956         var width, height;
24957         
24958         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
24959             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
24960             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
24961             
24962             this.minWidth = width;
24963             this.minHeight = height;
24964             
24965             if(this.rotate == 90 || this.rotate == 270){
24966                 this.minWidth = height;
24967                 this.minHeight = width;
24968             }
24969         }
24970         
24971         height = 300;
24972         width = Math.ceil(this.minWidth * height / this.minHeight);
24973         
24974         if(this.minWidth > this.minHeight){
24975             width = 300;
24976             height = Math.ceil(this.minHeight * width / this.minWidth);
24977         }
24978         
24979         this.thumbEl.setStyle({
24980             width : width + 'px',
24981             height : height + 'px'
24982         });
24983
24984         return;
24985             
24986     },
24987     
24988     setThumbBoxPosition : function()
24989     {
24990         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
24991         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
24992         
24993         this.thumbEl.setLeft(x);
24994         this.thumbEl.setTop(y);
24995         
24996     },
24997     
24998     baseRotateLevel : function()
24999     {
25000         this.baseRotate = 1;
25001         
25002         if(
25003                 typeof(this.exif) != 'undefined' &&
25004                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
25005                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
25006         ){
25007             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
25008         }
25009         
25010         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
25011         
25012     },
25013     
25014     baseScaleLevel : function()
25015     {
25016         var width, height;
25017         
25018         if(this.isDocument){
25019             
25020             if(this.baseRotate == 6 || this.baseRotate == 8){
25021             
25022                 height = this.thumbEl.getHeight();
25023                 this.baseScale = height / this.imageEl.OriginWidth;
25024
25025                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
25026                     width = this.thumbEl.getWidth();
25027                     this.baseScale = width / this.imageEl.OriginHeight;
25028                 }
25029
25030                 return;
25031             }
25032
25033             height = this.thumbEl.getHeight();
25034             this.baseScale = height / this.imageEl.OriginHeight;
25035
25036             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
25037                 width = this.thumbEl.getWidth();
25038                 this.baseScale = width / this.imageEl.OriginWidth;
25039             }
25040
25041             return;
25042         }
25043         
25044         if(this.baseRotate == 6 || this.baseRotate == 8){
25045             
25046             width = this.thumbEl.getHeight();
25047             this.baseScale = width / this.imageEl.OriginHeight;
25048             
25049             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
25050                 height = this.thumbEl.getWidth();
25051                 this.baseScale = height / this.imageEl.OriginHeight;
25052             }
25053             
25054             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25055                 height = this.thumbEl.getWidth();
25056                 this.baseScale = height / this.imageEl.OriginHeight;
25057                 
25058                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
25059                     width = this.thumbEl.getHeight();
25060                     this.baseScale = width / this.imageEl.OriginWidth;
25061                 }
25062             }
25063             
25064             return;
25065         }
25066         
25067         width = this.thumbEl.getWidth();
25068         this.baseScale = width / this.imageEl.OriginWidth;
25069         
25070         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
25071             height = this.thumbEl.getHeight();
25072             this.baseScale = height / this.imageEl.OriginHeight;
25073         }
25074         
25075         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25076             
25077             height = this.thumbEl.getHeight();
25078             this.baseScale = height / this.imageEl.OriginHeight;
25079             
25080             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
25081                 width = this.thumbEl.getWidth();
25082                 this.baseScale = width / this.imageEl.OriginWidth;
25083             }
25084             
25085         }
25086         
25087         return;
25088     },
25089     
25090     getScaleLevel : function()
25091     {
25092         return this.baseScale * Math.pow(1.1, this.scale);
25093     },
25094     
25095     onTouchStart : function(e)
25096     {
25097         if(!this.canvasLoaded){
25098             this.beforeSelectFile(e);
25099             return;
25100         }
25101         
25102         var touches = e.browserEvent.touches;
25103         
25104         if(!touches){
25105             return;
25106         }
25107         
25108         if(touches.length == 1){
25109             this.onMouseDown(e);
25110             return;
25111         }
25112         
25113         if(touches.length != 2){
25114             return;
25115         }
25116         
25117         var coords = [];
25118         
25119         for(var i = 0, finger; finger = touches[i]; i++){
25120             coords.push(finger.pageX, finger.pageY);
25121         }
25122         
25123         var x = Math.pow(coords[0] - coords[2], 2);
25124         var y = Math.pow(coords[1] - coords[3], 2);
25125         
25126         this.startDistance = Math.sqrt(x + y);
25127         
25128         this.startScale = this.scale;
25129         
25130         this.pinching = true;
25131         this.dragable = false;
25132         
25133     },
25134     
25135     onTouchMove : function(e)
25136     {
25137         if(!this.pinching && !this.dragable){
25138             return;
25139         }
25140         
25141         var touches = e.browserEvent.touches;
25142         
25143         if(!touches){
25144             return;
25145         }
25146         
25147         if(this.dragable){
25148             this.onMouseMove(e);
25149             return;
25150         }
25151         
25152         var coords = [];
25153         
25154         for(var i = 0, finger; finger = touches[i]; i++){
25155             coords.push(finger.pageX, finger.pageY);
25156         }
25157         
25158         var x = Math.pow(coords[0] - coords[2], 2);
25159         var y = Math.pow(coords[1] - coords[3], 2);
25160         
25161         this.endDistance = Math.sqrt(x + y);
25162         
25163         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
25164         
25165         if(!this.zoomable()){
25166             this.scale = this.startScale;
25167             return;
25168         }
25169         
25170         this.draw();
25171         
25172     },
25173     
25174     onTouchEnd : function(e)
25175     {
25176         this.pinching = false;
25177         this.dragable = false;
25178         
25179     },
25180     
25181     prepare : function(file)
25182     {   
25183         this.file = false;
25184         this.exif = {};
25185         
25186         if(typeof(file) === 'string'){
25187             this.loadCanvas(file);
25188             return;
25189         }
25190         
25191         if(!file || !this.urlAPI){
25192             return;
25193         }
25194         
25195         this.file = file;
25196         this.cropType = file.type;
25197         
25198         var _this = this;
25199         
25200         if(this.fireEvent('prepare', this, this.file) != false){
25201             
25202             var reader = new FileReader();
25203             
25204             reader.onload = function (e) {
25205                 if (e.target.error) {
25206                     Roo.log(e.target.error);
25207                     return;
25208                 }
25209                 
25210                 var buffer = e.target.result,
25211                     dataView = new DataView(buffer),
25212                     offset = 2,
25213                     maxOffset = dataView.byteLength - 4,
25214                     markerBytes,
25215                     markerLength;
25216                 
25217                 if (dataView.getUint16(0) === 0xffd8) {
25218                     while (offset < maxOffset) {
25219                         markerBytes = dataView.getUint16(offset);
25220                         
25221                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
25222                             markerLength = dataView.getUint16(offset + 2) + 2;
25223                             if (offset + markerLength > dataView.byteLength) {
25224                                 Roo.log('Invalid meta data: Invalid segment size.');
25225                                 break;
25226                             }
25227                             
25228                             if(markerBytes == 0xffe1){
25229                                 _this.parseExifData(
25230                                     dataView,
25231                                     offset,
25232                                     markerLength
25233                                 );
25234                             }
25235                             
25236                             offset += markerLength;
25237                             
25238                             continue;
25239                         }
25240                         
25241                         break;
25242                     }
25243                     
25244                 }
25245                 
25246                 var url = _this.urlAPI.createObjectURL(_this.file);
25247                 
25248                 _this.loadCanvas(url);
25249                 
25250                 return;
25251             }
25252             
25253             reader.readAsArrayBuffer(this.file);
25254             
25255         }
25256         
25257     },
25258     
25259     parseExifData : function(dataView, offset, length)
25260     {
25261         var tiffOffset = offset + 10,
25262             littleEndian,
25263             dirOffset;
25264     
25265         if (dataView.getUint32(offset + 4) !== 0x45786966) {
25266             // No Exif data, might be XMP data instead
25267             return;
25268         }
25269         
25270         // Check for the ASCII code for "Exif" (0x45786966):
25271         if (dataView.getUint32(offset + 4) !== 0x45786966) {
25272             // No Exif data, might be XMP data instead
25273             return;
25274         }
25275         if (tiffOffset + 8 > dataView.byteLength) {
25276             Roo.log('Invalid Exif data: Invalid segment size.');
25277             return;
25278         }
25279         // Check for the two null bytes:
25280         if (dataView.getUint16(offset + 8) !== 0x0000) {
25281             Roo.log('Invalid Exif data: Missing byte alignment offset.');
25282             return;
25283         }
25284         // Check the byte alignment:
25285         switch (dataView.getUint16(tiffOffset)) {
25286         case 0x4949:
25287             littleEndian = true;
25288             break;
25289         case 0x4D4D:
25290             littleEndian = false;
25291             break;
25292         default:
25293             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
25294             return;
25295         }
25296         // Check for the TIFF tag marker (0x002A):
25297         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
25298             Roo.log('Invalid Exif data: Missing TIFF marker.');
25299             return;
25300         }
25301         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
25302         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
25303         
25304         this.parseExifTags(
25305             dataView,
25306             tiffOffset,
25307             tiffOffset + dirOffset,
25308             littleEndian
25309         );
25310     },
25311     
25312     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
25313     {
25314         var tagsNumber,
25315             dirEndOffset,
25316             i;
25317         if (dirOffset + 6 > dataView.byteLength) {
25318             Roo.log('Invalid Exif data: Invalid directory offset.');
25319             return;
25320         }
25321         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
25322         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
25323         if (dirEndOffset + 4 > dataView.byteLength) {
25324             Roo.log('Invalid Exif data: Invalid directory size.');
25325             return;
25326         }
25327         for (i = 0; i < tagsNumber; i += 1) {
25328             this.parseExifTag(
25329                 dataView,
25330                 tiffOffset,
25331                 dirOffset + 2 + 12 * i, // tag offset
25332                 littleEndian
25333             );
25334         }
25335         // Return the offset to the next directory:
25336         return dataView.getUint32(dirEndOffset, littleEndian);
25337     },
25338     
25339     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
25340     {
25341         var tag = dataView.getUint16(offset, littleEndian);
25342         
25343         this.exif[tag] = this.getExifValue(
25344             dataView,
25345             tiffOffset,
25346             offset,
25347             dataView.getUint16(offset + 2, littleEndian), // tag type
25348             dataView.getUint32(offset + 4, littleEndian), // tag length
25349             littleEndian
25350         );
25351     },
25352     
25353     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
25354     {
25355         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
25356             tagSize,
25357             dataOffset,
25358             values,
25359             i,
25360             str,
25361             c;
25362     
25363         if (!tagType) {
25364             Roo.log('Invalid Exif data: Invalid tag type.');
25365             return;
25366         }
25367         
25368         tagSize = tagType.size * length;
25369         // Determine if the value is contained in the dataOffset bytes,
25370         // or if the value at the dataOffset is a pointer to the actual data:
25371         dataOffset = tagSize > 4 ?
25372                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
25373         if (dataOffset + tagSize > dataView.byteLength) {
25374             Roo.log('Invalid Exif data: Invalid data offset.');
25375             return;
25376         }
25377         if (length === 1) {
25378             return tagType.getValue(dataView, dataOffset, littleEndian);
25379         }
25380         values = [];
25381         for (i = 0; i < length; i += 1) {
25382             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
25383         }
25384         
25385         if (tagType.ascii) {
25386             str = '';
25387             // Concatenate the chars:
25388             for (i = 0; i < values.length; i += 1) {
25389                 c = values[i];
25390                 // Ignore the terminating NULL byte(s):
25391                 if (c === '\u0000') {
25392                     break;
25393                 }
25394                 str += c;
25395             }
25396             return str;
25397         }
25398         return values;
25399     }
25400     
25401 });
25402
25403 Roo.apply(Roo.bootstrap.UploadCropbox, {
25404     tags : {
25405         'Orientation': 0x0112
25406     },
25407     
25408     Orientation: {
25409             1: 0, //'top-left',
25410 //            2: 'top-right',
25411             3: 180, //'bottom-right',
25412 //            4: 'bottom-left',
25413 //            5: 'left-top',
25414             6: 90, //'right-top',
25415 //            7: 'right-bottom',
25416             8: 270 //'left-bottom'
25417     },
25418     
25419     exifTagTypes : {
25420         // byte, 8-bit unsigned int:
25421         1: {
25422             getValue: function (dataView, dataOffset) {
25423                 return dataView.getUint8(dataOffset);
25424             },
25425             size: 1
25426         },
25427         // ascii, 8-bit byte:
25428         2: {
25429             getValue: function (dataView, dataOffset) {
25430                 return String.fromCharCode(dataView.getUint8(dataOffset));
25431             },
25432             size: 1,
25433             ascii: true
25434         },
25435         // short, 16 bit int:
25436         3: {
25437             getValue: function (dataView, dataOffset, littleEndian) {
25438                 return dataView.getUint16(dataOffset, littleEndian);
25439             },
25440             size: 2
25441         },
25442         // long, 32 bit int:
25443         4: {
25444             getValue: function (dataView, dataOffset, littleEndian) {
25445                 return dataView.getUint32(dataOffset, littleEndian);
25446             },
25447             size: 4
25448         },
25449         // rational = two long values, first is numerator, second is denominator:
25450         5: {
25451             getValue: function (dataView, dataOffset, littleEndian) {
25452                 return dataView.getUint32(dataOffset, littleEndian) /
25453                     dataView.getUint32(dataOffset + 4, littleEndian);
25454             },
25455             size: 8
25456         },
25457         // slong, 32 bit signed int:
25458         9: {
25459             getValue: function (dataView, dataOffset, littleEndian) {
25460                 return dataView.getInt32(dataOffset, littleEndian);
25461             },
25462             size: 4
25463         },
25464         // srational, two slongs, first is numerator, second is denominator:
25465         10: {
25466             getValue: function (dataView, dataOffset, littleEndian) {
25467                 return dataView.getInt32(dataOffset, littleEndian) /
25468                     dataView.getInt32(dataOffset + 4, littleEndian);
25469             },
25470             size: 8
25471         }
25472     },
25473     
25474     footer : {
25475         STANDARD : [
25476             {
25477                 tag : 'div',
25478                 cls : 'btn-group roo-upload-cropbox-rotate-left',
25479                 action : 'rotate-left',
25480                 cn : [
25481                     {
25482                         tag : 'button',
25483                         cls : 'btn btn-default',
25484                         html : '<i class="fa fa-undo"></i>'
25485                     }
25486                 ]
25487             },
25488             {
25489                 tag : 'div',
25490                 cls : 'btn-group roo-upload-cropbox-picture',
25491                 action : 'picture',
25492                 cn : [
25493                     {
25494                         tag : 'button',
25495                         cls : 'btn btn-default',
25496                         html : '<i class="fa fa-picture-o"></i>'
25497                     }
25498                 ]
25499             },
25500             {
25501                 tag : 'div',
25502                 cls : 'btn-group roo-upload-cropbox-rotate-right',
25503                 action : 'rotate-right',
25504                 cn : [
25505                     {
25506                         tag : 'button',
25507                         cls : 'btn btn-default',
25508                         html : '<i class="fa fa-repeat"></i>'
25509                     }
25510                 ]
25511             }
25512         ],
25513         DOCUMENT : [
25514             {
25515                 tag : 'div',
25516                 cls : 'btn-group roo-upload-cropbox-rotate-left',
25517                 action : 'rotate-left',
25518                 cn : [
25519                     {
25520                         tag : 'button',
25521                         cls : 'btn btn-default',
25522                         html : '<i class="fa fa-undo"></i>'
25523                     }
25524                 ]
25525             },
25526             {
25527                 tag : 'div',
25528                 cls : 'btn-group roo-upload-cropbox-download',
25529                 action : 'download',
25530                 cn : [
25531                     {
25532                         tag : 'button',
25533                         cls : 'btn btn-default',
25534                         html : '<i class="fa fa-download"></i>'
25535                     }
25536                 ]
25537             },
25538             {
25539                 tag : 'div',
25540                 cls : 'btn-group roo-upload-cropbox-crop',
25541                 action : 'crop',
25542                 cn : [
25543                     {
25544                         tag : 'button',
25545                         cls : 'btn btn-default',
25546                         html : '<i class="fa fa-crop"></i>'
25547                     }
25548                 ]
25549             },
25550             {
25551                 tag : 'div',
25552                 cls : 'btn-group roo-upload-cropbox-trash',
25553                 action : 'trash',
25554                 cn : [
25555                     {
25556                         tag : 'button',
25557                         cls : 'btn btn-default',
25558                         html : '<i class="fa fa-trash"></i>'
25559                     }
25560                 ]
25561             },
25562             {
25563                 tag : 'div',
25564                 cls : 'btn-group roo-upload-cropbox-rotate-right',
25565                 action : 'rotate-right',
25566                 cn : [
25567                     {
25568                         tag : 'button',
25569                         cls : 'btn btn-default',
25570                         html : '<i class="fa fa-repeat"></i>'
25571                     }
25572                 ]
25573             }
25574         ],
25575         ROTATOR : [
25576             {
25577                 tag : 'div',
25578                 cls : 'btn-group roo-upload-cropbox-rotate-left',
25579                 action : 'rotate-left',
25580                 cn : [
25581                     {
25582                         tag : 'button',
25583                         cls : 'btn btn-default',
25584                         html : '<i class="fa fa-undo"></i>'
25585                     }
25586                 ]
25587             },
25588             {
25589                 tag : 'div',
25590                 cls : 'btn-group roo-upload-cropbox-rotate-right',
25591                 action : 'rotate-right',
25592                 cn : [
25593                     {
25594                         tag : 'button',
25595                         cls : 'btn btn-default',
25596                         html : '<i class="fa fa-repeat"></i>'
25597                     }
25598                 ]
25599             }
25600         ]
25601     }
25602 });
25603
25604 /*
25605 * Licence: LGPL
25606 */
25607
25608 /**
25609  * @class Roo.bootstrap.DocumentManager
25610  * @extends Roo.bootstrap.Component
25611  * Bootstrap DocumentManager class
25612  * @cfg {String} paramName default 'imageUpload'
25613  * @cfg {String} method default POST
25614  * @cfg {String} url action url
25615  * @cfg {Number} boxes number of boxes default 12
25616  * @cfg {Boolean} multiple multiple upload default true
25617  * @cfg {Number} minWidth default 300
25618  * @cfg {Number} minHeight default 300
25619  * @cfg {Number} thumbSize default 300
25620  * @cfg {String} fieldLabel
25621  * @cfg {Number} labelWidth default 4
25622  * @cfg {String} labelAlign (left|top) default left
25623  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
25624  * 
25625  * @constructor
25626  * Create a new DocumentManager
25627  * @param {Object} config The config object
25628  */
25629
25630 Roo.bootstrap.DocumentManager = function(config){
25631     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
25632     
25633     this.addEvents({
25634         /**
25635          * @event initial
25636          * Fire when initial the DocumentManager
25637          * @param {Roo.bootstrap.DocumentManager} this
25638          */
25639         "initial" : true,
25640         /**
25641          * @event inspect
25642          * inspect selected file
25643          * @param {Roo.bootstrap.DocumentManager} this
25644          * @param {File} file
25645          */
25646         "inspect" : true,
25647         /**
25648          * @event exception
25649          * Fire when xhr load exception
25650          * @param {Roo.bootstrap.DocumentManager} this
25651          * @param {XMLHttpRequest} xhr
25652          */
25653         "exception" : true,
25654         /**
25655          * @event prepare
25656          * prepare the form data
25657          * @param {Roo.bootstrap.DocumentManager} this
25658          * @param {Object} formData
25659          */
25660         "prepare" : true,
25661         /**
25662          * @event remove
25663          * Fire when remove the file
25664          * @param {Roo.bootstrap.DocumentManager} this
25665          * @param {Object} file
25666          */
25667         "remove" : true,
25668         /**
25669          * @event refresh
25670          * Fire after refresh the file
25671          * @param {Roo.bootstrap.DocumentManager} this
25672          */
25673         "refresh" : true,
25674         /**
25675          * @event click
25676          * Fire after click the image
25677          * @param {Roo.bootstrap.DocumentManager} this
25678          * @param {Object} file
25679          */
25680         "click" : true,
25681         /**
25682          * @event edit
25683          * Fire when upload a image and editable set to true
25684          * @param {Roo.bootstrap.DocumentManager} this
25685          * @param {Object} file
25686          */
25687         "edit" : true
25688         
25689     });
25690 };
25691
25692 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
25693     
25694     boxes : 12,
25695     inputName : '',
25696     minWidth : 300,
25697     minHeight : 300,
25698     thumbSize : 300,
25699     multiple : true,
25700     files : [],
25701     method : 'POST',
25702     url : '',
25703     paramName : 'imageUpload',
25704     fieldLabel : '',
25705     labelWidth : 4,
25706     labelAlign : 'left',
25707     editable : true,
25708     delegates : [],
25709     
25710     getAutoCreate : function()
25711     {   
25712         var managerWidget = {
25713             tag : 'div',
25714             cls : 'roo-document-manager',
25715             cn : [
25716                 {
25717                     tag : 'input',
25718                     cls : 'roo-document-manager-selector',
25719                     type : 'file'
25720                 },
25721                 {
25722                     tag : 'div',
25723                     cls : 'roo-document-manager-uploader',
25724                     cn : [
25725                         {
25726                             tag : 'div',
25727                             cls : 'roo-document-manager-upload-btn',
25728                             html : '<i class="fa fa-plus"></i>'
25729                         }
25730                     ]
25731                     
25732                 }
25733             ]
25734         };
25735         
25736         var content = [
25737             {
25738                 tag : 'div',
25739                 cls : 'column col-md-12',
25740                 cn : managerWidget
25741             }
25742         ];
25743         
25744         if(this.fieldLabel.length){
25745             
25746             content = [
25747                 {
25748                     tag : 'div',
25749                     cls : 'column col-md-12',
25750                     html : this.fieldLabel
25751                 },
25752                 {
25753                     tag : 'div',
25754                     cls : 'column col-md-12',
25755                     cn : managerWidget
25756                 }
25757             ];
25758
25759             if(this.labelAlign == 'left'){
25760                 content = [
25761                     {
25762                         tag : 'div',
25763                         cls : 'column col-md-' + this.labelWidth,
25764                         html : this.fieldLabel
25765                     },
25766                     {
25767                         tag : 'div',
25768                         cls : 'column col-md-' + (12 - this.labelWidth),
25769                         cn : managerWidget
25770                     }
25771                 ];
25772                 
25773             }
25774         }
25775         
25776         var cfg = {
25777             tag : 'div',
25778             cls : 'row clearfix',
25779             cn : content
25780         };
25781         
25782         return cfg;
25783         
25784     },
25785     
25786     initEvents : function()
25787     {
25788         this.managerEl = this.el.select('.roo-document-manager', true).first();
25789         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25790         
25791         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
25792         this.selectorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25793         this.selectorEl.hide();
25794         
25795         if(this.multiple){
25796             this.selectorEl.attr('multiple', 'multiple');
25797         }
25798         
25799         this.selectorEl.on('change', this.onFileSelected, this);
25800         
25801         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
25802         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25803         
25804         this.uploader.on('click', this.onUploaderClick, this);
25805         
25806         this.renderProgressDialog();
25807         
25808         var _this = this;
25809         
25810         window.addEventListener("resize", function() { _this.refresh(); } );
25811         
25812         this.fireEvent('initial', this);
25813     },
25814     
25815     renderProgressDialog : function()
25816     {
25817         var _this = this;
25818         
25819         this.progressDialog = new Roo.bootstrap.Modal({
25820             cls : 'roo-document-manager-progress-dialog',
25821             allow_close : false,
25822             title : '',
25823             buttons : [
25824                 {
25825                     name  :'cancel',
25826                     weight : 'danger',
25827                     html : 'Cancel'
25828                 }
25829             ], 
25830             listeners : { 
25831                 btnclick : function() {
25832                     _this.uploadCancel();
25833                     this.hide();
25834                 }
25835             }
25836         });
25837          
25838         this.progressDialog.render(Roo.get(document.body));
25839          
25840         this.progress = new Roo.bootstrap.Progress({
25841             cls : 'roo-document-manager-progress',
25842             active : true,
25843             striped : true
25844         });
25845         
25846         this.progress.render(this.progressDialog.getChildContainer());
25847         
25848         this.progressBar = new Roo.bootstrap.ProgressBar({
25849             cls : 'roo-document-manager-progress-bar',
25850             aria_valuenow : 0,
25851             aria_valuemin : 0,
25852             aria_valuemax : 12,
25853             panel : 'success'
25854         });
25855         
25856         this.progressBar.render(this.progress.getChildContainer());
25857     },
25858     
25859     onUploaderClick : function(e)
25860     {
25861         e.preventDefault();
25862         this.selectorEl.dom.click();
25863     },
25864     
25865     onFileSelected : function(e)
25866     {
25867         e.preventDefault();
25868         
25869         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
25870             return;
25871         }
25872         
25873         Roo.each(this.selectorEl.dom.files, function(file){
25874             if(this.fireEvent('inspect', this, file) != false){
25875                 this.files.push(file);
25876             }
25877         }, this);
25878         
25879         this.queue();
25880         
25881     },
25882     
25883     queue : function()
25884     {
25885         this.selectorEl.dom.value = '';
25886         
25887         if(!this.files.length){
25888             return;
25889         }
25890         
25891         if(this.files.length > this.boxes){
25892             this.files = this.files.slice(0, this.boxes);
25893         }
25894         
25895         this.uploader.show();
25896         
25897         if(this.files.length > this.boxes - 1){
25898             this.uploader.hide();
25899         }
25900         
25901         var _this = this;
25902         
25903         var files = [];
25904         
25905         Roo.each(this.files, function(file){
25906             
25907             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
25908                 var f = this.renderPreview(file);
25909                 files.push(f);
25910                 return;
25911             }
25912             
25913             this.delegates.push(
25914                 (function(){
25915                     _this.process(file);
25916                 }).createDelegate(this)
25917             );
25918             
25919         }, this);
25920         
25921         this.files = files;
25922         
25923         if(!this.delegates.length){
25924             this.refresh();
25925             return;
25926         }
25927         
25928         this.progressBar.aria_valuemax = this.delegates.length;
25929         
25930         this.arrange();
25931         
25932         return;
25933     },
25934     
25935     arrange : function()
25936     {
25937         if(!this.delegates.length){
25938             this.progressDialog.hide();
25939             this.refresh();
25940             return;
25941         }
25942         
25943         var delegate = this.delegates.shift();
25944         
25945         this.progressDialog.show();
25946         
25947         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
25948         
25949         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
25950         
25951         delegate();
25952     },
25953     
25954     refresh : function()
25955     {
25956         this.uploader.show();
25957         
25958         if(this.files.length > this.boxes - 1){
25959             this.uploader.hide();
25960         }
25961         
25962         Roo.isTouch ? this.closable(false) : this.closable(true);
25963         
25964         this.fireEvent('refresh', this);
25965     },
25966     
25967     onRemove : function(e, el, o)
25968     {
25969         e.preventDefault();
25970         
25971         this.fireEvent('remove', this, o);
25972         
25973     },
25974     
25975     remove : function(o)
25976     {
25977         var files = [];
25978         
25979         Roo.each(this.files, function(file){
25980             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
25981                 files.push(file);
25982                 return;
25983             }
25984
25985             o.target.remove();
25986
25987         }, this);
25988         
25989         this.files = files;
25990         
25991         this.refresh();
25992     },
25993     
25994     onClick : function(e, el, o)
25995     {
25996         e.preventDefault();
25997         
25998         this.fireEvent('click', this, o);
25999         
26000     },
26001     
26002     closable : function(closable)
26003     {
26004         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
26005             
26006             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26007             
26008             if(closable){
26009                 el.show();
26010                 return;
26011             }
26012             
26013             el.hide();
26014             
26015         }, this);
26016     },
26017     
26018     xhrOnLoad : function(xhr)
26019     {
26020         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26021             el.remove();
26022         }, this);
26023         
26024         if (xhr.readyState !== 4) {
26025             this.arrange();
26026             this.fireEvent('exception', this, xhr);
26027             return;
26028         }
26029
26030         var response = Roo.decode(xhr.responseText);
26031         
26032         if(!response.success){
26033             this.arrange();
26034             this.fireEvent('exception', this, xhr);
26035             return;
26036         }
26037         
26038         var file = this.renderPreview(response.data);
26039         
26040         this.files.push(file);
26041         
26042         this.arrange();
26043         
26044     },
26045     
26046     xhrOnError : function()
26047     {
26048         Roo.log('xhr on error');
26049         
26050         var response = Roo.decode(xhr.responseText);
26051           
26052         Roo.log(response);
26053         
26054         this.arrange();
26055     },
26056     
26057     process : function(file)
26058     {
26059         if(this.editable && file.type.indexOf('image') != -1){
26060             this.fireEvent('edit', this, file);
26061             return;
26062         }
26063         
26064         this.uploadStart(file, false);
26065         
26066         return;
26067     },
26068     
26069     uploadStart : function(file, crop)
26070     {
26071         this.xhr = new XMLHttpRequest();
26072         
26073         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26074             this.arrange();
26075             return;
26076         }
26077         
26078         file.xhr = this.xhr;
26079             
26080         this.managerEl.createChild({
26081             tag : 'div',
26082             cls : 'roo-document-manager-loading',
26083             cn : [
26084                 {
26085                     tag : 'div',
26086                     tooltip : file.name,
26087                     cls : 'roo-document-manager-thumb',
26088                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26089                 }
26090             ]
26091
26092         });
26093
26094         this.xhr.open(this.method, this.url, true);
26095         
26096         var headers = {
26097             "Accept": "application/json",
26098             "Cache-Control": "no-cache",
26099             "X-Requested-With": "XMLHttpRequest"
26100         };
26101         
26102         for (var headerName in headers) {
26103             var headerValue = headers[headerName];
26104             if (headerValue) {
26105                 this.xhr.setRequestHeader(headerName, headerValue);
26106             }
26107         }
26108         
26109         var _this = this;
26110         
26111         this.xhr.onload = function()
26112         {
26113             _this.xhrOnLoad(_this.xhr);
26114         }
26115         
26116         this.xhr.onerror = function()
26117         {
26118             _this.xhrOnError(_this.xhr);
26119         }
26120         
26121         var formData = new FormData();
26122
26123         formData.append('returnHTML', 'NO');
26124         
26125         if(crop){
26126             formData.append('crop', crop);
26127         }
26128         
26129         formData.append(this.paramName, file, file.name);
26130         
26131         if(this.fireEvent('prepare', this, formData) != false){
26132             this.xhr.send(formData);
26133         };
26134     },
26135     
26136     uploadCancel : function()
26137     {
26138         this.xhr.abort();
26139         
26140         this.delegates = [];
26141         
26142         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26143             el.remove();
26144         }, this);
26145         
26146         this.arrange();
26147     },
26148     
26149     renderPreview : function(file)
26150     {
26151         if(typeof(file.target) != 'undefined' && file.target){
26152             return file;
26153         }
26154         
26155         var previewEl = this.managerEl.createChild({
26156             tag : 'div',
26157             cls : 'roo-document-manager-preview',
26158             cn : [
26159                 {
26160                     tag : 'div',
26161                     tooltip : file.filename,
26162                     cls : 'roo-document-manager-thumb',
26163                     html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
26164                 },
26165                 {
26166                     tag : 'button',
26167                     cls : 'close',
26168                     html : '<i class="fa fa-times-circle"></i>'
26169                 }
26170             ]
26171         });
26172
26173         var close = previewEl.select('button.close', true).first();
26174
26175         close.on('click', this.onRemove, this, file);
26176
26177         file.target = previewEl;
26178
26179         var image = previewEl.select('img', true).first();
26180         
26181         var _this = this;
26182         
26183         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
26184         
26185         image.on('click', this.onClick, this, file);
26186         
26187         return file;
26188         
26189     },
26190     
26191     onPreviewLoad : function(file, image)
26192     {
26193         if(typeof(file.target) == 'undefined' || !file.target){
26194             return;
26195         }
26196         
26197         var width = image.dom.naturalWidth || image.dom.width;
26198         var height = image.dom.naturalHeight || image.dom.height;
26199         
26200         if(width > height){
26201             file.target.addClass('wide');
26202             return;
26203         }
26204         
26205         file.target.addClass('tall');
26206         return;
26207         
26208     }
26209 });
26210
26211 /*
26212 * Licence: LGPL
26213 */
26214
26215 /**
26216  * @class Roo.bootstrap.DocumentViewer
26217  * @extends Roo.bootstrap.Component
26218  * Bootstrap DocumentViewer class
26219  * 
26220  * @constructor
26221  * Create a new DocumentViewer
26222  * @param {Object} config The config object
26223  */
26224
26225 Roo.bootstrap.DocumentViewer = function(config){
26226     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
26227     
26228     this.addEvents({
26229         /**
26230          * @event initial
26231          * Fire after initEvent
26232          * @param {Roo.bootstrap.DocumentViewer} this
26233          */
26234         "initial" : true,
26235         /**
26236          * @event click
26237          * Fire after click
26238          * @param {Roo.bootstrap.DocumentViewer} this
26239          */
26240         "click" : true,
26241         /**
26242          * @event trash
26243          * Fire after trash button
26244          * @param {Roo.bootstrap.DocumentViewer} this
26245          */
26246         "trash" : true
26247         
26248     });
26249 };
26250
26251 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
26252     
26253     getAutoCreate : function()
26254     {
26255         var cfg = {
26256             tag : 'div',
26257             cls : 'roo-document-viewer',
26258             cn : [
26259                 {
26260                     tag : 'div',
26261                     cls : 'roo-document-viewer-body',
26262                     cn : [
26263                         {
26264                             tag : 'div',
26265                             cls : 'roo-document-viewer-thumb',
26266                             cn : [
26267                                 {
26268                                     tag : 'img',
26269                                     cls : 'roo-document-viewer-image'
26270                                 }
26271                             ]
26272                         }
26273                     ]
26274                 },
26275                 {
26276                     tag : 'div',
26277                     cls : 'roo-document-viewer-footer',
26278                     cn : {
26279                         tag : 'div',
26280                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
26281                         cn : [
26282                             {
26283                                 tag : 'div',
26284                                 cls : 'btn-group',
26285                                 cn : [
26286                                     {
26287                                         tag : 'button',
26288                                         cls : 'btn btn-default roo-document-viewer-trash',
26289                                         html : '<i class="fa fa-trash"></i>'
26290                                     }
26291                                 ]
26292                             }
26293                         ]
26294                     }
26295                 }
26296             ]
26297         };
26298         
26299         return cfg;
26300     },
26301     
26302     initEvents : function()
26303     {
26304         
26305         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
26306         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26307         
26308         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
26309         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26310         
26311         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
26312         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26313         
26314         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
26315         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26316         
26317         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
26318         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26319         
26320         this.bodyEl.on('click', this.onClick, this);
26321         
26322         this.trashBtn.on('click', this.onTrash, this);
26323         
26324     },
26325     
26326     initial : function()
26327     {
26328 //        this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
26329         
26330         
26331         this.fireEvent('initial', this);
26332         
26333     },
26334     
26335     onClick : function(e)
26336     {
26337         e.preventDefault();
26338         
26339         this.fireEvent('click', this);
26340     },
26341     
26342     onTrash : function(e)
26343     {
26344         e.preventDefault();
26345         
26346         this.fireEvent('trash', this);
26347     }
26348     
26349 });