4b93713b2c0b5de5b423384edc85ea434fabbdbd
[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 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: 'about:blank',
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             src : 'about:blank'  // just incase src get's set to undefined?!?
1443         };
1444         
1445         cfg.html = this.html || cfg.html;
1446         
1447         cfg.src = this.src || cfg.src;
1448         
1449         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1450             cfg.cls += ' img-' + this.border;
1451         }
1452         
1453         if(this.alt){
1454             cfg.alt = this.alt;
1455         }
1456         
1457         if(this.href){
1458             var a = {
1459                 tag: 'a',
1460                 href: this.href,
1461                 cn: [
1462                     cfg
1463                 ]
1464             };
1465             
1466             if(this.target){
1467                 a.target = this.target;
1468             }
1469             
1470         }
1471         
1472         return (this.href) ? a : cfg;
1473     },
1474     
1475     initEvents: function() 
1476     {
1477         if(!this.href){
1478             this.el.on('click', this.onClick, this);
1479         }
1480         
1481     },
1482     
1483     onClick : function(e)
1484     {
1485         Roo.log('img onclick');
1486         this.fireEvent('click', this, e);
1487     }
1488    
1489 });
1490
1491  /*
1492  * - LGPL
1493  *
1494  * image
1495  * 
1496  */
1497
1498
1499 /**
1500  * @class Roo.bootstrap.Link
1501  * @extends Roo.bootstrap.Component
1502  * Bootstrap Link Class
1503  * @cfg {String} alt image alternative text
1504  * @cfg {String} href a tag href
1505  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1506  * @cfg {String} html the content of the link.
1507  * @cfg {String} anchor name for the anchor link
1508
1509  * @cfg {Boolean} preventDefault (true | false) default false
1510
1511  * 
1512  * @constructor
1513  * Create a new Input
1514  * @param {Object} config The config object
1515  */
1516
1517 Roo.bootstrap.Link = function(config){
1518     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1519     
1520     this.addEvents({
1521         // img events
1522         /**
1523          * @event click
1524          * The img click event for the img.
1525          * @param {Roo.EventObject} e
1526          */
1527         "click" : true
1528     });
1529 };
1530
1531 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1532     
1533     href: false,
1534     target: false,
1535     preventDefault: false,
1536     anchor : false,
1537     alt : false,
1538
1539     getAutoCreate : function()
1540     {
1541         
1542         var cfg = {
1543             tag: 'a'
1544         };
1545         // anchor's do not require html/href...
1546         if (this.anchor === false) {
1547             cfg.html = this.html || '';
1548             cfg.href = this.href || '#';
1549         } else {
1550             cfg.name = this.anchor;
1551             if (this.html !== false) {
1552                 cfg.html = this.html;
1553             }
1554             if (this.href !== false) {
1555                 cfg.href = this.href;
1556             }
1557         }
1558         
1559         if(this.alt !== false){
1560             cfg.alt = this.alt;
1561         }
1562         
1563         
1564         if(this.target !== false) {
1565             cfg.target = this.target;
1566         }
1567         
1568         return cfg;
1569     },
1570     
1571     initEvents: function() {
1572         
1573         if(!this.href || this.preventDefault){
1574             this.el.on('click', this.onClick, this);
1575         }
1576     },
1577     
1578     onClick : function(e)
1579     {
1580         if(this.preventDefault){
1581             e.preventDefault();
1582         }
1583         //Roo.log('img onclick');
1584         this.fireEvent('click', this, e);
1585     }
1586    
1587 });
1588
1589  /*
1590  * - LGPL
1591  *
1592  * header
1593  * 
1594  */
1595
1596 /**
1597  * @class Roo.bootstrap.Header
1598  * @extends Roo.bootstrap.Component
1599  * Bootstrap Header class
1600  * @cfg {String} html content of header
1601  * @cfg {Number} level (1|2|3|4|5|6) default 1
1602  * 
1603  * @constructor
1604  * Create a new Header
1605  * @param {Object} config The config object
1606  */
1607
1608
1609 Roo.bootstrap.Header  = function(config){
1610     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1611 };
1612
1613 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1614     
1615     //href : false,
1616     html : false,
1617     level : 1,
1618     
1619     
1620     
1621     getAutoCreate : function(){
1622         
1623         
1624         
1625         var cfg = {
1626             tag: 'h' + (1 *this.level),
1627             html: this.html || ''
1628         } ;
1629         
1630         return cfg;
1631     }
1632    
1633 });
1634
1635  
1636
1637  /*
1638  * Based on:
1639  * Ext JS Library 1.1.1
1640  * Copyright(c) 2006-2007, Ext JS, LLC.
1641  *
1642  * Originally Released Under LGPL - original licence link has changed is not relivant.
1643  *
1644  * Fork - LGPL
1645  * <script type="text/javascript">
1646  */
1647  
1648 /**
1649  * @class Roo.bootstrap.MenuMgr
1650  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1651  * @singleton
1652  */
1653 Roo.bootstrap.MenuMgr = function(){
1654    var menus, active, groups = {}, attached = false, lastShow = new Date();
1655
1656    // private - called when first menu is created
1657    function init(){
1658        menus = {};
1659        active = new Roo.util.MixedCollection();
1660        Roo.get(document).addKeyListener(27, function(){
1661            if(active.length > 0){
1662                hideAll();
1663            }
1664        });
1665    }
1666
1667    // private
1668    function hideAll(){
1669        if(active && active.length > 0){
1670            var c = active.clone();
1671            c.each(function(m){
1672                m.hide();
1673            });
1674        }
1675    }
1676
1677    // private
1678    function onHide(m){
1679        active.remove(m);
1680        if(active.length < 1){
1681            Roo.get(document).un("mouseup", onMouseDown);
1682             
1683            attached = false;
1684        }
1685    }
1686
1687    // private
1688    function onShow(m){
1689        var last = active.last();
1690        lastShow = new Date();
1691        active.add(m);
1692        if(!attached){
1693           Roo.get(document).on("mouseup", onMouseDown);
1694            
1695            attached = true;
1696        }
1697        if(m.parentMenu){
1698           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1699           m.parentMenu.activeChild = m;
1700        }else if(last && last.isVisible()){
1701           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1702        }
1703    }
1704
1705    // private
1706    function onBeforeHide(m){
1707        if(m.activeChild){
1708            m.activeChild.hide();
1709        }
1710        if(m.autoHideTimer){
1711            clearTimeout(m.autoHideTimer);
1712            delete m.autoHideTimer;
1713        }
1714    }
1715
1716    // private
1717    function onBeforeShow(m){
1718        var pm = m.parentMenu;
1719        if(!pm && !m.allowOtherMenus){
1720            hideAll();
1721        }else if(pm && pm.activeChild && active != m){
1722            pm.activeChild.hide();
1723        }
1724    }
1725
1726    // private this should really trigger on mouseup..
1727    function onMouseDown(e){
1728         Roo.log("on Mouse Up");
1729         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu") && !e.getTarget('.user-menu')){
1730             Roo.log("hideAll");
1731             hideAll();
1732             e.stopEvent();
1733         }
1734         
1735         
1736    }
1737
1738    // private
1739    function onBeforeCheck(mi, state){
1740        if(state){
1741            var g = groups[mi.group];
1742            for(var i = 0, l = g.length; i < l; i++){
1743                if(g[i] != mi){
1744                    g[i].setChecked(false);
1745                }
1746            }
1747        }
1748    }
1749
1750    return {
1751
1752        /**
1753         * Hides all menus that are currently visible
1754         */
1755        hideAll : function(){
1756             hideAll();  
1757        },
1758
1759        // private
1760        register : function(menu){
1761            if(!menus){
1762                init();
1763            }
1764            menus[menu.id] = menu;
1765            menu.on("beforehide", onBeforeHide);
1766            menu.on("hide", onHide);
1767            menu.on("beforeshow", onBeforeShow);
1768            menu.on("show", onShow);
1769            var g = menu.group;
1770            if(g && menu.events["checkchange"]){
1771                if(!groups[g]){
1772                    groups[g] = [];
1773                }
1774                groups[g].push(menu);
1775                menu.on("checkchange", onCheck);
1776            }
1777        },
1778
1779         /**
1780          * Returns a {@link Roo.menu.Menu} object
1781          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1782          * be used to generate and return a new Menu instance.
1783          */
1784        get : function(menu){
1785            if(typeof menu == "string"){ // menu id
1786                return menus[menu];
1787            }else if(menu.events){  // menu instance
1788                return menu;
1789            }
1790            /*else if(typeof menu.length == 'number'){ // array of menu items?
1791                return new Roo.bootstrap.Menu({items:menu});
1792            }else{ // otherwise, must be a config
1793                return new Roo.bootstrap.Menu(menu);
1794            }
1795            */
1796            return false;
1797        },
1798
1799        // private
1800        unregister : function(menu){
1801            delete menus[menu.id];
1802            menu.un("beforehide", onBeforeHide);
1803            menu.un("hide", onHide);
1804            menu.un("beforeshow", onBeforeShow);
1805            menu.un("show", onShow);
1806            var g = menu.group;
1807            if(g && menu.events["checkchange"]){
1808                groups[g].remove(menu);
1809                menu.un("checkchange", onCheck);
1810            }
1811        },
1812
1813        // private
1814        registerCheckable : function(menuItem){
1815            var g = menuItem.group;
1816            if(g){
1817                if(!groups[g]){
1818                    groups[g] = [];
1819                }
1820                groups[g].push(menuItem);
1821                menuItem.on("beforecheckchange", onBeforeCheck);
1822            }
1823        },
1824
1825        // private
1826        unregisterCheckable : function(menuItem){
1827            var g = menuItem.group;
1828            if(g){
1829                groups[g].remove(menuItem);
1830                menuItem.un("beforecheckchange", onBeforeCheck);
1831            }
1832        }
1833    };
1834 }();/*
1835  * - LGPL
1836  *
1837  * menu
1838  * 
1839  */
1840
1841 /**
1842  * @class Roo.bootstrap.Menu
1843  * @extends Roo.bootstrap.Component
1844  * Bootstrap Menu class - container for MenuItems
1845  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1846  * 
1847  * @constructor
1848  * Create a new Menu
1849  * @param {Object} config The config object
1850  */
1851
1852
1853 Roo.bootstrap.Menu = function(config){
1854     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1855     if (this.registerMenu) {
1856         Roo.bootstrap.MenuMgr.register(this);
1857     }
1858     this.addEvents({
1859         /**
1860          * @event beforeshow
1861          * Fires before this menu is displayed
1862          * @param {Roo.menu.Menu} this
1863          */
1864         beforeshow : true,
1865         /**
1866          * @event beforehide
1867          * Fires before this menu is hidden
1868          * @param {Roo.menu.Menu} this
1869          */
1870         beforehide : true,
1871         /**
1872          * @event show
1873          * Fires after this menu is displayed
1874          * @param {Roo.menu.Menu} this
1875          */
1876         show : true,
1877         /**
1878          * @event hide
1879          * Fires after this menu is hidden
1880          * @param {Roo.menu.Menu} this
1881          */
1882         hide : true,
1883         /**
1884          * @event click
1885          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1886          * @param {Roo.menu.Menu} this
1887          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1888          * @param {Roo.EventObject} e
1889          */
1890         click : true,
1891         /**
1892          * @event mouseover
1893          * Fires when the mouse is hovering over this menu
1894          * @param {Roo.menu.Menu} this
1895          * @param {Roo.EventObject} e
1896          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1897          */
1898         mouseover : true,
1899         /**
1900          * @event mouseout
1901          * Fires when the mouse exits this menu
1902          * @param {Roo.menu.Menu} this
1903          * @param {Roo.EventObject} e
1904          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1905          */
1906         mouseout : true,
1907         /**
1908          * @event itemclick
1909          * Fires when a menu item contained in this menu is clicked
1910          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1911          * @param {Roo.EventObject} e
1912          */
1913         itemclick: true
1914     });
1915     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1916 };
1917
1918 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1919     
1920    /// html : false,
1921     //align : '',
1922     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1923     type: false,
1924     /**
1925      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1926      */
1927     registerMenu : true,
1928     
1929     menuItems :false, // stores the menu items..
1930     
1931     hidden:true,
1932     
1933     parentMenu : false,
1934     
1935     getChildContainer : function() {
1936         return this.el;  
1937     },
1938     
1939     getAutoCreate : function(){
1940          
1941         //if (['right'].indexOf(this.align)!==-1) {
1942         //    cfg.cn[1].cls += ' pull-right'
1943         //}
1944         
1945         
1946         var cfg = {
1947             tag : 'ul',
1948             cls : 'dropdown-menu' ,
1949             style : 'z-index:1000'
1950             
1951         };
1952         
1953         if (this.type === 'submenu') {
1954             cfg.cls = 'submenu active';
1955         }
1956         if (this.type === 'treeview') {
1957             cfg.cls = 'treeview-menu';
1958         }
1959         
1960         return cfg;
1961     },
1962     initEvents : function() {
1963         
1964        // Roo.log("ADD event");
1965        // Roo.log(this.triggerEl.dom);
1966         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
1967         
1968         this.triggerEl.addClass('dropdown-toggle');
1969         
1970         
1971         
1972         
1973         if (Roo.isTouch) {
1974             this.el.on('touchstart'  , this.onTouch, this);
1975         }
1976         this.el.on('click' , this.onClick, this);
1977
1978         this.el.on("mouseover", this.onMouseOver, this);
1979         this.el.on("mouseout", this.onMouseOut, this);
1980         
1981         
1982     },
1983     findTargetItem : function(e){
1984         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1985         if(!t){
1986             return false;
1987         }
1988         //Roo.log(t);         Roo.log(t.id);
1989         if(t && t.id){
1990             //Roo.log(this.menuitems);
1991             return this.menuitems.get(t.id);
1992             
1993             //return this.items.get(t.menuItemId);
1994         }
1995         
1996         return false;
1997     },
1998     
1999     onTouch : function(e) {
2000         e.stopEvent();
2001         this.onClick(e);
2002     },
2003     
2004     onClick : function(e){
2005         Roo.log("menu.onClick");
2006         var t = this.findTargetItem(e);
2007         if(!t || t.isContainer){
2008             return;
2009         }
2010         Roo.log(e);
2011         /*
2012         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2013             if(t == this.activeItem && t.shouldDeactivate(e)){
2014                 this.activeItem.deactivate();
2015                 delete this.activeItem;
2016                 return;
2017             }
2018             if(t.canActivate){
2019                 this.setActiveItem(t, true);
2020             }
2021             return;
2022             
2023             
2024         }
2025         */
2026        
2027         Roo.log('pass click event');
2028         
2029         t.onClick(e);
2030         
2031         this.fireEvent("click", this, t, e);
2032         
2033         this.hide();
2034     },
2035      onMouseOver : function(e){
2036         var t  = this.findTargetItem(e);
2037         //Roo.log(t);
2038         //if(t){
2039         //    if(t.canActivate && !t.disabled){
2040         //        this.setActiveItem(t, true);
2041         //    }
2042         //}
2043         
2044         this.fireEvent("mouseover", this, e, t);
2045     },
2046     isVisible : function(){
2047         return !this.hidden;
2048     },
2049      onMouseOut : function(e){
2050         var t  = this.findTargetItem(e);
2051         
2052         //if(t ){
2053         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2054         //        this.activeItem.deactivate();
2055         //        delete this.activeItem;
2056         //    }
2057         //}
2058         this.fireEvent("mouseout", this, e, t);
2059     },
2060     
2061     
2062     /**
2063      * Displays this menu relative to another element
2064      * @param {String/HTMLElement/Roo.Element} element The element to align to
2065      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2066      * the element (defaults to this.defaultAlign)
2067      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2068      */
2069     show : function(el, pos, parentMenu){
2070         this.parentMenu = parentMenu;
2071         if(!this.el){
2072             this.render();
2073         }
2074         this.fireEvent("beforeshow", this);
2075         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2076     },
2077      /**
2078      * Displays this menu at a specific xy position
2079      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2080      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2081      */
2082     showAt : function(xy, parentMenu, /* private: */_e){
2083         this.parentMenu = parentMenu;
2084         if(!this.el){
2085             this.render();
2086         }
2087         if(_e !== false){
2088             this.fireEvent("beforeshow", this);
2089             //xy = this.el.adjustForConstraints(xy);
2090         }
2091         
2092         //this.el.show();
2093         this.hideMenuItems();
2094         this.hidden = false;
2095         this.triggerEl.addClass('open');
2096         
2097         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2098             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2099         }
2100         
2101         this.el.setXY(xy);
2102         this.focus();
2103         this.fireEvent("show", this);
2104     },
2105     
2106     focus : function(){
2107         return;
2108         if(!this.hidden){
2109             this.doFocus.defer(50, this);
2110         }
2111     },
2112
2113     doFocus : function(){
2114         if(!this.hidden){
2115             this.focusEl.focus();
2116         }
2117     },
2118
2119     /**
2120      * Hides this menu and optionally all parent menus
2121      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2122      */
2123     hide : function(deep){
2124         
2125         this.hideMenuItems();
2126         if(this.el && this.isVisible()){
2127             this.fireEvent("beforehide", this);
2128             if(this.activeItem){
2129                 this.activeItem.deactivate();
2130                 this.activeItem = null;
2131             }
2132             this.triggerEl.removeClass('open');;
2133             this.hidden = true;
2134             this.fireEvent("hide", this);
2135         }
2136         if(deep === true && this.parentMenu){
2137             this.parentMenu.hide(true);
2138         }
2139     },
2140     
2141     onTriggerPress  : function(e)
2142     {
2143         
2144         Roo.log('trigger press');
2145         //Roo.log(e.getTarget());
2146        // Roo.log(this.triggerEl.dom);
2147         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
2148             return;
2149         }
2150         
2151         if (this.isVisible()) {
2152             Roo.log('hide');
2153             this.hide();
2154         } else {
2155             Roo.log('show');
2156             this.show(this.triggerEl, false, false);
2157         }
2158         
2159         e.stopEvent();
2160     },
2161     
2162          
2163        
2164     
2165     hideMenuItems : function()
2166     {
2167         //$(backdrop).remove()
2168         Roo.select('.open',true).each(function(aa) {
2169             
2170             aa.removeClass('open');
2171           //var parent = getParent($(this))
2172           //var relatedTarget = { relatedTarget: this }
2173           
2174            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2175           //if (e.isDefaultPrevented()) return
2176            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2177         })
2178     },
2179     addxtypeChild : function (tree, cntr) {
2180         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2181           
2182         this.menuitems.add(comp);
2183         return comp;
2184
2185     },
2186     getEl : function()
2187     {
2188         Roo.log(this.el);
2189         return this.el;
2190     }
2191 });
2192
2193  
2194  /*
2195  * - LGPL
2196  *
2197  * menu item
2198  * 
2199  */
2200
2201
2202 /**
2203  * @class Roo.bootstrap.MenuItem
2204  * @extends Roo.bootstrap.Component
2205  * Bootstrap MenuItem class
2206  * @cfg {String} html the menu label
2207  * @cfg {String} href the link
2208  * @cfg {Boolean} preventDefault (true | false) default true
2209  * @cfg {Boolean} isContainer (true | false) default false
2210  * 
2211  * 
2212  * @constructor
2213  * Create a new MenuItem
2214  * @param {Object} config The config object
2215  */
2216
2217
2218 Roo.bootstrap.MenuItem = function(config){
2219     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2220     this.addEvents({
2221         // raw events
2222         /**
2223          * @event click
2224          * The raw click event for the entire grid.
2225          * @param {Roo.bootstrap.MenuItem} this
2226          * @param {Roo.EventObject} e
2227          */
2228         "click" : true
2229     });
2230 };
2231
2232 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2233     
2234     href : false,
2235     html : false,
2236     preventDefault: true,
2237     isContainer : false,
2238     
2239     getAutoCreate : function(){
2240         
2241         if(this.isContainer){
2242             return {
2243                 tag: 'li',
2244                 cls: 'dropdown-menu-item'
2245             };
2246         }
2247         
2248         var cfg= {
2249             tag: 'li',
2250             cls: 'dropdown-menu-item',
2251             cn: [
2252                     {
2253                         tag : 'a',
2254                         href : '#',
2255                         html : 'Link'
2256                     }
2257                 ]
2258         };
2259         if (this.parent().type == 'treeview') {
2260             cfg.cls = 'treeview-menu';
2261         }
2262         
2263         cfg.cn[0].href = this.href || cfg.cn[0].href ;
2264         cfg.cn[0].html = this.html || cfg.cn[0].html ;
2265         return cfg;
2266     },
2267     
2268     initEvents: function() {
2269         
2270         //this.el.select('a').on('click', this.onClick, this);
2271         
2272     },
2273     onClick : function(e)
2274     {
2275         Roo.log('item on click ');
2276         //if(this.preventDefault){
2277         //    e.preventDefault();
2278         //}
2279         //this.parent().hideMenuItems();
2280         
2281         this.fireEvent('click', this, e);
2282     },
2283     getEl : function()
2284     {
2285         return this.el;
2286     }
2287 });
2288
2289  
2290
2291  /*
2292  * - LGPL
2293  *
2294  * menu separator
2295  * 
2296  */
2297
2298
2299 /**
2300  * @class Roo.bootstrap.MenuSeparator
2301  * @extends Roo.bootstrap.Component
2302  * Bootstrap MenuSeparator class
2303  * 
2304  * @constructor
2305  * Create a new MenuItem
2306  * @param {Object} config The config object
2307  */
2308
2309
2310 Roo.bootstrap.MenuSeparator = function(config){
2311     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2312 };
2313
2314 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2315     
2316     getAutoCreate : function(){
2317         var cfg = {
2318             cls: 'divider',
2319             tag : 'li'
2320         };
2321         
2322         return cfg;
2323     }
2324    
2325 });
2326
2327  
2328
2329  
2330 /*
2331 * Licence: LGPL
2332 */
2333
2334 /**
2335  * @class Roo.bootstrap.Modal
2336  * @extends Roo.bootstrap.Component
2337  * Bootstrap Modal class
2338  * @cfg {String} title Title of dialog
2339  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2340  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn 
2341  * @cfg {Boolean} specificTitle default false
2342  * @cfg {Array} buttons Array of buttons or standard button set..
2343  * @cfg {String} buttonPosition (left|right|center) default right
2344  * @cfg {Boolean} animate default true
2345  * @cfg {Boolean} allow_close default true
2346  * 
2347  * @constructor
2348  * Create a new Modal Dialog
2349  * @param {Object} config The config object
2350  */
2351
2352 Roo.bootstrap.Modal = function(config){
2353     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2354     this.addEvents({
2355         // raw events
2356         /**
2357          * @event btnclick
2358          * The raw btnclick event for the button
2359          * @param {Roo.EventObject} e
2360          */
2361         "btnclick" : true
2362     });
2363     this.buttons = this.buttons || [];
2364      
2365     if (this.tmpl) {
2366         this.tmpl = Roo.factory(this.tmpl);
2367     }
2368     
2369 };
2370
2371 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2372     
2373     title : 'test dialog',
2374    
2375     buttons : false,
2376     
2377     // set on load...
2378      
2379     html: false,
2380     
2381     tmp: false,
2382     
2383     specificTitle: false,
2384     
2385     buttonPosition: 'right',
2386     
2387     allow_close : true,
2388     
2389     animate : true,
2390     
2391     
2392      // private
2393     bodyEl:  false,
2394     footerEl:  false,
2395     titleEl:  false,
2396     closeEl:  false,
2397     
2398     
2399     onRender : function(ct, position)
2400     {
2401         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2402      
2403         if(!this.el){
2404             var cfg = Roo.apply({},  this.getAutoCreate());
2405             cfg.id = Roo.id();
2406             //if(!cfg.name){
2407             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2408             //}
2409             //if (!cfg.name.length) {
2410             //    delete cfg.name;
2411            // }
2412             if (this.cls) {
2413                 cfg.cls += ' ' + this.cls;
2414             }
2415             if (this.style) {
2416                 cfg.style = this.style;
2417             }
2418             this.el = Roo.get(document.body).createChild(cfg, position);
2419         }
2420         //var type = this.el.dom.type;
2421         
2422         
2423         
2424         
2425         if(this.tabIndex !== undefined){
2426             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2427         }
2428         
2429         
2430         this.bodyEl = this.el.select('.modal-body',true).first();
2431         this.closeEl = this.el.select('.modal-header .close', true).first();
2432         this.footerEl = this.el.select('.modal-footer',true).first();
2433         this.titleEl = this.el.select('.modal-title',true).first();
2434         
2435         
2436          
2437         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2438         this.maskEl.enableDisplayMode("block");
2439         this.maskEl.hide();
2440         //this.el.addClass("x-dlg-modal");
2441     
2442         if (this.buttons.length) {
2443             Roo.each(this.buttons, function(bb) {
2444                 var b = Roo.apply({}, bb);
2445                 b.xns = b.xns || Roo.bootstrap;
2446                 b.xtype = b.xtype || 'Button';
2447                 if (typeof(b.listeners) == 'undefined') {
2448                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2449                 }
2450                 
2451                 var btn = Roo.factory(b);
2452                 
2453                 btn.onRender(this.el.select('.modal-footer div').first());
2454                 
2455             },this);
2456         }
2457         // render the children.
2458         var nitems = [];
2459         
2460         if(typeof(this.items) != 'undefined'){
2461             var items = this.items;
2462             delete this.items;
2463
2464             for(var i =0;i < items.length;i++) {
2465                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2466             }
2467         }
2468         
2469         this.items = nitems;
2470         
2471         // where are these used - they used to be body/close/footer
2472         
2473        
2474         this.initEvents();
2475         //this.el.addClass([this.fieldClass, this.cls]);
2476         
2477     },
2478     
2479     getAutoCreate : function(){
2480         
2481         
2482         var bdy = {
2483                 cls : 'modal-body',
2484                 html : this.html || ''
2485         };
2486         
2487         var title = {
2488             tag: 'h4',
2489             cls : 'modal-title',
2490             html : this.title
2491         };
2492         
2493         if(this.specificTitle){
2494             title = this.title;
2495             
2496         };
2497         
2498         var header = [];
2499         if (this.allow_close) {
2500             header.push({
2501                 tag: 'button',
2502                 cls : 'close',
2503                 html : '&times'
2504             });
2505         }
2506         header.push(title);
2507         
2508         var modal = {
2509             cls: "modal",
2510             style : 'display: none',
2511             cn : [
2512                 {
2513                     cls: "modal-dialog",
2514                     cn : [
2515                         {
2516                             cls : "modal-content",
2517                             cn : [
2518                                 {
2519                                     cls : 'modal-header',
2520                                     cn : header
2521                                 },
2522                                 bdy,
2523                                 {
2524                                     cls : 'modal-footer',
2525                                     cn : [
2526                                         {
2527                                             tag: 'div',
2528                                             cls: 'btn-' + this.buttonPosition
2529                                         }
2530                                     ]
2531                                     
2532                                 }
2533                                 
2534                                 
2535                             ]
2536                             
2537                         }
2538                     ]
2539                         
2540                 }
2541             ]
2542         };
2543         
2544         if(this.animate){
2545             modal.cls += ' fade';
2546         }
2547         
2548         return modal;
2549           
2550     },
2551     getChildContainer : function() {
2552          
2553          return this.bodyEl;
2554         
2555     },
2556     getButtonContainer : function() {
2557          return this.el.select('.modal-footer div',true).first();
2558         
2559     },
2560     initEvents : function()
2561     {
2562         if (this.allow_close) {
2563             this.closeEl.on('click', this.hide, this);
2564         }
2565         
2566         var _this = this;
2567         
2568         window.addEventListener("resize", function() { _this.resize(); } );
2569
2570     },
2571     
2572     resize : function()
2573     {
2574         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2575     },
2576     
2577     show : function() {
2578         
2579         if (!this.rendered) {
2580             this.render();
2581         }
2582         
2583         this.el.setStyle('display', 'block');
2584         
2585         if(this.animate){
2586             var _this = this;
2587             (function(){ _this.el.addClass('in'); }).defer(50);
2588         }else{
2589             this.el.addClass('in');
2590         }
2591         
2592         // not sure how we can show data in here.. 
2593         //if (this.tmpl) {
2594         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2595         //}
2596         
2597         Roo.get(document.body).addClass("x-body-masked");
2598         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2599         this.maskEl.show();
2600         this.el.setStyle('zIndex', '10001');
2601        
2602         this.fireEvent('show', this);
2603         
2604         
2605     },
2606     hide : function()
2607     {
2608         this.maskEl.hide();
2609         Roo.get(document.body).removeClass("x-body-masked");
2610         this.el.removeClass('in');
2611         
2612         if(this.animate){
2613             var _this = this;
2614             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2615         }else{
2616             this.el.setStyle('display', 'none');
2617         }
2618         
2619         this.fireEvent('hide', this);
2620     },
2621     
2622     addButton : function(str, cb)
2623     {
2624          
2625         
2626         var b = Roo.apply({}, { html : str } );
2627         b.xns = b.xns || Roo.bootstrap;
2628         b.xtype = b.xtype || 'Button';
2629         if (typeof(b.listeners) == 'undefined') {
2630             b.listeners = { click : cb.createDelegate(this)  };
2631         }
2632         
2633         var btn = Roo.factory(b);
2634            
2635         btn.onRender(this.el.select('.modal-footer div').first());
2636         
2637         return btn;   
2638        
2639     },
2640     
2641     setDefaultButton : function(btn)
2642     {
2643         //this.el.select('.modal-footer').()
2644     },
2645     resizeTo: function(w,h)
2646     {
2647         // skip..
2648     },
2649     setContentSize  : function(w, h)
2650     {
2651         
2652     },
2653     onButtonClick: function(btn,e)
2654     {
2655         //Roo.log([a,b,c]);
2656         this.fireEvent('btnclick', btn.name, e);
2657     },
2658      /**
2659      * Set the title of the Dialog
2660      * @param {String} str new Title
2661      */
2662     setTitle: function(str) {
2663         this.titleEl.dom.innerHTML = str;    
2664     },
2665     /**
2666      * Set the body of the Dialog
2667      * @param {String} str new Title
2668      */
2669     setBody: function(str) {
2670         this.bodyEl.dom.innerHTML = str;    
2671     },
2672     /**
2673      * Set the body of the Dialog using the template
2674      * @param {Obj} data - apply this data to the template and replace the body contents.
2675      */
2676     applyBody: function(obj)
2677     {
2678         if (!this.tmpl) {
2679             Roo.log("Error - using apply Body without a template");
2680             //code
2681         }
2682         this.tmpl.overwrite(this.bodyEl, obj);
2683     }
2684     
2685 });
2686
2687
2688 Roo.apply(Roo.bootstrap.Modal,  {
2689     /**
2690          * Button config that displays a single OK button
2691          * @type Object
2692          */
2693         OK :  [{
2694             name : 'ok',
2695             weight : 'primary',
2696             html : 'OK'
2697         }], 
2698         /**
2699          * Button config that displays Yes and No buttons
2700          * @type Object
2701          */
2702         YESNO : [
2703             {
2704                 name  : 'no',
2705                 html : 'No'
2706             },
2707             {
2708                 name  :'yes',
2709                 weight : 'primary',
2710                 html : 'Yes'
2711             }
2712         ],
2713         
2714         /**
2715          * Button config that displays OK and Cancel buttons
2716          * @type Object
2717          */
2718         OKCANCEL : [
2719             {
2720                name : 'cancel',
2721                 html : 'Cancel'
2722             },
2723             {
2724                 name : 'ok',
2725                 weight : 'primary',
2726                 html : 'OK'
2727             }
2728         ],
2729         /**
2730          * Button config that displays Yes, No and Cancel buttons
2731          * @type Object
2732          */
2733         YESNOCANCEL : [
2734             {
2735                 name : 'yes',
2736                 weight : 'primary',
2737                 html : 'Yes'
2738             },
2739             {
2740                 name : 'no',
2741                 html : 'No'
2742             },
2743             {
2744                 name : 'cancel',
2745                 html : 'Cancel'
2746             }
2747         ]
2748 });
2749  
2750  /*
2751  * - LGPL
2752  *
2753  * messagebox - can be used as a replace
2754  * 
2755  */
2756 /**
2757  * @class Roo.MessageBox
2758  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2759  * Example usage:
2760  *<pre><code>
2761 // Basic alert:
2762 Roo.Msg.alert('Status', 'Changes saved successfully.');
2763
2764 // Prompt for user data:
2765 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2766     if (btn == 'ok'){
2767         // process text value...
2768     }
2769 });
2770
2771 // Show a dialog using config options:
2772 Roo.Msg.show({
2773    title:'Save Changes?',
2774    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2775    buttons: Roo.Msg.YESNOCANCEL,
2776    fn: processResult,
2777    animEl: 'elId'
2778 });
2779 </code></pre>
2780  * @singleton
2781  */
2782 Roo.bootstrap.MessageBox = function(){
2783     var dlg, opt, mask, waitTimer;
2784     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2785     var buttons, activeTextEl, bwidth;
2786
2787     
2788     // private
2789     var handleButton = function(button){
2790         dlg.hide();
2791         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2792     };
2793
2794     // private
2795     var handleHide = function(){
2796         if(opt && opt.cls){
2797             dlg.el.removeClass(opt.cls);
2798         }
2799         //if(waitTimer){
2800         //    Roo.TaskMgr.stop(waitTimer);
2801         //    waitTimer = null;
2802         //}
2803     };
2804
2805     // private
2806     var updateButtons = function(b){
2807         var width = 0;
2808         if(!b){
2809             buttons["ok"].hide();
2810             buttons["cancel"].hide();
2811             buttons["yes"].hide();
2812             buttons["no"].hide();
2813             //dlg.footer.dom.style.display = 'none';
2814             return width;
2815         }
2816         dlg.footerEl.dom.style.display = '';
2817         for(var k in buttons){
2818             if(typeof buttons[k] != "function"){
2819                 if(b[k]){
2820                     buttons[k].show();
2821                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2822                     width += buttons[k].el.getWidth()+15;
2823                 }else{
2824                     buttons[k].hide();
2825                 }
2826             }
2827         }
2828         return width;
2829     };
2830
2831     // private
2832     var handleEsc = function(d, k, e){
2833         if(opt && opt.closable !== false){
2834             dlg.hide();
2835         }
2836         if(e){
2837             e.stopEvent();
2838         }
2839     };
2840
2841     return {
2842         /**
2843          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2844          * @return {Roo.BasicDialog} The BasicDialog element
2845          */
2846         getDialog : function(){
2847            if(!dlg){
2848                 dlg = new Roo.bootstrap.Modal( {
2849                     //draggable: true,
2850                     //resizable:false,
2851                     //constraintoviewport:false,
2852                     //fixedcenter:true,
2853                     //collapsible : false,
2854                     //shim:true,
2855                     //modal: true,
2856                   //  width:400,
2857                   //  height:100,
2858                     //buttonAlign:"center",
2859                     closeClick : function(){
2860                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2861                             handleButton("no");
2862                         }else{
2863                             handleButton("cancel");
2864                         }
2865                     }
2866                 });
2867                 dlg.render();
2868                 dlg.on("hide", handleHide);
2869                 mask = dlg.mask;
2870                 //dlg.addKeyListener(27, handleEsc);
2871                 buttons = {};
2872                 this.buttons = buttons;
2873                 var bt = this.buttonText;
2874                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2875                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2876                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2877                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2878                 //Roo.log(buttons);
2879                 bodyEl = dlg.bodyEl.createChild({
2880
2881                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2882                         '<textarea class="roo-mb-textarea"></textarea>' +
2883                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2884                 });
2885                 msgEl = bodyEl.dom.firstChild;
2886                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2887                 textboxEl.enableDisplayMode();
2888                 textboxEl.addKeyListener([10,13], function(){
2889                     if(dlg.isVisible() && opt && opt.buttons){
2890                         if(opt.buttons.ok){
2891                             handleButton("ok");
2892                         }else if(opt.buttons.yes){
2893                             handleButton("yes");
2894                         }
2895                     }
2896                 });
2897                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2898                 textareaEl.enableDisplayMode();
2899                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2900                 progressEl.enableDisplayMode();
2901                 var pf = progressEl.dom.firstChild;
2902                 if (pf) {
2903                     pp = Roo.get(pf.firstChild);
2904                     pp.setHeight(pf.offsetHeight);
2905                 }
2906                 
2907             }
2908             return dlg;
2909         },
2910
2911         /**
2912          * Updates the message box body text
2913          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2914          * the XHTML-compliant non-breaking space character '&amp;#160;')
2915          * @return {Roo.MessageBox} This message box
2916          */
2917         updateText : function(text){
2918             if(!dlg.isVisible() && !opt.width){
2919                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2920             }
2921             msgEl.innerHTML = text || '&#160;';
2922       
2923             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2924             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2925             var w = Math.max(
2926                     Math.min(opt.width || cw , this.maxWidth), 
2927                     Math.max(opt.minWidth || this.minWidth, bwidth)
2928             );
2929             if(opt.prompt){
2930                 activeTextEl.setWidth(w);
2931             }
2932             if(dlg.isVisible()){
2933                 dlg.fixedcenter = false;
2934             }
2935             // to big, make it scroll. = But as usual stupid IE does not support
2936             // !important..
2937             
2938             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2939                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2940                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2941             } else {
2942                 bodyEl.dom.style.height = '';
2943                 bodyEl.dom.style.overflowY = '';
2944             }
2945             if (cw > w) {
2946                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2947             } else {
2948                 bodyEl.dom.style.overflowX = '';
2949             }
2950             
2951             dlg.setContentSize(w, bodyEl.getHeight());
2952             if(dlg.isVisible()){
2953                 dlg.fixedcenter = true;
2954             }
2955             return this;
2956         },
2957
2958         /**
2959          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2960          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2961          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2962          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2963          * @return {Roo.MessageBox} This message box
2964          */
2965         updateProgress : function(value, text){
2966             if(text){
2967                 this.updateText(text);
2968             }
2969             if (pp) { // weird bug on my firefox - for some reason this is not defined
2970                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2971             }
2972             return this;
2973         },        
2974
2975         /**
2976          * Returns true if the message box is currently displayed
2977          * @return {Boolean} True if the message box is visible, else false
2978          */
2979         isVisible : function(){
2980             return dlg && dlg.isVisible();  
2981         },
2982
2983         /**
2984          * Hides the message box if it is displayed
2985          */
2986         hide : function(){
2987             if(this.isVisible()){
2988                 dlg.hide();
2989             }  
2990         },
2991
2992         /**
2993          * Displays a new message box, or reinitializes an existing message box, based on the config options
2994          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2995          * The following config object properties are supported:
2996          * <pre>
2997 Property    Type             Description
2998 ----------  ---------------  ------------------------------------------------------------------------------------
2999 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3000                                    closes (defaults to undefined)
3001 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3002                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3003 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3004                                    progress and wait dialogs will ignore this property and always hide the
3005                                    close button as they can only be closed programmatically.
3006 cls               String           A custom CSS class to apply to the message box element
3007 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3008                                    displayed (defaults to 75)
3009 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3010                                    function will be btn (the name of the button that was clicked, if applicable,
3011                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3012                                    Progress and wait dialogs will ignore this option since they do not respond to
3013                                    user actions and can only be closed programmatically, so any required function
3014                                    should be called by the same code after it closes the dialog.
3015 icon              String           A CSS class that provides a background image to be used as an icon for
3016                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3017 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3018 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3019 modal             Boolean          False to allow user interaction with the page while the message box is
3020                                    displayed (defaults to true)
3021 msg               String           A string that will replace the existing message box body text (defaults
3022                                    to the XHTML-compliant non-breaking space character '&#160;')
3023 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3024 progress          Boolean          True to display a progress bar (defaults to false)
3025 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3026 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3027 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3028 title             String           The title text
3029 value             String           The string value to set into the active textbox element if displayed
3030 wait              Boolean          True to display a progress bar (defaults to false)
3031 width             Number           The width of the dialog in pixels
3032 </pre>
3033          *
3034          * Example usage:
3035          * <pre><code>
3036 Roo.Msg.show({
3037    title: 'Address',
3038    msg: 'Please enter your address:',
3039    width: 300,
3040    buttons: Roo.MessageBox.OKCANCEL,
3041    multiline: true,
3042    fn: saveAddress,
3043    animEl: 'addAddressBtn'
3044 });
3045 </code></pre>
3046          * @param {Object} config Configuration options
3047          * @return {Roo.MessageBox} This message box
3048          */
3049         show : function(options)
3050         {
3051             
3052             // this causes nightmares if you show one dialog after another
3053             // especially on callbacks..
3054              
3055             if(this.isVisible()){
3056                 
3057                 this.hide();
3058                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3059                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3060                 Roo.log("New Dialog Message:" +  options.msg )
3061                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3062                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3063                 
3064             }
3065             var d = this.getDialog();
3066             opt = options;
3067             d.setTitle(opt.title || "&#160;");
3068             d.closeEl.setDisplayed(opt.closable !== false);
3069             activeTextEl = textboxEl;
3070             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3071             if(opt.prompt){
3072                 if(opt.multiline){
3073                     textboxEl.hide();
3074                     textareaEl.show();
3075                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3076                         opt.multiline : this.defaultTextHeight);
3077                     activeTextEl = textareaEl;
3078                 }else{
3079                     textboxEl.show();
3080                     textareaEl.hide();
3081                 }
3082             }else{
3083                 textboxEl.hide();
3084                 textareaEl.hide();
3085             }
3086             progressEl.setDisplayed(opt.progress === true);
3087             this.updateProgress(0);
3088             activeTextEl.dom.value = opt.value || "";
3089             if(opt.prompt){
3090                 dlg.setDefaultButton(activeTextEl);
3091             }else{
3092                 var bs = opt.buttons;
3093                 var db = null;
3094                 if(bs && bs.ok){
3095                     db = buttons["ok"];
3096                 }else if(bs && bs.yes){
3097                     db = buttons["yes"];
3098                 }
3099                 dlg.setDefaultButton(db);
3100             }
3101             bwidth = updateButtons(opt.buttons);
3102             this.updateText(opt.msg);
3103             if(opt.cls){
3104                 d.el.addClass(opt.cls);
3105             }
3106             d.proxyDrag = opt.proxyDrag === true;
3107             d.modal = opt.modal !== false;
3108             d.mask = opt.modal !== false ? mask : false;
3109             if(!d.isVisible()){
3110                 // force it to the end of the z-index stack so it gets a cursor in FF
3111                 document.body.appendChild(dlg.el.dom);
3112                 d.animateTarget = null;
3113                 d.show(options.animEl);
3114             }
3115             return this;
3116         },
3117
3118         /**
3119          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3120          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3121          * and closing the message box when the process is complete.
3122          * @param {String} title The title bar text
3123          * @param {String} msg The message box body text
3124          * @return {Roo.MessageBox} This message box
3125          */
3126         progress : function(title, msg){
3127             this.show({
3128                 title : title,
3129                 msg : msg,
3130                 buttons: false,
3131                 progress:true,
3132                 closable:false,
3133                 minWidth: this.minProgressWidth,
3134                 modal : true
3135             });
3136             return this;
3137         },
3138
3139         /**
3140          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3141          * If a callback function is passed it will be called after the user clicks the button, and the
3142          * id of the button that was clicked will be passed as the only parameter to the callback
3143          * (could also be the top-right close button).
3144          * @param {String} title The title bar text
3145          * @param {String} msg The message box body text
3146          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3147          * @param {Object} scope (optional) The scope of the callback function
3148          * @return {Roo.MessageBox} This message box
3149          */
3150         alert : function(title, msg, fn, scope){
3151             this.show({
3152                 title : title,
3153                 msg : msg,
3154                 buttons: this.OK,
3155                 fn: fn,
3156                 scope : scope,
3157                 modal : true
3158             });
3159             return this;
3160         },
3161
3162         /**
3163          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3164          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3165          * You are responsible for closing the message box when the process is complete.
3166          * @param {String} msg The message box body text
3167          * @param {String} title (optional) The title bar text
3168          * @return {Roo.MessageBox} This message box
3169          */
3170         wait : function(msg, title){
3171             this.show({
3172                 title : title,
3173                 msg : msg,
3174                 buttons: false,
3175                 closable:false,
3176                 progress:true,
3177                 modal:true,
3178                 width:300,
3179                 wait:true
3180             });
3181             waitTimer = Roo.TaskMgr.start({
3182                 run: function(i){
3183                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3184                 },
3185                 interval: 1000
3186             });
3187             return this;
3188         },
3189
3190         /**
3191          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3192          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3193          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3194          * @param {String} title The title bar text
3195          * @param {String} msg The message box body text
3196          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3197          * @param {Object} scope (optional) The scope of the callback function
3198          * @return {Roo.MessageBox} This message box
3199          */
3200         confirm : function(title, msg, fn, scope){
3201             this.show({
3202                 title : title,
3203                 msg : msg,
3204                 buttons: this.YESNO,
3205                 fn: fn,
3206                 scope : scope,
3207                 modal : true
3208             });
3209             return this;
3210         },
3211
3212         /**
3213          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3214          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3215          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3216          * (could also be the top-right close button) and the text that was entered will be passed as the two
3217          * parameters to the callback.
3218          * @param {String} title The title bar text
3219          * @param {String} msg The message box body text
3220          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3221          * @param {Object} scope (optional) The scope of the callback function
3222          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3223          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3224          * @return {Roo.MessageBox} This message box
3225          */
3226         prompt : function(title, msg, fn, scope, multiline){
3227             this.show({
3228                 title : title,
3229                 msg : msg,
3230                 buttons: this.OKCANCEL,
3231                 fn: fn,
3232                 minWidth:250,
3233                 scope : scope,
3234                 prompt:true,
3235                 multiline: multiline,
3236                 modal : true
3237             });
3238             return this;
3239         },
3240
3241         /**
3242          * Button config that displays a single OK button
3243          * @type Object
3244          */
3245         OK : {ok:true},
3246         /**
3247          * Button config that displays Yes and No buttons
3248          * @type Object
3249          */
3250         YESNO : {yes:true, no:true},
3251         /**
3252          * Button config that displays OK and Cancel buttons
3253          * @type Object
3254          */
3255         OKCANCEL : {ok:true, cancel:true},
3256         /**
3257          * Button config that displays Yes, No and Cancel buttons
3258          * @type Object
3259          */
3260         YESNOCANCEL : {yes:true, no:true, cancel:true},
3261
3262         /**
3263          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3264          * @type Number
3265          */
3266         defaultTextHeight : 75,
3267         /**
3268          * The maximum width in pixels of the message box (defaults to 600)
3269          * @type Number
3270          */
3271         maxWidth : 600,
3272         /**
3273          * The minimum width in pixels of the message box (defaults to 100)
3274          * @type Number
3275          */
3276         minWidth : 100,
3277         /**
3278          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3279          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3280          * @type Number
3281          */
3282         minProgressWidth : 250,
3283         /**
3284          * An object containing the default button text strings that can be overriden for localized language support.
3285          * Supported properties are: ok, cancel, yes and no.
3286          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3287          * @type Object
3288          */
3289         buttonText : {
3290             ok : "OK",
3291             cancel : "Cancel",
3292             yes : "Yes",
3293             no : "No"
3294         }
3295     };
3296 }();
3297
3298 /**
3299  * Shorthand for {@link Roo.MessageBox}
3300  */
3301 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3302 Roo.Msg = Roo.Msg || Roo.MessageBox;
3303 /*
3304  * - LGPL
3305  *
3306  * navbar
3307  * 
3308  */
3309
3310 /**
3311  * @class Roo.bootstrap.Navbar
3312  * @extends Roo.bootstrap.Component
3313  * Bootstrap Navbar class
3314
3315  * @constructor
3316  * Create a new Navbar
3317  * @param {Object} config The config object
3318  */
3319
3320
3321 Roo.bootstrap.Navbar = function(config){
3322     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3323     
3324 };
3325
3326 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3327     
3328     
3329    
3330     // private
3331     navItems : false,
3332     loadMask : false,
3333     
3334     
3335     getAutoCreate : function(){
3336         
3337         
3338         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3339         
3340     },
3341     
3342     initEvents :function ()
3343     {
3344         //Roo.log(this.el.select('.navbar-toggle',true));
3345         this.el.select('.navbar-toggle',true).on('click', function() {
3346            // Roo.log('click');
3347             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3348         }, this);
3349         
3350         var mark = {
3351             tag: "div",
3352             cls:"x-dlg-mask"
3353         };
3354         
3355         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3356         
3357         var size = this.el.getSize();
3358         this.maskEl.setSize(size.width, size.height);
3359         this.maskEl.enableDisplayMode("block");
3360         this.maskEl.hide();
3361         
3362         if(this.loadMask){
3363             this.maskEl.show();
3364         }
3365     },
3366     
3367     
3368     getChildContainer : function()
3369     {
3370         if (this.el.select('.collapse').getCount()) {
3371             return this.el.select('.collapse',true).first();
3372         }
3373         
3374         return this.el;
3375     },
3376     
3377     mask : function()
3378     {
3379         this.maskEl.show();
3380     },
3381     
3382     unmask : function()
3383     {
3384         this.maskEl.hide();
3385     } 
3386     
3387     
3388     
3389     
3390 });
3391
3392
3393
3394  
3395
3396  /*
3397  * - LGPL
3398  *
3399  * navbar
3400  * 
3401  */
3402
3403 /**
3404  * @class Roo.bootstrap.NavSimplebar
3405  * @extends Roo.bootstrap.Navbar
3406  * Bootstrap Sidebar class
3407  *
3408  * @cfg {Boolean} inverse is inverted color
3409  * 
3410  * @cfg {String} type (nav | pills | tabs)
3411  * @cfg {Boolean} arrangement stacked | justified
3412  * @cfg {String} align (left | right) alignment
3413  * 
3414  * @cfg {Boolean} main (true|false) main nav bar? default false
3415  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3416  * 
3417  * @cfg {String} tag (header|footer|nav|div) default is nav 
3418
3419  * 
3420  * 
3421  * 
3422  * @constructor
3423  * Create a new Sidebar
3424  * @param {Object} config The config object
3425  */
3426
3427
3428 Roo.bootstrap.NavSimplebar = function(config){
3429     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3430 };
3431
3432 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3433     
3434     inverse: false,
3435     
3436     type: false,
3437     arrangement: '',
3438     align : false,
3439     
3440     
3441     
3442     main : false,
3443     
3444     
3445     tag : false,
3446     
3447     
3448     getAutoCreate : function(){
3449         
3450         
3451         var cfg = {
3452             tag : this.tag || 'div',
3453             cls : 'navbar'
3454         };
3455           
3456         
3457         cfg.cn = [
3458             {
3459                 cls: 'nav',
3460                 tag : 'ul'
3461             }
3462         ];
3463         
3464          
3465         this.type = this.type || 'nav';
3466         if (['tabs','pills'].indexOf(this.type)!==-1) {
3467             cfg.cn[0].cls += ' nav-' + this.type
3468         
3469         
3470         } else {
3471             if (this.type!=='nav') {
3472                 Roo.log('nav type must be nav/tabs/pills')
3473             }
3474             cfg.cn[0].cls += ' navbar-nav'
3475         }
3476         
3477         
3478         
3479         
3480         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3481             cfg.cn[0].cls += ' nav-' + this.arrangement;
3482         }
3483         
3484         
3485         if (this.align === 'right') {
3486             cfg.cn[0].cls += ' navbar-right';
3487         }
3488         
3489         if (this.inverse) {
3490             cfg.cls += ' navbar-inverse';
3491             
3492         }
3493         
3494         
3495         return cfg;
3496     
3497         
3498     }
3499     
3500     
3501     
3502 });
3503
3504
3505
3506  
3507
3508  
3509        /*
3510  * - LGPL
3511  *
3512  * navbar
3513  * 
3514  */
3515
3516 /**
3517  * @class Roo.bootstrap.NavHeaderbar
3518  * @extends Roo.bootstrap.NavSimplebar
3519  * Bootstrap Sidebar class
3520  *
3521  * @cfg {String} brand what is brand
3522  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3523  * @cfg {String} brand_href href of the brand
3524  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3525  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3526  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3527  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3528  * 
3529  * @constructor
3530  * Create a new Sidebar
3531  * @param {Object} config The config object
3532  */
3533
3534
3535 Roo.bootstrap.NavHeaderbar = function(config){
3536     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3537       
3538 };
3539
3540 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3541     
3542     position: '',
3543     brand: '',
3544     brand_href: false,
3545     srButton : true,
3546     autohide : false,
3547     desktopCenter : false,
3548    
3549     
3550     getAutoCreate : function(){
3551         
3552         var   cfg = {
3553             tag: this.nav || 'nav',
3554             cls: 'navbar',
3555             role: 'navigation',
3556             cn: []
3557         };
3558         
3559         var cn = cfg.cn;
3560         if (this.desktopCenter) {
3561             cn.push({cls : 'container', cn : []});
3562             cn = cn[0].cn;
3563         }
3564         
3565         if(this.srButton){
3566             cn.push({
3567                 tag: 'div',
3568                 cls: 'navbar-header',
3569                 cn: [
3570                     {
3571                         tag: 'button',
3572                         type: 'button',
3573                         cls: 'navbar-toggle',
3574                         'data-toggle': 'collapse',
3575                         cn: [
3576                             {
3577                                 tag: 'span',
3578                                 cls: 'sr-only',
3579                                 html: 'Toggle navigation'
3580                             },
3581                             {
3582                                 tag: 'span',
3583                                 cls: 'icon-bar'
3584                             },
3585                             {
3586                                 tag: 'span',
3587                                 cls: 'icon-bar'
3588                             },
3589                             {
3590                                 tag: 'span',
3591                                 cls: 'icon-bar'
3592                             }
3593                         ]
3594                     }
3595                 ]
3596             });
3597         }
3598         
3599         cn.push({
3600             tag: 'div',
3601             cls: 'collapse navbar-collapse',
3602             cn : []
3603         });
3604         
3605         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3606         
3607         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3608             cfg.cls += ' navbar-' + this.position;
3609             
3610             // tag can override this..
3611             
3612             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3613         }
3614         
3615         if (this.brand !== '') {
3616             cn[0].cn.push({
3617                 tag: 'a',
3618                 href: this.brand_href ? this.brand_href : '#',
3619                 cls: 'navbar-brand',
3620                 cn: [
3621                 this.brand
3622                 ]
3623             });
3624         }
3625         
3626         if(this.main){
3627             cfg.cls += ' main-nav';
3628         }
3629         
3630         
3631         return cfg;
3632
3633         
3634     },
3635     getHeaderChildContainer : function()
3636     {
3637         if (this.el.select('.navbar-header').getCount()) {
3638             return this.el.select('.navbar-header',true).first();
3639         }
3640         
3641         return this.getChildContainer();
3642     },
3643     
3644     
3645     initEvents : function()
3646     {
3647         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3648         
3649         if (this.autohide) {
3650             
3651             var prevScroll = 0;
3652             var ft = this.el;
3653             
3654             Roo.get(document).on('scroll',function(e) {
3655                 var ns = Roo.get(document).getScroll().top;
3656                 var os = prevScroll;
3657                 prevScroll = ns;
3658                 
3659                 if(ns > os){
3660                     ft.removeClass('slideDown');
3661                     ft.addClass('slideUp');
3662                     return;
3663                 }
3664                 ft.removeClass('slideUp');
3665                 ft.addClass('slideDown');
3666                  
3667               
3668           },this);
3669         }
3670     }    
3671     
3672 });
3673
3674
3675
3676  
3677
3678  /*
3679  * - LGPL
3680  *
3681  * navbar
3682  * 
3683  */
3684
3685 /**
3686  * @class Roo.bootstrap.NavSidebar
3687  * @extends Roo.bootstrap.Navbar
3688  * Bootstrap Sidebar class
3689  * 
3690  * @constructor
3691  * Create a new Sidebar
3692  * @param {Object} config The config object
3693  */
3694
3695
3696 Roo.bootstrap.NavSidebar = function(config){
3697     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3698 };
3699
3700 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3701     
3702     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3703     
3704     getAutoCreate : function(){
3705         
3706         
3707         return  {
3708             tag: 'div',
3709             cls: 'sidebar sidebar-nav'
3710         };
3711     
3712         
3713     }
3714     
3715     
3716     
3717 });
3718
3719
3720
3721  
3722
3723  /*
3724  * - LGPL
3725  *
3726  * nav group
3727  * 
3728  */
3729
3730 /**
3731  * @class Roo.bootstrap.NavGroup
3732  * @extends Roo.bootstrap.Component
3733  * Bootstrap NavGroup class
3734  * @cfg {String} align (left|right)
3735  * @cfg {Boolean} inverse
3736  * @cfg {String} type (nav|pills|tab) default nav
3737  * @cfg {String} navId - reference Id for navbar.
3738
3739  * 
3740  * @constructor
3741  * Create a new nav group
3742  * @param {Object} config The config object
3743  */
3744
3745 Roo.bootstrap.NavGroup = function(config){
3746     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3747     this.navItems = [];
3748    
3749     Roo.bootstrap.NavGroup.register(this);
3750      this.addEvents({
3751         /**
3752              * @event changed
3753              * Fires when the active item changes
3754              * @param {Roo.bootstrap.NavGroup} this
3755              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3756              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3757          */
3758         'changed': true
3759      });
3760     
3761 };
3762
3763 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3764     
3765     align: '',
3766     inverse: false,
3767     form: false,
3768     type: 'nav',
3769     navId : '',
3770     // private
3771     
3772     navItems : false, 
3773     
3774     getAutoCreate : function()
3775     {
3776         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3777         
3778         cfg = {
3779             tag : 'ul',
3780             cls: 'nav' 
3781         };
3782         
3783         if (['tabs','pills'].indexOf(this.type)!==-1) {
3784             cfg.cls += ' nav-' + this.type
3785         } else {
3786             if (this.type!=='nav') {
3787                 Roo.log('nav type must be nav/tabs/pills')
3788             }
3789             cfg.cls += ' navbar-nav'
3790         }
3791         
3792         if (this.parent().sidebar) {
3793             cfg = {
3794                 tag: 'ul',
3795                 cls: 'dashboard-menu sidebar-menu'
3796             };
3797             
3798             return cfg;
3799         }
3800         
3801         if (this.form === true) {
3802             cfg = {
3803                 tag: 'form',
3804                 cls: 'navbar-form'
3805             };
3806             
3807             if (this.align === 'right') {
3808                 cfg.cls += ' navbar-right';
3809             } else {
3810                 cfg.cls += ' navbar-left';
3811             }
3812         }
3813         
3814         if (this.align === 'right') {
3815             cfg.cls += ' navbar-right';
3816         }
3817         
3818         if (this.inverse) {
3819             cfg.cls += ' navbar-inverse';
3820             
3821         }
3822         
3823         
3824         return cfg;
3825     },
3826     /**
3827     * sets the active Navigation item
3828     * @param {Roo.bootstrap.NavItem} the new current navitem
3829     */
3830     setActiveItem : function(item)
3831     {
3832         var prev = false;
3833         Roo.each(this.navItems, function(v){
3834             if (v == item) {
3835                 return ;
3836             }
3837             if (v.isActive()) {
3838                 v.setActive(false, true);
3839                 prev = v;
3840                 
3841             }
3842             
3843         });
3844
3845         item.setActive(true, true);
3846         this.fireEvent('changed', this, item, prev);
3847         
3848         
3849     },
3850     /**
3851     * gets the active Navigation item
3852     * @return {Roo.bootstrap.NavItem} the current navitem
3853     */
3854     getActive : function()
3855     {
3856         
3857         var prev = false;
3858         Roo.each(this.navItems, function(v){
3859             
3860             if (v.isActive()) {
3861                 prev = v;
3862                 
3863             }
3864             
3865         });
3866         return prev;
3867     },
3868     
3869     indexOfNav : function()
3870     {
3871         
3872         var prev = false;
3873         Roo.each(this.navItems, function(v,i){
3874             
3875             if (v.isActive()) {
3876                 prev = i;
3877                 
3878             }
3879             
3880         });
3881         return prev;
3882     },
3883     /**
3884     * adds a Navigation item
3885     * @param {Roo.bootstrap.NavItem} the navitem to add
3886     */
3887     addItem : function(cfg)
3888     {
3889         var cn = new Roo.bootstrap.NavItem(cfg);
3890         this.register(cn);
3891         cn.parentId = this.id;
3892         cn.onRender(this.el, null);
3893         return cn;
3894     },
3895     /**
3896     * register a Navigation item
3897     * @param {Roo.bootstrap.NavItem} the navitem to add
3898     */
3899     register : function(item)
3900     {
3901         this.navItems.push( item);
3902         item.navId = this.navId;
3903     
3904     },
3905     
3906     /**
3907     * clear all the Navigation item
3908     */
3909    
3910     clearAll : function()
3911     {
3912         this.navItems = [];
3913         this.el.dom.innerHTML = '';
3914     },
3915     
3916     getNavItem: function(tabId)
3917     {
3918         var ret = false;
3919         Roo.each(this.navItems, function(e) {
3920             if (e.tabId == tabId) {
3921                ret =  e;
3922                return false;
3923             }
3924             return true;
3925             
3926         });
3927         return ret;
3928     },
3929     
3930     setActiveNext : function()
3931     {
3932         var i = this.indexOfNav(this.getActive());
3933         if (i > this.navItems.length) {
3934             return;
3935         }
3936         this.setActiveItem(this.navItems[i+1]);
3937     },
3938     setActivePrev : function()
3939     {
3940         var i = this.indexOfNav(this.getActive());
3941         if (i  < 1) {
3942             return;
3943         }
3944         this.setActiveItem(this.navItems[i-1]);
3945     },
3946     clearWasActive : function(except) {
3947         Roo.each(this.navItems, function(e) {
3948             if (e.tabId != except.tabId && e.was_active) {
3949                e.was_active = false;
3950                return false;
3951             }
3952             return true;
3953             
3954         });
3955     },
3956     getWasActive : function ()
3957     {
3958         var r = false;
3959         Roo.each(this.navItems, function(e) {
3960             if (e.was_active) {
3961                r = e;
3962                return false;
3963             }
3964             return true;
3965             
3966         });
3967         return r;
3968     }
3969     
3970     
3971 });
3972
3973  
3974 Roo.apply(Roo.bootstrap.NavGroup, {
3975     
3976     groups: {},
3977      /**
3978     * register a Navigation Group
3979     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3980     */
3981     register : function(navgrp)
3982     {
3983         this.groups[navgrp.navId] = navgrp;
3984         
3985     },
3986     /**
3987     * fetch a Navigation Group based on the navigation ID
3988     * @param {string} the navgroup to add
3989     * @returns {Roo.bootstrap.NavGroup} the navgroup 
3990     */
3991     get: function(navId) {
3992         if (typeof(this.groups[navId]) == 'undefined') {
3993             return false;
3994             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3995         }
3996         return this.groups[navId] ;
3997     }
3998     
3999     
4000     
4001 });
4002
4003  /*
4004  * - LGPL
4005  *
4006  * row
4007  * 
4008  */
4009
4010 /**
4011  * @class Roo.bootstrap.NavItem
4012  * @extends Roo.bootstrap.Component
4013  * Bootstrap Navbar.NavItem class
4014  * @cfg {String} href  link to
4015  * @cfg {String} html content of button
4016  * @cfg {String} badge text inside badge
4017  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4018  * @cfg {String} glyphicon name of glyphicon
4019  * @cfg {String} icon name of font awesome icon
4020  * @cfg {Boolean} active Is item active
4021  * @cfg {Boolean} disabled Is item disabled
4022  
4023  * @cfg {Boolean} preventDefault (true | false) default false
4024  * @cfg {String} tabId the tab that this item activates.
4025  * @cfg {String} tagtype (a|span) render as a href or span?
4026  * @cfg {Boolean} animateRef (true|false) link to element default false  
4027   
4028  * @constructor
4029  * Create a new Navbar Item
4030  * @param {Object} config The config object
4031  */
4032 Roo.bootstrap.NavItem = function(config){
4033     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4034     this.addEvents({
4035         // raw events
4036         /**
4037          * @event click
4038          * The raw click event for the entire grid.
4039          * @param {Roo.EventObject} e
4040          */
4041         "click" : true,
4042          /**
4043             * @event changed
4044             * Fires when the active item active state changes
4045             * @param {Roo.bootstrap.NavItem} this
4046             * @param {boolean} state the new state
4047              
4048          */
4049         'changed': true,
4050         /**
4051             * @event scrollto
4052             * Fires when scroll to element
4053             * @param {Roo.bootstrap.NavItem} this
4054             * @param {Object} options
4055             * @param {Roo.EventObject} e
4056              
4057          */
4058         'scrollto': true
4059     });
4060    
4061 };
4062
4063 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4064     
4065     href: false,
4066     html: '',
4067     badge: '',
4068     icon: false,
4069     glyphicon: false,
4070     active: false,
4071     preventDefault : false,
4072     tabId : false,
4073     tagtype : 'a',
4074     disabled : false,
4075     animateRef : false,
4076     was_active : false,
4077     
4078     getAutoCreate : function(){
4079          
4080         var cfg = {
4081             tag: 'li',
4082             cls: 'nav-item'
4083             
4084         };
4085         
4086         if (this.active) {
4087             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4088         }
4089         if (this.disabled) {
4090             cfg.cls += ' disabled';
4091         }
4092         
4093         if (this.href || this.html || this.glyphicon || this.icon) {
4094             cfg.cn = [
4095                 {
4096                     tag: this.tagtype,
4097                     href : this.href || "#",
4098                     html: this.html || ''
4099                 }
4100             ];
4101             
4102             if (this.icon) {
4103                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4104             }
4105
4106             if(this.glyphicon) {
4107                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4108             }
4109             
4110             if (this.menu) {
4111                 
4112                 cfg.cn[0].html += " <span class='caret'></span>";
4113              
4114             }
4115             
4116             if (this.badge !== '') {
4117                  
4118                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4119             }
4120         }
4121         
4122         
4123         
4124         return cfg;
4125     },
4126     initEvents: function() 
4127     {
4128         if (typeof (this.menu) != 'undefined') {
4129             this.menu.parentType = this.xtype;
4130             this.menu.triggerEl = this.el;
4131             this.menu = this.addxtype(Roo.apply({}, this.menu));
4132         }
4133         
4134         this.el.select('a',true).on('click', this.onClick, this);
4135         
4136         if(this.tagtype == 'span'){
4137             this.el.select('span',true).on('click', this.onClick, this);
4138         }
4139        
4140         // at this point parent should be available..
4141         this.parent().register(this);
4142     },
4143     
4144     onClick : function(e)
4145     {
4146         if(
4147                 this.preventDefault || 
4148                 this.href == '#' 
4149         ){
4150             
4151             e.preventDefault();
4152         }
4153         
4154         if (this.disabled) {
4155             return;
4156         }
4157         
4158         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4159         if (tg && tg.transition) {
4160             Roo.log("waiting for the transitionend");
4161             return;
4162         }
4163         
4164         
4165         
4166         //Roo.log("fire event clicked");
4167         if(this.fireEvent('click', this, e) === false){
4168             return;
4169         };
4170         
4171         if(this.tagtype == 'span'){
4172             return;
4173         }
4174         
4175         //Roo.log(this.href);
4176         var ael = this.el.select('a',true).first();
4177         //Roo.log(ael);
4178         
4179         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4180             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4181             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4182                 return; // ignore... - it's a 'hash' to another page.
4183             }
4184             
4185             e.preventDefault();
4186             this.scrollToElement(e);
4187         }
4188         
4189         
4190         var p =  this.parent();
4191    
4192         if (['tabs','pills'].indexOf(p.type)!==-1) {
4193             if (typeof(p.setActiveItem) !== 'undefined') {
4194                 p.setActiveItem(this);
4195             }
4196         }
4197         
4198         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4199         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4200             // remove the collapsed menu expand...
4201             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4202         }
4203     },
4204     
4205     isActive: function () {
4206         return this.active
4207     },
4208     setActive : function(state, fire, is_was_active)
4209     {
4210         if (this.active && !state && this.navId) {
4211             this.was_active = true;
4212             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4213             if (nv) {
4214                 nv.clearWasActive(this);
4215             }
4216             
4217         }
4218         this.active = state;
4219         
4220         if (!state ) {
4221             this.el.removeClass('active');
4222         } else if (!this.el.hasClass('active')) {
4223             this.el.addClass('active');
4224         }
4225         if (fire) {
4226             this.fireEvent('changed', this, state);
4227         }
4228         
4229         // show a panel if it's registered and related..
4230         
4231         if (!this.navId || !this.tabId || !state || is_was_active) {
4232             return;
4233         }
4234         
4235         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4236         if (!tg) {
4237             return;
4238         }
4239         var pan = tg.getPanelByName(this.tabId);
4240         if (!pan) {
4241             return;
4242         }
4243         // if we can not flip to new panel - go back to old nav highlight..
4244         if (false == tg.showPanel(pan)) {
4245             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4246             if (nv) {
4247                 var onav = nv.getWasActive();
4248                 if (onav) {
4249                     onav.setActive(true, false, true);
4250                 }
4251             }
4252             
4253         }
4254         
4255         
4256         
4257     },
4258      // this should not be here...
4259     setDisabled : function(state)
4260     {
4261         this.disabled = state;
4262         if (!state ) {
4263             this.el.removeClass('disabled');
4264         } else if (!this.el.hasClass('disabled')) {
4265             this.el.addClass('disabled');
4266         }
4267         
4268     },
4269     
4270     /**
4271      * Fetch the element to display the tooltip on.
4272      * @return {Roo.Element} defaults to this.el
4273      */
4274     tooltipEl : function()
4275     {
4276         return this.el.select('' + this.tagtype + '', true).first();
4277     },
4278     
4279     scrollToElement : function(e)
4280     {
4281         var c = document.body;
4282         
4283         /*
4284          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4285          */
4286         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4287             c = document.documentElement;
4288         }
4289         
4290         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4291         
4292         if(!target){
4293             return;
4294         }
4295
4296         var o = target.calcOffsetsTo(c);
4297         
4298         var options = {
4299             target : target,
4300             value : o[1]
4301         };
4302         
4303         this.fireEvent('scrollto', this, options, e);
4304         
4305         Roo.get(c).scrollTo('top', options.value, true);
4306         
4307         return;
4308     }
4309 });
4310  
4311
4312  /*
4313  * - LGPL
4314  *
4315  * sidebar item
4316  *
4317  *  li
4318  *    <span> icon </span>
4319  *    <span> text </span>
4320  *    <span>badge </span>
4321  */
4322
4323 /**
4324  * @class Roo.bootstrap.NavSidebarItem
4325  * @extends Roo.bootstrap.NavItem
4326  * Bootstrap Navbar.NavSidebarItem class
4327  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4328  * @constructor
4329  * Create a new Navbar Button
4330  * @param {Object} config The config object
4331  */
4332 Roo.bootstrap.NavSidebarItem = function(config){
4333     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4334     this.addEvents({
4335         // raw events
4336         /**
4337          * @event click
4338          * The raw click event for the entire grid.
4339          * @param {Roo.EventObject} e
4340          */
4341         "click" : true,
4342          /**
4343             * @event changed
4344             * Fires when the active item active state changes
4345             * @param {Roo.bootstrap.NavSidebarItem} this
4346             * @param {boolean} state the new state
4347              
4348          */
4349         'changed': true
4350     });
4351    
4352 };
4353
4354 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4355     
4356     badgeWeight : 'default',
4357     
4358     getAutoCreate : function(){
4359         
4360         
4361         var a = {
4362                 tag: 'a',
4363                 href : this.href || '#',
4364                 cls: '',
4365                 html : '',
4366                 cn : []
4367         };
4368         var cfg = {
4369             tag: 'li',
4370             cls: '',
4371             cn: [ a ]
4372         };
4373         var span = {
4374             tag: 'span',
4375             html : this.html || ''
4376         };
4377         
4378         
4379         if (this.active) {
4380             cfg.cls += ' active';
4381         }
4382         
4383         if (this.disabled) {
4384             cfg.cls += ' disabled';
4385         }
4386         
4387         // left icon..
4388         if (this.glyphicon || this.icon) {
4389             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4390             a.cn.push({ tag : 'i', cls : c }) ;
4391         }
4392         // html..
4393         a.cn.push(span);
4394         // then badge..
4395         if (this.badge !== '') {
4396             
4397             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4398         }
4399         // fi
4400         if (this.menu) {
4401             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4402             a.cls += 'dropdown-toggle treeview' ;
4403             
4404         }
4405         
4406         
4407         
4408         return cfg;
4409          
4410            
4411     },
4412     
4413     initEvents : function()
4414     { 
4415         this.el.on('click', this.onClick, this);
4416        
4417     
4418         if(this.badge !== ''){
4419  
4420             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4421         }
4422         
4423     },
4424     
4425     onClick : function(e)
4426     {
4427         if(this.disabled){
4428             e.preventDefault();
4429             return;
4430         }
4431         
4432         if(this.preventDefault){
4433             e.preventDefault();
4434         }
4435         
4436         this.fireEvent('click', this);
4437     },
4438     
4439     disable : function()
4440     {
4441         this.setDisabled(true);
4442     },
4443     
4444     enable : function()
4445     {
4446         this.setDisabled(false);
4447     },
4448     
4449     setDisabled : function(state)
4450     {
4451         if(this.disabled == state){
4452             return;
4453         }
4454         
4455         this.disabled = state;
4456         
4457         if (state) {
4458             this.el.addClass('disabled');
4459             return;
4460         }
4461         
4462         this.el.removeClass('disabled');
4463         
4464         return;
4465     },
4466     
4467     setActive : function(state)
4468     {
4469         if(this.active == state){
4470             return;
4471         }
4472         
4473         this.active = state;
4474         
4475         if (state) {
4476             this.el.addClass('active');
4477             return;
4478         }
4479         
4480         this.el.removeClass('active');
4481         
4482         return;
4483     },
4484     
4485     isActive: function () 
4486     {
4487         return this.active;
4488     },
4489     
4490     setBadge : function(str)
4491     {
4492         if(!this.badgeEl){
4493             return;
4494         }
4495         
4496         this.badgeEl.dom.innerHTML = str;
4497     }
4498     
4499    
4500      
4501  
4502 });
4503  
4504
4505  /*
4506  * - LGPL
4507  *
4508  * row
4509  * 
4510  */
4511
4512 /**
4513  * @class Roo.bootstrap.Row
4514  * @extends Roo.bootstrap.Component
4515  * Bootstrap Row class (contains columns...)
4516  * 
4517  * @constructor
4518  * Create a new Row
4519  * @param {Object} config The config object
4520  */
4521
4522 Roo.bootstrap.Row = function(config){
4523     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4524 };
4525
4526 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4527     
4528     getAutoCreate : function(){
4529        return {
4530             cls: 'row clearfix'
4531        };
4532     }
4533     
4534     
4535 });
4536
4537  
4538
4539  /*
4540  * - LGPL
4541  *
4542  * element
4543  * 
4544  */
4545
4546 /**
4547  * @class Roo.bootstrap.Element
4548  * @extends Roo.bootstrap.Component
4549  * Bootstrap Element class
4550  * @cfg {String} html contents of the element
4551  * @cfg {String} tag tag of the element
4552  * @cfg {String} cls class of the element
4553  * @cfg {Boolean} preventDefault (true|false) default false
4554  * @cfg {Boolean} clickable (true|false) default false
4555  * 
4556  * @constructor
4557  * Create a new Element
4558  * @param {Object} config The config object
4559  */
4560
4561 Roo.bootstrap.Element = function(config){
4562     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4563     
4564     this.addEvents({
4565         // raw events
4566         /**
4567          * @event click
4568          * When a element is chick
4569          * @param {Roo.bootstrap.Element} this
4570          * @param {Roo.EventObject} e
4571          */
4572         "click" : true
4573     });
4574 };
4575
4576 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4577     
4578     tag: 'div',
4579     cls: '',
4580     html: '',
4581     preventDefault: false, 
4582     clickable: false,
4583     
4584     getAutoCreate : function(){
4585         
4586         var cfg = {
4587             tag: this.tag,
4588             cls: this.cls,
4589             html: this.html
4590         };
4591         
4592         return cfg;
4593     },
4594     
4595     initEvents: function() 
4596     {
4597         Roo.bootstrap.Element.superclass.initEvents.call(this);
4598         
4599         if(this.clickable){
4600             this.el.on('click', this.onClick, this);
4601         }
4602         
4603     },
4604     
4605     onClick : function(e)
4606     {
4607         if(this.preventDefault){
4608             e.preventDefault();
4609         }
4610         
4611         this.fireEvent('click', this, e);
4612     },
4613     
4614     getValue : function()
4615     {
4616         return this.el.dom.innerHTML;
4617     },
4618     
4619     setValue : function(value)
4620     {
4621         this.el.dom.innerHTML = value;
4622     }
4623    
4624 });
4625
4626  
4627
4628  /*
4629  * - LGPL
4630  *
4631  * pagination
4632  * 
4633  */
4634
4635 /**
4636  * @class Roo.bootstrap.Pagination
4637  * @extends Roo.bootstrap.Component
4638  * Bootstrap Pagination class
4639  * @cfg {String} size xs | sm | md | lg
4640  * @cfg {Boolean} inverse false | true
4641  * 
4642  * @constructor
4643  * Create a new Pagination
4644  * @param {Object} config The config object
4645  */
4646
4647 Roo.bootstrap.Pagination = function(config){
4648     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4649 };
4650
4651 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4652     
4653     cls: false,
4654     size: false,
4655     inverse: false,
4656     
4657     getAutoCreate : function(){
4658         var cfg = {
4659             tag: 'ul',
4660                 cls: 'pagination'
4661         };
4662         if (this.inverse) {
4663             cfg.cls += ' inverse';
4664         }
4665         if (this.html) {
4666             cfg.html=this.html;
4667         }
4668         if (this.cls) {
4669             cfg.cls += " " + this.cls;
4670         }
4671         return cfg;
4672     }
4673    
4674 });
4675
4676  
4677
4678  /*
4679  * - LGPL
4680  *
4681  * Pagination item
4682  * 
4683  */
4684
4685
4686 /**
4687  * @class Roo.bootstrap.PaginationItem
4688  * @extends Roo.bootstrap.Component
4689  * Bootstrap PaginationItem class
4690  * @cfg {String} html text
4691  * @cfg {String} href the link
4692  * @cfg {Boolean} preventDefault (true | false) default true
4693  * @cfg {Boolean} active (true | false) default false
4694  * @cfg {Boolean} disabled default false
4695  * 
4696  * 
4697  * @constructor
4698  * Create a new PaginationItem
4699  * @param {Object} config The config object
4700  */
4701
4702
4703 Roo.bootstrap.PaginationItem = function(config){
4704     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4705     this.addEvents({
4706         // raw events
4707         /**
4708          * @event click
4709          * The raw click event for the entire grid.
4710          * @param {Roo.EventObject} e
4711          */
4712         "click" : true
4713     });
4714 };
4715
4716 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4717     
4718     href : false,
4719     html : false,
4720     preventDefault: true,
4721     active : false,
4722     cls : false,
4723     disabled: false,
4724     
4725     getAutoCreate : function(){
4726         var cfg= {
4727             tag: 'li',
4728             cn: [
4729                 {
4730                     tag : 'a',
4731                     href : this.href ? this.href : '#',
4732                     html : this.html ? this.html : ''
4733                 }
4734             ]
4735         };
4736         
4737         if(this.cls){
4738             cfg.cls = this.cls;
4739         }
4740         
4741         if(this.disabled){
4742             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4743         }
4744         
4745         if(this.active){
4746             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4747         }
4748         
4749         return cfg;
4750     },
4751     
4752     initEvents: function() {
4753         
4754         this.el.on('click', this.onClick, this);
4755         
4756     },
4757     onClick : function(e)
4758     {
4759         Roo.log('PaginationItem on click ');
4760         if(this.preventDefault){
4761             e.preventDefault();
4762         }
4763         
4764         if(this.disabled){
4765             return;
4766         }
4767         
4768         this.fireEvent('click', this, e);
4769     }
4770    
4771 });
4772
4773  
4774
4775  /*
4776  * - LGPL
4777  *
4778  * slider
4779  * 
4780  */
4781
4782
4783 /**
4784  * @class Roo.bootstrap.Slider
4785  * @extends Roo.bootstrap.Component
4786  * Bootstrap Slider class
4787  *    
4788  * @constructor
4789  * Create a new Slider
4790  * @param {Object} config The config object
4791  */
4792
4793 Roo.bootstrap.Slider = function(config){
4794     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4795 };
4796
4797 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4798     
4799     getAutoCreate : function(){
4800         
4801         var cfg = {
4802             tag: 'div',
4803             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4804             cn: [
4805                 {
4806                     tag: 'a',
4807                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4808                 }
4809             ]
4810         };
4811         
4812         return cfg;
4813     }
4814    
4815 });
4816
4817  /*
4818  * Based on:
4819  * Ext JS Library 1.1.1
4820  * Copyright(c) 2006-2007, Ext JS, LLC.
4821  *
4822  * Originally Released Under LGPL - original licence link has changed is not relivant.
4823  *
4824  * Fork - LGPL
4825  * <script type="text/javascript">
4826  */
4827  
4828
4829 /**
4830  * @class Roo.grid.ColumnModel
4831  * @extends Roo.util.Observable
4832  * This is the default implementation of a ColumnModel used by the Grid. It defines
4833  * the columns in the grid.
4834  * <br>Usage:<br>
4835  <pre><code>
4836  var colModel = new Roo.grid.ColumnModel([
4837         {header: "Ticker", width: 60, sortable: true, locked: true},
4838         {header: "Company Name", width: 150, sortable: true},
4839         {header: "Market Cap.", width: 100, sortable: true},
4840         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4841         {header: "Employees", width: 100, sortable: true, resizable: false}
4842  ]);
4843  </code></pre>
4844  * <p>
4845  
4846  * The config options listed for this class are options which may appear in each
4847  * individual column definition.
4848  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4849  * @constructor
4850  * @param {Object} config An Array of column config objects. See this class's
4851  * config objects for details.
4852 */
4853 Roo.grid.ColumnModel = function(config){
4854         /**
4855      * The config passed into the constructor
4856      */
4857     this.config = config;
4858     this.lookup = {};
4859
4860     // if no id, create one
4861     // if the column does not have a dataIndex mapping,
4862     // map it to the order it is in the config
4863     for(var i = 0, len = config.length; i < len; i++){
4864         var c = config[i];
4865         if(typeof c.dataIndex == "undefined"){
4866             c.dataIndex = i;
4867         }
4868         if(typeof c.renderer == "string"){
4869             c.renderer = Roo.util.Format[c.renderer];
4870         }
4871         if(typeof c.id == "undefined"){
4872             c.id = Roo.id();
4873         }
4874         if(c.editor && c.editor.xtype){
4875             c.editor  = Roo.factory(c.editor, Roo.grid);
4876         }
4877         if(c.editor && c.editor.isFormField){
4878             c.editor = new Roo.grid.GridEditor(c.editor);
4879         }
4880         this.lookup[c.id] = c;
4881     }
4882
4883     /**
4884      * The width of columns which have no width specified (defaults to 100)
4885      * @type Number
4886      */
4887     this.defaultWidth = 100;
4888
4889     /**
4890      * Default sortable of columns which have no sortable specified (defaults to false)
4891      * @type Boolean
4892      */
4893     this.defaultSortable = false;
4894
4895     this.addEvents({
4896         /**
4897              * @event widthchange
4898              * Fires when the width of a column changes.
4899              * @param {ColumnModel} this
4900              * @param {Number} columnIndex The column index
4901              * @param {Number} newWidth The new width
4902              */
4903             "widthchange": true,
4904         /**
4905              * @event headerchange
4906              * Fires when the text of a header changes.
4907              * @param {ColumnModel} this
4908              * @param {Number} columnIndex The column index
4909              * @param {Number} newText The new header text
4910              */
4911             "headerchange": true,
4912         /**
4913              * @event hiddenchange
4914              * Fires when a column is hidden or "unhidden".
4915              * @param {ColumnModel} this
4916              * @param {Number} columnIndex The column index
4917              * @param {Boolean} hidden true if hidden, false otherwise
4918              */
4919             "hiddenchange": true,
4920             /**
4921          * @event columnmoved
4922          * Fires when a column is moved.
4923          * @param {ColumnModel} this
4924          * @param {Number} oldIndex
4925          * @param {Number} newIndex
4926          */
4927         "columnmoved" : true,
4928         /**
4929          * @event columlockchange
4930          * Fires when a column's locked state is changed
4931          * @param {ColumnModel} this
4932          * @param {Number} colIndex
4933          * @param {Boolean} locked true if locked
4934          */
4935         "columnlockchange" : true
4936     });
4937     Roo.grid.ColumnModel.superclass.constructor.call(this);
4938 };
4939 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4940     /**
4941      * @cfg {String} header The header text to display in the Grid view.
4942      */
4943     /**
4944      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4945      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4946      * specified, the column's index is used as an index into the Record's data Array.
4947      */
4948     /**
4949      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4950      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4951      */
4952     /**
4953      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4954      * Defaults to the value of the {@link #defaultSortable} property.
4955      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4956      */
4957     /**
4958      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4959      */
4960     /**
4961      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4962      */
4963     /**
4964      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4965      */
4966     /**
4967      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4968      */
4969     /**
4970      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4971      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4972      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4973      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4974      */
4975        /**
4976      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4977      */
4978     /**
4979      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4980      */
4981     /**
4982      * @cfg {String} cursor (Optional)
4983      */
4984     /**
4985      * @cfg {String} tooltip (Optional)
4986      */
4987     /**
4988      * @cfg {Number} xs (Optional)
4989      */
4990     /**
4991      * @cfg {Number} sm (Optional)
4992      */
4993     /**
4994      * @cfg {Number} md (Optional)
4995      */
4996     /**
4997      * @cfg {Number} lg (Optional)
4998      */
4999     /**
5000      * Returns the id of the column at the specified index.
5001      * @param {Number} index The column index
5002      * @return {String} the id
5003      */
5004     getColumnId : function(index){
5005         return this.config[index].id;
5006     },
5007
5008     /**
5009      * Returns the column for a specified id.
5010      * @param {String} id The column id
5011      * @return {Object} the column
5012      */
5013     getColumnById : function(id){
5014         return this.lookup[id];
5015     },
5016
5017     
5018     /**
5019      * Returns the column for a specified dataIndex.
5020      * @param {String} dataIndex The column dataIndex
5021      * @return {Object|Boolean} the column or false if not found
5022      */
5023     getColumnByDataIndex: function(dataIndex){
5024         var index = this.findColumnIndex(dataIndex);
5025         return index > -1 ? this.config[index] : false;
5026     },
5027     
5028     /**
5029      * Returns the index for a specified column id.
5030      * @param {String} id The column id
5031      * @return {Number} the index, or -1 if not found
5032      */
5033     getIndexById : function(id){
5034         for(var i = 0, len = this.config.length; i < len; i++){
5035             if(this.config[i].id == id){
5036                 return i;
5037             }
5038         }
5039         return -1;
5040     },
5041     
5042     /**
5043      * Returns the index for a specified column dataIndex.
5044      * @param {String} dataIndex The column dataIndex
5045      * @return {Number} the index, or -1 if not found
5046      */
5047     
5048     findColumnIndex : function(dataIndex){
5049         for(var i = 0, len = this.config.length; i < len; i++){
5050             if(this.config[i].dataIndex == dataIndex){
5051                 return i;
5052             }
5053         }
5054         return -1;
5055     },
5056     
5057     
5058     moveColumn : function(oldIndex, newIndex){
5059         var c = this.config[oldIndex];
5060         this.config.splice(oldIndex, 1);
5061         this.config.splice(newIndex, 0, c);
5062         this.dataMap = null;
5063         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5064     },
5065
5066     isLocked : function(colIndex){
5067         return this.config[colIndex].locked === true;
5068     },
5069
5070     setLocked : function(colIndex, value, suppressEvent){
5071         if(this.isLocked(colIndex) == value){
5072             return;
5073         }
5074         this.config[colIndex].locked = value;
5075         if(!suppressEvent){
5076             this.fireEvent("columnlockchange", this, colIndex, value);
5077         }
5078     },
5079
5080     getTotalLockedWidth : function(){
5081         var totalWidth = 0;
5082         for(var i = 0; i < this.config.length; i++){
5083             if(this.isLocked(i) && !this.isHidden(i)){
5084                 this.totalWidth += this.getColumnWidth(i);
5085             }
5086         }
5087         return totalWidth;
5088     },
5089
5090     getLockedCount : function(){
5091         for(var i = 0, len = this.config.length; i < len; i++){
5092             if(!this.isLocked(i)){
5093                 return i;
5094             }
5095         }
5096     },
5097
5098     /**
5099      * Returns the number of columns.
5100      * @return {Number}
5101      */
5102     getColumnCount : function(visibleOnly){
5103         if(visibleOnly === true){
5104             var c = 0;
5105             for(var i = 0, len = this.config.length; i < len; i++){
5106                 if(!this.isHidden(i)){
5107                     c++;
5108                 }
5109             }
5110             return c;
5111         }
5112         return this.config.length;
5113     },
5114
5115     /**
5116      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5117      * @param {Function} fn
5118      * @param {Object} scope (optional)
5119      * @return {Array} result
5120      */
5121     getColumnsBy : function(fn, scope){
5122         var r = [];
5123         for(var i = 0, len = this.config.length; i < len; i++){
5124             var c = this.config[i];
5125             if(fn.call(scope||this, c, i) === true){
5126                 r[r.length] = c;
5127             }
5128         }
5129         return r;
5130     },
5131
5132     /**
5133      * Returns true if the specified column is sortable.
5134      * @param {Number} col The column index
5135      * @return {Boolean}
5136      */
5137     isSortable : function(col){
5138         if(typeof this.config[col].sortable == "undefined"){
5139             return this.defaultSortable;
5140         }
5141         return this.config[col].sortable;
5142     },
5143
5144     /**
5145      * Returns the rendering (formatting) function defined for the column.
5146      * @param {Number} col The column index.
5147      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5148      */
5149     getRenderer : function(col){
5150         if(!this.config[col].renderer){
5151             return Roo.grid.ColumnModel.defaultRenderer;
5152         }
5153         return this.config[col].renderer;
5154     },
5155
5156     /**
5157      * Sets the rendering (formatting) function for a column.
5158      * @param {Number} col The column index
5159      * @param {Function} fn The function to use to process the cell's raw data
5160      * to return HTML markup for the grid view. The render function is called with
5161      * the following parameters:<ul>
5162      * <li>Data value.</li>
5163      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5164      * <li>css A CSS style string to apply to the table cell.</li>
5165      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5166      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5167      * <li>Row index</li>
5168      * <li>Column index</li>
5169      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5170      */
5171     setRenderer : function(col, fn){
5172         this.config[col].renderer = fn;
5173     },
5174
5175     /**
5176      * Returns the width for the specified column.
5177      * @param {Number} col The column index
5178      * @return {Number}
5179      */
5180     getColumnWidth : function(col){
5181         return this.config[col].width * 1 || this.defaultWidth;
5182     },
5183
5184     /**
5185      * Sets the width for a column.
5186      * @param {Number} col The column index
5187      * @param {Number} width The new width
5188      */
5189     setColumnWidth : function(col, width, suppressEvent){
5190         this.config[col].width = width;
5191         this.totalWidth = null;
5192         if(!suppressEvent){
5193              this.fireEvent("widthchange", this, col, width);
5194         }
5195     },
5196
5197     /**
5198      * Returns the total width of all columns.
5199      * @param {Boolean} includeHidden True to include hidden column widths
5200      * @return {Number}
5201      */
5202     getTotalWidth : function(includeHidden){
5203         if(!this.totalWidth){
5204             this.totalWidth = 0;
5205             for(var i = 0, len = this.config.length; i < len; i++){
5206                 if(includeHidden || !this.isHidden(i)){
5207                     this.totalWidth += this.getColumnWidth(i);
5208                 }
5209             }
5210         }
5211         return this.totalWidth;
5212     },
5213
5214     /**
5215      * Returns the header for the specified column.
5216      * @param {Number} col The column index
5217      * @return {String}
5218      */
5219     getColumnHeader : function(col){
5220         return this.config[col].header;
5221     },
5222
5223     /**
5224      * Sets the header for a column.
5225      * @param {Number} col The column index
5226      * @param {String} header The new header
5227      */
5228     setColumnHeader : function(col, header){
5229         this.config[col].header = header;
5230         this.fireEvent("headerchange", this, col, header);
5231     },
5232
5233     /**
5234      * Returns the tooltip for the specified column.
5235      * @param {Number} col The column index
5236      * @return {String}
5237      */
5238     getColumnTooltip : function(col){
5239             return this.config[col].tooltip;
5240     },
5241     /**
5242      * Sets the tooltip for a column.
5243      * @param {Number} col The column index
5244      * @param {String} tooltip The new tooltip
5245      */
5246     setColumnTooltip : function(col, tooltip){
5247             this.config[col].tooltip = tooltip;
5248     },
5249
5250     /**
5251      * Returns the dataIndex for the specified column.
5252      * @param {Number} col The column index
5253      * @return {Number}
5254      */
5255     getDataIndex : function(col){
5256         return this.config[col].dataIndex;
5257     },
5258
5259     /**
5260      * Sets the dataIndex for a column.
5261      * @param {Number} col The column index
5262      * @param {Number} dataIndex The new dataIndex
5263      */
5264     setDataIndex : function(col, dataIndex){
5265         this.config[col].dataIndex = dataIndex;
5266     },
5267
5268     
5269     
5270     /**
5271      * Returns true if the cell is editable.
5272      * @param {Number} colIndex The column index
5273      * @param {Number} rowIndex The row index
5274      * @return {Boolean}
5275      */
5276     isCellEditable : function(colIndex, rowIndex){
5277         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5278     },
5279
5280     /**
5281      * Returns the editor defined for the cell/column.
5282      * return false or null to disable editing.
5283      * @param {Number} colIndex The column index
5284      * @param {Number} rowIndex The row index
5285      * @return {Object}
5286      */
5287     getCellEditor : function(colIndex, rowIndex){
5288         return this.config[colIndex].editor;
5289     },
5290
5291     /**
5292      * Sets if a column is editable.
5293      * @param {Number} col The column index
5294      * @param {Boolean} editable True if the column is editable
5295      */
5296     setEditable : function(col, editable){
5297         this.config[col].editable = editable;
5298     },
5299
5300
5301     /**
5302      * Returns true if the column is hidden.
5303      * @param {Number} colIndex The column index
5304      * @return {Boolean}
5305      */
5306     isHidden : function(colIndex){
5307         return this.config[colIndex].hidden;
5308     },
5309
5310
5311     /**
5312      * Returns true if the column width cannot be changed
5313      */
5314     isFixed : function(colIndex){
5315         return this.config[colIndex].fixed;
5316     },
5317
5318     /**
5319      * Returns true if the column can be resized
5320      * @return {Boolean}
5321      */
5322     isResizable : function(colIndex){
5323         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5324     },
5325     /**
5326      * Sets if a column is hidden.
5327      * @param {Number} colIndex The column index
5328      * @param {Boolean} hidden True if the column is hidden
5329      */
5330     setHidden : function(colIndex, hidden){
5331         this.config[colIndex].hidden = hidden;
5332         this.totalWidth = null;
5333         this.fireEvent("hiddenchange", this, colIndex, hidden);
5334     },
5335
5336     /**
5337      * Sets the editor for a column.
5338      * @param {Number} col The column index
5339      * @param {Object} editor The editor object
5340      */
5341     setEditor : function(col, editor){
5342         this.config[col].editor = editor;
5343     }
5344 });
5345
5346 Roo.grid.ColumnModel.defaultRenderer = function(value){
5347         if(typeof value == "string" && value.length < 1){
5348             return "&#160;";
5349         }
5350         return value;
5351 };
5352
5353 // Alias for backwards compatibility
5354 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5355 /*
5356  * Based on:
5357  * Ext JS Library 1.1.1
5358  * Copyright(c) 2006-2007, Ext JS, LLC.
5359  *
5360  * Originally Released Under LGPL - original licence link has changed is not relivant.
5361  *
5362  * Fork - LGPL
5363  * <script type="text/javascript">
5364  */
5365  
5366 /**
5367  * @class Roo.LoadMask
5368  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5369  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5370  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5371  * element's UpdateManager load indicator and will be destroyed after the initial load.
5372  * @constructor
5373  * Create a new LoadMask
5374  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5375  * @param {Object} config The config object
5376  */
5377 Roo.LoadMask = function(el, config){
5378     this.el = Roo.get(el);
5379     Roo.apply(this, config);
5380     if(this.store){
5381         this.store.on('beforeload', this.onBeforeLoad, this);
5382         this.store.on('load', this.onLoad, this);
5383         this.store.on('loadexception', this.onLoadException, this);
5384         this.removeMask = false;
5385     }else{
5386         var um = this.el.getUpdateManager();
5387         um.showLoadIndicator = false; // disable the default indicator
5388         um.on('beforeupdate', this.onBeforeLoad, this);
5389         um.on('update', this.onLoad, this);
5390         um.on('failure', this.onLoad, this);
5391         this.removeMask = true;
5392     }
5393 };
5394
5395 Roo.LoadMask.prototype = {
5396     /**
5397      * @cfg {Boolean} removeMask
5398      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5399      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5400      */
5401     /**
5402      * @cfg {String} msg
5403      * The text to display in a centered loading message box (defaults to 'Loading...')
5404      */
5405     msg : 'Loading...',
5406     /**
5407      * @cfg {String} msgCls
5408      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5409      */
5410     msgCls : 'x-mask-loading',
5411
5412     /**
5413      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5414      * @type Boolean
5415      */
5416     disabled: false,
5417
5418     /**
5419      * Disables the mask to prevent it from being displayed
5420      */
5421     disable : function(){
5422        this.disabled = true;
5423     },
5424
5425     /**
5426      * Enables the mask so that it can be displayed
5427      */
5428     enable : function(){
5429         this.disabled = false;
5430     },
5431     
5432     onLoadException : function()
5433     {
5434         Roo.log(arguments);
5435         
5436         if (typeof(arguments[3]) != 'undefined') {
5437             Roo.MessageBox.alert("Error loading",arguments[3]);
5438         } 
5439         /*
5440         try {
5441             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5442                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5443             }   
5444         } catch(e) {
5445             
5446         }
5447         */
5448     
5449         
5450         
5451         this.el.unmask(this.removeMask);
5452     },
5453     // private
5454     onLoad : function()
5455     {
5456         this.el.unmask(this.removeMask);
5457     },
5458
5459     // private
5460     onBeforeLoad : function(){
5461         if(!this.disabled){
5462             this.el.mask(this.msg, this.msgCls);
5463         }
5464     },
5465
5466     // private
5467     destroy : function(){
5468         if(this.store){
5469             this.store.un('beforeload', this.onBeforeLoad, this);
5470             this.store.un('load', this.onLoad, this);
5471             this.store.un('loadexception', this.onLoadException, this);
5472         }else{
5473             var um = this.el.getUpdateManager();
5474             um.un('beforeupdate', this.onBeforeLoad, this);
5475             um.un('update', this.onLoad, this);
5476             um.un('failure', this.onLoad, this);
5477         }
5478     }
5479 };/*
5480  * - LGPL
5481  *
5482  * table
5483  * 
5484  */
5485
5486 /**
5487  * @class Roo.bootstrap.Table
5488  * @extends Roo.bootstrap.Component
5489  * Bootstrap Table class
5490  * @cfg {String} cls table class
5491  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5492  * @cfg {String} bgcolor Specifies the background color for a table
5493  * @cfg {Number} border Specifies whether the table cells should have borders or not
5494  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5495  * @cfg {Number} cellspacing Specifies the space between cells
5496  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5497  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5498  * @cfg {String} sortable Specifies that the table should be sortable
5499  * @cfg {String} summary Specifies a summary of the content of a table
5500  * @cfg {Number} width Specifies the width of a table
5501  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5502  * 
5503  * @cfg {boolean} striped Should the rows be alternative striped
5504  * @cfg {boolean} bordered Add borders to the table
5505  * @cfg {boolean} hover Add hover highlighting
5506  * @cfg {boolean} condensed Format condensed
5507  * @cfg {boolean} responsive Format condensed
5508  * @cfg {Boolean} loadMask (true|false) default false
5509  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5510  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5511  * @cfg {Boolean} rowSelection (true|false) default false
5512  * @cfg {Boolean} cellSelection (true|false) default false
5513  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5514  
5515  * 
5516  * @constructor
5517  * Create a new Table
5518  * @param {Object} config The config object
5519  */
5520
5521 Roo.bootstrap.Table = function(config){
5522     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5523     
5524     // BC...
5525     this.rowSelection = (typeof(config.RowSelection) != 'undefined') ? config.RowSelection : this.rowSelection;
5526     this.cellSelection = (typeof(config.CellSelection) != 'undefined') ? config.CellSelection : this.cellSelection;
5527     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5528     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5529     
5530     
5531     if (this.sm) {
5532         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5533         this.sm = this.selModel;
5534         this.sm.xmodule = this.xmodule || false;
5535     }
5536     if (this.cm && typeof(this.cm.config) == 'undefined') {
5537         this.colModel = new Roo.grid.ColumnModel(this.cm);
5538         this.cm = this.colModel;
5539         this.cm.xmodule = this.xmodule || false;
5540     }
5541     if (this.store) {
5542         this.store= Roo.factory(this.store, Roo.data);
5543         this.ds = this.store;
5544         this.ds.xmodule = this.xmodule || false;
5545          
5546     }
5547     if (this.footer && this.store) {
5548         this.footer.dataSource = this.ds;
5549         this.footer = Roo.factory(this.footer);
5550     }
5551     
5552     /** @private */
5553     this.addEvents({
5554         /**
5555          * @event cellclick
5556          * Fires when a cell is clicked
5557          * @param {Roo.bootstrap.Table} this
5558          * @param {Roo.Element} el
5559          * @param {Number} rowIndex
5560          * @param {Number} columnIndex
5561          * @param {Roo.EventObject} e
5562          */
5563         "cellclick" : true,
5564         /**
5565          * @event celldblclick
5566          * Fires when a cell is double clicked
5567          * @param {Roo.bootstrap.Table} this
5568          * @param {Roo.Element} el
5569          * @param {Number} rowIndex
5570          * @param {Number} columnIndex
5571          * @param {Roo.EventObject} e
5572          */
5573         "celldblclick" : true,
5574         /**
5575          * @event rowclick
5576          * Fires when a row is clicked
5577          * @param {Roo.bootstrap.Table} this
5578          * @param {Roo.Element} el
5579          * @param {Number} rowIndex
5580          * @param {Roo.EventObject} e
5581          */
5582         "rowclick" : true,
5583         /**
5584          * @event rowdblclick
5585          * Fires when a row is double clicked
5586          * @param {Roo.bootstrap.Table} this
5587          * @param {Roo.Element} el
5588          * @param {Number} rowIndex
5589          * @param {Roo.EventObject} e
5590          */
5591         "rowdblclick" : true,
5592         /**
5593          * @event mouseover
5594          * Fires when a mouseover occur
5595          * @param {Roo.bootstrap.Table} this
5596          * @param {Roo.Element} el
5597          * @param {Number} rowIndex
5598          * @param {Number} columnIndex
5599          * @param {Roo.EventObject} e
5600          */
5601         "mouseover" : true,
5602         /**
5603          * @event mouseout
5604          * Fires when a mouseout occur
5605          * @param {Roo.bootstrap.Table} this
5606          * @param {Roo.Element} el
5607          * @param {Number} rowIndex
5608          * @param {Number} columnIndex
5609          * @param {Roo.EventObject} e
5610          */
5611         "mouseout" : true,
5612         /**
5613          * @event rowclass
5614          * Fires when a row is rendered, so you can change add a style to it.
5615          * @param {Roo.bootstrap.Table} this
5616          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5617          */
5618         'rowclass' : true,
5619           /**
5620          * @event rowsrendered
5621          * Fires when all the  rows have been rendered
5622          * @param {Roo.bootstrap.Table} this
5623          */
5624         'rowsrendered' : true
5625         
5626     });
5627 };
5628
5629 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5630     
5631     cls: false,
5632     align: false,
5633     bgcolor: false,
5634     border: false,
5635     cellpadding: false,
5636     cellspacing: false,
5637     frame: false,
5638     rules: false,
5639     sortable: false,
5640     summary: false,
5641     width: false,
5642     striped : false,
5643     bordered: false,
5644     hover:  false,
5645     condensed : false,
5646     responsive : false,
5647     sm : false,
5648     cm : false,
5649     store : false,
5650     loadMask : false,
5651     footerShow : true,
5652     headerShow : true,
5653   
5654     rowSelection : false,
5655     cellSelection : false,
5656     layout : false,
5657     
5658     // Roo.Element - the tbody
5659     mainBody: false, 
5660     
5661     getAutoCreate : function(){
5662         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5663         
5664         cfg = {
5665             tag: 'table',
5666             cls : 'table',
5667             cn : []
5668         };
5669             
5670         if (this.striped) {
5671             cfg.cls += ' table-striped';
5672         }
5673         
5674         if (this.hover) {
5675             cfg.cls += ' table-hover';
5676         }
5677         if (this.bordered) {
5678             cfg.cls += ' table-bordered';
5679         }
5680         if (this.condensed) {
5681             cfg.cls += ' table-condensed';
5682         }
5683         if (this.responsive) {
5684             cfg.cls += ' table-responsive';
5685         }
5686         
5687         if (this.cls) {
5688             cfg.cls+=  ' ' +this.cls;
5689         }
5690         
5691         // this lot should be simplifed...
5692         
5693         if (this.align) {
5694             cfg.align=this.align;
5695         }
5696         if (this.bgcolor) {
5697             cfg.bgcolor=this.bgcolor;
5698         }
5699         if (this.border) {
5700             cfg.border=this.border;
5701         }
5702         if (this.cellpadding) {
5703             cfg.cellpadding=this.cellpadding;
5704         }
5705         if (this.cellspacing) {
5706             cfg.cellspacing=this.cellspacing;
5707         }
5708         if (this.frame) {
5709             cfg.frame=this.frame;
5710         }
5711         if (this.rules) {
5712             cfg.rules=this.rules;
5713         }
5714         if (this.sortable) {
5715             cfg.sortable=this.sortable;
5716         }
5717         if (this.summary) {
5718             cfg.summary=this.summary;
5719         }
5720         if (this.width) {
5721             cfg.width=this.width;
5722         }
5723         if (this.layout) {
5724             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5725         }
5726         
5727         if(this.store || this.cm){
5728             if(this.headerShow){
5729                 cfg.cn.push(this.renderHeader());
5730             }
5731             
5732             cfg.cn.push(this.renderBody());
5733             
5734             if(this.footerShow){
5735                 cfg.cn.push(this.renderFooter());
5736             }
5737             
5738             cfg.cls+=  ' TableGrid';
5739         }
5740         
5741         return { cn : [ cfg ] };
5742     },
5743     
5744     initEvents : function()
5745     {   
5746         if(!this.store || !this.cm){
5747             return;
5748         }
5749         
5750         //Roo.log('initEvents with ds!!!!');
5751         
5752         this.mainBody = this.el.select('tbody', true).first();
5753         
5754         
5755         var _this = this;
5756         
5757         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5758             e.on('click', _this.sort, _this);
5759         });
5760         
5761         this.el.on("click", this.onClick, this);
5762         this.el.on("dblclick", this.onDblClick, this);
5763         
5764         // why is this done????? = it breaks dialogs??
5765         //this.parent().el.setStyle('position', 'relative');
5766         
5767         
5768         if (this.footer) {
5769             this.footer.parentId = this.id;
5770             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5771         }
5772         
5773         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5774         
5775         this.store.on('load', this.onLoad, this);
5776         this.store.on('beforeload', this.onBeforeLoad, this);
5777         this.store.on('update', this.onUpdate, this);
5778         this.store.on('add', this.onAdd, this);
5779         
5780     },
5781     
5782     onMouseover : function(e, el)
5783     {
5784         var cell = Roo.get(el);
5785         
5786         if(!cell){
5787             return;
5788         }
5789         
5790         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5791             cell = cell.findParent('td', false, true);
5792         }
5793         
5794         var row = cell.findParent('tr', false, true);
5795         var cellIndex = cell.dom.cellIndex;
5796         var rowIndex = row.dom.rowIndex - 1; // start from 0
5797         
5798         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5799         
5800     },
5801     
5802     onMouseout : function(e, el)
5803     {
5804         var cell = Roo.get(el);
5805         
5806         if(!cell){
5807             return;
5808         }
5809         
5810         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5811             cell = cell.findParent('td', false, true);
5812         }
5813         
5814         var row = cell.findParent('tr', false, true);
5815         var cellIndex = cell.dom.cellIndex;
5816         var rowIndex = row.dom.rowIndex - 1; // start from 0
5817         
5818         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5819         
5820     },
5821     
5822     onClick : function(e, el)
5823     {
5824         var cell = Roo.get(el);
5825         
5826         if(!cell || (!this.cellSelection && !this.rowSelection)){
5827             return;
5828         }
5829         
5830         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5831             cell = cell.findParent('td', false, true);
5832         }
5833         
5834         if(!cell || typeof(cell) == 'undefined'){
5835             return;
5836         }
5837         
5838         var row = cell.findParent('tr', false, true);
5839         
5840         if(!row || typeof(row) == 'undefined'){
5841             return;
5842         }
5843         
5844         var cellIndex = cell.dom.cellIndex;
5845         var rowIndex = this.getRowIndex(row);
5846         
5847         // why??? - should these not be based on SelectionModel?
5848         if(this.cellSelection){
5849             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5850         }
5851         
5852         if(this.rowSelection){
5853             this.fireEvent('rowclick', this, row, rowIndex, e);
5854         }
5855         
5856         
5857     },
5858     
5859     onDblClick : function(e,el)
5860     {
5861         var cell = Roo.get(el);
5862         
5863         if(!cell || (!this.CellSelection && !this.RowSelection)){
5864             return;
5865         }
5866         
5867         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5868             cell = cell.findParent('td', false, true);
5869         }
5870         
5871         if(!cell || typeof(cell) == 'undefined'){
5872             return;
5873         }
5874         
5875         var row = cell.findParent('tr', false, true);
5876         
5877         if(!row || typeof(row) == 'undefined'){
5878             return;
5879         }
5880         
5881         var cellIndex = cell.dom.cellIndex;
5882         var rowIndex = this.getRowIndex(row);
5883         
5884         if(this.CellSelection){
5885             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5886         }
5887         
5888         if(this.RowSelection){
5889             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5890         }
5891     },
5892     
5893     sort : function(e,el)
5894     {
5895         var col = Roo.get(el);
5896         
5897         if(!col.hasClass('sortable')){
5898             return;
5899         }
5900         
5901         var sort = col.attr('sort');
5902         var dir = 'ASC';
5903         
5904         if(col.hasClass('glyphicon-arrow-up')){
5905             dir = 'DESC';
5906         }
5907         
5908         this.store.sortInfo = {field : sort, direction : dir};
5909         
5910         if (this.footer) {
5911             Roo.log("calling footer first");
5912             this.footer.onClick('first');
5913         } else {
5914         
5915             this.store.load({ params : { start : 0 } });
5916         }
5917     },
5918     
5919     renderHeader : function()
5920     {
5921         var header = {
5922             tag: 'thead',
5923             cn : []
5924         };
5925         
5926         var cm = this.cm;
5927         
5928         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5929             
5930             var config = cm.config[i];
5931             
5932             var c = {
5933                 tag: 'th',
5934                 style : '',
5935                 html: cm.getColumnHeader(i)
5936             };
5937             
5938             var hh = '';
5939             
5940             if(typeof(config.lgHeader) != 'undefined'){
5941                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
5942             }
5943             
5944             if(typeof(config.mdHeader) != 'undefined'){
5945                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
5946             }
5947             
5948             if(typeof(config.smHeader) != 'undefined'){
5949                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
5950             }
5951             
5952             if(typeof(config.xsHeader) != 'undefined'){
5953                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
5954             }
5955             
5956             if(hh.length){
5957                 c.html = hh;
5958             }
5959             
5960             if(typeof(config.tooltip) != 'undefined'){
5961                 c.tooltip = config.tooltip;
5962             }
5963             
5964             if(typeof(config.colspan) != 'undefined'){
5965                 c.colspan = config.colspan;
5966             }
5967             
5968             if(typeof(config.hidden) != 'undefined' && config.hidden){
5969                 c.style += ' display:none;';
5970             }
5971             
5972             if(typeof(config.dataIndex) != 'undefined'){
5973                 c.sort = config.dataIndex;
5974             }
5975             
5976             if(typeof(config.sortable) != 'undefined' && config.sortable){
5977                 c.cls = 'sortable';
5978             }
5979             
5980             if(typeof(config.align) != 'undefined' && config.align.length){
5981                 c.style += ' text-align:' + config.align + ';';
5982             }
5983             
5984             if(typeof(config.width) != 'undefined'){
5985                 c.style += ' width:' + config.width + 'px;';
5986             }
5987             
5988             if(typeof(config.cls) != 'undefined'){
5989                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
5990             }
5991             
5992             ['xs','sm','md','lg'].map(function(size){
5993                 
5994                 if(typeof(config[size]) == 'undefined'){
5995                     return;
5996                 }
5997                 
5998                 if (!config[size]) { // 0 = hidden
5999                     cfg.cls += ' hidden-' + size;
6000                     return;
6001                 }
6002                 
6003                 cfg.cls += ' col-' + size + '-' + config[size];
6004
6005             });
6006             
6007             header.cn.push(c)
6008         }
6009         
6010         return header;
6011     },
6012     
6013     renderBody : function()
6014     {
6015         var body = {
6016             tag: 'tbody',
6017             cn : [
6018                 {
6019                     tag: 'tr',
6020                     cn : [
6021                         {
6022                             tag : 'td',
6023                             colspan :  this.cm.getColumnCount()
6024                         }
6025                     ]
6026                 }
6027             ]
6028         };
6029         
6030         return body;
6031     },
6032     
6033     renderFooter : function()
6034     {
6035         var footer = {
6036             tag: 'tfoot',
6037             cn : [
6038                 {
6039                     tag: 'tr',
6040                     cn : [
6041                         {
6042                             tag : 'td',
6043                             colspan :  this.cm.getColumnCount()
6044                         }
6045                     ]
6046                 }
6047             ]
6048         };
6049         
6050         return footer;
6051     },
6052     
6053     
6054     
6055     onLoad : function()
6056     {
6057         Roo.log('ds onload');
6058         this.clear();
6059         
6060         var _this = this;
6061         var cm = this.cm;
6062         var ds = this.store;
6063         
6064         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6065             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
6066             
6067             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6068                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
6069             }
6070             
6071             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6072                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
6073             }
6074         });
6075         
6076         var tbody =  this.mainBody;
6077               
6078         if(ds.getCount() > 0){
6079             ds.data.each(function(d,rowIndex){
6080                 var row =  this.renderRow(cm, ds, rowIndex);
6081                 
6082                 tbody.createChild(row);
6083                 
6084                 var _this = this;
6085                 
6086                 if(row.cellObjects.length){
6087                     Roo.each(row.cellObjects, function(r){
6088                         _this.renderCellObject(r);
6089                     })
6090                 }
6091                 
6092             }, this);
6093         }
6094         
6095         Roo.each(this.el.select('tbody td', true).elements, function(e){
6096             e.on('mouseover', _this.onMouseover, _this);
6097         });
6098         
6099         Roo.each(this.el.select('tbody td', true).elements, function(e){
6100             e.on('mouseout', _this.onMouseout, _this);
6101         });
6102         this.fireEvent('rowsrendered', this);
6103         //if(this.loadMask){
6104         //    this.maskEl.hide();
6105         //}
6106     },
6107     
6108     
6109     onUpdate : function(ds,record)
6110     {
6111         this.refreshRow(record);
6112     },
6113     
6114     onRemove : function(ds, record, index, isUpdate){
6115         if(isUpdate !== true){
6116             this.fireEvent("beforerowremoved", this, index, record);
6117         }
6118         var bt = this.mainBody.dom;
6119         
6120         var rows = this.el.select('tbody > tr', true).elements;
6121         
6122         if(typeof(rows[index]) != 'undefined'){
6123             bt.removeChild(rows[index].dom);
6124         }
6125         
6126 //        if(bt.rows[index]){
6127 //            bt.removeChild(bt.rows[index]);
6128 //        }
6129         
6130         if(isUpdate !== true){
6131             //this.stripeRows(index);
6132             //this.syncRowHeights(index, index);
6133             //this.layout();
6134             this.fireEvent("rowremoved", this, index, record);
6135         }
6136     },
6137     
6138     onAdd : function(ds, records, rowIndex)
6139     {
6140         //Roo.log('on Add called');
6141         // - note this does not handle multiple adding very well..
6142         var bt = this.mainBody.dom;
6143         for (var i =0 ; i < records.length;i++) {
6144             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6145             //Roo.log(records[i]);
6146             //Roo.log(this.store.getAt(rowIndex+i));
6147             this.insertRow(this.store, rowIndex + i, false);
6148             return;
6149         }
6150         
6151     },
6152     
6153     
6154     refreshRow : function(record){
6155         var ds = this.store, index;
6156         if(typeof record == 'number'){
6157             index = record;
6158             record = ds.getAt(index);
6159         }else{
6160             index = ds.indexOf(record);
6161         }
6162         this.insertRow(ds, index, true);
6163         this.onRemove(ds, record, index+1, true);
6164         //this.syncRowHeights(index, index);
6165         //this.layout();
6166         this.fireEvent("rowupdated", this, index, record);
6167     },
6168     
6169     insertRow : function(dm, rowIndex, isUpdate){
6170         
6171         if(!isUpdate){
6172             this.fireEvent("beforerowsinserted", this, rowIndex);
6173         }
6174             //var s = this.getScrollState();
6175         var row = this.renderRow(this.cm, this.store, rowIndex);
6176         // insert before rowIndex..
6177         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6178         
6179         var _this = this;
6180                 
6181         if(row.cellObjects.length){
6182             Roo.each(row.cellObjects, function(r){
6183                 _this.renderCellObject(r);
6184             })
6185         }
6186             
6187         if(!isUpdate){
6188             this.fireEvent("rowsinserted", this, rowIndex);
6189             //this.syncRowHeights(firstRow, lastRow);
6190             //this.stripeRows(firstRow);
6191             //this.layout();
6192         }
6193         
6194     },
6195     
6196     
6197     getRowDom : function(rowIndex)
6198     {
6199         var rows = this.el.select('tbody > tr', true).elements;
6200         
6201         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6202         
6203     },
6204     // returns the object tree for a tr..
6205   
6206     
6207     renderRow : function(cm, ds, rowIndex) 
6208     {
6209         
6210         var d = ds.getAt(rowIndex);
6211         
6212         var row = {
6213             tag : 'tr',
6214             cn : []
6215         };
6216             
6217         var cellObjects = [];
6218         
6219         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6220             var config = cm.config[i];
6221             
6222             var renderer = cm.getRenderer(i);
6223             var value = '';
6224             var id = false;
6225             
6226             if(typeof(renderer) !== 'undefined'){
6227                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6228             }
6229             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6230             // and are rendered into the cells after the row is rendered - using the id for the element.
6231             
6232             if(typeof(value) === 'object'){
6233                 id = Roo.id();
6234                 cellObjects.push({
6235                     container : id,
6236                     cfg : value 
6237                 })
6238             }
6239             
6240             var rowcfg = {
6241                 record: d,
6242                 rowIndex : rowIndex,
6243                 colIndex : i,
6244                 rowClass : ''
6245             };
6246
6247             this.fireEvent('rowclass', this, rowcfg);
6248             
6249             var td = {
6250                 tag: 'td',
6251                 cls : rowcfg.rowClass,
6252                 style: '',
6253                 html: (typeof(value) === 'object') ? '' : value
6254             };
6255             
6256             if (id) {
6257                 td.id = id;
6258             }
6259             
6260             if(typeof(config.colspan) != 'undefined'){
6261                 td.colspan = config.colspan;
6262             }
6263             
6264             if(typeof(config.hidden) != 'undefined' && config.hidden){
6265                 td.style += ' display:none;';
6266             }
6267             
6268             if(typeof(config.align) != 'undefined' && config.align.length){
6269                 td.style += ' text-align:' + config.align + ';';
6270             }
6271             
6272             if(typeof(config.width) != 'undefined'){
6273                 td.style += ' width:' +  config.width + 'px;';
6274             }
6275             
6276             if(typeof(config.cursor) != 'undefined'){
6277                 td.style += ' cursor:' +  config.cursor + ';';
6278             }
6279             
6280             if(typeof(config.cls) != 'undefined'){
6281                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6282             }
6283             
6284             ['xs','sm','md','lg'].map(function(size){
6285                 
6286                 if(typeof(config[size]) == 'undefined'){
6287                     return;
6288                 }
6289                 
6290                 if (!config[size]) { // 0 = hidden
6291                     td.cls += ' hidden-' + size;
6292                     return;
6293                 }
6294                 
6295                 td.cls += ' col-' + size + '-' + config[size];
6296
6297             });
6298              
6299             row.cn.push(td);
6300            
6301         }
6302         
6303         row.cellObjects = cellObjects;
6304         
6305         return row;
6306           
6307     },
6308     
6309     
6310     
6311     onBeforeLoad : function()
6312     {
6313         //Roo.log('ds onBeforeLoad');
6314         
6315         //this.clear();
6316         
6317         //if(this.loadMask){
6318         //    this.maskEl.show();
6319         //}
6320     },
6321      /**
6322      * Remove all rows
6323      */
6324     clear : function()
6325     {
6326         this.el.select('tbody', true).first().dom.innerHTML = '';
6327     },
6328     /**
6329      * Show or hide a row.
6330      * @param {Number} rowIndex to show or hide
6331      * @param {Boolean} state hide
6332      */
6333     setRowVisibility : function(rowIndex, state)
6334     {
6335         var bt = this.mainBody.dom;
6336         
6337         var rows = this.el.select('tbody > tr', true).elements;
6338         
6339         if(typeof(rows[rowIndex]) == 'undefined'){
6340             return;
6341         }
6342         rows[rowIndex].dom.style.display = state ? '' : 'none';
6343     },
6344     
6345     
6346     getSelectionModel : function(){
6347         if(!this.selModel){
6348             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6349         }
6350         return this.selModel;
6351     },
6352     /*
6353      * Render the Roo.bootstrap object from renderder
6354      */
6355     renderCellObject : function(r)
6356     {
6357         var _this = this;
6358         
6359         var t = r.cfg.render(r.container);
6360         
6361         if(r.cfg.cn){
6362             Roo.each(r.cfg.cn, function(c){
6363                 var child = {
6364                     container: t.getChildContainer(),
6365                     cfg: c
6366                 };
6367                 _this.renderCellObject(child);
6368             })
6369         }
6370     },
6371     
6372     getRowIndex : function(row)
6373     {
6374         var rowIndex = -1;
6375         
6376         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6377             if(el != row){
6378                 return;
6379             }
6380             
6381             rowIndex = index;
6382         });
6383         
6384         return rowIndex;
6385     }
6386    
6387 });
6388
6389  
6390
6391  /*
6392  * - LGPL
6393  *
6394  * table cell
6395  * 
6396  */
6397
6398 /**
6399  * @class Roo.bootstrap.TableCell
6400  * @extends Roo.bootstrap.Component
6401  * Bootstrap TableCell class
6402  * @cfg {String} html cell contain text
6403  * @cfg {String} cls cell class
6404  * @cfg {String} tag cell tag (td|th) default td
6405  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6406  * @cfg {String} align Aligns the content in a cell
6407  * @cfg {String} axis Categorizes cells
6408  * @cfg {String} bgcolor Specifies the background color of a cell
6409  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6410  * @cfg {Number} colspan Specifies the number of columns a cell should span
6411  * @cfg {String} headers Specifies one or more header cells a cell is related to
6412  * @cfg {Number} height Sets the height of a cell
6413  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6414  * @cfg {Number} rowspan Sets the number of rows a cell should span
6415  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6416  * @cfg {String} valign Vertical aligns the content in a cell
6417  * @cfg {Number} width Specifies the width of a cell
6418  * 
6419  * @constructor
6420  * Create a new TableCell
6421  * @param {Object} config The config object
6422  */
6423
6424 Roo.bootstrap.TableCell = function(config){
6425     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6426 };
6427
6428 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6429     
6430     html: false,
6431     cls: false,
6432     tag: false,
6433     abbr: false,
6434     align: false,
6435     axis: false,
6436     bgcolor: false,
6437     charoff: false,
6438     colspan: false,
6439     headers: false,
6440     height: false,
6441     nowrap: false,
6442     rowspan: false,
6443     scope: false,
6444     valign: false,
6445     width: false,
6446     
6447     
6448     getAutoCreate : function(){
6449         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6450         
6451         cfg = {
6452             tag: 'td'
6453         };
6454         
6455         if(this.tag){
6456             cfg.tag = this.tag;
6457         }
6458         
6459         if (this.html) {
6460             cfg.html=this.html
6461         }
6462         if (this.cls) {
6463             cfg.cls=this.cls
6464         }
6465         if (this.abbr) {
6466             cfg.abbr=this.abbr
6467         }
6468         if (this.align) {
6469             cfg.align=this.align
6470         }
6471         if (this.axis) {
6472             cfg.axis=this.axis
6473         }
6474         if (this.bgcolor) {
6475             cfg.bgcolor=this.bgcolor
6476         }
6477         if (this.charoff) {
6478             cfg.charoff=this.charoff
6479         }
6480         if (this.colspan) {
6481             cfg.colspan=this.colspan
6482         }
6483         if (this.headers) {
6484             cfg.headers=this.headers
6485         }
6486         if (this.height) {
6487             cfg.height=this.height
6488         }
6489         if (this.nowrap) {
6490             cfg.nowrap=this.nowrap
6491         }
6492         if (this.rowspan) {
6493             cfg.rowspan=this.rowspan
6494         }
6495         if (this.scope) {
6496             cfg.scope=this.scope
6497         }
6498         if (this.valign) {
6499             cfg.valign=this.valign
6500         }
6501         if (this.width) {
6502             cfg.width=this.width
6503         }
6504         
6505         
6506         return cfg;
6507     }
6508    
6509 });
6510
6511  
6512
6513  /*
6514  * - LGPL
6515  *
6516  * table row
6517  * 
6518  */
6519
6520 /**
6521  * @class Roo.bootstrap.TableRow
6522  * @extends Roo.bootstrap.Component
6523  * Bootstrap TableRow class
6524  * @cfg {String} cls row class
6525  * @cfg {String} align Aligns the content in a table row
6526  * @cfg {String} bgcolor Specifies a background color for a table row
6527  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6528  * @cfg {String} valign Vertical aligns the content in a table row
6529  * 
6530  * @constructor
6531  * Create a new TableRow
6532  * @param {Object} config The config object
6533  */
6534
6535 Roo.bootstrap.TableRow = function(config){
6536     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6537 };
6538
6539 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6540     
6541     cls: false,
6542     align: false,
6543     bgcolor: false,
6544     charoff: false,
6545     valign: false,
6546     
6547     getAutoCreate : function(){
6548         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6549         
6550         cfg = {
6551             tag: 'tr'
6552         };
6553             
6554         if(this.cls){
6555             cfg.cls = this.cls;
6556         }
6557         if(this.align){
6558             cfg.align = this.align;
6559         }
6560         if(this.bgcolor){
6561             cfg.bgcolor = this.bgcolor;
6562         }
6563         if(this.charoff){
6564             cfg.charoff = this.charoff;
6565         }
6566         if(this.valign){
6567             cfg.valign = this.valign;
6568         }
6569         
6570         return cfg;
6571     }
6572    
6573 });
6574
6575  
6576
6577  /*
6578  * - LGPL
6579  *
6580  * table body
6581  * 
6582  */
6583
6584 /**
6585  * @class Roo.bootstrap.TableBody
6586  * @extends Roo.bootstrap.Component
6587  * Bootstrap TableBody class
6588  * @cfg {String} cls element class
6589  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6590  * @cfg {String} align Aligns the content inside the element
6591  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6592  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6593  * 
6594  * @constructor
6595  * Create a new TableBody
6596  * @param {Object} config The config object
6597  */
6598
6599 Roo.bootstrap.TableBody = function(config){
6600     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6601 };
6602
6603 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6604     
6605     cls: false,
6606     tag: false,
6607     align: false,
6608     charoff: false,
6609     valign: false,
6610     
6611     getAutoCreate : function(){
6612         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6613         
6614         cfg = {
6615             tag: 'tbody'
6616         };
6617             
6618         if (this.cls) {
6619             cfg.cls=this.cls
6620         }
6621         if(this.tag){
6622             cfg.tag = this.tag;
6623         }
6624         
6625         if(this.align){
6626             cfg.align = this.align;
6627         }
6628         if(this.charoff){
6629             cfg.charoff = this.charoff;
6630         }
6631         if(this.valign){
6632             cfg.valign = this.valign;
6633         }
6634         
6635         return cfg;
6636     }
6637     
6638     
6639 //    initEvents : function()
6640 //    {
6641 //        
6642 //        if(!this.store){
6643 //            return;
6644 //        }
6645 //        
6646 //        this.store = Roo.factory(this.store, Roo.data);
6647 //        this.store.on('load', this.onLoad, this);
6648 //        
6649 //        this.store.load();
6650 //        
6651 //    },
6652 //    
6653 //    onLoad: function () 
6654 //    {   
6655 //        this.fireEvent('load', this);
6656 //    }
6657 //    
6658 //   
6659 });
6660
6661  
6662
6663  /*
6664  * Based on:
6665  * Ext JS Library 1.1.1
6666  * Copyright(c) 2006-2007, Ext JS, LLC.
6667  *
6668  * Originally Released Under LGPL - original licence link has changed is not relivant.
6669  *
6670  * Fork - LGPL
6671  * <script type="text/javascript">
6672  */
6673
6674 // as we use this in bootstrap.
6675 Roo.namespace('Roo.form');
6676  /**
6677  * @class Roo.form.Action
6678  * Internal Class used to handle form actions
6679  * @constructor
6680  * @param {Roo.form.BasicForm} el The form element or its id
6681  * @param {Object} config Configuration options
6682  */
6683
6684  
6685  
6686 // define the action interface
6687 Roo.form.Action = function(form, options){
6688     this.form = form;
6689     this.options = options || {};
6690 };
6691 /**
6692  * Client Validation Failed
6693  * @const 
6694  */
6695 Roo.form.Action.CLIENT_INVALID = 'client';
6696 /**
6697  * Server Validation Failed
6698  * @const 
6699  */
6700 Roo.form.Action.SERVER_INVALID = 'server';
6701  /**
6702  * Connect to Server Failed
6703  * @const 
6704  */
6705 Roo.form.Action.CONNECT_FAILURE = 'connect';
6706 /**
6707  * Reading Data from Server Failed
6708  * @const 
6709  */
6710 Roo.form.Action.LOAD_FAILURE = 'load';
6711
6712 Roo.form.Action.prototype = {
6713     type : 'default',
6714     failureType : undefined,
6715     response : undefined,
6716     result : undefined,
6717
6718     // interface method
6719     run : function(options){
6720
6721     },
6722
6723     // interface method
6724     success : function(response){
6725
6726     },
6727
6728     // interface method
6729     handleResponse : function(response){
6730
6731     },
6732
6733     // default connection failure
6734     failure : function(response){
6735         
6736         this.response = response;
6737         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6738         this.form.afterAction(this, false);
6739     },
6740
6741     processResponse : function(response){
6742         this.response = response;
6743         if(!response.responseText){
6744             return true;
6745         }
6746         this.result = this.handleResponse(response);
6747         return this.result;
6748     },
6749
6750     // utility functions used internally
6751     getUrl : function(appendParams){
6752         var url = this.options.url || this.form.url || this.form.el.dom.action;
6753         if(appendParams){
6754             var p = this.getParams();
6755             if(p){
6756                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6757             }
6758         }
6759         return url;
6760     },
6761
6762     getMethod : function(){
6763         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6764     },
6765
6766     getParams : function(){
6767         var bp = this.form.baseParams;
6768         var p = this.options.params;
6769         if(p){
6770             if(typeof p == "object"){
6771                 p = Roo.urlEncode(Roo.applyIf(p, bp));
6772             }else if(typeof p == 'string' && bp){
6773                 p += '&' + Roo.urlEncode(bp);
6774             }
6775         }else if(bp){
6776             p = Roo.urlEncode(bp);
6777         }
6778         return p;
6779     },
6780
6781     createCallback : function(){
6782         return {
6783             success: this.success,
6784             failure: this.failure,
6785             scope: this,
6786             timeout: (this.form.timeout*1000),
6787             upload: this.form.fileUpload ? this.success : undefined
6788         };
6789     }
6790 };
6791
6792 Roo.form.Action.Submit = function(form, options){
6793     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6794 };
6795
6796 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6797     type : 'submit',
6798
6799     haveProgress : false,
6800     uploadComplete : false,
6801     
6802     // uploadProgress indicator.
6803     uploadProgress : function()
6804     {
6805         if (!this.form.progressUrl) {
6806             return;
6807         }
6808         
6809         if (!this.haveProgress) {
6810             Roo.MessageBox.progress("Uploading", "Uploading");
6811         }
6812         if (this.uploadComplete) {
6813            Roo.MessageBox.hide();
6814            return;
6815         }
6816         
6817         this.haveProgress = true;
6818    
6819         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6820         
6821         var c = new Roo.data.Connection();
6822         c.request({
6823             url : this.form.progressUrl,
6824             params: {
6825                 id : uid
6826             },
6827             method: 'GET',
6828             success : function(req){
6829                //console.log(data);
6830                 var rdata = false;
6831                 var edata;
6832                 try  {
6833                    rdata = Roo.decode(req.responseText)
6834                 } catch (e) {
6835                     Roo.log("Invalid data from server..");
6836                     Roo.log(edata);
6837                     return;
6838                 }
6839                 if (!rdata || !rdata.success) {
6840                     Roo.log(rdata);
6841                     Roo.MessageBox.alert(Roo.encode(rdata));
6842                     return;
6843                 }
6844                 var data = rdata.data;
6845                 
6846                 if (this.uploadComplete) {
6847                    Roo.MessageBox.hide();
6848                    return;
6849                 }
6850                    
6851                 if (data){
6852                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6853                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6854                     );
6855                 }
6856                 this.uploadProgress.defer(2000,this);
6857             },
6858        
6859             failure: function(data) {
6860                 Roo.log('progress url failed ');
6861                 Roo.log(data);
6862             },
6863             scope : this
6864         });
6865            
6866     },
6867     
6868     
6869     run : function()
6870     {
6871         // run get Values on the form, so it syncs any secondary forms.
6872         this.form.getValues();
6873         
6874         var o = this.options;
6875         var method = this.getMethod();
6876         var isPost = method == 'POST';
6877         if(o.clientValidation === false || this.form.isValid()){
6878             
6879             if (this.form.progressUrl) {
6880                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6881                     (new Date() * 1) + '' + Math.random());
6882                     
6883             } 
6884             
6885             
6886             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6887                 form:this.form.el.dom,
6888                 url:this.getUrl(!isPost),
6889                 method: method,
6890                 params:isPost ? this.getParams() : null,
6891                 isUpload: this.form.fileUpload
6892             }));
6893             
6894             this.uploadProgress();
6895
6896         }else if (o.clientValidation !== false){ // client validation failed
6897             this.failureType = Roo.form.Action.CLIENT_INVALID;
6898             this.form.afterAction(this, false);
6899         }
6900     },
6901
6902     success : function(response)
6903     {
6904         this.uploadComplete= true;
6905         if (this.haveProgress) {
6906             Roo.MessageBox.hide();
6907         }
6908         
6909         
6910         var result = this.processResponse(response);
6911         if(result === true || result.success){
6912             this.form.afterAction(this, true);
6913             return;
6914         }
6915         if(result.errors){
6916             this.form.markInvalid(result.errors);
6917             this.failureType = Roo.form.Action.SERVER_INVALID;
6918         }
6919         this.form.afterAction(this, false);
6920     },
6921     failure : function(response)
6922     {
6923         this.uploadComplete= true;
6924         if (this.haveProgress) {
6925             Roo.MessageBox.hide();
6926         }
6927         
6928         this.response = response;
6929         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6930         this.form.afterAction(this, false);
6931     },
6932     
6933     handleResponse : function(response){
6934         if(this.form.errorReader){
6935             var rs = this.form.errorReader.read(response);
6936             var errors = [];
6937             if(rs.records){
6938                 for(var i = 0, len = rs.records.length; i < len; i++) {
6939                     var r = rs.records[i];
6940                     errors[i] = r.data;
6941                 }
6942             }
6943             if(errors.length < 1){
6944                 errors = null;
6945             }
6946             return {
6947                 success : rs.success,
6948                 errors : errors
6949             };
6950         }
6951         var ret = false;
6952         try {
6953             ret = Roo.decode(response.responseText);
6954         } catch (e) {
6955             ret = {
6956                 success: false,
6957                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6958                 errors : []
6959             };
6960         }
6961         return ret;
6962         
6963     }
6964 });
6965
6966
6967 Roo.form.Action.Load = function(form, options){
6968     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6969     this.reader = this.form.reader;
6970 };
6971
6972 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6973     type : 'load',
6974
6975     run : function(){
6976         
6977         Roo.Ajax.request(Roo.apply(
6978                 this.createCallback(), {
6979                     method:this.getMethod(),
6980                     url:this.getUrl(false),
6981                     params:this.getParams()
6982         }));
6983     },
6984
6985     success : function(response){
6986         
6987         var result = this.processResponse(response);
6988         if(result === true || !result.success || !result.data){
6989             this.failureType = Roo.form.Action.LOAD_FAILURE;
6990             this.form.afterAction(this, false);
6991             return;
6992         }
6993         this.form.clearInvalid();
6994         this.form.setValues(result.data);
6995         this.form.afterAction(this, true);
6996     },
6997
6998     handleResponse : function(response){
6999         if(this.form.reader){
7000             var rs = this.form.reader.read(response);
7001             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7002             return {
7003                 success : rs.success,
7004                 data : data
7005             };
7006         }
7007         return Roo.decode(response.responseText);
7008     }
7009 });
7010
7011 Roo.form.Action.ACTION_TYPES = {
7012     'load' : Roo.form.Action.Load,
7013     'submit' : Roo.form.Action.Submit
7014 };/*
7015  * - LGPL
7016  *
7017  * form
7018  * 
7019  */
7020
7021 /**
7022  * @class Roo.bootstrap.Form
7023  * @extends Roo.bootstrap.Component
7024  * Bootstrap Form class
7025  * @cfg {String} method  GET | POST (default POST)
7026  * @cfg {String} labelAlign top | left (default top)
7027  * @cfg {String} align left  | right - for navbars
7028  * @cfg {Boolean} loadMask load mask when submit (default true)
7029
7030  * 
7031  * @constructor
7032  * Create a new Form
7033  * @param {Object} config The config object
7034  */
7035
7036
7037 Roo.bootstrap.Form = function(config){
7038     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7039     this.addEvents({
7040         /**
7041          * @event clientvalidation
7042          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7043          * @param {Form} this
7044          * @param {Boolean} valid true if the form has passed client-side validation
7045          */
7046         clientvalidation: true,
7047         /**
7048          * @event beforeaction
7049          * Fires before any action is performed. Return false to cancel the action.
7050          * @param {Form} this
7051          * @param {Action} action The action to be performed
7052          */
7053         beforeaction: true,
7054         /**
7055          * @event actionfailed
7056          * Fires when an action fails.
7057          * @param {Form} this
7058          * @param {Action} action The action that failed
7059          */
7060         actionfailed : true,
7061         /**
7062          * @event actioncomplete
7063          * Fires when an action is completed.
7064          * @param {Form} this
7065          * @param {Action} action The action that completed
7066          */
7067         actioncomplete : true
7068     });
7069     
7070 };
7071
7072 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7073       
7074      /**
7075      * @cfg {String} method
7076      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7077      */
7078     method : 'POST',
7079     /**
7080      * @cfg {String} url
7081      * The URL to use for form actions if one isn't supplied in the action options.
7082      */
7083     /**
7084      * @cfg {Boolean} fileUpload
7085      * Set to true if this form is a file upload.
7086      */
7087      
7088     /**
7089      * @cfg {Object} baseParams
7090      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7091      */
7092       
7093     /**
7094      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7095      */
7096     timeout: 30,
7097     /**
7098      * @cfg {Sting} align (left|right) for navbar forms
7099      */
7100     align : 'left',
7101
7102     // private
7103     activeAction : null,
7104  
7105     /**
7106      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7107      * element by passing it or its id or mask the form itself by passing in true.
7108      * @type Mixed
7109      */
7110     waitMsgTarget : false,
7111     
7112     loadMask : true,
7113     
7114     getAutoCreate : function(){
7115         
7116         var cfg = {
7117             tag: 'form',
7118             method : this.method || 'POST',
7119             id : this.id || Roo.id(),
7120             cls : ''
7121         };
7122         if (this.parent().xtype.match(/^Nav/)) {
7123             cfg.cls = 'navbar-form navbar-' + this.align;
7124             
7125         }
7126         
7127         if (this.labelAlign == 'left' ) {
7128             cfg.cls += ' form-horizontal';
7129         }
7130         
7131         
7132         return cfg;
7133     },
7134     initEvents : function()
7135     {
7136         this.el.on('submit', this.onSubmit, this);
7137         // this was added as random key presses on the form where triggering form submit.
7138         this.el.on('keypress', function(e) {
7139             if (e.getCharCode() != 13) {
7140                 return true;
7141             }
7142             // we might need to allow it for textareas.. and some other items.
7143             // check e.getTarget().
7144             
7145             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7146                 return true;
7147             }
7148         
7149             Roo.log("keypress blocked");
7150             
7151             e.preventDefault();
7152             return false;
7153         });
7154         
7155     },
7156     // private
7157     onSubmit : function(e){
7158         e.stopEvent();
7159     },
7160     
7161      /**
7162      * Returns true if client-side validation on the form is successful.
7163      * @return Boolean
7164      */
7165     isValid : function(){
7166         var items = this.getItems();
7167         var valid = true;
7168         items.each(function(f){
7169            if(!f.validate()){
7170                valid = false;
7171                
7172            }
7173         });
7174         return valid;
7175     },
7176     /**
7177      * Returns true if any fields in this form have changed since their original load.
7178      * @return Boolean
7179      */
7180     isDirty : function(){
7181         var dirty = false;
7182         var items = this.getItems();
7183         items.each(function(f){
7184            if(f.isDirty()){
7185                dirty = true;
7186                return false;
7187            }
7188            return true;
7189         });
7190         return dirty;
7191     },
7192      /**
7193      * Performs a predefined action (submit or load) or custom actions you define on this form.
7194      * @param {String} actionName The name of the action type
7195      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7196      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7197      * accept other config options):
7198      * <pre>
7199 Property          Type             Description
7200 ----------------  ---------------  ----------------------------------------------------------------------------------
7201 url               String           The url for the action (defaults to the form's url)
7202 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7203 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7204 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7205                                    validate the form on the client (defaults to false)
7206      * </pre>
7207      * @return {BasicForm} this
7208      */
7209     doAction : function(action, options){
7210         if(typeof action == 'string'){
7211             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7212         }
7213         if(this.fireEvent('beforeaction', this, action) !== false){
7214             this.beforeAction(action);
7215             action.run.defer(100, action);
7216         }
7217         return this;
7218     },
7219     
7220     // private
7221     beforeAction : function(action){
7222         var o = action.options;
7223         
7224         if(this.loadMask){
7225             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7226         }
7227         // not really supported yet.. ??
7228         
7229         //if(this.waitMsgTarget === true){
7230         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7231         //}else if(this.waitMsgTarget){
7232         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7233         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7234         //}else {
7235         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7236        // }
7237          
7238     },
7239
7240     // private
7241     afterAction : function(action, success){
7242         this.activeAction = null;
7243         var o = action.options;
7244         
7245         //if(this.waitMsgTarget === true){
7246             this.el.unmask();
7247         //}else if(this.waitMsgTarget){
7248         //    this.waitMsgTarget.unmask();
7249         //}else{
7250         //    Roo.MessageBox.updateProgress(1);
7251         //    Roo.MessageBox.hide();
7252        // }
7253         // 
7254         if(success){
7255             if(o.reset){
7256                 this.reset();
7257             }
7258             Roo.callback(o.success, o.scope, [this, action]);
7259             this.fireEvent('actioncomplete', this, action);
7260             
7261         }else{
7262             
7263             // failure condition..
7264             // we have a scenario where updates need confirming.
7265             // eg. if a locking scenario exists..
7266             // we look for { errors : { needs_confirm : true }} in the response.
7267             if (
7268                 (typeof(action.result) != 'undefined')  &&
7269                 (typeof(action.result.errors) != 'undefined')  &&
7270                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7271            ){
7272                 var _t = this;
7273                 Roo.log("not supported yet");
7274                  /*
7275                 
7276                 Roo.MessageBox.confirm(
7277                     "Change requires confirmation",
7278                     action.result.errorMsg,
7279                     function(r) {
7280                         if (r != 'yes') {
7281                             return;
7282                         }
7283                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7284                     }
7285                     
7286                 );
7287                 */
7288                 
7289                 
7290                 return;
7291             }
7292             
7293             Roo.callback(o.failure, o.scope, [this, action]);
7294             // show an error message if no failed handler is set..
7295             if (!this.hasListener('actionfailed')) {
7296                 Roo.log("need to add dialog support");
7297                 /*
7298                 Roo.MessageBox.alert("Error",
7299                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7300                         action.result.errorMsg :
7301                         "Saving Failed, please check your entries or try again"
7302                 );
7303                 */
7304             }
7305             
7306             this.fireEvent('actionfailed', this, action);
7307         }
7308         
7309     },
7310     /**
7311      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7312      * @param {String} id The value to search for
7313      * @return Field
7314      */
7315     findField : function(id){
7316         var items = this.getItems();
7317         var field = items.get(id);
7318         if(!field){
7319              items.each(function(f){
7320                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7321                     field = f;
7322                     return false;
7323                 }
7324                 return true;
7325             });
7326         }
7327         return field || null;
7328     },
7329      /**
7330      * Mark fields in this form invalid in bulk.
7331      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7332      * @return {BasicForm} this
7333      */
7334     markInvalid : function(errors){
7335         if(errors instanceof Array){
7336             for(var i = 0, len = errors.length; i < len; i++){
7337                 var fieldError = errors[i];
7338                 var f = this.findField(fieldError.id);
7339                 if(f){
7340                     f.markInvalid(fieldError.msg);
7341                 }
7342             }
7343         }else{
7344             var field, id;
7345             for(id in errors){
7346                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7347                     field.markInvalid(errors[id]);
7348                 }
7349             }
7350         }
7351         //Roo.each(this.childForms || [], function (f) {
7352         //    f.markInvalid(errors);
7353         //});
7354         
7355         return this;
7356     },
7357
7358     /**
7359      * Set values for fields in this form in bulk.
7360      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7361      * @return {BasicForm} this
7362      */
7363     setValues : function(values){
7364         if(values instanceof Array){ // array of objects
7365             for(var i = 0, len = values.length; i < len; i++){
7366                 var v = values[i];
7367                 var f = this.findField(v.id);
7368                 if(f){
7369                     f.setValue(v.value);
7370                     if(this.trackResetOnLoad){
7371                         f.originalValue = f.getValue();
7372                     }
7373                 }
7374             }
7375         }else{ // object hash
7376             var field, id;
7377             for(id in values){
7378                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7379                     
7380                     if (field.setFromData && 
7381                         field.valueField && 
7382                         field.displayField &&
7383                         // combos' with local stores can 
7384                         // be queried via setValue()
7385                         // to set their value..
7386                         (field.store && !field.store.isLocal)
7387                         ) {
7388                         // it's a combo
7389                         var sd = { };
7390                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7391                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7392                         field.setFromData(sd);
7393                         
7394                     } else {
7395                         field.setValue(values[id]);
7396                     }
7397                     
7398                     
7399                     if(this.trackResetOnLoad){
7400                         field.originalValue = field.getValue();
7401                     }
7402                 }
7403             }
7404         }
7405          
7406         //Roo.each(this.childForms || [], function (f) {
7407         //    f.setValues(values);
7408         //});
7409                 
7410         return this;
7411     },
7412
7413     /**
7414      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7415      * they are returned as an array.
7416      * @param {Boolean} asString
7417      * @return {Object}
7418      */
7419     getValues : function(asString){
7420         //if (this.childForms) {
7421             // copy values from the child forms
7422         //    Roo.each(this.childForms, function (f) {
7423         //        this.setValues(f.getValues());
7424         //    }, this);
7425         //}
7426         
7427         
7428         
7429         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7430         if(asString === true){
7431             return fs;
7432         }
7433         return Roo.urlDecode(fs);
7434     },
7435     
7436     /**
7437      * Returns the fields in this form as an object with key/value pairs. 
7438      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7439      * @return {Object}
7440      */
7441     getFieldValues : function(with_hidden)
7442     {
7443         var items = this.getItems();
7444         var ret = {};
7445         items.each(function(f){
7446             if (!f.getName()) {
7447                 return;
7448             }
7449             var v = f.getValue();
7450             if (f.inputType =='radio') {
7451                 if (typeof(ret[f.getName()]) == 'undefined') {
7452                     ret[f.getName()] = ''; // empty..
7453                 }
7454                 
7455                 if (!f.el.dom.checked) {
7456                     return;
7457                     
7458                 }
7459                 v = f.el.dom.value;
7460                 
7461             }
7462             
7463             // not sure if this supported any more..
7464             if ((typeof(v) == 'object') && f.getRawValue) {
7465                 v = f.getRawValue() ; // dates..
7466             }
7467             // combo boxes where name != hiddenName...
7468             if (f.name != f.getName()) {
7469                 ret[f.name] = f.getRawValue();
7470             }
7471             ret[f.getName()] = v;
7472         });
7473         
7474         return ret;
7475     },
7476
7477     /**
7478      * Clears all invalid messages in this form.
7479      * @return {BasicForm} this
7480      */
7481     clearInvalid : function(){
7482         var items = this.getItems();
7483         
7484         items.each(function(f){
7485            f.clearInvalid();
7486         });
7487         
7488         
7489         
7490         return this;
7491     },
7492
7493     /**
7494      * Resets this form.
7495      * @return {BasicForm} this
7496      */
7497     reset : function(){
7498         var items = this.getItems();
7499         items.each(function(f){
7500             f.reset();
7501         });
7502         
7503         Roo.each(this.childForms || [], function (f) {
7504             f.reset();
7505         });
7506        
7507         
7508         return this;
7509     },
7510     getItems : function()
7511     {
7512         var r=new Roo.util.MixedCollection(false, function(o){
7513             return o.id || (o.id = Roo.id());
7514         });
7515         var iter = function(el) {
7516             if (el.inputEl) {
7517                 r.add(el);
7518             }
7519             if (!el.items) {
7520                 return;
7521             }
7522             Roo.each(el.items,function(e) {
7523                 iter(e);
7524             });
7525             
7526             
7527         };
7528         
7529         iter(this);
7530         return r;
7531         
7532         
7533         
7534         
7535     }
7536     
7537 });
7538
7539  
7540 /*
7541  * Based on:
7542  * Ext JS Library 1.1.1
7543  * Copyright(c) 2006-2007, Ext JS, LLC.
7544  *
7545  * Originally Released Under LGPL - original licence link has changed is not relivant.
7546  *
7547  * Fork - LGPL
7548  * <script type="text/javascript">
7549  */
7550 /**
7551  * @class Roo.form.VTypes
7552  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7553  * @singleton
7554  */
7555 Roo.form.VTypes = function(){
7556     // closure these in so they are only created once.
7557     var alpha = /^[a-zA-Z_]+$/;
7558     var alphanum = /^[a-zA-Z0-9_]+$/;
7559     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
7560     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7561
7562     // All these messages and functions are configurable
7563     return {
7564         /**
7565          * The function used to validate email addresses
7566          * @param {String} value The email address
7567          */
7568         'email' : function(v){
7569             return email.test(v);
7570         },
7571         /**
7572          * The error text to display when the email validation function returns false
7573          * @type String
7574          */
7575         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7576         /**
7577          * The keystroke filter mask to be applied on email input
7578          * @type RegExp
7579          */
7580         'emailMask' : /[a-z0-9_\.\-@]/i,
7581
7582         /**
7583          * The function used to validate URLs
7584          * @param {String} value The URL
7585          */
7586         'url' : function(v){
7587             return url.test(v);
7588         },
7589         /**
7590          * The error text to display when the url validation function returns false
7591          * @type String
7592          */
7593         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7594         
7595         /**
7596          * The function used to validate alpha values
7597          * @param {String} value The value
7598          */
7599         'alpha' : function(v){
7600             return alpha.test(v);
7601         },
7602         /**
7603          * The error text to display when the alpha validation function returns false
7604          * @type String
7605          */
7606         'alphaText' : 'This field should only contain letters and _',
7607         /**
7608          * The keystroke filter mask to be applied on alpha input
7609          * @type RegExp
7610          */
7611         'alphaMask' : /[a-z_]/i,
7612
7613         /**
7614          * The function used to validate alphanumeric values
7615          * @param {String} value The value
7616          */
7617         'alphanum' : function(v){
7618             return alphanum.test(v);
7619         },
7620         /**
7621          * The error text to display when the alphanumeric validation function returns false
7622          * @type String
7623          */
7624         'alphanumText' : 'This field should only contain letters, numbers and _',
7625         /**
7626          * The keystroke filter mask to be applied on alphanumeric input
7627          * @type RegExp
7628          */
7629         'alphanumMask' : /[a-z0-9_]/i
7630     };
7631 }();/*
7632  * - LGPL
7633  *
7634  * Input
7635  * 
7636  */
7637
7638 /**
7639  * @class Roo.bootstrap.Input
7640  * @extends Roo.bootstrap.Component
7641  * Bootstrap Input class
7642  * @cfg {Boolean} disabled is it disabled
7643  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7644  * @cfg {String} name name of the input
7645  * @cfg {string} fieldLabel - the label associated
7646  * @cfg {string} placeholder - placeholder to put in text.
7647  * @cfg {string}  before - input group add on before
7648  * @cfg {string} after - input group add on after
7649  * @cfg {string} size - (lg|sm) or leave empty..
7650  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7651  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7652  * @cfg {Number} md colspan out of 12 for computer-sized screens
7653  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7654  * @cfg {string} value default value of the input
7655  * @cfg {Number} labelWidth set the width of label (0-12)
7656  * @cfg {String} labelAlign (top|left)
7657  * @cfg {Boolean} readOnly Specifies that the field should be read-only
7658  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7659
7660  * @cfg {String} align (left|center|right) Default left
7661  * @cfg {Boolean} forceFeedback (true|false) Default false
7662  * 
7663  * 
7664  * 
7665  * 
7666  * @constructor
7667  * Create a new Input
7668  * @param {Object} config The config object
7669  */
7670
7671 Roo.bootstrap.Input = function(config){
7672     Roo.bootstrap.Input.superclass.constructor.call(this, config);
7673    
7674         this.addEvents({
7675             /**
7676              * @event focus
7677              * Fires when this field receives input focus.
7678              * @param {Roo.form.Field} this
7679              */
7680             focus : true,
7681             /**
7682              * @event blur
7683              * Fires when this field loses input focus.
7684              * @param {Roo.form.Field} this
7685              */
7686             blur : true,
7687             /**
7688              * @event specialkey
7689              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
7690              * {@link Roo.EventObject#getKey} to determine which key was pressed.
7691              * @param {Roo.form.Field} this
7692              * @param {Roo.EventObject} e The event object
7693              */
7694             specialkey : true,
7695             /**
7696              * @event change
7697              * Fires just before the field blurs if the field value has changed.
7698              * @param {Roo.form.Field} this
7699              * @param {Mixed} newValue The new value
7700              * @param {Mixed} oldValue The original value
7701              */
7702             change : true,
7703             /**
7704              * @event invalid
7705              * Fires after the field has been marked as invalid.
7706              * @param {Roo.form.Field} this
7707              * @param {String} msg The validation message
7708              */
7709             invalid : true,
7710             /**
7711              * @event valid
7712              * Fires after the field has been validated with no errors.
7713              * @param {Roo.form.Field} this
7714              */
7715             valid : true,
7716              /**
7717              * @event keyup
7718              * Fires after the key up
7719              * @param {Roo.form.Field} this
7720              * @param {Roo.EventObject}  e The event Object
7721              */
7722             keyup : true
7723         });
7724 };
7725
7726 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
7727      /**
7728      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7729       automatic validation (defaults to "keyup").
7730      */
7731     validationEvent : "keyup",
7732      /**
7733      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7734      */
7735     validateOnBlur : true,
7736     /**
7737      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7738      */
7739     validationDelay : 250,
7740      /**
7741      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7742      */
7743     focusClass : "x-form-focus",  // not needed???
7744     
7745        
7746     /**
7747      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7748      */
7749     invalidClass : "has-warning",
7750     
7751     /**
7752      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7753      */
7754     validClass : "has-success",
7755     
7756     /**
7757      * @cfg {Boolean} hasFeedback (true|false) default true
7758      */
7759     hasFeedback : true,
7760     
7761     /**
7762      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7763      */
7764     invalidFeedbackClass : "glyphicon-warning-sign",
7765     
7766     /**
7767      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7768      */
7769     validFeedbackClass : "glyphicon-ok",
7770     
7771     /**
7772      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7773      */
7774     selectOnFocus : false,
7775     
7776      /**
7777      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7778      */
7779     maskRe : null,
7780        /**
7781      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7782      */
7783     vtype : null,
7784     
7785       /**
7786      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7787      */
7788     disableKeyFilter : false,
7789     
7790        /**
7791      * @cfg {Boolean} disabled True to disable the field (defaults to false).
7792      */
7793     disabled : false,
7794      /**
7795      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7796      */
7797     allowBlank : true,
7798     /**
7799      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7800      */
7801     blankText : "This field is required",
7802     
7803      /**
7804      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7805      */
7806     minLength : 0,
7807     /**
7808      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7809      */
7810     maxLength : Number.MAX_VALUE,
7811     /**
7812      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7813      */
7814     minLengthText : "The minimum length for this field is {0}",
7815     /**
7816      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7817      */
7818     maxLengthText : "The maximum length for this field is {0}",
7819   
7820     
7821     /**
7822      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7823      * If available, this function will be called only after the basic validators all return true, and will be passed the
7824      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7825      */
7826     validator : null,
7827     /**
7828      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7829      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7830      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7831      */
7832     regex : null,
7833     /**
7834      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7835      */
7836     regexText : "",
7837     
7838     autocomplete: false,
7839     
7840     
7841     fieldLabel : '',
7842     inputType : 'text',
7843     
7844     name : false,
7845     placeholder: false,
7846     before : false,
7847     after : false,
7848     size : false,
7849     hasFocus : false,
7850     preventMark: false,
7851     isFormField : true,
7852     value : '',
7853     labelWidth : 2,
7854     labelAlign : false,
7855     readOnly : false,
7856     align : false,
7857     formatedValue : false,
7858     forceFeedback : false,
7859     
7860     parentLabelAlign : function()
7861     {
7862         var parent = this;
7863         while (parent.parent()) {
7864             parent = parent.parent();
7865             if (typeof(parent.labelAlign) !='undefined') {
7866                 return parent.labelAlign;
7867             }
7868         }
7869         return 'left';
7870         
7871     },
7872     
7873     getAutoCreate : function(){
7874         
7875         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7876         
7877         var id = Roo.id();
7878         
7879         var cfg = {};
7880         
7881         if(this.inputType != 'hidden'){
7882             cfg.cls = 'form-group' //input-group
7883         }
7884         
7885         var input =  {
7886             tag: 'input',
7887             id : id,
7888             type : this.inputType,
7889             value : this.value,
7890             cls : 'form-control',
7891             placeholder : this.placeholder || '',
7892             autocomplete : this.autocomplete || 'new-password'
7893         };
7894         
7895         
7896         if(this.align){
7897             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7898         }
7899         
7900         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7901             input.maxLength = this.maxLength;
7902         }
7903         
7904         if (this.disabled) {
7905             input.disabled=true;
7906         }
7907         
7908         if (this.readOnly) {
7909             input.readonly=true;
7910         }
7911         
7912         if (this.name) {
7913             input.name = this.name;
7914         }
7915         if (this.size) {
7916             input.cls += ' input-' + this.size;
7917         }
7918         var settings=this;
7919         ['xs','sm','md','lg'].map(function(size){
7920             if (settings[size]) {
7921                 cfg.cls += ' col-' + size + '-' + settings[size];
7922             }
7923         });
7924         
7925         var inputblock = input;
7926         
7927         var feedback = {
7928             tag: 'span',
7929             cls: 'glyphicon form-control-feedback'
7930         };
7931             
7932         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7933             
7934             inputblock = {
7935                 cls : 'has-feedback',
7936                 cn :  [
7937                     input,
7938                     feedback
7939                 ] 
7940             };  
7941         }
7942         
7943         if (this.before || this.after) {
7944             
7945             inputblock = {
7946                 cls : 'input-group',
7947                 cn :  [] 
7948             };
7949             
7950             if (this.before && typeof(this.before) == 'string') {
7951                 
7952                 inputblock.cn.push({
7953                     tag :'span',
7954                     cls : 'roo-input-before input-group-addon',
7955                     html : this.before
7956                 });
7957             }
7958             if (this.before && typeof(this.before) == 'object') {
7959                 this.before = Roo.factory(this.before);
7960                 Roo.log(this.before);
7961                 inputblock.cn.push({
7962                     tag :'span',
7963                     cls : 'roo-input-before input-group-' +
7964                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7965                 });
7966             }
7967             
7968             inputblock.cn.push(input);
7969             
7970             if (this.after && typeof(this.after) == 'string') {
7971                 inputblock.cn.push({
7972                     tag :'span',
7973                     cls : 'roo-input-after input-group-addon',
7974                     html : this.after
7975                 });
7976             }
7977             if (this.after && typeof(this.after) == 'object') {
7978                 this.after = Roo.factory(this.after);
7979                 Roo.log(this.after);
7980                 inputblock.cn.push({
7981                     tag :'span',
7982                     cls : 'roo-input-after input-group-' +
7983                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7984                 });
7985             }
7986             
7987             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7988                 inputblock.cls += ' has-feedback';
7989                 inputblock.cn.push(feedback);
7990             }
7991         };
7992         
7993         if (align ==='left' && this.fieldLabel.length) {
7994                 Roo.log("left and has label");
7995                 cfg.cn = [
7996                     
7997                     {
7998                         tag: 'label',
7999                         'for' :  id,
8000                         cls : 'control-label col-sm-' + this.labelWidth,
8001                         html : this.fieldLabel
8002                         
8003                     },
8004                     {
8005                         cls : "col-sm-" + (12 - this.labelWidth), 
8006                         cn: [
8007                             inputblock
8008                         ]
8009                     }
8010                     
8011                 ];
8012         } else if ( this.fieldLabel.length) {
8013                 Roo.log(" label");
8014                  cfg.cn = [
8015                    
8016                     {
8017                         tag: 'label',
8018                         //cls : 'input-group-addon',
8019                         html : this.fieldLabel
8020                         
8021                     },
8022                     
8023                     inputblock
8024                     
8025                 ];
8026
8027         } else {
8028             
8029                 Roo.log(" no label && no align");
8030                 cfg.cn = [
8031                     
8032                         inputblock
8033                     
8034                 ];
8035                 
8036                 
8037         };
8038         Roo.log('input-parentType: ' + this.parentType);
8039         
8040         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8041            cfg.cls += ' navbar-form';
8042            Roo.log(cfg);
8043         }
8044         
8045         return cfg;
8046         
8047     },
8048     /**
8049      * return the real input element.
8050      */
8051     inputEl: function ()
8052     {
8053         return this.el.select('input.form-control',true).first();
8054     },
8055     
8056     tooltipEl : function()
8057     {
8058         return this.inputEl();
8059     },
8060     
8061     setDisabled : function(v)
8062     {
8063         var i  = this.inputEl().dom;
8064         if (!v) {
8065             i.removeAttribute('disabled');
8066             return;
8067             
8068         }
8069         i.setAttribute('disabled','true');
8070     },
8071     initEvents : function()
8072     {
8073           
8074         this.inputEl().on("keydown" , this.fireKey,  this);
8075         this.inputEl().on("focus", this.onFocus,  this);
8076         this.inputEl().on("blur", this.onBlur,  this);
8077         
8078         this.inputEl().relayEvent('keyup', this);
8079  
8080         // reference to original value for reset
8081         this.originalValue = this.getValue();
8082         //Roo.form.TextField.superclass.initEvents.call(this);
8083         if(this.validationEvent == 'keyup'){
8084             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8085             this.inputEl().on('keyup', this.filterValidation, this);
8086         }
8087         else if(this.validationEvent !== false){
8088             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8089         }
8090         
8091         if(this.selectOnFocus){
8092             this.on("focus", this.preFocus, this);
8093             
8094         }
8095         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8096             this.inputEl().on("keypress", this.filterKeys, this);
8097         }
8098        /* if(this.grow){
8099             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8100             this.el.on("click", this.autoSize,  this);
8101         }
8102         */
8103         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8104             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8105         }
8106         
8107         if (typeof(this.before) == 'object') {
8108             this.before.render(this.el.select('.roo-input-before',true).first());
8109         }
8110         if (typeof(this.after) == 'object') {
8111             this.after.render(this.el.select('.roo-input-after',true).first());
8112         }
8113         
8114         
8115     },
8116     filterValidation : function(e){
8117         if(!e.isNavKeyPress()){
8118             this.validationTask.delay(this.validationDelay);
8119         }
8120     },
8121      /**
8122      * Validates the field value
8123      * @return {Boolean} True if the value is valid, else false
8124      */
8125     validate : function(){
8126         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8127         if(this.disabled || this.validateValue(this.getRawValue())){
8128             this.markValid();
8129             return true;
8130         }
8131         
8132         this.markInvalid();
8133         return false;
8134     },
8135     
8136     
8137     /**
8138      * Validates a value according to the field's validation rules and marks the field as invalid
8139      * if the validation fails
8140      * @param {Mixed} value The value to validate
8141      * @return {Boolean} True if the value is valid, else false
8142      */
8143     validateValue : function(value){
8144         if(value.length < 1)  { // if it's blank
8145             if(this.allowBlank){
8146                 return true;
8147             }
8148             return false;
8149         }
8150         
8151         if(value.length < this.minLength){
8152             return false;
8153         }
8154         if(value.length > this.maxLength){
8155             return false;
8156         }
8157         if(this.vtype){
8158             var vt = Roo.form.VTypes;
8159             if(!vt[this.vtype](value, this)){
8160                 return false;
8161             }
8162         }
8163         if(typeof this.validator == "function"){
8164             var msg = this.validator(value);
8165             if(msg !== true){
8166                 return false;
8167             }
8168         }
8169         
8170         if(this.regex && !this.regex.test(value)){
8171             return false;
8172         }
8173         
8174         return true;
8175     },
8176
8177     
8178     
8179      // private
8180     fireKey : function(e){
8181         //Roo.log('field ' + e.getKey());
8182         if(e.isNavKeyPress()){
8183             this.fireEvent("specialkey", this, e);
8184         }
8185     },
8186     focus : function (selectText){
8187         if(this.rendered){
8188             this.inputEl().focus();
8189             if(selectText === true){
8190                 this.inputEl().dom.select();
8191             }
8192         }
8193         return this;
8194     } ,
8195     
8196     onFocus : function(){
8197         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8198            // this.el.addClass(this.focusClass);
8199         }
8200         if(!this.hasFocus){
8201             this.hasFocus = true;
8202             this.startValue = this.getValue();
8203             this.fireEvent("focus", this);
8204         }
8205     },
8206     
8207     beforeBlur : Roo.emptyFn,
8208
8209     
8210     // private
8211     onBlur : function(){
8212         this.beforeBlur();
8213         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8214             //this.el.removeClass(this.focusClass);
8215         }
8216         this.hasFocus = false;
8217         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8218             this.validate();
8219         }
8220         var v = this.getValue();
8221         if(String(v) !== String(this.startValue)){
8222             this.fireEvent('change', this, v, this.startValue);
8223         }
8224         this.fireEvent("blur", this);
8225     },
8226     
8227     /**
8228      * Resets the current field value to the originally loaded value and clears any validation messages
8229      */
8230     reset : function(){
8231         this.setValue(this.originalValue);
8232         this.validate();
8233     },
8234      /**
8235      * Returns the name of the field
8236      * @return {Mixed} name The name field
8237      */
8238     getName: function(){
8239         return this.name;
8240     },
8241      /**
8242      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8243      * @return {Mixed} value The field value
8244      */
8245     getValue : function(){
8246         
8247         var v = this.inputEl().getValue();
8248         
8249         return v;
8250     },
8251     /**
8252      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8253      * @return {Mixed} value The field value
8254      */
8255     getRawValue : function(){
8256         var v = this.inputEl().getValue();
8257         
8258         return v;
8259     },
8260     
8261     /**
8262      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8263      * @param {Mixed} value The value to set
8264      */
8265     setRawValue : function(v){
8266         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8267     },
8268     
8269     selectText : function(start, end){
8270         var v = this.getRawValue();
8271         if(v.length > 0){
8272             start = start === undefined ? 0 : start;
8273             end = end === undefined ? v.length : end;
8274             var d = this.inputEl().dom;
8275             if(d.setSelectionRange){
8276                 d.setSelectionRange(start, end);
8277             }else if(d.createTextRange){
8278                 var range = d.createTextRange();
8279                 range.moveStart("character", start);
8280                 range.moveEnd("character", v.length-end);
8281                 range.select();
8282             }
8283         }
8284     },
8285     
8286     /**
8287      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8288      * @param {Mixed} value The value to set
8289      */
8290     setValue : function(v){
8291         this.value = v;
8292         if(this.rendered){
8293             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8294             this.validate();
8295         }
8296     },
8297     
8298     /*
8299     processValue : function(value){
8300         if(this.stripCharsRe){
8301             var newValue = value.replace(this.stripCharsRe, '');
8302             if(newValue !== value){
8303                 this.setRawValue(newValue);
8304                 return newValue;
8305             }
8306         }
8307         return value;
8308     },
8309   */
8310     preFocus : function(){
8311         
8312         if(this.selectOnFocus){
8313             this.inputEl().dom.select();
8314         }
8315     },
8316     filterKeys : function(e){
8317         var k = e.getKey();
8318         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8319             return;
8320         }
8321         var c = e.getCharCode(), cc = String.fromCharCode(c);
8322         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8323             return;
8324         }
8325         if(!this.maskRe.test(cc)){
8326             e.stopEvent();
8327         }
8328     },
8329      /**
8330      * Clear any invalid styles/messages for this field
8331      */
8332     clearInvalid : function(){
8333         
8334         if(!this.el || this.preventMark){ // not rendered
8335             return;
8336         }
8337         this.el.removeClass(this.invalidClass);
8338         
8339         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8340             
8341             var feedback = this.el.select('.form-control-feedback', true).first();
8342             
8343             if(feedback){
8344                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8345             }
8346             
8347         }
8348         
8349         this.fireEvent('valid', this);
8350     },
8351     
8352      /**
8353      * Mark this field as valid
8354      */
8355     markValid : function()
8356     {
8357         if(!this.el  || this.preventMark){ // not rendered
8358             return;
8359         }
8360         
8361         this.el.removeClass([this.invalidClass, this.validClass]);
8362         
8363         var feedback = this.el.select('.form-control-feedback', true).first();
8364             
8365         if(feedback){
8366             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8367         }
8368
8369         if(this.disabled || this.allowBlank){
8370             return;
8371         }
8372         
8373         var formGroup = this.el.findParent('.form-group', false, true);
8374         
8375         if(formGroup){
8376             
8377             var label = formGroup.select('label', true).first();
8378             var icon = formGroup.select('i.fa-star', true).first();
8379             
8380             if(label && icon){
8381                 icon.remove();
8382             }
8383         }
8384         
8385         this.el.addClass(this.validClass);
8386         
8387         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8388             
8389             var feedback = this.el.select('.form-control-feedback', true).first();
8390             
8391             if(feedback){
8392                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8393                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8394             }
8395             
8396         }
8397         
8398         this.fireEvent('valid', this);
8399     },
8400     
8401      /**
8402      * Mark this field as invalid
8403      * @param {String} msg The validation message
8404      */
8405     markInvalid : function(msg)
8406     {
8407         if(!this.el  || this.preventMark){ // not rendered
8408             return;
8409         }
8410         
8411         this.el.removeClass([this.invalidClass, this.validClass]);
8412         
8413         var feedback = this.el.select('.form-control-feedback', true).first();
8414             
8415         if(feedback){
8416             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8417         }
8418
8419         if(this.disabled || this.allowBlank){
8420             return;
8421         }
8422         
8423         var formGroup = this.el.findParent('.form-group', false, true);
8424         
8425         if(formGroup){
8426             var label = formGroup.select('label', true).first();
8427             var icon = formGroup.select('i.fa-star', true).first();
8428
8429             if(!this.getValue().length && label && !icon){
8430                 this.el.findParent('.form-group', false, true).createChild({
8431                     tag : 'i',
8432                     cls : 'text-danger fa fa-lg fa-star',
8433                     tooltip : 'This field is required',
8434                     style : 'margin-right:5px;'
8435                 }, label, true);
8436             }
8437         }
8438         
8439         
8440         this.el.addClass(this.invalidClass);
8441         
8442         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8443             
8444             var feedback = this.el.select('.form-control-feedback', true).first();
8445             
8446             if(feedback){
8447                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8448                 
8449                 if(this.getValue().length || this.forceFeedback){
8450                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8451                 }
8452                 
8453             }
8454             
8455         }
8456         
8457         this.fireEvent('invalid', this, msg);
8458     },
8459     // private
8460     SafariOnKeyDown : function(event)
8461     {
8462         // this is a workaround for a password hang bug on chrome/ webkit.
8463         
8464         var isSelectAll = false;
8465         
8466         if(this.inputEl().dom.selectionEnd > 0){
8467             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8468         }
8469         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8470             event.preventDefault();
8471             this.setValue('');
8472             return;
8473         }
8474         
8475         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
8476             
8477             event.preventDefault();
8478             // this is very hacky as keydown always get's upper case.
8479             //
8480             var cc = String.fromCharCode(event.getCharCode());
8481             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8482             
8483         }
8484     },
8485     adjustWidth : function(tag, w){
8486         tag = tag.toLowerCase();
8487         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8488             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8489                 if(tag == 'input'){
8490                     return w + 2;
8491                 }
8492                 if(tag == 'textarea'){
8493                     return w-2;
8494                 }
8495             }else if(Roo.isOpera){
8496                 if(tag == 'input'){
8497                     return w + 2;
8498                 }
8499                 if(tag == 'textarea'){
8500                     return w-2;
8501                 }
8502             }
8503         }
8504         return w;
8505     }
8506     
8507 });
8508
8509  
8510 /*
8511  * - LGPL
8512  *
8513  * Input
8514  * 
8515  */
8516
8517 /**
8518  * @class Roo.bootstrap.TextArea
8519  * @extends Roo.bootstrap.Input
8520  * Bootstrap TextArea class
8521  * @cfg {Number} cols Specifies the visible width of a text area
8522  * @cfg {Number} rows Specifies the visible number of lines in a text area
8523  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8524  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8525  * @cfg {string} html text
8526  * 
8527  * @constructor
8528  * Create a new TextArea
8529  * @param {Object} config The config object
8530  */
8531
8532 Roo.bootstrap.TextArea = function(config){
8533     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8534    
8535 };
8536
8537 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8538      
8539     cols : false,
8540     rows : 5,
8541     readOnly : false,
8542     warp : 'soft',
8543     resize : false,
8544     value: false,
8545     html: false,
8546     
8547     getAutoCreate : function(){
8548         
8549         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8550         
8551         var id = Roo.id();
8552         
8553         var cfg = {};
8554         
8555         var input =  {
8556             tag: 'textarea',
8557             id : id,
8558             warp : this.warp,
8559             rows : this.rows,
8560             value : this.value || '',
8561             html: this.html || '',
8562             cls : 'form-control',
8563             placeholder : this.placeholder || '' 
8564             
8565         };
8566         
8567         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8568             input.maxLength = this.maxLength;
8569         }
8570         
8571         if(this.resize){
8572             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8573         }
8574         
8575         if(this.cols){
8576             input.cols = this.cols;
8577         }
8578         
8579         if (this.readOnly) {
8580             input.readonly = true;
8581         }
8582         
8583         if (this.name) {
8584             input.name = this.name;
8585         }
8586         
8587         if (this.size) {
8588             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8589         }
8590         
8591         var settings=this;
8592         ['xs','sm','md','lg'].map(function(size){
8593             if (settings[size]) {
8594                 cfg.cls += ' col-' + size + '-' + settings[size];
8595             }
8596         });
8597         
8598         var inputblock = input;
8599         
8600         if(this.hasFeedback && !this.allowBlank){
8601             
8602             var feedback = {
8603                 tag: 'span',
8604                 cls: 'glyphicon form-control-feedback'
8605             };
8606
8607             inputblock = {
8608                 cls : 'has-feedback',
8609                 cn :  [
8610                     input,
8611                     feedback
8612                 ] 
8613             };  
8614         }
8615         
8616         
8617         if (this.before || this.after) {
8618             
8619             inputblock = {
8620                 cls : 'input-group',
8621                 cn :  [] 
8622             };
8623             if (this.before) {
8624                 inputblock.cn.push({
8625                     tag :'span',
8626                     cls : 'input-group-addon',
8627                     html : this.before
8628                 });
8629             }
8630             
8631             inputblock.cn.push(input);
8632             
8633             if(this.hasFeedback && !this.allowBlank){
8634                 inputblock.cls += ' has-feedback';
8635                 inputblock.cn.push(feedback);
8636             }
8637             
8638             if (this.after) {
8639                 inputblock.cn.push({
8640                     tag :'span',
8641                     cls : 'input-group-addon',
8642                     html : this.after
8643                 });
8644             }
8645             
8646         }
8647         
8648         if (align ==='left' && this.fieldLabel.length) {
8649                 Roo.log("left and has label");
8650                 cfg.cn = [
8651                     
8652                     {
8653                         tag: 'label',
8654                         'for' :  id,
8655                         cls : 'control-label col-sm-' + this.labelWidth,
8656                         html : this.fieldLabel
8657                         
8658                     },
8659                     {
8660                         cls : "col-sm-" + (12 - this.labelWidth), 
8661                         cn: [
8662                             inputblock
8663                         ]
8664                     }
8665                     
8666                 ];
8667         } else if ( this.fieldLabel.length) {
8668                 Roo.log(" label");
8669                  cfg.cn = [
8670                    
8671                     {
8672                         tag: 'label',
8673                         //cls : 'input-group-addon',
8674                         html : this.fieldLabel
8675                         
8676                     },
8677                     
8678                     inputblock
8679                     
8680                 ];
8681
8682         } else {
8683             
8684                    Roo.log(" no label && no align");
8685                 cfg.cn = [
8686                     
8687                         inputblock
8688                     
8689                 ];
8690                 
8691                 
8692         }
8693         
8694         if (this.disabled) {
8695             input.disabled=true;
8696         }
8697         
8698         return cfg;
8699         
8700     },
8701     /**
8702      * return the real textarea element.
8703      */
8704     inputEl: function ()
8705     {
8706         return this.el.select('textarea.form-control',true).first();
8707     },
8708     
8709     /**
8710      * Clear any invalid styles/messages for this field
8711      */
8712     clearInvalid : function()
8713     {
8714         
8715         if(!this.el || this.preventMark){ // not rendered
8716             return;
8717         }
8718         
8719         var label = this.el.select('label', true).first();
8720         var icon = this.el.select('i.fa-star', true).first();
8721         
8722         if(label && icon){
8723             icon.remove();
8724         }
8725         
8726         this.el.removeClass(this.invalidClass);
8727         
8728         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8729             
8730             var feedback = this.el.select('.form-control-feedback', true).first();
8731             
8732             if(feedback){
8733                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8734             }
8735             
8736         }
8737         
8738         this.fireEvent('valid', this);
8739     },
8740     
8741      /**
8742      * Mark this field as valid
8743      */
8744     markValid : function()
8745     {
8746         if(!this.el  || this.preventMark){ // not rendered
8747             return;
8748         }
8749         
8750         this.el.removeClass([this.invalidClass, this.validClass]);
8751         
8752         var feedback = this.el.select('.form-control-feedback', true).first();
8753             
8754         if(feedback){
8755             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8756         }
8757
8758         if(this.disabled || this.allowBlank){
8759             return;
8760         }
8761         
8762         var label = this.el.select('label', true).first();
8763         var icon = this.el.select('i.fa-star', true).first();
8764         
8765         if(label && icon){
8766             icon.remove();
8767         }
8768         
8769         this.el.addClass(this.validClass);
8770         
8771         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8772             
8773             var feedback = this.el.select('.form-control-feedback', true).first();
8774             
8775             if(feedback){
8776                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8777                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8778             }
8779             
8780         }
8781         
8782         this.fireEvent('valid', this);
8783     },
8784     
8785      /**
8786      * Mark this field as invalid
8787      * @param {String} msg The validation message
8788      */
8789     markInvalid : function(msg)
8790     {
8791         if(!this.el  || this.preventMark){ // not rendered
8792             return;
8793         }
8794         
8795         this.el.removeClass([this.invalidClass, this.validClass]);
8796         
8797         var feedback = this.el.select('.form-control-feedback', true).first();
8798             
8799         if(feedback){
8800             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8801         }
8802
8803         if(this.disabled || this.allowBlank){
8804             return;
8805         }
8806         
8807         var label = this.el.select('label', true).first();
8808         var icon = this.el.select('i.fa-star', true).first();
8809         
8810         if(!this.getValue().length && label && !icon){
8811             this.el.createChild({
8812                 tag : 'i',
8813                 cls : 'text-danger fa fa-lg fa-star',
8814                 tooltip : 'This field is required',
8815                 style : 'margin-right:5px;'
8816             }, label, true);
8817         }
8818
8819         this.el.addClass(this.invalidClass);
8820         
8821         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8822             
8823             var feedback = this.el.select('.form-control-feedback', true).first();
8824             
8825             if(feedback){
8826                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8827                 
8828                 if(this.getValue().length || this.forceFeedback){
8829                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8830                 }
8831                 
8832             }
8833             
8834         }
8835         
8836         this.fireEvent('invalid', this, msg);
8837     }
8838 });
8839
8840  
8841 /*
8842  * - LGPL
8843  *
8844  * trigger field - base class for combo..
8845  * 
8846  */
8847  
8848 /**
8849  * @class Roo.bootstrap.TriggerField
8850  * @extends Roo.bootstrap.Input
8851  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8852  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8853  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8854  * for which you can provide a custom implementation.  For example:
8855  * <pre><code>
8856 var trigger = new Roo.bootstrap.TriggerField();
8857 trigger.onTriggerClick = myTriggerFn;
8858 trigger.applyTo('my-field');
8859 </code></pre>
8860  *
8861  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8862  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8863  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
8864  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8865  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8866
8867  * @constructor
8868  * Create a new TriggerField.
8869  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8870  * to the base TextField)
8871  */
8872 Roo.bootstrap.TriggerField = function(config){
8873     this.mimicing = false;
8874     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8875 };
8876
8877 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
8878     /**
8879      * @cfg {String} triggerClass A CSS class to apply to the trigger
8880      */
8881      /**
8882      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8883      */
8884     hideTrigger:false,
8885
8886     /**
8887      * @cfg {Boolean} removable (true|false) special filter default false
8888      */
8889     removable : false,
8890     
8891     /** @cfg {Boolean} grow @hide */
8892     /** @cfg {Number} growMin @hide */
8893     /** @cfg {Number} growMax @hide */
8894
8895     /**
8896      * @hide 
8897      * @method
8898      */
8899     autoSize: Roo.emptyFn,
8900     // private
8901     monitorTab : true,
8902     // private
8903     deferHeight : true,
8904
8905     
8906     actionMode : 'wrap',
8907     
8908     caret : false,
8909     
8910     
8911     getAutoCreate : function(){
8912        
8913         var align = this.labelAlign || this.parentLabelAlign();
8914         
8915         var id = Roo.id();
8916         
8917         var cfg = {
8918             cls: 'form-group' //input-group
8919         };
8920         
8921         
8922         var input =  {
8923             tag: 'input',
8924             id : id,
8925             type : this.inputType,
8926             cls : 'form-control',
8927             autocomplete: 'new-password',
8928             placeholder : this.placeholder || '' 
8929             
8930         };
8931         if (this.name) {
8932             input.name = this.name;
8933         }
8934         if (this.size) {
8935             input.cls += ' input-' + this.size;
8936         }
8937         
8938         if (this.disabled) {
8939             input.disabled=true;
8940         }
8941         
8942         var inputblock = input;
8943         
8944         if(this.hasFeedback && !this.allowBlank){
8945             
8946             var feedback = {
8947                 tag: 'span',
8948                 cls: 'glyphicon form-control-feedback'
8949             };
8950             
8951             if(this.removable && !this.editable && !this.tickable){
8952                 inputblock = {
8953                     cls : 'has-feedback',
8954                     cn :  [
8955                         inputblock,
8956                         {
8957                             tag: 'button',
8958                             html : 'x',
8959                             cls : 'roo-combo-removable-btn close'
8960                         },
8961                         feedback
8962                     ] 
8963                 };
8964             } else {
8965                 inputblock = {
8966                     cls : 'has-feedback',
8967                     cn :  [
8968                         inputblock,
8969                         feedback
8970                     ] 
8971                 };
8972             }
8973
8974         } else {
8975             if(this.removable && !this.editable && !this.tickable){
8976                 inputblock = {
8977                     cls : 'roo-removable',
8978                     cn :  [
8979                         inputblock,
8980                         {
8981                             tag: 'button',
8982                             html : 'x',
8983                             cls : 'roo-combo-removable-btn close'
8984                         }
8985                     ] 
8986                 };
8987             }
8988         }
8989         
8990         if (this.before || this.after) {
8991             
8992             inputblock = {
8993                 cls : 'input-group',
8994                 cn :  [] 
8995             };
8996             if (this.before) {
8997                 inputblock.cn.push({
8998                     tag :'span',
8999                     cls : 'input-group-addon',
9000                     html : this.before
9001                 });
9002             }
9003             
9004             inputblock.cn.push(input);
9005             
9006             if(this.hasFeedback && !this.allowBlank){
9007                 inputblock.cls += ' has-feedback';
9008                 inputblock.cn.push(feedback);
9009             }
9010             
9011             if (this.after) {
9012                 inputblock.cn.push({
9013                     tag :'span',
9014                     cls : 'input-group-addon',
9015                     html : this.after
9016                 });
9017             }
9018             
9019         };
9020         
9021         var box = {
9022             tag: 'div',
9023             cn: [
9024                 {
9025                     tag: 'input',
9026                     type : 'hidden',
9027                     cls: 'form-hidden-field'
9028                 },
9029                 inputblock
9030             ]
9031             
9032         };
9033         
9034         if(this.multiple){
9035             Roo.log('multiple');
9036             
9037             box = {
9038                 tag: 'div',
9039                 cn: [
9040                     {
9041                         tag: 'input',
9042                         type : 'hidden',
9043                         cls: 'form-hidden-field'
9044                     },
9045                     {
9046                         tag: 'ul',
9047                         cls: 'select2-choices',
9048                         cn:[
9049                             {
9050                                 tag: 'li',
9051                                 cls: 'select2-search-field',
9052                                 cn: [
9053
9054                                     inputblock
9055                                 ]
9056                             }
9057                         ]
9058                     }
9059                 ]
9060             }
9061         };
9062         
9063         var combobox = {
9064             cls: 'select2-container input-group',
9065             cn: [
9066                 box
9067 //                {
9068 //                    tag: 'ul',
9069 //                    cls: 'typeahead typeahead-long dropdown-menu',
9070 //                    style: 'display:none'
9071 //                }
9072             ]
9073         };
9074         
9075         if(!this.multiple && this.showToggleBtn){
9076             
9077             var caret = {
9078                         tag: 'span',
9079                         cls: 'caret'
9080              };
9081             if (this.caret != false) {
9082                 caret = {
9083                      tag: 'i',
9084                      cls: 'fa fa-' + this.caret
9085                 };
9086                 
9087             }
9088             
9089             combobox.cn.push({
9090                 tag :'span',
9091                 cls : 'input-group-addon btn dropdown-toggle',
9092                 cn : [
9093                     caret,
9094                     {
9095                         tag: 'span',
9096                         cls: 'combobox-clear',
9097                         cn  : [
9098                             {
9099                                 tag : 'i',
9100                                 cls: 'icon-remove'
9101                             }
9102                         ]
9103                     }
9104                 ]
9105
9106             })
9107         }
9108         
9109         if(this.multiple){
9110             combobox.cls += ' select2-container-multi';
9111         }
9112         
9113         if (align ==='left' && this.fieldLabel.length) {
9114             
9115                 Roo.log("left and has label");
9116                 cfg.cn = [
9117                     
9118                     {
9119                         tag: 'label',
9120                         'for' :  id,
9121                         cls : 'control-label col-sm-' + this.labelWidth,
9122                         html : this.fieldLabel
9123                         
9124                     },
9125                     {
9126                         cls : "col-sm-" + (12 - this.labelWidth), 
9127                         cn: [
9128                             combobox
9129                         ]
9130                     }
9131                     
9132                 ];
9133         } else if ( this.fieldLabel.length) {
9134                 Roo.log(" label");
9135                  cfg.cn = [
9136                    
9137                     {
9138                         tag: 'label',
9139                         //cls : 'input-group-addon',
9140                         html : this.fieldLabel
9141                         
9142                     },
9143                     
9144                     combobox
9145                     
9146                 ];
9147
9148         } else {
9149             
9150                 Roo.log(" no label && no align");
9151                 cfg = combobox
9152                      
9153                 
9154         }
9155          
9156         var settings=this;
9157         ['xs','sm','md','lg'].map(function(size){
9158             if (settings[size]) {
9159                 cfg.cls += ' col-' + size + '-' + settings[size];
9160             }
9161         });
9162         Roo.log(cfg);
9163         return cfg;
9164         
9165     },
9166     
9167     
9168     
9169     // private
9170     onResize : function(w, h){
9171 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9172 //        if(typeof w == 'number'){
9173 //            var x = w - this.trigger.getWidth();
9174 //            this.inputEl().setWidth(this.adjustWidth('input', x));
9175 //            this.trigger.setStyle('left', x+'px');
9176 //        }
9177     },
9178
9179     // private
9180     adjustSize : Roo.BoxComponent.prototype.adjustSize,
9181
9182     // private
9183     getResizeEl : function(){
9184         return this.inputEl();
9185     },
9186
9187     // private
9188     getPositionEl : function(){
9189         return this.inputEl();
9190     },
9191
9192     // private
9193     alignErrorIcon : function(){
9194         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9195     },
9196
9197     // private
9198     initEvents : function(){
9199         
9200         this.createList();
9201         
9202         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9203         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9204         if(!this.multiple && this.showToggleBtn){
9205             this.trigger = this.el.select('span.dropdown-toggle',true).first();
9206             if(this.hideTrigger){
9207                 this.trigger.setDisplayed(false);
9208             }
9209             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9210         }
9211         
9212         if(this.multiple){
9213             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9214         }
9215         
9216         if(this.removable && !this.editable && !this.tickable){
9217             var close = this.closeTriggerEl();
9218             
9219             if(close){
9220                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9221                 close.on('click', this.removeBtnClick, this, close);
9222             }
9223         }
9224         
9225         //this.trigger.addClassOnOver('x-form-trigger-over');
9226         //this.trigger.addClassOnClick('x-form-trigger-click');
9227         
9228         //if(!this.width){
9229         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9230         //}
9231     },
9232     
9233     closeTriggerEl : function()
9234     {
9235         var close = this.el.select('.roo-combo-removable-btn', true).first();
9236         return close ? close : false;
9237     },
9238     
9239     removeBtnClick : function(e, h, el)
9240     {
9241         e.preventDefault();
9242         
9243         if(this.fireEvent("remove", this) !== false){
9244             this.reset();
9245         }
9246     },
9247     
9248     createList : function()
9249     {
9250         this.list = Roo.get(document.body).createChild({
9251             tag: 'ul',
9252             cls: 'typeahead typeahead-long dropdown-menu',
9253             style: 'display:none'
9254         });
9255         
9256         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9257         
9258     },
9259
9260     // private
9261     initTrigger : function(){
9262        
9263     },
9264
9265     // private
9266     onDestroy : function(){
9267         if(this.trigger){
9268             this.trigger.removeAllListeners();
9269           //  this.trigger.remove();
9270         }
9271         //if(this.wrap){
9272         //    this.wrap.remove();
9273         //}
9274         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9275     },
9276
9277     // private
9278     onFocus : function(){
9279         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9280         /*
9281         if(!this.mimicing){
9282             this.wrap.addClass('x-trigger-wrap-focus');
9283             this.mimicing = true;
9284             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9285             if(this.monitorTab){
9286                 this.el.on("keydown", this.checkTab, this);
9287             }
9288         }
9289         */
9290     },
9291
9292     // private
9293     checkTab : function(e){
9294         if(e.getKey() == e.TAB){
9295             this.triggerBlur();
9296         }
9297     },
9298
9299     // private
9300     onBlur : function(){
9301         // do nothing
9302     },
9303
9304     // private
9305     mimicBlur : function(e, t){
9306         /*
9307         if(!this.wrap.contains(t) && this.validateBlur()){
9308             this.triggerBlur();
9309         }
9310         */
9311     },
9312
9313     // private
9314     triggerBlur : function(){
9315         this.mimicing = false;
9316         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9317         if(this.monitorTab){
9318             this.el.un("keydown", this.checkTab, this);
9319         }
9320         //this.wrap.removeClass('x-trigger-wrap-focus');
9321         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9322     },
9323
9324     // private
9325     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9326     validateBlur : function(e, t){
9327         return true;
9328     },
9329
9330     // private
9331     onDisable : function(){
9332         this.inputEl().dom.disabled = true;
9333         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9334         //if(this.wrap){
9335         //    this.wrap.addClass('x-item-disabled');
9336         //}
9337     },
9338
9339     // private
9340     onEnable : function(){
9341         this.inputEl().dom.disabled = false;
9342         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9343         //if(this.wrap){
9344         //    this.el.removeClass('x-item-disabled');
9345         //}
9346     },
9347
9348     // private
9349     onShow : function(){
9350         var ae = this.getActionEl();
9351         
9352         if(ae){
9353             ae.dom.style.display = '';
9354             ae.dom.style.visibility = 'visible';
9355         }
9356     },
9357
9358     // private
9359     
9360     onHide : function(){
9361         var ae = this.getActionEl();
9362         ae.dom.style.display = 'none';
9363     },
9364
9365     /**
9366      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
9367      * by an implementing function.
9368      * @method
9369      * @param {EventObject} e
9370      */
9371     onTriggerClick : Roo.emptyFn
9372 });
9373  /*
9374  * Based on:
9375  * Ext JS Library 1.1.1
9376  * Copyright(c) 2006-2007, Ext JS, LLC.
9377  *
9378  * Originally Released Under LGPL - original licence link has changed is not relivant.
9379  *
9380  * Fork - LGPL
9381  * <script type="text/javascript">
9382  */
9383
9384
9385 /**
9386  * @class Roo.data.SortTypes
9387  * @singleton
9388  * Defines the default sorting (casting?) comparison functions used when sorting data.
9389  */
9390 Roo.data.SortTypes = {
9391     /**
9392      * Default sort that does nothing
9393      * @param {Mixed} s The value being converted
9394      * @return {Mixed} The comparison value
9395      */
9396     none : function(s){
9397         return s;
9398     },
9399     
9400     /**
9401      * The regular expression used to strip tags
9402      * @type {RegExp}
9403      * @property
9404      */
9405     stripTagsRE : /<\/?[^>]+>/gi,
9406     
9407     /**
9408      * Strips all HTML tags to sort on text only
9409      * @param {Mixed} s The value being converted
9410      * @return {String} The comparison value
9411      */
9412     asText : function(s){
9413         return String(s).replace(this.stripTagsRE, "");
9414     },
9415     
9416     /**
9417      * Strips all HTML tags to sort on text only - Case insensitive
9418      * @param {Mixed} s The value being converted
9419      * @return {String} The comparison value
9420      */
9421     asUCText : function(s){
9422         return String(s).toUpperCase().replace(this.stripTagsRE, "");
9423     },
9424     
9425     /**
9426      * Case insensitive string
9427      * @param {Mixed} s The value being converted
9428      * @return {String} The comparison value
9429      */
9430     asUCString : function(s) {
9431         return String(s).toUpperCase();
9432     },
9433     
9434     /**
9435      * Date sorting
9436      * @param {Mixed} s The value being converted
9437      * @return {Number} The comparison value
9438      */
9439     asDate : function(s) {
9440         if(!s){
9441             return 0;
9442         }
9443         if(s instanceof Date){
9444             return s.getTime();
9445         }
9446         return Date.parse(String(s));
9447     },
9448     
9449     /**
9450      * Float sorting
9451      * @param {Mixed} s The value being converted
9452      * @return {Float} The comparison value
9453      */
9454     asFloat : function(s) {
9455         var val = parseFloat(String(s).replace(/,/g, ""));
9456         if(isNaN(val)) {
9457             val = 0;
9458         }
9459         return val;
9460     },
9461     
9462     /**
9463      * Integer sorting
9464      * @param {Mixed} s The value being converted
9465      * @return {Number} The comparison value
9466      */
9467     asInt : function(s) {
9468         var val = parseInt(String(s).replace(/,/g, ""));
9469         if(isNaN(val)) {
9470             val = 0;
9471         }
9472         return val;
9473     }
9474 };/*
9475  * Based on:
9476  * Ext JS Library 1.1.1
9477  * Copyright(c) 2006-2007, Ext JS, LLC.
9478  *
9479  * Originally Released Under LGPL - original licence link has changed is not relivant.
9480  *
9481  * Fork - LGPL
9482  * <script type="text/javascript">
9483  */
9484
9485 /**
9486 * @class Roo.data.Record
9487  * Instances of this class encapsulate both record <em>definition</em> information, and record
9488  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9489  * to access Records cached in an {@link Roo.data.Store} object.<br>
9490  * <p>
9491  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9492  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9493  * objects.<br>
9494  * <p>
9495  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9496  * @constructor
9497  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9498  * {@link #create}. The parameters are the same.
9499  * @param {Array} data An associative Array of data values keyed by the field name.
9500  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9501  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9502  * not specified an integer id is generated.
9503  */
9504 Roo.data.Record = function(data, id){
9505     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9506     this.data = data;
9507 };
9508
9509 /**
9510  * Generate a constructor for a specific record layout.
9511  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9512  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9513  * Each field definition object may contain the following properties: <ul>
9514  * <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,
9515  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9516  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9517  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9518  * is being used, then this is a string containing the javascript expression to reference the data relative to 
9519  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9520  * to the data item relative to the record element. If the mapping expression is the same as the field name,
9521  * this may be omitted.</p></li>
9522  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9523  * <ul><li>auto (Default, implies no conversion)</li>
9524  * <li>string</li>
9525  * <li>int</li>
9526  * <li>float</li>
9527  * <li>boolean</li>
9528  * <li>date</li></ul></p></li>
9529  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9530  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9531  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9532  * by the Reader into an object that will be stored in the Record. It is passed the
9533  * following parameters:<ul>
9534  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9535  * </ul></p></li>
9536  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9537  * </ul>
9538  * <br>usage:<br><pre><code>
9539 var TopicRecord = Roo.data.Record.create(
9540     {name: 'title', mapping: 'topic_title'},
9541     {name: 'author', mapping: 'username'},
9542     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9543     {name: 'lastPost', mapping: 'post_time', type: 'date'},
9544     {name: 'lastPoster', mapping: 'user2'},
9545     {name: 'excerpt', mapping: 'post_text'}
9546 );
9547
9548 var myNewRecord = new TopicRecord({
9549     title: 'Do my job please',
9550     author: 'noobie',
9551     totalPosts: 1,
9552     lastPost: new Date(),
9553     lastPoster: 'Animal',
9554     excerpt: 'No way dude!'
9555 });
9556 myStore.add(myNewRecord);
9557 </code></pre>
9558  * @method create
9559  * @static
9560  */
9561 Roo.data.Record.create = function(o){
9562     var f = function(){
9563         f.superclass.constructor.apply(this, arguments);
9564     };
9565     Roo.extend(f, Roo.data.Record);
9566     var p = f.prototype;
9567     p.fields = new Roo.util.MixedCollection(false, function(field){
9568         return field.name;
9569     });
9570     for(var i = 0, len = o.length; i < len; i++){
9571         p.fields.add(new Roo.data.Field(o[i]));
9572     }
9573     f.getField = function(name){
9574         return p.fields.get(name);  
9575     };
9576     return f;
9577 };
9578
9579 Roo.data.Record.AUTO_ID = 1000;
9580 Roo.data.Record.EDIT = 'edit';
9581 Roo.data.Record.REJECT = 'reject';
9582 Roo.data.Record.COMMIT = 'commit';
9583
9584 Roo.data.Record.prototype = {
9585     /**
9586      * Readonly flag - true if this record has been modified.
9587      * @type Boolean
9588      */
9589     dirty : false,
9590     editing : false,
9591     error: null,
9592     modified: null,
9593
9594     // private
9595     join : function(store){
9596         this.store = store;
9597     },
9598
9599     /**
9600      * Set the named field to the specified value.
9601      * @param {String} name The name of the field to set.
9602      * @param {Object} value The value to set the field to.
9603      */
9604     set : function(name, value){
9605         if(this.data[name] == value){
9606             return;
9607         }
9608         this.dirty = true;
9609         if(!this.modified){
9610             this.modified = {};
9611         }
9612         if(typeof this.modified[name] == 'undefined'){
9613             this.modified[name] = this.data[name];
9614         }
9615         this.data[name] = value;
9616         if(!this.editing && this.store){
9617             this.store.afterEdit(this);
9618         }       
9619     },
9620
9621     /**
9622      * Get the value of the named field.
9623      * @param {String} name The name of the field to get the value of.
9624      * @return {Object} The value of the field.
9625      */
9626     get : function(name){
9627         return this.data[name]; 
9628     },
9629
9630     // private
9631     beginEdit : function(){
9632         this.editing = true;
9633         this.modified = {}; 
9634     },
9635
9636     // private
9637     cancelEdit : function(){
9638         this.editing = false;
9639         delete this.modified;
9640     },
9641
9642     // private
9643     endEdit : function(){
9644         this.editing = false;
9645         if(this.dirty && this.store){
9646             this.store.afterEdit(this);
9647         }
9648     },
9649
9650     /**
9651      * Usually called by the {@link Roo.data.Store} which owns the Record.
9652      * Rejects all changes made to the Record since either creation, or the last commit operation.
9653      * Modified fields are reverted to their original values.
9654      * <p>
9655      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9656      * of reject operations.
9657      */
9658     reject : function(){
9659         var m = this.modified;
9660         for(var n in m){
9661             if(typeof m[n] != "function"){
9662                 this.data[n] = m[n];
9663             }
9664         }
9665         this.dirty = false;
9666         delete this.modified;
9667         this.editing = false;
9668         if(this.store){
9669             this.store.afterReject(this);
9670         }
9671     },
9672
9673     /**
9674      * Usually called by the {@link Roo.data.Store} which owns the Record.
9675      * Commits all changes made to the Record since either creation, or the last commit operation.
9676      * <p>
9677      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9678      * of commit operations.
9679      */
9680     commit : function(){
9681         this.dirty = false;
9682         delete this.modified;
9683         this.editing = false;
9684         if(this.store){
9685             this.store.afterCommit(this);
9686         }
9687     },
9688
9689     // private
9690     hasError : function(){
9691         return this.error != null;
9692     },
9693
9694     // private
9695     clearError : function(){
9696         this.error = null;
9697     },
9698
9699     /**
9700      * Creates a copy of this record.
9701      * @param {String} id (optional) A new record id if you don't want to use this record's id
9702      * @return {Record}
9703      */
9704     copy : function(newId) {
9705         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9706     }
9707 };/*
9708  * Based on:
9709  * Ext JS Library 1.1.1
9710  * Copyright(c) 2006-2007, Ext JS, LLC.
9711  *
9712  * Originally Released Under LGPL - original licence link has changed is not relivant.
9713  *
9714  * Fork - LGPL
9715  * <script type="text/javascript">
9716  */
9717
9718
9719
9720 /**
9721  * @class Roo.data.Store
9722  * @extends Roo.util.Observable
9723  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9724  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9725  * <p>
9726  * 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
9727  * has no knowledge of the format of the data returned by the Proxy.<br>
9728  * <p>
9729  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9730  * instances from the data object. These records are cached and made available through accessor functions.
9731  * @constructor
9732  * Creates a new Store.
9733  * @param {Object} config A config object containing the objects needed for the Store to access data,
9734  * and read the data into Records.
9735  */
9736 Roo.data.Store = function(config){
9737     this.data = new Roo.util.MixedCollection(false);
9738     this.data.getKey = function(o){
9739         return o.id;
9740     };
9741     this.baseParams = {};
9742     // private
9743     this.paramNames = {
9744         "start" : "start",
9745         "limit" : "limit",
9746         "sort" : "sort",
9747         "dir" : "dir",
9748         "multisort" : "_multisort"
9749     };
9750
9751     if(config && config.data){
9752         this.inlineData = config.data;
9753         delete config.data;
9754     }
9755
9756     Roo.apply(this, config);
9757     
9758     if(this.reader){ // reader passed
9759         this.reader = Roo.factory(this.reader, Roo.data);
9760         this.reader.xmodule = this.xmodule || false;
9761         if(!this.recordType){
9762             this.recordType = this.reader.recordType;
9763         }
9764         if(this.reader.onMetaChange){
9765             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9766         }
9767     }
9768
9769     if(this.recordType){
9770         this.fields = this.recordType.prototype.fields;
9771     }
9772     this.modified = [];
9773
9774     this.addEvents({
9775         /**
9776          * @event datachanged
9777          * Fires when the data cache has changed, and a widget which is using this Store
9778          * as a Record cache should refresh its view.
9779          * @param {Store} this
9780          */
9781         datachanged : true,
9782         /**
9783          * @event metachange
9784          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9785          * @param {Store} this
9786          * @param {Object} meta The JSON metadata
9787          */
9788         metachange : true,
9789         /**
9790          * @event add
9791          * Fires when Records have been added to the Store
9792          * @param {Store} this
9793          * @param {Roo.data.Record[]} records The array of Records added
9794          * @param {Number} index The index at which the record(s) were added
9795          */
9796         add : true,
9797         /**
9798          * @event remove
9799          * Fires when a Record has been removed from the Store
9800          * @param {Store} this
9801          * @param {Roo.data.Record} record The Record that was removed
9802          * @param {Number} index The index at which the record was removed
9803          */
9804         remove : true,
9805         /**
9806          * @event update
9807          * Fires when a Record has been updated
9808          * @param {Store} this
9809          * @param {Roo.data.Record} record The Record that was updated
9810          * @param {String} operation The update operation being performed.  Value may be one of:
9811          * <pre><code>
9812  Roo.data.Record.EDIT
9813  Roo.data.Record.REJECT
9814  Roo.data.Record.COMMIT
9815          * </code></pre>
9816          */
9817         update : true,
9818         /**
9819          * @event clear
9820          * Fires when the data cache has been cleared.
9821          * @param {Store} this
9822          */
9823         clear : true,
9824         /**
9825          * @event beforeload
9826          * Fires before a request is made for a new data object.  If the beforeload handler returns false
9827          * the load action will be canceled.
9828          * @param {Store} this
9829          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9830          */
9831         beforeload : true,
9832         /**
9833          * @event beforeloadadd
9834          * Fires after a new set of Records has been loaded.
9835          * @param {Store} this
9836          * @param {Roo.data.Record[]} records The Records that were loaded
9837          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9838          */
9839         beforeloadadd : true,
9840         /**
9841          * @event load
9842          * Fires after a new set of Records has been loaded, before they are added to the store.
9843          * @param {Store} this
9844          * @param {Roo.data.Record[]} records The Records that were loaded
9845          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9846          * @params {Object} return from reader
9847          */
9848         load : true,
9849         /**
9850          * @event loadexception
9851          * Fires if an exception occurs in the Proxy during loading.
9852          * Called with the signature of the Proxy's "loadexception" event.
9853          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9854          * 
9855          * @param {Proxy} 
9856          * @param {Object} return from JsonData.reader() - success, totalRecords, records
9857          * @param {Object} load options 
9858          * @param {Object} jsonData from your request (normally this contains the Exception)
9859          */
9860         loadexception : true
9861     });
9862     
9863     if(this.proxy){
9864         this.proxy = Roo.factory(this.proxy, Roo.data);
9865         this.proxy.xmodule = this.xmodule || false;
9866         this.relayEvents(this.proxy,  ["loadexception"]);
9867     }
9868     this.sortToggle = {};
9869     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9870
9871     Roo.data.Store.superclass.constructor.call(this);
9872
9873     if(this.inlineData){
9874         this.loadData(this.inlineData);
9875         delete this.inlineData;
9876     }
9877 };
9878
9879 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9880      /**
9881     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
9882     * without a remote query - used by combo/forms at present.
9883     */
9884     
9885     /**
9886     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9887     */
9888     /**
9889     * @cfg {Array} data Inline data to be loaded when the store is initialized.
9890     */
9891     /**
9892     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9893     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9894     */
9895     /**
9896     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9897     * on any HTTP request
9898     */
9899     /**
9900     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9901     */
9902     /**
9903     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9904     */
9905     multiSort: false,
9906     /**
9907     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9908     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9909     */
9910     remoteSort : false,
9911
9912     /**
9913     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9914      * loaded or when a record is removed. (defaults to false).
9915     */
9916     pruneModifiedRecords : false,
9917
9918     // private
9919     lastOptions : null,
9920
9921     /**
9922      * Add Records to the Store and fires the add event.
9923      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9924      */
9925     add : function(records){
9926         records = [].concat(records);
9927         for(var i = 0, len = records.length; i < len; i++){
9928             records[i].join(this);
9929         }
9930         var index = this.data.length;
9931         this.data.addAll(records);
9932         this.fireEvent("add", this, records, index);
9933     },
9934
9935     /**
9936      * Remove a Record from the Store and fires the remove event.
9937      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9938      */
9939     remove : function(record){
9940         var index = this.data.indexOf(record);
9941         this.data.removeAt(index);
9942         if(this.pruneModifiedRecords){
9943             this.modified.remove(record);
9944         }
9945         this.fireEvent("remove", this, record, index);
9946     },
9947
9948     /**
9949      * Remove all Records from the Store and fires the clear event.
9950      */
9951     removeAll : function(){
9952         this.data.clear();
9953         if(this.pruneModifiedRecords){
9954             this.modified = [];
9955         }
9956         this.fireEvent("clear", this);
9957     },
9958
9959     /**
9960      * Inserts Records to the Store at the given index and fires the add event.
9961      * @param {Number} index The start index at which to insert the passed Records.
9962      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9963      */
9964     insert : function(index, records){
9965         records = [].concat(records);
9966         for(var i = 0, len = records.length; i < len; i++){
9967             this.data.insert(index, records[i]);
9968             records[i].join(this);
9969         }
9970         this.fireEvent("add", this, records, index);
9971     },
9972
9973     /**
9974      * Get the index within the cache of the passed Record.
9975      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9976      * @return {Number} The index of the passed Record. Returns -1 if not found.
9977      */
9978     indexOf : function(record){
9979         return this.data.indexOf(record);
9980     },
9981
9982     /**
9983      * Get the index within the cache of the Record with the passed id.
9984      * @param {String} id The id of the Record to find.
9985      * @return {Number} The index of the Record. Returns -1 if not found.
9986      */
9987     indexOfId : function(id){
9988         return this.data.indexOfKey(id);
9989     },
9990
9991     /**
9992      * Get the Record with the specified id.
9993      * @param {String} id The id of the Record to find.
9994      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
9995      */
9996     getById : function(id){
9997         return this.data.key(id);
9998     },
9999
10000     /**
10001      * Get the Record at the specified index.
10002      * @param {Number} index The index of the Record to find.
10003      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10004      */
10005     getAt : function(index){
10006         return this.data.itemAt(index);
10007     },
10008
10009     /**
10010      * Returns a range of Records between specified indices.
10011      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10012      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10013      * @return {Roo.data.Record[]} An array of Records
10014      */
10015     getRange : function(start, end){
10016         return this.data.getRange(start, end);
10017     },
10018
10019     // private
10020     storeOptions : function(o){
10021         o = Roo.apply({}, o);
10022         delete o.callback;
10023         delete o.scope;
10024         this.lastOptions = o;
10025     },
10026
10027     /**
10028      * Loads the Record cache from the configured Proxy using the configured Reader.
10029      * <p>
10030      * If using remote paging, then the first load call must specify the <em>start</em>
10031      * and <em>limit</em> properties in the options.params property to establish the initial
10032      * position within the dataset, and the number of Records to cache on each read from the Proxy.
10033      * <p>
10034      * <strong>It is important to note that for remote data sources, loading is asynchronous,
10035      * and this call will return before the new data has been loaded. Perform any post-processing
10036      * in a callback function, or in a "load" event handler.</strong>
10037      * <p>
10038      * @param {Object} options An object containing properties which control loading options:<ul>
10039      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10040      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10041      * passed the following arguments:<ul>
10042      * <li>r : Roo.data.Record[]</li>
10043      * <li>options: Options object from the load call</li>
10044      * <li>success: Boolean success indicator</li></ul></li>
10045      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10046      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10047      * </ul>
10048      */
10049     load : function(options){
10050         options = options || {};
10051         if(this.fireEvent("beforeload", this, options) !== false){
10052             this.storeOptions(options);
10053             var p = Roo.apply(options.params || {}, this.baseParams);
10054             // if meta was not loaded from remote source.. try requesting it.
10055             if (!this.reader.metaFromRemote) {
10056                 p._requestMeta = 1;
10057             }
10058             if(this.sortInfo && this.remoteSort){
10059                 var pn = this.paramNames;
10060                 p[pn["sort"]] = this.sortInfo.field;
10061                 p[pn["dir"]] = this.sortInfo.direction;
10062             }
10063             if (this.multiSort) {
10064                 var pn = this.paramNames;
10065                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10066             }
10067             
10068             this.proxy.load(p, this.reader, this.loadRecords, this, options);
10069         }
10070     },
10071
10072     /**
10073      * Reloads the Record cache from the configured Proxy using the configured Reader and
10074      * the options from the last load operation performed.
10075      * @param {Object} options (optional) An object containing properties which may override the options
10076      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10077      * the most recently used options are reused).
10078      */
10079     reload : function(options){
10080         this.load(Roo.applyIf(options||{}, this.lastOptions));
10081     },
10082
10083     // private
10084     // Called as a callback by the Reader during a load operation.
10085     loadRecords : function(o, options, success){
10086         if(!o || success === false){
10087             if(success !== false){
10088                 this.fireEvent("load", this, [], options, o);
10089             }
10090             if(options.callback){
10091                 options.callback.call(options.scope || this, [], options, false);
10092             }
10093             return;
10094         }
10095         // if data returned failure - throw an exception.
10096         if (o.success === false) {
10097             // show a message if no listener is registered.
10098             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10099                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10100             }
10101             // loadmask wil be hooked into this..
10102             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10103             return;
10104         }
10105         var r = o.records, t = o.totalRecords || r.length;
10106         
10107         this.fireEvent("beforeloadadd", this, r, options, o);
10108         
10109         if(!options || options.add !== true){
10110             if(this.pruneModifiedRecords){
10111                 this.modified = [];
10112             }
10113             for(var i = 0, len = r.length; i < len; i++){
10114                 r[i].join(this);
10115             }
10116             if(this.snapshot){
10117                 this.data = this.snapshot;
10118                 delete this.snapshot;
10119             }
10120             this.data.clear();
10121             this.data.addAll(r);
10122             this.totalLength = t;
10123             this.applySort();
10124             this.fireEvent("datachanged", this);
10125         }else{
10126             this.totalLength = Math.max(t, this.data.length+r.length);
10127             this.add(r);
10128         }
10129         this.fireEvent("load", this, r, options, o);
10130         if(options.callback){
10131             options.callback.call(options.scope || this, r, options, true);
10132         }
10133     },
10134
10135
10136     /**
10137      * Loads data from a passed data block. A Reader which understands the format of the data
10138      * must have been configured in the constructor.
10139      * @param {Object} data The data block from which to read the Records.  The format of the data expected
10140      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10141      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10142      */
10143     loadData : function(o, append){
10144         var r = this.reader.readRecords(o);
10145         this.loadRecords(r, {add: append}, true);
10146     },
10147
10148     /**
10149      * Gets the number of cached records.
10150      * <p>
10151      * <em>If using paging, this may not be the total size of the dataset. If the data object
10152      * used by the Reader contains the dataset size, then the getTotalCount() function returns
10153      * the data set size</em>
10154      */
10155     getCount : function(){
10156         return this.data.length || 0;
10157     },
10158
10159     /**
10160      * Gets the total number of records in the dataset as returned by the server.
10161      * <p>
10162      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10163      * the dataset size</em>
10164      */
10165     getTotalCount : function(){
10166         return this.totalLength || 0;
10167     },
10168
10169     /**
10170      * Returns the sort state of the Store as an object with two properties:
10171      * <pre><code>
10172  field {String} The name of the field by which the Records are sorted
10173  direction {String} The sort order, "ASC" or "DESC"
10174      * </code></pre>
10175      */
10176     getSortState : function(){
10177         return this.sortInfo;
10178     },
10179
10180     // private
10181     applySort : function(){
10182         if(this.sortInfo && !this.remoteSort){
10183             var s = this.sortInfo, f = s.field;
10184             var st = this.fields.get(f).sortType;
10185             var fn = function(r1, r2){
10186                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10187                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10188             };
10189             this.data.sort(s.direction, fn);
10190             if(this.snapshot && this.snapshot != this.data){
10191                 this.snapshot.sort(s.direction, fn);
10192             }
10193         }
10194     },
10195
10196     /**
10197      * Sets the default sort column and order to be used by the next load operation.
10198      * @param {String} fieldName The name of the field to sort by.
10199      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10200      */
10201     setDefaultSort : function(field, dir){
10202         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10203     },
10204
10205     /**
10206      * Sort the Records.
10207      * If remote sorting is used, the sort is performed on the server, and the cache is
10208      * reloaded. If local sorting is used, the cache is sorted internally.
10209      * @param {String} fieldName The name of the field to sort by.
10210      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10211      */
10212     sort : function(fieldName, dir){
10213         var f = this.fields.get(fieldName);
10214         if(!dir){
10215             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10216             
10217             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10218                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10219             }else{
10220                 dir = f.sortDir;
10221             }
10222         }
10223         this.sortToggle[f.name] = dir;
10224         this.sortInfo = {field: f.name, direction: dir};
10225         if(!this.remoteSort){
10226             this.applySort();
10227             this.fireEvent("datachanged", this);
10228         }else{
10229             this.load(this.lastOptions);
10230         }
10231     },
10232
10233     /**
10234      * Calls the specified function for each of the Records in the cache.
10235      * @param {Function} fn The function to call. The Record is passed as the first parameter.
10236      * Returning <em>false</em> aborts and exits the iteration.
10237      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10238      */
10239     each : function(fn, scope){
10240         this.data.each(fn, scope);
10241     },
10242
10243     /**
10244      * Gets all records modified since the last commit.  Modified records are persisted across load operations
10245      * (e.g., during paging).
10246      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10247      */
10248     getModifiedRecords : function(){
10249         return this.modified;
10250     },
10251
10252     // private
10253     createFilterFn : function(property, value, anyMatch){
10254         if(!value.exec){ // not a regex
10255             value = String(value);
10256             if(value.length == 0){
10257                 return false;
10258             }
10259             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10260         }
10261         return function(r){
10262             return value.test(r.data[property]);
10263         };
10264     },
10265
10266     /**
10267      * Sums the value of <i>property</i> for each record between start and end and returns the result.
10268      * @param {String} property A field on your records
10269      * @param {Number} start The record index to start at (defaults to 0)
10270      * @param {Number} end The last record index to include (defaults to length - 1)
10271      * @return {Number} The sum
10272      */
10273     sum : function(property, start, end){
10274         var rs = this.data.items, v = 0;
10275         start = start || 0;
10276         end = (end || end === 0) ? end : rs.length-1;
10277
10278         for(var i = start; i <= end; i++){
10279             v += (rs[i].data[property] || 0);
10280         }
10281         return v;
10282     },
10283
10284     /**
10285      * Filter the records by a specified property.
10286      * @param {String} field A field on your records
10287      * @param {String/RegExp} value Either a string that the field
10288      * should start with or a RegExp to test against the field
10289      * @param {Boolean} anyMatch True to match any part not just the beginning
10290      */
10291     filter : function(property, value, anyMatch){
10292         var fn = this.createFilterFn(property, value, anyMatch);
10293         return fn ? this.filterBy(fn) : this.clearFilter();
10294     },
10295
10296     /**
10297      * Filter by a function. The specified function will be called with each
10298      * record in this data source. If the function returns true the record is included,
10299      * otherwise it is filtered.
10300      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10301      * @param {Object} scope (optional) The scope of the function (defaults to this)
10302      */
10303     filterBy : function(fn, scope){
10304         this.snapshot = this.snapshot || this.data;
10305         this.data = this.queryBy(fn, scope||this);
10306         this.fireEvent("datachanged", this);
10307     },
10308
10309     /**
10310      * Query the records by a specified property.
10311      * @param {String} field A field on your records
10312      * @param {String/RegExp} value Either a string that the field
10313      * should start with or a RegExp to test against the field
10314      * @param {Boolean} anyMatch True to match any part not just the beginning
10315      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10316      */
10317     query : function(property, value, anyMatch){
10318         var fn = this.createFilterFn(property, value, anyMatch);
10319         return fn ? this.queryBy(fn) : this.data.clone();
10320     },
10321
10322     /**
10323      * Query by a function. The specified function will be called with each
10324      * record in this data source. If the function returns true the record is included
10325      * in the results.
10326      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10327      * @param {Object} scope (optional) The scope of the function (defaults to this)
10328       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10329      **/
10330     queryBy : function(fn, scope){
10331         var data = this.snapshot || this.data;
10332         return data.filterBy(fn, scope||this);
10333     },
10334
10335     /**
10336      * Collects unique values for a particular dataIndex from this store.
10337      * @param {String} dataIndex The property to collect
10338      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10339      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10340      * @return {Array} An array of the unique values
10341      **/
10342     collect : function(dataIndex, allowNull, bypassFilter){
10343         var d = (bypassFilter === true && this.snapshot) ?
10344                 this.snapshot.items : this.data.items;
10345         var v, sv, r = [], l = {};
10346         for(var i = 0, len = d.length; i < len; i++){
10347             v = d[i].data[dataIndex];
10348             sv = String(v);
10349             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10350                 l[sv] = true;
10351                 r[r.length] = v;
10352             }
10353         }
10354         return r;
10355     },
10356
10357     /**
10358      * Revert to a view of the Record cache with no filtering applied.
10359      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10360      */
10361     clearFilter : function(suppressEvent){
10362         if(this.snapshot && this.snapshot != this.data){
10363             this.data = this.snapshot;
10364             delete this.snapshot;
10365             if(suppressEvent !== true){
10366                 this.fireEvent("datachanged", this);
10367             }
10368         }
10369     },
10370
10371     // private
10372     afterEdit : function(record){
10373         if(this.modified.indexOf(record) == -1){
10374             this.modified.push(record);
10375         }
10376         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10377     },
10378     
10379     // private
10380     afterReject : function(record){
10381         this.modified.remove(record);
10382         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10383     },
10384
10385     // private
10386     afterCommit : function(record){
10387         this.modified.remove(record);
10388         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10389     },
10390
10391     /**
10392      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10393      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10394      */
10395     commitChanges : function(){
10396         var m = this.modified.slice(0);
10397         this.modified = [];
10398         for(var i = 0, len = m.length; i < len; i++){
10399             m[i].commit();
10400         }
10401     },
10402
10403     /**
10404      * Cancel outstanding changes on all changed records.
10405      */
10406     rejectChanges : function(){
10407         var m = this.modified.slice(0);
10408         this.modified = [];
10409         for(var i = 0, len = m.length; i < len; i++){
10410             m[i].reject();
10411         }
10412     },
10413
10414     onMetaChange : function(meta, rtype, o){
10415         this.recordType = rtype;
10416         this.fields = rtype.prototype.fields;
10417         delete this.snapshot;
10418         this.sortInfo = meta.sortInfo || this.sortInfo;
10419         this.modified = [];
10420         this.fireEvent('metachange', this, this.reader.meta);
10421     },
10422     
10423     moveIndex : function(data, type)
10424     {
10425         var index = this.indexOf(data);
10426         
10427         var newIndex = index + type;
10428         
10429         this.remove(data);
10430         
10431         this.insert(newIndex, data);
10432         
10433     }
10434 });/*
10435  * Based on:
10436  * Ext JS Library 1.1.1
10437  * Copyright(c) 2006-2007, Ext JS, LLC.
10438  *
10439  * Originally Released Under LGPL - original licence link has changed is not relivant.
10440  *
10441  * Fork - LGPL
10442  * <script type="text/javascript">
10443  */
10444
10445 /**
10446  * @class Roo.data.SimpleStore
10447  * @extends Roo.data.Store
10448  * Small helper class to make creating Stores from Array data easier.
10449  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10450  * @cfg {Array} fields An array of field definition objects, or field name strings.
10451  * @cfg {Array} data The multi-dimensional array of data
10452  * @constructor
10453  * @param {Object} config
10454  */
10455 Roo.data.SimpleStore = function(config){
10456     Roo.data.SimpleStore.superclass.constructor.call(this, {
10457         isLocal : true,
10458         reader: new Roo.data.ArrayReader({
10459                 id: config.id
10460             },
10461             Roo.data.Record.create(config.fields)
10462         ),
10463         proxy : new Roo.data.MemoryProxy(config.data)
10464     });
10465     this.load();
10466 };
10467 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10468  * Based on:
10469  * Ext JS Library 1.1.1
10470  * Copyright(c) 2006-2007, Ext JS, LLC.
10471  *
10472  * Originally Released Under LGPL - original licence link has changed is not relivant.
10473  *
10474  * Fork - LGPL
10475  * <script type="text/javascript">
10476  */
10477
10478 /**
10479 /**
10480  * @extends Roo.data.Store
10481  * @class Roo.data.JsonStore
10482  * Small helper class to make creating Stores for JSON data easier. <br/>
10483 <pre><code>
10484 var store = new Roo.data.JsonStore({
10485     url: 'get-images.php',
10486     root: 'images',
10487     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10488 });
10489 </code></pre>
10490  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10491  * JsonReader and HttpProxy (unless inline data is provided).</b>
10492  * @cfg {Array} fields An array of field definition objects, or field name strings.
10493  * @constructor
10494  * @param {Object} config
10495  */
10496 Roo.data.JsonStore = function(c){
10497     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10498         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10499         reader: new Roo.data.JsonReader(c, c.fields)
10500     }));
10501 };
10502 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10503  * Based on:
10504  * Ext JS Library 1.1.1
10505  * Copyright(c) 2006-2007, Ext JS, LLC.
10506  *
10507  * Originally Released Under LGPL - original licence link has changed is not relivant.
10508  *
10509  * Fork - LGPL
10510  * <script type="text/javascript">
10511  */
10512
10513  
10514 Roo.data.Field = function(config){
10515     if(typeof config == "string"){
10516         config = {name: config};
10517     }
10518     Roo.apply(this, config);
10519     
10520     if(!this.type){
10521         this.type = "auto";
10522     }
10523     
10524     var st = Roo.data.SortTypes;
10525     // named sortTypes are supported, here we look them up
10526     if(typeof this.sortType == "string"){
10527         this.sortType = st[this.sortType];
10528     }
10529     
10530     // set default sortType for strings and dates
10531     if(!this.sortType){
10532         switch(this.type){
10533             case "string":
10534                 this.sortType = st.asUCString;
10535                 break;
10536             case "date":
10537                 this.sortType = st.asDate;
10538                 break;
10539             default:
10540                 this.sortType = st.none;
10541         }
10542     }
10543
10544     // define once
10545     var stripRe = /[\$,%]/g;
10546
10547     // prebuilt conversion function for this field, instead of
10548     // switching every time we're reading a value
10549     if(!this.convert){
10550         var cv, dateFormat = this.dateFormat;
10551         switch(this.type){
10552             case "":
10553             case "auto":
10554             case undefined:
10555                 cv = function(v){ return v; };
10556                 break;
10557             case "string":
10558                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10559                 break;
10560             case "int":
10561                 cv = function(v){
10562                     return v !== undefined && v !== null && v !== '' ?
10563                            parseInt(String(v).replace(stripRe, ""), 10) : '';
10564                     };
10565                 break;
10566             case "float":
10567                 cv = function(v){
10568                     return v !== undefined && v !== null && v !== '' ?
10569                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
10570                     };
10571                 break;
10572             case "bool":
10573             case "boolean":
10574                 cv = function(v){ return v === true || v === "true" || v == 1; };
10575                 break;
10576             case "date":
10577                 cv = function(v){
10578                     if(!v){
10579                         return '';
10580                     }
10581                     if(v instanceof Date){
10582                         return v;
10583                     }
10584                     if(dateFormat){
10585                         if(dateFormat == "timestamp"){
10586                             return new Date(v*1000);
10587                         }
10588                         return Date.parseDate(v, dateFormat);
10589                     }
10590                     var parsed = Date.parse(v);
10591                     return parsed ? new Date(parsed) : null;
10592                 };
10593              break;
10594             
10595         }
10596         this.convert = cv;
10597     }
10598 };
10599
10600 Roo.data.Field.prototype = {
10601     dateFormat: null,
10602     defaultValue: "",
10603     mapping: null,
10604     sortType : null,
10605     sortDir : "ASC"
10606 };/*
10607  * Based on:
10608  * Ext JS Library 1.1.1
10609  * Copyright(c) 2006-2007, Ext JS, LLC.
10610  *
10611  * Originally Released Under LGPL - original licence link has changed is not relivant.
10612  *
10613  * Fork - LGPL
10614  * <script type="text/javascript">
10615  */
10616  
10617 // Base class for reading structured data from a data source.  This class is intended to be
10618 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10619
10620 /**
10621  * @class Roo.data.DataReader
10622  * Base class for reading structured data from a data source.  This class is intended to be
10623  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10624  */
10625
10626 Roo.data.DataReader = function(meta, recordType){
10627     
10628     this.meta = meta;
10629     
10630     this.recordType = recordType instanceof Array ? 
10631         Roo.data.Record.create(recordType) : recordType;
10632 };
10633
10634 Roo.data.DataReader.prototype = {
10635      /**
10636      * Create an empty record
10637      * @param {Object} data (optional) - overlay some values
10638      * @return {Roo.data.Record} record created.
10639      */
10640     newRow :  function(d) {
10641         var da =  {};
10642         this.recordType.prototype.fields.each(function(c) {
10643             switch( c.type) {
10644                 case 'int' : da[c.name] = 0; break;
10645                 case 'date' : da[c.name] = new Date(); break;
10646                 case 'float' : da[c.name] = 0.0; break;
10647                 case 'boolean' : da[c.name] = false; break;
10648                 default : da[c.name] = ""; break;
10649             }
10650             
10651         });
10652         return new this.recordType(Roo.apply(da, d));
10653     }
10654     
10655 };/*
10656  * Based on:
10657  * Ext JS Library 1.1.1
10658  * Copyright(c) 2006-2007, Ext JS, LLC.
10659  *
10660  * Originally Released Under LGPL - original licence link has changed is not relivant.
10661  *
10662  * Fork - LGPL
10663  * <script type="text/javascript">
10664  */
10665
10666 /**
10667  * @class Roo.data.DataProxy
10668  * @extends Roo.data.Observable
10669  * This class is an abstract base class for implementations which provide retrieval of
10670  * unformatted data objects.<br>
10671  * <p>
10672  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10673  * (of the appropriate type which knows how to parse the data object) to provide a block of
10674  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10675  * <p>
10676  * Custom implementations must implement the load method as described in
10677  * {@link Roo.data.HttpProxy#load}.
10678  */
10679 Roo.data.DataProxy = function(){
10680     this.addEvents({
10681         /**
10682          * @event beforeload
10683          * Fires before a network request is made to retrieve a data object.
10684          * @param {Object} This DataProxy object.
10685          * @param {Object} params The params parameter to the load function.
10686          */
10687         beforeload : true,
10688         /**
10689          * @event load
10690          * Fires before the load method's callback is called.
10691          * @param {Object} This DataProxy object.
10692          * @param {Object} o The data object.
10693          * @param {Object} arg The callback argument object passed to the load function.
10694          */
10695         load : true,
10696         /**
10697          * @event loadexception
10698          * Fires if an Exception occurs during data retrieval.
10699          * @param {Object} This DataProxy object.
10700          * @param {Object} o The data object.
10701          * @param {Object} arg The callback argument object passed to the load function.
10702          * @param {Object} e The Exception.
10703          */
10704         loadexception : true
10705     });
10706     Roo.data.DataProxy.superclass.constructor.call(this);
10707 };
10708
10709 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10710
10711     /**
10712      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10713      */
10714 /*
10715  * Based on:
10716  * Ext JS Library 1.1.1
10717  * Copyright(c) 2006-2007, Ext JS, LLC.
10718  *
10719  * Originally Released Under LGPL - original licence link has changed is not relivant.
10720  *
10721  * Fork - LGPL
10722  * <script type="text/javascript">
10723  */
10724 /**
10725  * @class Roo.data.MemoryProxy
10726  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10727  * to the Reader when its load method is called.
10728  * @constructor
10729  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10730  */
10731 Roo.data.MemoryProxy = function(data){
10732     if (data.data) {
10733         data = data.data;
10734     }
10735     Roo.data.MemoryProxy.superclass.constructor.call(this);
10736     this.data = data;
10737 };
10738
10739 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10740     /**
10741      * Load data from the requested source (in this case an in-memory
10742      * data object passed to the constructor), read the data object into
10743      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10744      * process that block using the passed callback.
10745      * @param {Object} params This parameter is not used by the MemoryProxy class.
10746      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10747      * object into a block of Roo.data.Records.
10748      * @param {Function} callback The function into which to pass the block of Roo.data.records.
10749      * The function must be passed <ul>
10750      * <li>The Record block object</li>
10751      * <li>The "arg" argument from the load function</li>
10752      * <li>A boolean success indicator</li>
10753      * </ul>
10754      * @param {Object} scope The scope in which to call the callback
10755      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10756      */
10757     load : function(params, reader, callback, scope, arg){
10758         params = params || {};
10759         var result;
10760         try {
10761             result = reader.readRecords(this.data);
10762         }catch(e){
10763             this.fireEvent("loadexception", this, arg, null, e);
10764             callback.call(scope, null, arg, false);
10765             return;
10766         }
10767         callback.call(scope, result, arg, true);
10768     },
10769     
10770     // private
10771     update : function(params, records){
10772         
10773     }
10774 });/*
10775  * Based on:
10776  * Ext JS Library 1.1.1
10777  * Copyright(c) 2006-2007, Ext JS, LLC.
10778  *
10779  * Originally Released Under LGPL - original licence link has changed is not relivant.
10780  *
10781  * Fork - LGPL
10782  * <script type="text/javascript">
10783  */
10784 /**
10785  * @class Roo.data.HttpProxy
10786  * @extends Roo.data.DataProxy
10787  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10788  * configured to reference a certain URL.<br><br>
10789  * <p>
10790  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10791  * from which the running page was served.<br><br>
10792  * <p>
10793  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10794  * <p>
10795  * Be aware that to enable the browser to parse an XML document, the server must set
10796  * the Content-Type header in the HTTP response to "text/xml".
10797  * @constructor
10798  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10799  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
10800  * will be used to make the request.
10801  */
10802 Roo.data.HttpProxy = function(conn){
10803     Roo.data.HttpProxy.superclass.constructor.call(this);
10804     // is conn a conn config or a real conn?
10805     this.conn = conn;
10806     this.useAjax = !conn || !conn.events;
10807   
10808 };
10809
10810 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10811     // thse are take from connection...
10812     
10813     /**
10814      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10815      */
10816     /**
10817      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10818      * extra parameters to each request made by this object. (defaults to undefined)
10819      */
10820     /**
10821      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10822      *  to each request made by this object. (defaults to undefined)
10823      */
10824     /**
10825      * @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)
10826      */
10827     /**
10828      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10829      */
10830      /**
10831      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10832      * @type Boolean
10833      */
10834   
10835
10836     /**
10837      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10838      * @type Boolean
10839      */
10840     /**
10841      * Return the {@link Roo.data.Connection} object being used by this Proxy.
10842      * @return {Connection} The Connection object. This object may be used to subscribe to events on
10843      * a finer-grained basis than the DataProxy events.
10844      */
10845     getConnection : function(){
10846         return this.useAjax ? Roo.Ajax : this.conn;
10847     },
10848
10849     /**
10850      * Load data from the configured {@link Roo.data.Connection}, read the data object into
10851      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10852      * process that block using the passed callback.
10853      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10854      * for the request to the remote server.
10855      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10856      * object into a block of Roo.data.Records.
10857      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10858      * The function must be passed <ul>
10859      * <li>The Record block object</li>
10860      * <li>The "arg" argument from the load function</li>
10861      * <li>A boolean success indicator</li>
10862      * </ul>
10863      * @param {Object} scope The scope in which to call the callback
10864      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10865      */
10866     load : function(params, reader, callback, scope, arg){
10867         if(this.fireEvent("beforeload", this, params) !== false){
10868             var  o = {
10869                 params : params || {},
10870                 request: {
10871                     callback : callback,
10872                     scope : scope,
10873                     arg : arg
10874                 },
10875                 reader: reader,
10876                 callback : this.loadResponse,
10877                 scope: this
10878             };
10879             if(this.useAjax){
10880                 Roo.applyIf(o, this.conn);
10881                 if(this.activeRequest){
10882                     Roo.Ajax.abort(this.activeRequest);
10883                 }
10884                 this.activeRequest = Roo.Ajax.request(o);
10885             }else{
10886                 this.conn.request(o);
10887             }
10888         }else{
10889             callback.call(scope||this, null, arg, false);
10890         }
10891     },
10892
10893     // private
10894     loadResponse : function(o, success, response){
10895         delete this.activeRequest;
10896         if(!success){
10897             this.fireEvent("loadexception", this, o, response);
10898             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10899             return;
10900         }
10901         var result;
10902         try {
10903             result = o.reader.read(response);
10904         }catch(e){
10905             this.fireEvent("loadexception", this, o, response, e);
10906             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10907             return;
10908         }
10909         
10910         this.fireEvent("load", this, o, o.request.arg);
10911         o.request.callback.call(o.request.scope, result, o.request.arg, true);
10912     },
10913
10914     // private
10915     update : function(dataSet){
10916
10917     },
10918
10919     // private
10920     updateResponse : function(dataSet){
10921
10922     }
10923 });/*
10924  * Based on:
10925  * Ext JS Library 1.1.1
10926  * Copyright(c) 2006-2007, Ext JS, LLC.
10927  *
10928  * Originally Released Under LGPL - original licence link has changed is not relivant.
10929  *
10930  * Fork - LGPL
10931  * <script type="text/javascript">
10932  */
10933
10934 /**
10935  * @class Roo.data.ScriptTagProxy
10936  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10937  * other than the originating domain of the running page.<br><br>
10938  * <p>
10939  * <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
10940  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10941  * <p>
10942  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10943  * source code that is used as the source inside a &lt;script> tag.<br><br>
10944  * <p>
10945  * In order for the browser to process the returned data, the server must wrap the data object
10946  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10947  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10948  * depending on whether the callback name was passed:
10949  * <p>
10950  * <pre><code>
10951 boolean scriptTag = false;
10952 String cb = request.getParameter("callback");
10953 if (cb != null) {
10954     scriptTag = true;
10955     response.setContentType("text/javascript");
10956 } else {
10957     response.setContentType("application/x-json");
10958 }
10959 Writer out = response.getWriter();
10960 if (scriptTag) {
10961     out.write(cb + "(");
10962 }
10963 out.print(dataBlock.toJsonString());
10964 if (scriptTag) {
10965     out.write(");");
10966 }
10967 </pre></code>
10968  *
10969  * @constructor
10970  * @param {Object} config A configuration object.
10971  */
10972 Roo.data.ScriptTagProxy = function(config){
10973     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10974     Roo.apply(this, config);
10975     this.head = document.getElementsByTagName("head")[0];
10976 };
10977
10978 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10979
10980 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10981     /**
10982      * @cfg {String} url The URL from which to request the data object.
10983      */
10984     /**
10985      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
10986      */
10987     timeout : 30000,
10988     /**
10989      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
10990      * the server the name of the callback function set up by the load call to process the returned data object.
10991      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
10992      * javascript output which calls this named function passing the data object as its only parameter.
10993      */
10994     callbackParam : "callback",
10995     /**
10996      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
10997      * name to the request.
10998      */
10999     nocache : true,
11000
11001     /**
11002      * Load data from the configured URL, read the data object into
11003      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11004      * process that block using the passed callback.
11005      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11006      * for the request to the remote server.
11007      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11008      * object into a block of Roo.data.Records.
11009      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11010      * The function must be passed <ul>
11011      * <li>The Record block object</li>
11012      * <li>The "arg" argument from the load function</li>
11013      * <li>A boolean success indicator</li>
11014      * </ul>
11015      * @param {Object} scope The scope in which to call the callback
11016      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11017      */
11018     load : function(params, reader, callback, scope, arg){
11019         if(this.fireEvent("beforeload", this, params) !== false){
11020
11021             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11022
11023             var url = this.url;
11024             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11025             if(this.nocache){
11026                 url += "&_dc=" + (new Date().getTime());
11027             }
11028             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11029             var trans = {
11030                 id : transId,
11031                 cb : "stcCallback"+transId,
11032                 scriptId : "stcScript"+transId,
11033                 params : params,
11034                 arg : arg,
11035                 url : url,
11036                 callback : callback,
11037                 scope : scope,
11038                 reader : reader
11039             };
11040             var conn = this;
11041
11042             window[trans.cb] = function(o){
11043                 conn.handleResponse(o, trans);
11044             };
11045
11046             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11047
11048             if(this.autoAbort !== false){
11049                 this.abort();
11050             }
11051
11052             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11053
11054             var script = document.createElement("script");
11055             script.setAttribute("src", url);
11056             script.setAttribute("type", "text/javascript");
11057             script.setAttribute("id", trans.scriptId);
11058             this.head.appendChild(script);
11059
11060             this.trans = trans;
11061         }else{
11062             callback.call(scope||this, null, arg, false);
11063         }
11064     },
11065
11066     // private
11067     isLoading : function(){
11068         return this.trans ? true : false;
11069     },
11070
11071     /**
11072      * Abort the current server request.
11073      */
11074     abort : function(){
11075         if(this.isLoading()){
11076             this.destroyTrans(this.trans);
11077         }
11078     },
11079
11080     // private
11081     destroyTrans : function(trans, isLoaded){
11082         this.head.removeChild(document.getElementById(trans.scriptId));
11083         clearTimeout(trans.timeoutId);
11084         if(isLoaded){
11085             window[trans.cb] = undefined;
11086             try{
11087                 delete window[trans.cb];
11088             }catch(e){}
11089         }else{
11090             // if hasn't been loaded, wait for load to remove it to prevent script error
11091             window[trans.cb] = function(){
11092                 window[trans.cb] = undefined;
11093                 try{
11094                     delete window[trans.cb];
11095                 }catch(e){}
11096             };
11097         }
11098     },
11099
11100     // private
11101     handleResponse : function(o, trans){
11102         this.trans = false;
11103         this.destroyTrans(trans, true);
11104         var result;
11105         try {
11106             result = trans.reader.readRecords(o);
11107         }catch(e){
11108             this.fireEvent("loadexception", this, o, trans.arg, e);
11109             trans.callback.call(trans.scope||window, null, trans.arg, false);
11110             return;
11111         }
11112         this.fireEvent("load", this, o, trans.arg);
11113         trans.callback.call(trans.scope||window, result, trans.arg, true);
11114     },
11115
11116     // private
11117     handleFailure : function(trans){
11118         this.trans = false;
11119         this.destroyTrans(trans, false);
11120         this.fireEvent("loadexception", this, null, trans.arg);
11121         trans.callback.call(trans.scope||window, null, trans.arg, false);
11122     }
11123 });/*
11124  * Based on:
11125  * Ext JS Library 1.1.1
11126  * Copyright(c) 2006-2007, Ext JS, LLC.
11127  *
11128  * Originally Released Under LGPL - original licence link has changed is not relivant.
11129  *
11130  * Fork - LGPL
11131  * <script type="text/javascript">
11132  */
11133
11134 /**
11135  * @class Roo.data.JsonReader
11136  * @extends Roo.data.DataReader
11137  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11138  * based on mappings in a provided Roo.data.Record constructor.
11139  * 
11140  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11141  * in the reply previously. 
11142  * 
11143  * <p>
11144  * Example code:
11145  * <pre><code>
11146 var RecordDef = Roo.data.Record.create([
11147     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
11148     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
11149 ]);
11150 var myReader = new Roo.data.JsonReader({
11151     totalProperty: "results",    // The property which contains the total dataset size (optional)
11152     root: "rows",                // The property which contains an Array of row objects
11153     id: "id"                     // The property within each row object that provides an ID for the record (optional)
11154 }, RecordDef);
11155 </code></pre>
11156  * <p>
11157  * This would consume a JSON file like this:
11158  * <pre><code>
11159 { 'results': 2, 'rows': [
11160     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11161     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11162 }
11163 </code></pre>
11164  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11165  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11166  * paged from the remote server.
11167  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11168  * @cfg {String} root name of the property which contains the Array of row objects.
11169  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11170  * @cfg {Array} fields Array of field definition objects
11171  * @constructor
11172  * Create a new JsonReader
11173  * @param {Object} meta Metadata configuration options
11174  * @param {Object} recordType Either an Array of field definition objects,
11175  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11176  */
11177 Roo.data.JsonReader = function(meta, recordType){
11178     
11179     meta = meta || {};
11180     // set some defaults:
11181     Roo.applyIf(meta, {
11182         totalProperty: 'total',
11183         successProperty : 'success',
11184         root : 'data',
11185         id : 'id'
11186     });
11187     
11188     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11189 };
11190 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11191     
11192     /**
11193      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
11194      * Used by Store query builder to append _requestMeta to params.
11195      * 
11196      */
11197     metaFromRemote : false,
11198     /**
11199      * This method is only used by a DataProxy which has retrieved data from a remote server.
11200      * @param {Object} response The XHR object which contains the JSON data in its responseText.
11201      * @return {Object} data A data block which is used by an Roo.data.Store object as
11202      * a cache of Roo.data.Records.
11203      */
11204     read : function(response){
11205         var json = response.responseText;
11206        
11207         var o = /* eval:var:o */ eval("("+json+")");
11208         if(!o) {
11209             throw {message: "JsonReader.read: Json object not found"};
11210         }
11211         
11212         if(o.metaData){
11213             
11214             delete this.ef;
11215             this.metaFromRemote = true;
11216             this.meta = o.metaData;
11217             this.recordType = Roo.data.Record.create(o.metaData.fields);
11218             this.onMetaChange(this.meta, this.recordType, o);
11219         }
11220         return this.readRecords(o);
11221     },
11222
11223     // private function a store will implement
11224     onMetaChange : function(meta, recordType, o){
11225
11226     },
11227
11228     /**
11229          * @ignore
11230          */
11231     simpleAccess: function(obj, subsc) {
11232         return obj[subsc];
11233     },
11234
11235         /**
11236          * @ignore
11237          */
11238     getJsonAccessor: function(){
11239         var re = /[\[\.]/;
11240         return function(expr) {
11241             try {
11242                 return(re.test(expr))
11243                     ? new Function("obj", "return obj." + expr)
11244                     : function(obj){
11245                         return obj[expr];
11246                     };
11247             } catch(e){}
11248             return Roo.emptyFn;
11249         };
11250     }(),
11251
11252     /**
11253      * Create a data block containing Roo.data.Records from an XML document.
11254      * @param {Object} o An object which contains an Array of row objects in the property specified
11255      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11256      * which contains the total size of the dataset.
11257      * @return {Object} data A data block which is used by an Roo.data.Store object as
11258      * a cache of Roo.data.Records.
11259      */
11260     readRecords : function(o){
11261         /**
11262          * After any data loads, the raw JSON data is available for further custom processing.
11263          * @type Object
11264          */
11265         this.o = o;
11266         var s = this.meta, Record = this.recordType,
11267             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11268
11269 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
11270         if (!this.ef) {
11271             if(s.totalProperty) {
11272                     this.getTotal = this.getJsonAccessor(s.totalProperty);
11273                 }
11274                 if(s.successProperty) {
11275                     this.getSuccess = this.getJsonAccessor(s.successProperty);
11276                 }
11277                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11278                 if (s.id) {
11279                         var g = this.getJsonAccessor(s.id);
11280                         this.getId = function(rec) {
11281                                 var r = g(rec);  
11282                                 return (r === undefined || r === "") ? null : r;
11283                         };
11284                 } else {
11285                         this.getId = function(){return null;};
11286                 }
11287             this.ef = [];
11288             for(var jj = 0; jj < fl; jj++){
11289                 f = fi[jj];
11290                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11291                 this.ef[jj] = this.getJsonAccessor(map);
11292             }
11293         }
11294
11295         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11296         if(s.totalProperty){
11297             var vt = parseInt(this.getTotal(o), 10);
11298             if(!isNaN(vt)){
11299                 totalRecords = vt;
11300             }
11301         }
11302         if(s.successProperty){
11303             var vs = this.getSuccess(o);
11304             if(vs === false || vs === 'false'){
11305                 success = false;
11306             }
11307         }
11308         var records = [];
11309         for(var i = 0; i < c; i++){
11310                 var n = root[i];
11311             var values = {};
11312             var id = this.getId(n);
11313             for(var j = 0; j < fl; j++){
11314                 f = fi[j];
11315             var v = this.ef[j](n);
11316             if (!f.convert) {
11317                 Roo.log('missing convert for ' + f.name);
11318                 Roo.log(f);
11319                 continue;
11320             }
11321             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11322             }
11323             var record = new Record(values, id);
11324             record.json = n;
11325             records[i] = record;
11326         }
11327         return {
11328             raw : o,
11329             success : success,
11330             records : records,
11331             totalRecords : totalRecords
11332         };
11333     }
11334 });/*
11335  * Based on:
11336  * Ext JS Library 1.1.1
11337  * Copyright(c) 2006-2007, Ext JS, LLC.
11338  *
11339  * Originally Released Under LGPL - original licence link has changed is not relivant.
11340  *
11341  * Fork - LGPL
11342  * <script type="text/javascript">
11343  */
11344
11345 /**
11346  * @class Roo.data.ArrayReader
11347  * @extends Roo.data.DataReader
11348  * Data reader class to create an Array of Roo.data.Record objects from an Array.
11349  * Each element of that Array represents a row of data fields. The
11350  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11351  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11352  * <p>
11353  * Example code:.
11354  * <pre><code>
11355 var RecordDef = Roo.data.Record.create([
11356     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
11357     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
11358 ]);
11359 var myReader = new Roo.data.ArrayReader({
11360     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
11361 }, RecordDef);
11362 </code></pre>
11363  * <p>
11364  * This would consume an Array like this:
11365  * <pre><code>
11366 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11367   </code></pre>
11368  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11369  * @constructor
11370  * Create a new JsonReader
11371  * @param {Object} meta Metadata configuration options.
11372  * @param {Object} recordType Either an Array of field definition objects
11373  * as specified to {@link Roo.data.Record#create},
11374  * or an {@link Roo.data.Record} object
11375  * created using {@link Roo.data.Record#create}.
11376  */
11377 Roo.data.ArrayReader = function(meta, recordType){
11378     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11379 };
11380
11381 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11382     /**
11383      * Create a data block containing Roo.data.Records from an XML document.
11384      * @param {Object} o An Array of row objects which represents the dataset.
11385      * @return {Object} data A data block which is used by an Roo.data.Store object as
11386      * a cache of Roo.data.Records.
11387      */
11388     readRecords : function(o){
11389         var sid = this.meta ? this.meta.id : null;
11390         var recordType = this.recordType, fields = recordType.prototype.fields;
11391         var records = [];
11392         var root = o;
11393             for(var i = 0; i < root.length; i++){
11394                     var n = root[i];
11395                 var values = {};
11396                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11397                 for(var j = 0, jlen = fields.length; j < jlen; j++){
11398                 var f = fields.items[j];
11399                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11400                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11401                 v = f.convert(v);
11402                 values[f.name] = v;
11403             }
11404                 var record = new recordType(values, id);
11405                 record.json = n;
11406                 records[records.length] = record;
11407             }
11408             return {
11409                 records : records,
11410                 totalRecords : records.length
11411             };
11412     }
11413 });/*
11414  * - LGPL
11415  * * 
11416  */
11417
11418 /**
11419  * @class Roo.bootstrap.ComboBox
11420  * @extends Roo.bootstrap.TriggerField
11421  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11422  * @cfg {Boolean} append (true|false) default false
11423  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11424  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11425  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11426  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11427  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11428  * @cfg {Boolean} animate default true
11429  * @cfg {Boolean} emptyResultText only for touch device
11430  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11431  * @constructor
11432  * Create a new ComboBox.
11433  * @param {Object} config Configuration options
11434  */
11435 Roo.bootstrap.ComboBox = function(config){
11436     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11437     this.addEvents({
11438         /**
11439          * @event expand
11440          * Fires when the dropdown list is expanded
11441              * @param {Roo.bootstrap.ComboBox} combo This combo box
11442              */
11443         'expand' : true,
11444         /**
11445          * @event collapse
11446          * Fires when the dropdown list is collapsed
11447              * @param {Roo.bootstrap.ComboBox} combo This combo box
11448              */
11449         'collapse' : true,
11450         /**
11451          * @event beforeselect
11452          * Fires before a list item is selected. Return false to cancel the selection.
11453              * @param {Roo.bootstrap.ComboBox} combo This combo box
11454              * @param {Roo.data.Record} record The data record returned from the underlying store
11455              * @param {Number} index The index of the selected item in the dropdown list
11456              */
11457         'beforeselect' : true,
11458         /**
11459          * @event select
11460          * Fires when a list item is selected
11461              * @param {Roo.bootstrap.ComboBox} combo This combo box
11462              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11463              * @param {Number} index The index of the selected item in the dropdown list
11464              */
11465         'select' : true,
11466         /**
11467          * @event beforequery
11468          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11469          * The event object passed has these properties:
11470              * @param {Roo.bootstrap.ComboBox} combo This combo box
11471              * @param {String} query The query
11472              * @param {Boolean} forceAll true to force "all" query
11473              * @param {Boolean} cancel true to cancel the query
11474              * @param {Object} e The query event object
11475              */
11476         'beforequery': true,
11477          /**
11478          * @event add
11479          * Fires when the 'add' icon is pressed (add a listener to enable add button)
11480              * @param {Roo.bootstrap.ComboBox} combo This combo box
11481              */
11482         'add' : true,
11483         /**
11484          * @event edit
11485          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11486              * @param {Roo.bootstrap.ComboBox} combo This combo box
11487              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11488              */
11489         'edit' : true,
11490         /**
11491          * @event remove
11492          * Fires when the remove value from the combobox array
11493              * @param {Roo.bootstrap.ComboBox} combo This combo box
11494              */
11495         'remove' : true,
11496         /**
11497          * @event specialfilter
11498          * Fires when specialfilter
11499             * @param {Roo.bootstrap.ComboBox} combo This combo box
11500             */
11501         'specialfilter' : true,
11502         /**
11503          * @event tick
11504          * Fires when tick the element
11505             * @param {Roo.bootstrap.ComboBox} combo This combo box
11506             */
11507         'tick' : true,
11508         /**
11509          * @event touchviewdisplay
11510          * Fires when touch view require special display (default is using displayField)
11511             * @param {Roo.bootstrap.ComboBox} combo This combo box
11512             * @param {Object} cfg set html .
11513             */
11514         'touchviewdisplay' : true
11515         
11516     });
11517     
11518     this.item = [];
11519     this.tickItems = [];
11520     
11521     this.selectedIndex = -1;
11522     if(this.mode == 'local'){
11523         if(config.queryDelay === undefined){
11524             this.queryDelay = 10;
11525         }
11526         if(config.minChars === undefined){
11527             this.minChars = 0;
11528         }
11529     }
11530 };
11531
11532 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11533      
11534     /**
11535      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11536      * rendering into an Roo.Editor, defaults to false)
11537      */
11538     /**
11539      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11540      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11541      */
11542     /**
11543      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11544      */
11545     /**
11546      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11547      * the dropdown list (defaults to undefined, with no header element)
11548      */
11549
11550      /**
11551      * @cfg {String/Roo.Template} tpl The template to use to render the output
11552      */
11553      
11554      /**
11555      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11556      */
11557     listWidth: undefined,
11558     /**
11559      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11560      * mode = 'remote' or 'text' if mode = 'local')
11561      */
11562     displayField: undefined,
11563     
11564     /**
11565      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11566      * mode = 'remote' or 'value' if mode = 'local'). 
11567      * Note: use of a valueField requires the user make a selection
11568      * in order for a value to be mapped.
11569      */
11570     valueField: undefined,
11571     
11572     
11573     /**
11574      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11575      * field's data value (defaults to the underlying DOM element's name)
11576      */
11577     hiddenName: undefined,
11578     /**
11579      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11580      */
11581     listClass: '',
11582     /**
11583      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11584      */
11585     selectedClass: 'active',
11586     
11587     /**
11588      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11589      */
11590     shadow:'sides',
11591     /**
11592      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11593      * anchor positions (defaults to 'tl-bl')
11594      */
11595     listAlign: 'tl-bl?',
11596     /**
11597      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11598      */
11599     maxHeight: 300,
11600     /**
11601      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
11602      * query specified by the allQuery config option (defaults to 'query')
11603      */
11604     triggerAction: 'query',
11605     /**
11606      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11607      * (defaults to 4, does not apply if editable = false)
11608      */
11609     minChars : 4,
11610     /**
11611      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11612      * delay (typeAheadDelay) if it matches a known value (defaults to false)
11613      */
11614     typeAhead: false,
11615     /**
11616      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11617      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11618      */
11619     queryDelay: 500,
11620     /**
11621      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11622      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
11623      */
11624     pageSize: 0,
11625     /**
11626      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
11627      * when editable = true (defaults to false)
11628      */
11629     selectOnFocus:false,
11630     /**
11631      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11632      */
11633     queryParam: 'query',
11634     /**
11635      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
11636      * when mode = 'remote' (defaults to 'Loading...')
11637      */
11638     loadingText: 'Loading...',
11639     /**
11640      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11641      */
11642     resizable: false,
11643     /**
11644      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11645      */
11646     handleHeight : 8,
11647     /**
11648      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11649      * traditional select (defaults to true)
11650      */
11651     editable: true,
11652     /**
11653      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11654      */
11655     allQuery: '',
11656     /**
11657      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11658      */
11659     mode: 'remote',
11660     /**
11661      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11662      * listWidth has a higher value)
11663      */
11664     minListWidth : 70,
11665     /**
11666      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11667      * allow the user to set arbitrary text into the field (defaults to false)
11668      */
11669     forceSelection:false,
11670     /**
11671      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11672      * if typeAhead = true (defaults to 250)
11673      */
11674     typeAheadDelay : 250,
11675     /**
11676      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11677      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11678      */
11679     valueNotFoundText : undefined,
11680     /**
11681      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11682      */
11683     blockFocus : false,
11684     
11685     /**
11686      * @cfg {Boolean} disableClear Disable showing of clear button.
11687      */
11688     disableClear : false,
11689     /**
11690      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
11691      */
11692     alwaysQuery : false,
11693     
11694     /**
11695      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
11696      */
11697     multiple : false,
11698     
11699     /**
11700      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11701      */
11702     invalidClass : "has-warning",
11703     
11704     /**
11705      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11706      */
11707     validClass : "has-success",
11708     
11709     /**
11710      * @cfg {Boolean} specialFilter (true|false) special filter default false
11711      */
11712     specialFilter : false,
11713     
11714     /**
11715      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
11716      */
11717     mobileTouchView : true,
11718     
11719     //private
11720     addicon : false,
11721     editicon: false,
11722     
11723     page: 0,
11724     hasQuery: false,
11725     append: false,
11726     loadNext: false,
11727     autoFocus : true,
11728     tickable : false,
11729     btnPosition : 'right',
11730     triggerList : true,
11731     showToggleBtn : true,
11732     animate : true,
11733     emptyResultText: 'Empty',
11734     triggerText : 'Select',
11735     
11736     // element that contains real text value.. (when hidden is used..)
11737     
11738     getAutoCreate : function()
11739     {
11740         var cfg = false;
11741         
11742         /*
11743          * Touch Devices
11744          */
11745         
11746         if(Roo.isTouch && this.mobileTouchView){
11747             cfg = this.getAutoCreateTouchView();
11748             return cfg;;
11749         }
11750         
11751         /*
11752          *  Normal ComboBox
11753          */
11754         if(!this.tickable){
11755             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11756             return cfg;
11757         }
11758         
11759         /*
11760          *  ComboBox with tickable selections
11761          */
11762              
11763         var align = this.labelAlign || this.parentLabelAlign();
11764         
11765         cfg = {
11766             cls : 'form-group roo-combobox-tickable' //input-group
11767         };
11768         
11769         var buttons = {
11770             tag : 'div',
11771             cls : 'tickable-buttons',
11772             cn : [
11773                 {
11774                     tag : 'button',
11775                     type : 'button',
11776                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11777                     html : this.triggerText
11778                 },
11779                 {
11780                     tag : 'button',
11781                     type : 'button',
11782                     name : 'ok',
11783                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11784                     html : 'Done'
11785                 },
11786                 {
11787                     tag : 'button',
11788                     type : 'button',
11789                     name : 'cancel',
11790                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11791                     html : 'Cancel'
11792                 }
11793             ]
11794         };
11795         
11796         if(this.editable){
11797             buttons.cn.unshift({
11798                 tag: 'input',
11799                 cls: 'select2-search-field-input'
11800             });
11801         }
11802         
11803         var _this = this;
11804         
11805         Roo.each(buttons.cn, function(c){
11806             if (_this.size) {
11807                 c.cls += ' btn-' + _this.size;
11808             }
11809
11810             if (_this.disabled) {
11811                 c.disabled = true;
11812             }
11813         });
11814         
11815         var box = {
11816             tag: 'div',
11817             cn: [
11818                 {
11819                     tag: 'input',
11820                     type : 'hidden',
11821                     cls: 'form-hidden-field'
11822                 },
11823                 {
11824                     tag: 'ul',
11825                     cls: 'select2-choices',
11826                     cn:[
11827                         {
11828                             tag: 'li',
11829                             cls: 'select2-search-field',
11830                             cn: [
11831
11832                                 buttons
11833                             ]
11834                         }
11835                     ]
11836                 }
11837             ]
11838         };
11839         
11840         var combobox = {
11841             cls: 'select2-container input-group select2-container-multi',
11842             cn: [
11843                 box
11844 //                {
11845 //                    tag: 'ul',
11846 //                    cls: 'typeahead typeahead-long dropdown-menu',
11847 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
11848 //                }
11849             ]
11850         };
11851         
11852         if(this.hasFeedback && !this.allowBlank){
11853             
11854             var feedback = {
11855                 tag: 'span',
11856                 cls: 'glyphicon form-control-feedback'
11857             };
11858
11859             combobox.cn.push(feedback);
11860         }
11861         
11862         if (align ==='left' && this.fieldLabel.length) {
11863             
11864                 Roo.log("left and has label");
11865                 cfg.cn = [
11866                     
11867                     {
11868                         tag: 'label',
11869                         'for' :  id,
11870                         cls : 'control-label col-sm-' + this.labelWidth,
11871                         html : this.fieldLabel
11872                         
11873                     },
11874                     {
11875                         cls : "col-sm-" + (12 - this.labelWidth), 
11876                         cn: [
11877                             combobox
11878                         ]
11879                     }
11880                     
11881                 ];
11882         } else if ( this.fieldLabel.length) {
11883                 Roo.log(" label");
11884                  cfg.cn = [
11885                    
11886                     {
11887                         tag: 'label',
11888                         //cls : 'input-group-addon',
11889                         html : this.fieldLabel
11890                         
11891                     },
11892                     
11893                     combobox
11894                     
11895                 ];
11896
11897         } else {
11898             
11899                 Roo.log(" no label && no align");
11900                 cfg = combobox
11901                      
11902                 
11903         }
11904          
11905         var settings=this;
11906         ['xs','sm','md','lg'].map(function(size){
11907             if (settings[size]) {
11908                 cfg.cls += ' col-' + size + '-' + settings[size];
11909             }
11910         });
11911         
11912         return cfg;
11913         
11914     },
11915     
11916     _initEventsCalled : false,
11917     
11918     // private
11919     initEvents: function()
11920     {
11921         
11922         if (this._initEventsCalled) { // as we call render... prevent looping...
11923             return;
11924         }
11925         this._initEventsCalled = true;
11926         
11927         if (!this.store) {
11928             throw "can not find store for combo";
11929         }
11930         
11931         this.store = Roo.factory(this.store, Roo.data);
11932         
11933         // if we are building from html. then this element is so complex, that we can not really
11934         // use the rendered HTML.
11935         // so we have to trash and replace the previous code.
11936         if (Roo.XComponent.build_from_html) {
11937             
11938             // remove this element....
11939             var e = this.el.dom, k=0;
11940             while (e ) { e = e.previousSibling;  ++k;}
11941
11942             this.el.remove();
11943             
11944             this.el=false;
11945             this.rendered = false;
11946             
11947             this.render(this.parent().getChildContainer(true), k);
11948             
11949             
11950             
11951         }
11952         
11953         
11954         /*
11955          * Touch Devices
11956          */
11957         
11958         if(Roo.isTouch && this.mobileTouchView){
11959             this.initTouchView();
11960             return;
11961         }
11962         
11963         if(this.tickable){
11964             this.initTickableEvents();
11965             return;
11966         }
11967         
11968         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11969         
11970         if(this.hiddenName){
11971             
11972             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11973             
11974             this.hiddenField.dom.value =
11975                 this.hiddenValue !== undefined ? this.hiddenValue :
11976                 this.value !== undefined ? this.value : '';
11977
11978             // prevent input submission
11979             this.el.dom.removeAttribute('name');
11980             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11981              
11982              
11983         }
11984         //if(Roo.isGecko){
11985         //    this.el.dom.setAttribute('autocomplete', 'off');
11986         //}
11987         
11988         var cls = 'x-combo-list';
11989         
11990         //this.list = new Roo.Layer({
11991         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
11992         //});
11993         
11994         var _this = this;
11995         
11996         (function(){
11997             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11998             _this.list.setWidth(lw);
11999         }).defer(100);
12000         
12001         this.list.on('mouseover', this.onViewOver, this);
12002         this.list.on('mousemove', this.onViewMove, this);
12003         
12004         this.list.on('scroll', this.onViewScroll, this);
12005         
12006         /*
12007         this.list.swallowEvent('mousewheel');
12008         this.assetHeight = 0;
12009
12010         if(this.title){
12011             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12012             this.assetHeight += this.header.getHeight();
12013         }
12014
12015         this.innerList = this.list.createChild({cls:cls+'-inner'});
12016         this.innerList.on('mouseover', this.onViewOver, this);
12017         this.innerList.on('mousemove', this.onViewMove, this);
12018         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12019         
12020         if(this.allowBlank && !this.pageSize && !this.disableClear){
12021             this.footer = this.list.createChild({cls:cls+'-ft'});
12022             this.pageTb = new Roo.Toolbar(this.footer);
12023            
12024         }
12025         if(this.pageSize){
12026             this.footer = this.list.createChild({cls:cls+'-ft'});
12027             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12028                     {pageSize: this.pageSize});
12029             
12030         }
12031         
12032         if (this.pageTb && this.allowBlank && !this.disableClear) {
12033             var _this = this;
12034             this.pageTb.add(new Roo.Toolbar.Fill(), {
12035                 cls: 'x-btn-icon x-btn-clear',
12036                 text: '&#160;',
12037                 handler: function()
12038                 {
12039                     _this.collapse();
12040                     _this.clearValue();
12041                     _this.onSelect(false, -1);
12042                 }
12043             });
12044         }
12045         if (this.footer) {
12046             this.assetHeight += this.footer.getHeight();
12047         }
12048         */
12049             
12050         if(!this.tpl){
12051             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12052         }
12053
12054         this.view = new Roo.View(this.list, this.tpl, {
12055             singleSelect:true, store: this.store, selectedClass: this.selectedClass
12056         });
12057         //this.view.wrapEl.setDisplayed(false);
12058         this.view.on('click', this.onViewClick, this);
12059         
12060         
12061         
12062         this.store.on('beforeload', this.onBeforeLoad, this);
12063         this.store.on('load', this.onLoad, this);
12064         this.store.on('loadexception', this.onLoadException, this);
12065         /*
12066         if(this.resizable){
12067             this.resizer = new Roo.Resizable(this.list,  {
12068                pinned:true, handles:'se'
12069             });
12070             this.resizer.on('resize', function(r, w, h){
12071                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12072                 this.listWidth = w;
12073                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12074                 this.restrictHeight();
12075             }, this);
12076             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12077         }
12078         */
12079         if(!this.editable){
12080             this.editable = true;
12081             this.setEditable(false);
12082         }
12083         
12084         /*
12085         
12086         if (typeof(this.events.add.listeners) != 'undefined') {
12087             
12088             this.addicon = this.wrap.createChild(
12089                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
12090        
12091             this.addicon.on('click', function(e) {
12092                 this.fireEvent('add', this);
12093             }, this);
12094         }
12095         if (typeof(this.events.edit.listeners) != 'undefined') {
12096             
12097             this.editicon = this.wrap.createChild(
12098                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
12099             if (this.addicon) {
12100                 this.editicon.setStyle('margin-left', '40px');
12101             }
12102             this.editicon.on('click', function(e) {
12103                 
12104                 // we fire even  if inothing is selected..
12105                 this.fireEvent('edit', this, this.lastData );
12106                 
12107             }, this);
12108         }
12109         */
12110         
12111         this.keyNav = new Roo.KeyNav(this.inputEl(), {
12112             "up" : function(e){
12113                 this.inKeyMode = true;
12114                 this.selectPrev();
12115             },
12116
12117             "down" : function(e){
12118                 if(!this.isExpanded()){
12119                     this.onTriggerClick();
12120                 }else{
12121                     this.inKeyMode = true;
12122                     this.selectNext();
12123                 }
12124             },
12125
12126             "enter" : function(e){
12127 //                this.onViewClick();
12128                 //return true;
12129                 this.collapse();
12130                 
12131                 if(this.fireEvent("specialkey", this, e)){
12132                     this.onViewClick(false);
12133                 }
12134                 
12135                 return true;
12136             },
12137
12138             "esc" : function(e){
12139                 this.collapse();
12140             },
12141
12142             "tab" : function(e){
12143                 this.collapse();
12144                 
12145                 if(this.fireEvent("specialkey", this, e)){
12146                     this.onViewClick(false);
12147                 }
12148                 
12149                 return true;
12150             },
12151
12152             scope : this,
12153
12154             doRelay : function(foo, bar, hname){
12155                 if(hname == 'down' || this.scope.isExpanded()){
12156                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12157                 }
12158                 return true;
12159             },
12160
12161             forceKeyDown: true
12162         });
12163         
12164         
12165         this.queryDelay = Math.max(this.queryDelay || 10,
12166                 this.mode == 'local' ? 10 : 250);
12167         
12168         
12169         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12170         
12171         if(this.typeAhead){
12172             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12173         }
12174         if(this.editable !== false){
12175             this.inputEl().on("keyup", this.onKeyUp, this);
12176         }
12177         if(this.forceSelection){
12178             this.inputEl().on('blur', this.doForce, this);
12179         }
12180         
12181         if(this.multiple){
12182             this.choices = this.el.select('ul.select2-choices', true).first();
12183             this.searchField = this.el.select('ul li.select2-search-field', true).first();
12184         }
12185     },
12186     
12187     initTickableEvents: function()
12188     {   
12189         this.createList();
12190         
12191         if(this.hiddenName){
12192             
12193             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12194             
12195             this.hiddenField.dom.value =
12196                 this.hiddenValue !== undefined ? this.hiddenValue :
12197                 this.value !== undefined ? this.value : '';
12198
12199             // prevent input submission
12200             this.el.dom.removeAttribute('name');
12201             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12202              
12203              
12204         }
12205         
12206 //        this.list = this.el.select('ul.dropdown-menu',true).first();
12207         
12208         this.choices = this.el.select('ul.select2-choices', true).first();
12209         this.searchField = this.el.select('ul li.select2-search-field', true).first();
12210         if(this.triggerList){
12211             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12212         }
12213          
12214         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12215         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12216         
12217         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12218         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12219         
12220         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12221         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12222         
12223         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12224         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12225         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12226         
12227         this.okBtn.hide();
12228         this.cancelBtn.hide();
12229         
12230         var _this = this;
12231         
12232         (function(){
12233             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12234             _this.list.setWidth(lw);
12235         }).defer(100);
12236         
12237         this.list.on('mouseover', this.onViewOver, this);
12238         this.list.on('mousemove', this.onViewMove, this);
12239         
12240         this.list.on('scroll', this.onViewScroll, this);
12241         
12242         if(!this.tpl){
12243             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>';
12244         }
12245
12246         this.view = new Roo.View(this.list, this.tpl, {
12247             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12248         });
12249         
12250         //this.view.wrapEl.setDisplayed(false);
12251         this.view.on('click', this.onViewClick, this);
12252         
12253         
12254         
12255         this.store.on('beforeload', this.onBeforeLoad, this);
12256         this.store.on('load', this.onLoad, this);
12257         this.store.on('loadexception', this.onLoadException, this);
12258         
12259         if(this.editable){
12260             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12261                 "up" : function(e){
12262                     this.inKeyMode = true;
12263                     this.selectPrev();
12264                 },
12265
12266                 "down" : function(e){
12267                     this.inKeyMode = true;
12268                     this.selectNext();
12269                 },
12270
12271                 "enter" : function(e){
12272                     if(this.fireEvent("specialkey", this, e)){
12273                         this.onViewClick(false);
12274                     }
12275                     
12276                     return true;
12277                 },
12278
12279                 "esc" : function(e){
12280                     this.onTickableFooterButtonClick(e, false, false);
12281                 },
12282
12283                 "tab" : function(e){
12284                     this.fireEvent("specialkey", this, e);
12285                     
12286                     this.onTickableFooterButtonClick(e, false, false);
12287                     
12288                     return true;
12289                 },
12290
12291                 scope : this,
12292
12293                 doRelay : function(e, fn, key){
12294                     if(this.scope.isExpanded()){
12295                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12296                     }
12297                     return true;
12298                 },
12299
12300                 forceKeyDown: true
12301             });
12302         }
12303         
12304         this.queryDelay = Math.max(this.queryDelay || 10,
12305                 this.mode == 'local' ? 10 : 250);
12306         
12307         
12308         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12309         
12310         if(this.typeAhead){
12311             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12312         }
12313         
12314         if(this.editable !== false){
12315             this.tickableInputEl().on("keyup", this.onKeyUp, this);
12316         }
12317         
12318     },
12319
12320     onDestroy : function(){
12321         if(this.view){
12322             this.view.setStore(null);
12323             this.view.el.removeAllListeners();
12324             this.view.el.remove();
12325             this.view.purgeListeners();
12326         }
12327         if(this.list){
12328             this.list.dom.innerHTML  = '';
12329         }
12330         
12331         if(this.store){
12332             this.store.un('beforeload', this.onBeforeLoad, this);
12333             this.store.un('load', this.onLoad, this);
12334             this.store.un('loadexception', this.onLoadException, this);
12335         }
12336         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12337     },
12338
12339     // private
12340     fireKey : function(e){
12341         if(e.isNavKeyPress() && !this.list.isVisible()){
12342             this.fireEvent("specialkey", this, e);
12343         }
12344     },
12345
12346     // private
12347     onResize: function(w, h){
12348 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12349 //        
12350 //        if(typeof w != 'number'){
12351 //            // we do not handle it!?!?
12352 //            return;
12353 //        }
12354 //        var tw = this.trigger.getWidth();
12355 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
12356 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
12357 //        var x = w - tw;
12358 //        this.inputEl().setWidth( this.adjustWidth('input', x));
12359 //            
12360 //        //this.trigger.setStyle('left', x+'px');
12361 //        
12362 //        if(this.list && this.listWidth === undefined){
12363 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12364 //            this.list.setWidth(lw);
12365 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12366 //        }
12367         
12368     
12369         
12370     },
12371
12372     /**
12373      * Allow or prevent the user from directly editing the field text.  If false is passed,
12374      * the user will only be able to select from the items defined in the dropdown list.  This method
12375      * is the runtime equivalent of setting the 'editable' config option at config time.
12376      * @param {Boolean} value True to allow the user to directly edit the field text
12377      */
12378     setEditable : function(value){
12379         if(value == this.editable){
12380             return;
12381         }
12382         this.editable = value;
12383         if(!value){
12384             this.inputEl().dom.setAttribute('readOnly', true);
12385             this.inputEl().on('mousedown', this.onTriggerClick,  this);
12386             this.inputEl().addClass('x-combo-noedit');
12387         }else{
12388             this.inputEl().dom.setAttribute('readOnly', false);
12389             this.inputEl().un('mousedown', this.onTriggerClick,  this);
12390             this.inputEl().removeClass('x-combo-noedit');
12391         }
12392     },
12393
12394     // private
12395     
12396     onBeforeLoad : function(combo,opts){
12397         if(!this.hasFocus){
12398             return;
12399         }
12400          if (!opts.add) {
12401             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12402          }
12403         this.restrictHeight();
12404         this.selectedIndex = -1;
12405     },
12406
12407     // private
12408     onLoad : function(){
12409         
12410         this.hasQuery = false;
12411         
12412         if(!this.hasFocus){
12413             return;
12414         }
12415         
12416         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12417             this.loading.hide();
12418         }
12419              
12420         if(this.store.getCount() > 0){
12421             this.expand();
12422             this.restrictHeight();
12423             if(this.lastQuery == this.allQuery){
12424                 if(this.editable && !this.tickable){
12425                     this.inputEl().dom.select();
12426                 }
12427                 
12428                 if(
12429                     !this.selectByValue(this.value, true) &&
12430                     this.autoFocus && 
12431                     (
12432                         !this.store.lastOptions ||
12433                         typeof(this.store.lastOptions.add) == 'undefined' || 
12434                         this.store.lastOptions.add != true
12435                     )
12436                 ){
12437                     this.select(0, true);
12438                 }
12439             }else{
12440                 if(this.autoFocus){
12441                     this.selectNext();
12442                 }
12443                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12444                     this.taTask.delay(this.typeAheadDelay);
12445                 }
12446             }
12447         }else{
12448             this.onEmptyResults();
12449         }
12450         
12451         //this.el.focus();
12452     },
12453     // private
12454     onLoadException : function()
12455     {
12456         this.hasQuery = false;
12457         
12458         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12459             this.loading.hide();
12460         }
12461         
12462         if(this.tickable && this.editable){
12463             return;
12464         }
12465         
12466         this.collapse();
12467         // only causes errors at present
12468         //Roo.log(this.store.reader.jsonData);
12469         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12470             // fixme
12471             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12472         //}
12473         
12474         
12475     },
12476     // private
12477     onTypeAhead : function(){
12478         if(this.store.getCount() > 0){
12479             var r = this.store.getAt(0);
12480             var newValue = r.data[this.displayField];
12481             var len = newValue.length;
12482             var selStart = this.getRawValue().length;
12483             
12484             if(selStart != len){
12485                 this.setRawValue(newValue);
12486                 this.selectText(selStart, newValue.length);
12487             }
12488         }
12489     },
12490
12491     // private
12492     onSelect : function(record, index){
12493         
12494         if(this.fireEvent('beforeselect', this, record, index) !== false){
12495         
12496             this.setFromData(index > -1 ? record.data : false);
12497             
12498             this.collapse();
12499             this.fireEvent('select', this, record, index);
12500         }
12501     },
12502
12503     /**
12504      * Returns the currently selected field value or empty string if no value is set.
12505      * @return {String} value The selected value
12506      */
12507     getValue : function(){
12508         
12509         if(this.multiple){
12510             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12511         }
12512         
12513         if(this.valueField){
12514             return typeof this.value != 'undefined' ? this.value : '';
12515         }else{
12516             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12517         }
12518     },
12519
12520     /**
12521      * Clears any text/value currently set in the field
12522      */
12523     clearValue : function(){
12524         if(this.hiddenField){
12525             this.hiddenField.dom.value = '';
12526         }
12527         this.value = '';
12528         this.setRawValue('');
12529         this.lastSelectionText = '';
12530         this.lastData = false;
12531         
12532         var close = this.closeTriggerEl();
12533         
12534         if(close){
12535             close.hide();
12536         }
12537         
12538     },
12539
12540     /**
12541      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
12542      * will be displayed in the field.  If the value does not match the data value of an existing item,
12543      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12544      * Otherwise the field will be blank (although the value will still be set).
12545      * @param {String} value The value to match
12546      */
12547     setValue : function(v){
12548         if(this.multiple){
12549             this.syncValue();
12550             return;
12551         }
12552         
12553         var text = v;
12554         if(this.valueField){
12555             var r = this.findRecord(this.valueField, v);
12556             if(r){
12557                 text = r.data[this.displayField];
12558             }else if(this.valueNotFoundText !== undefined){
12559                 text = this.valueNotFoundText;
12560             }
12561         }
12562         this.lastSelectionText = text;
12563         if(this.hiddenField){
12564             this.hiddenField.dom.value = v;
12565         }
12566         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12567         this.value = v;
12568         
12569         var close = this.closeTriggerEl();
12570         
12571         if(close){
12572             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12573         }
12574     },
12575     /**
12576      * @property {Object} the last set data for the element
12577      */
12578     
12579     lastData : false,
12580     /**
12581      * Sets the value of the field based on a object which is related to the record format for the store.
12582      * @param {Object} value the value to set as. or false on reset?
12583      */
12584     setFromData : function(o){
12585         
12586         if(this.multiple){
12587             this.addItem(o);
12588             return;
12589         }
12590             
12591         var dv = ''; // display value
12592         var vv = ''; // value value..
12593         this.lastData = o;
12594         if (this.displayField) {
12595             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12596         } else {
12597             // this is an error condition!!!
12598             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12599         }
12600         
12601         if(this.valueField){
12602             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12603         }
12604         
12605         var close = this.closeTriggerEl();
12606         
12607         if(close){
12608             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12609         }
12610         
12611         if(this.hiddenField){
12612             this.hiddenField.dom.value = vv;
12613             
12614             this.lastSelectionText = dv;
12615             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12616             this.value = vv;
12617             return;
12618         }
12619         // no hidden field.. - we store the value in 'value', but still display
12620         // display field!!!!
12621         this.lastSelectionText = dv;
12622         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12623         this.value = vv;
12624         
12625         
12626         
12627     },
12628     // private
12629     reset : function(){
12630         // overridden so that last data is reset..
12631         
12632         if(this.multiple){
12633             this.clearItem();
12634             return;
12635         }
12636         
12637         this.setValue(this.originalValue);
12638         this.clearInvalid();
12639         this.lastData = false;
12640         if (this.view) {
12641             this.view.clearSelections();
12642         }
12643     },
12644     // private
12645     findRecord : function(prop, value){
12646         var record;
12647         if(this.store.getCount() > 0){
12648             this.store.each(function(r){
12649                 if(r.data[prop] == value){
12650                     record = r;
12651                     return false;
12652                 }
12653                 return true;
12654             });
12655         }
12656         return record;
12657     },
12658     
12659     getName: function()
12660     {
12661         // returns hidden if it's set..
12662         if (!this.rendered) {return ''};
12663         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
12664         
12665     },
12666     // private
12667     onViewMove : function(e, t){
12668         this.inKeyMode = false;
12669     },
12670
12671     // private
12672     onViewOver : function(e, t){
12673         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12674             return;
12675         }
12676         var item = this.view.findItemFromChild(t);
12677         
12678         if(item){
12679             var index = this.view.indexOf(item);
12680             this.select(index, false);
12681         }
12682     },
12683
12684     // private
12685     onViewClick : function(view, doFocus, el, e)
12686     {
12687         var index = this.view.getSelectedIndexes()[0];
12688         
12689         var r = this.store.getAt(index);
12690         
12691         if(this.tickable){
12692             
12693             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12694                 return;
12695             }
12696             
12697             var rm = false;
12698             var _this = this;
12699             
12700             Roo.each(this.tickItems, function(v,k){
12701                 
12702                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12703                     Roo.log(v);
12704                     _this.tickItems.splice(k, 1);
12705                     
12706                     if(typeof(e) == 'undefined' && view == false){
12707                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12708                     }
12709                     
12710                     rm = true;
12711                     return;
12712                 }
12713             });
12714             
12715             if(rm){
12716                 return;
12717             }
12718             
12719             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
12720                 this.tickItems.push(r.data);
12721             }
12722             
12723             if(typeof(e) == 'undefined' && view == false){
12724                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12725             }
12726                     
12727             return;
12728         }
12729         
12730         if(r){
12731             this.onSelect(r, index);
12732         }
12733         if(doFocus !== false && !this.blockFocus){
12734             this.inputEl().focus();
12735         }
12736     },
12737
12738     // private
12739     restrictHeight : function(){
12740         //this.innerList.dom.style.height = '';
12741         //var inner = this.innerList.dom;
12742         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12743         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12744         //this.list.beginUpdate();
12745         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12746         this.list.alignTo(this.inputEl(), this.listAlign);
12747         this.list.alignTo(this.inputEl(), this.listAlign);
12748         //this.list.endUpdate();
12749     },
12750
12751     // private
12752     onEmptyResults : function(){
12753         
12754         if(this.tickable && this.editable){
12755             this.restrictHeight();
12756             return;
12757         }
12758         
12759         this.collapse();
12760     },
12761
12762     /**
12763      * Returns true if the dropdown list is expanded, else false.
12764      */
12765     isExpanded : function(){
12766         return this.list.isVisible();
12767     },
12768
12769     /**
12770      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12771      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12772      * @param {String} value The data value of the item to select
12773      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12774      * selected item if it is not currently in view (defaults to true)
12775      * @return {Boolean} True if the value matched an item in the list, else false
12776      */
12777     selectByValue : function(v, scrollIntoView){
12778         if(v !== undefined && v !== null){
12779             var r = this.findRecord(this.valueField || this.displayField, v);
12780             if(r){
12781                 this.select(this.store.indexOf(r), scrollIntoView);
12782                 return true;
12783             }
12784         }
12785         return false;
12786     },
12787
12788     /**
12789      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12790      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12791      * @param {Number} index The zero-based index of the list item to select
12792      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12793      * selected item if it is not currently in view (defaults to true)
12794      */
12795     select : function(index, scrollIntoView){
12796         this.selectedIndex = index;
12797         this.view.select(index);
12798         if(scrollIntoView !== false){
12799             var el = this.view.getNode(index);
12800             /*
12801              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12802              */
12803             if(el){
12804                 this.list.scrollChildIntoView(el, false);
12805             }
12806         }
12807     },
12808
12809     // private
12810     selectNext : function(){
12811         var ct = this.store.getCount();
12812         if(ct > 0){
12813             if(this.selectedIndex == -1){
12814                 this.select(0);
12815             }else if(this.selectedIndex < ct-1){
12816                 this.select(this.selectedIndex+1);
12817             }
12818         }
12819     },
12820
12821     // private
12822     selectPrev : function(){
12823         var ct = this.store.getCount();
12824         if(ct > 0){
12825             if(this.selectedIndex == -1){
12826                 this.select(0);
12827             }else if(this.selectedIndex != 0){
12828                 this.select(this.selectedIndex-1);
12829             }
12830         }
12831     },
12832
12833     // private
12834     onKeyUp : function(e){
12835         if(this.editable !== false && !e.isSpecialKey()){
12836             this.lastKey = e.getKey();
12837             this.dqTask.delay(this.queryDelay);
12838         }
12839     },
12840
12841     // private
12842     validateBlur : function(){
12843         return !this.list || !this.list.isVisible();   
12844     },
12845
12846     // private
12847     initQuery : function(){
12848         
12849         var v = this.getRawValue();
12850         
12851         if(this.tickable && this.editable){
12852             v = this.tickableInputEl().getValue();
12853         }
12854         
12855         this.doQuery(v);
12856     },
12857
12858     // private
12859     doForce : function(){
12860         if(this.inputEl().dom.value.length > 0){
12861             this.inputEl().dom.value =
12862                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12863              
12864         }
12865     },
12866
12867     /**
12868      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
12869      * query allowing the query action to be canceled if needed.
12870      * @param {String} query The SQL query to execute
12871      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12872      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
12873      * saved in the current store (defaults to false)
12874      */
12875     doQuery : function(q, forceAll){
12876         
12877         if(q === undefined || q === null){
12878             q = '';
12879         }
12880         var qe = {
12881             query: q,
12882             forceAll: forceAll,
12883             combo: this,
12884             cancel:false
12885         };
12886         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12887             return false;
12888         }
12889         q = qe.query;
12890         
12891         forceAll = qe.forceAll;
12892         if(forceAll === true || (q.length >= this.minChars)){
12893             
12894             this.hasQuery = true;
12895             
12896             if(this.lastQuery != q || this.alwaysQuery){
12897                 this.lastQuery = q;
12898                 if(this.mode == 'local'){
12899                     this.selectedIndex = -1;
12900                     if(forceAll){
12901                         this.store.clearFilter();
12902                     }else{
12903                         
12904                         if(this.specialFilter){
12905                             this.fireEvent('specialfilter', this);
12906                             this.onLoad();
12907                             return;
12908                         }
12909                         
12910                         this.store.filter(this.displayField, q);
12911                     }
12912                     
12913                     this.store.fireEvent("datachanged", this.store);
12914                     
12915                     this.onLoad();
12916                     
12917                     
12918                 }else{
12919                     
12920                     this.store.baseParams[this.queryParam] = q;
12921                     
12922                     var options = {params : this.getParams(q)};
12923                     
12924                     if(this.loadNext){
12925                         options.add = true;
12926                         options.params.start = this.page * this.pageSize;
12927                     }
12928                     
12929                     this.store.load(options);
12930                     
12931                     /*
12932                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
12933                      *  we should expand the list on onLoad
12934                      *  so command out it
12935                      */
12936 //                    this.expand();
12937                 }
12938             }else{
12939                 this.selectedIndex = -1;
12940                 this.onLoad();   
12941             }
12942         }
12943         
12944         this.loadNext = false;
12945     },
12946     
12947     // private
12948     getParams : function(q){
12949         var p = {};
12950         //p[this.queryParam] = q;
12951         
12952         if(this.pageSize){
12953             p.start = 0;
12954             p.limit = this.pageSize;
12955         }
12956         return p;
12957     },
12958
12959     /**
12960      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12961      */
12962     collapse : function(){
12963         if(!this.isExpanded()){
12964             return;
12965         }
12966         
12967         this.list.hide();
12968         
12969         if(this.tickable){
12970             this.hasFocus = false;
12971             this.okBtn.hide();
12972             this.cancelBtn.hide();
12973             this.trigger.show();
12974             
12975             if(this.editable){
12976                 this.tickableInputEl().dom.value = '';
12977                 this.tickableInputEl().blur();
12978             }
12979             
12980         }
12981         
12982         Roo.get(document).un('mousedown', this.collapseIf, this);
12983         Roo.get(document).un('mousewheel', this.collapseIf, this);
12984         if (!this.editable) {
12985             Roo.get(document).un('keydown', this.listKeyPress, this);
12986         }
12987         this.fireEvent('collapse', this);
12988     },
12989
12990     // private
12991     collapseIf : function(e){
12992         var in_combo  = e.within(this.el);
12993         var in_list =  e.within(this.list);
12994         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
12995         
12996         if (in_combo || in_list || is_list) {
12997             //e.stopPropagation();
12998             return;
12999         }
13000         
13001         if(this.tickable){
13002             this.onTickableFooterButtonClick(e, false, false);
13003         }
13004
13005         this.collapse();
13006         
13007     },
13008
13009     /**
13010      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13011      */
13012     expand : function(){
13013        
13014         if(this.isExpanded() || !this.hasFocus){
13015             return;
13016         }
13017         
13018         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13019         this.list.setWidth(lw);
13020         
13021         
13022          Roo.log('expand');
13023         
13024         this.list.show();
13025         
13026         this.restrictHeight();
13027         
13028         if(this.tickable){
13029             
13030             this.tickItems = Roo.apply([], this.item);
13031             
13032             this.okBtn.show();
13033             this.cancelBtn.show();
13034             this.trigger.hide();
13035             
13036             if(this.editable){
13037                 this.tickableInputEl().focus();
13038             }
13039             
13040         }
13041         
13042         Roo.get(document).on('mousedown', this.collapseIf, this);
13043         Roo.get(document).on('mousewheel', this.collapseIf, this);
13044         if (!this.editable) {
13045             Roo.get(document).on('keydown', this.listKeyPress, this);
13046         }
13047         
13048         this.fireEvent('expand', this);
13049     },
13050
13051     // private
13052     // Implements the default empty TriggerField.onTriggerClick function
13053     onTriggerClick : function(e)
13054     {
13055         Roo.log('trigger click');
13056         
13057         if(this.disabled || !this.triggerList){
13058             return;
13059         }
13060         
13061         this.page = 0;
13062         this.loadNext = false;
13063         
13064         if(this.isExpanded()){
13065             this.collapse();
13066             if (!this.blockFocus) {
13067                 this.inputEl().focus();
13068             }
13069             
13070         }else {
13071             this.hasFocus = true;
13072             if(this.triggerAction == 'all') {
13073                 this.doQuery(this.allQuery, true);
13074             } else {
13075                 this.doQuery(this.getRawValue());
13076             }
13077             if (!this.blockFocus) {
13078                 this.inputEl().focus();
13079             }
13080         }
13081     },
13082     
13083     onTickableTriggerClick : function(e)
13084     {
13085         if(this.disabled){
13086             return;
13087         }
13088         
13089         this.page = 0;
13090         this.loadNext = false;
13091         this.hasFocus = true;
13092         
13093         if(this.triggerAction == 'all') {
13094             this.doQuery(this.allQuery, true);
13095         } else {
13096             this.doQuery(this.getRawValue());
13097         }
13098     },
13099     
13100     onSearchFieldClick : function(e)
13101     {
13102         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13103             this.onTickableFooterButtonClick(e, false, false);
13104             return;
13105         }
13106         
13107         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13108             return;
13109         }
13110         
13111         this.page = 0;
13112         this.loadNext = false;
13113         this.hasFocus = true;
13114         
13115         if(this.triggerAction == 'all') {
13116             this.doQuery(this.allQuery, true);
13117         } else {
13118             this.doQuery(this.getRawValue());
13119         }
13120     },
13121     
13122     listKeyPress : function(e)
13123     {
13124         //Roo.log('listkeypress');
13125         // scroll to first matching element based on key pres..
13126         if (e.isSpecialKey()) {
13127             return false;
13128         }
13129         var k = String.fromCharCode(e.getKey()).toUpperCase();
13130         //Roo.log(k);
13131         var match  = false;
13132         var csel = this.view.getSelectedNodes();
13133         var cselitem = false;
13134         if (csel.length) {
13135             var ix = this.view.indexOf(csel[0]);
13136             cselitem  = this.store.getAt(ix);
13137             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13138                 cselitem = false;
13139             }
13140             
13141         }
13142         
13143         this.store.each(function(v) { 
13144             if (cselitem) {
13145                 // start at existing selection.
13146                 if (cselitem.id == v.id) {
13147                     cselitem = false;
13148                 }
13149                 return true;
13150             }
13151                 
13152             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13153                 match = this.store.indexOf(v);
13154                 return false;
13155             }
13156             return true;
13157         }, this);
13158         
13159         if (match === false) {
13160             return true; // no more action?
13161         }
13162         // scroll to?
13163         this.view.select(match);
13164         var sn = Roo.get(this.view.getSelectedNodes()[0]);
13165         sn.scrollIntoView(sn.dom.parentNode, false);
13166     },
13167     
13168     onViewScroll : function(e, t){
13169         
13170         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){
13171             return;
13172         }
13173         
13174         this.hasQuery = true;
13175         
13176         this.loading = this.list.select('.loading', true).first();
13177         
13178         if(this.loading === null){
13179             this.list.createChild({
13180                 tag: 'div',
13181                 cls: 'loading select2-more-results select2-active',
13182                 html: 'Loading more results...'
13183             });
13184             
13185             this.loading = this.list.select('.loading', true).first();
13186             
13187             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13188             
13189             this.loading.hide();
13190         }
13191         
13192         this.loading.show();
13193         
13194         var _combo = this;
13195         
13196         this.page++;
13197         this.loadNext = true;
13198         
13199         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13200         
13201         return;
13202     },
13203     
13204     addItem : function(o)
13205     {   
13206         var dv = ''; // display value
13207         
13208         if (this.displayField) {
13209             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13210         } else {
13211             // this is an error condition!!!
13212             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13213         }
13214         
13215         if(!dv.length){
13216             return;
13217         }
13218         
13219         var choice = this.choices.createChild({
13220             tag: 'li',
13221             cls: 'select2-search-choice',
13222             cn: [
13223                 {
13224                     tag: 'div',
13225                     html: dv
13226                 },
13227                 {
13228                     tag: 'a',
13229                     href: '#',
13230                     cls: 'select2-search-choice-close',
13231                     tabindex: '-1'
13232                 }
13233             ]
13234             
13235         }, this.searchField);
13236         
13237         var close = choice.select('a.select2-search-choice-close', true).first();
13238         
13239         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13240         
13241         this.item.push(o);
13242         
13243         this.lastData = o;
13244         
13245         this.syncValue();
13246         
13247         this.inputEl().dom.value = '';
13248         
13249         this.validate();
13250     },
13251     
13252     onRemoveItem : function(e, _self, o)
13253     {
13254         e.preventDefault();
13255         
13256         this.lastItem = Roo.apply([], this.item);
13257         
13258         var index = this.item.indexOf(o.data) * 1;
13259         
13260         if( index < 0){
13261             Roo.log('not this item?!');
13262             return;
13263         }
13264         
13265         this.item.splice(index, 1);
13266         o.item.remove();
13267         
13268         this.syncValue();
13269         
13270         this.fireEvent('remove', this, e);
13271         
13272         this.validate();
13273         
13274     },
13275     
13276     syncValue : function()
13277     {
13278         if(!this.item.length){
13279             this.clearValue();
13280             return;
13281         }
13282             
13283         var value = [];
13284         var _this = this;
13285         Roo.each(this.item, function(i){
13286             if(_this.valueField){
13287                 value.push(i[_this.valueField]);
13288                 return;
13289             }
13290
13291             value.push(i);
13292         });
13293
13294         this.value = value.join(',');
13295
13296         if(this.hiddenField){
13297             this.hiddenField.dom.value = this.value;
13298         }
13299         
13300         this.store.fireEvent("datachanged", this.store);
13301     },
13302     
13303     clearItem : function()
13304     {
13305         if(!this.multiple){
13306             return;
13307         }
13308         
13309         this.item = [];
13310         
13311         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
13312            c.remove();
13313         });
13314         
13315         this.syncValue();
13316         
13317         this.validate();
13318         
13319         if(this.tickable && !Roo.isTouch){
13320             this.view.refresh();
13321         }
13322     },
13323     
13324     inputEl: function ()
13325     {
13326         if(Roo.isTouch && this.mobileTouchView){
13327             return this.el.select('input.form-control',true).first();
13328         }
13329         
13330         if(this.tickable){
13331             return this.searchField;
13332         }
13333         
13334         return this.el.select('input.form-control',true).first();
13335     },
13336     
13337     
13338     onTickableFooterButtonClick : function(e, btn, el)
13339     {
13340         e.preventDefault();
13341         
13342         this.lastItem = Roo.apply([], this.item);
13343         
13344         if(btn && btn.name == 'cancel'){
13345             this.tickItems = Roo.apply([], this.item);
13346             this.collapse();
13347             return;
13348         }
13349         
13350         this.clearItem();
13351         
13352         var _this = this;
13353         
13354         Roo.each(this.tickItems, function(o){
13355             _this.addItem(o);
13356         });
13357         
13358         this.collapse();
13359         
13360     },
13361     
13362     validate : function()
13363     {
13364         var v = this.getRawValue();
13365         
13366         if(this.multiple){
13367             v = this.getValue();
13368         }
13369         
13370         if(this.disabled || this.allowBlank || v.length){
13371             this.markValid();
13372             return true;
13373         }
13374         
13375         this.markInvalid();
13376         return false;
13377     },
13378     
13379     tickableInputEl : function()
13380     {
13381         if(!this.tickable || !this.editable){
13382             return this.inputEl();
13383         }
13384         
13385         return this.inputEl().select('.select2-search-field-input', true).first();
13386     },
13387     
13388     
13389     getAutoCreateTouchView : function()
13390     {
13391         var id = Roo.id();
13392         
13393         var cfg = {
13394             cls: 'form-group' //input-group
13395         };
13396         
13397         var input =  {
13398             tag: 'input',
13399             id : id,
13400             type : this.inputType,
13401             cls : 'form-control x-combo-noedit',
13402             autocomplete: 'new-password',
13403             placeholder : this.placeholder || '',
13404             readonly : true
13405         };
13406         
13407         if (this.name) {
13408             input.name = this.name;
13409         }
13410         
13411         if (this.size) {
13412             input.cls += ' input-' + this.size;
13413         }
13414         
13415         if (this.disabled) {
13416             input.disabled = true;
13417         }
13418         
13419         var inputblock = {
13420             cls : '',
13421             cn : [
13422                 input
13423             ]
13424         };
13425         
13426         if(this.before){
13427             inputblock.cls += ' input-group';
13428             
13429             inputblock.cn.unshift({
13430                 tag :'span',
13431                 cls : 'input-group-addon',
13432                 html : this.before
13433             });
13434         }
13435         
13436         if(this.removable && !this.multiple){
13437             inputblock.cls += ' roo-removable';
13438             
13439             inputblock.cn.push({
13440                 tag: 'button',
13441                 html : 'x',
13442                 cls : 'roo-combo-removable-btn close'
13443             });
13444         }
13445
13446         if(this.hasFeedback && !this.allowBlank){
13447             
13448             inputblock.cls += ' has-feedback';
13449             
13450             inputblock.cn.push({
13451                 tag: 'span',
13452                 cls: 'glyphicon form-control-feedback'
13453             });
13454             
13455         }
13456         
13457         if (this.after) {
13458             
13459             inputblock.cls += (this.before) ? '' : ' input-group';
13460             
13461             inputblock.cn.push({
13462                 tag :'span',
13463                 cls : 'input-group-addon',
13464                 html : this.after
13465             });
13466         }
13467
13468         var box = {
13469             tag: 'div',
13470             cn: [
13471                 {
13472                     tag: 'input',
13473                     type : 'hidden',
13474                     cls: 'form-hidden-field'
13475                 },
13476                 inputblock
13477             ]
13478             
13479         };
13480         
13481         if(this.multiple){
13482             box = {
13483                 tag: 'div',
13484                 cn: [
13485                     {
13486                         tag: 'input',
13487                         type : 'hidden',
13488                         cls: 'form-hidden-field'
13489                     },
13490                     {
13491                         tag: 'ul',
13492                         cls: 'select2-choices',
13493                         cn:[
13494                             {
13495                                 tag: 'li',
13496                                 cls: 'select2-search-field',
13497                                 cn: [
13498
13499                                     inputblock
13500                                 ]
13501                             }
13502                         ]
13503                     }
13504                 ]
13505             }
13506         };
13507         
13508         var combobox = {
13509             cls: 'select2-container input-group',
13510             cn: [
13511                 box
13512             ]
13513         };
13514         
13515         if(this.multiple){
13516             combobox.cls += ' select2-container-multi';
13517         }
13518         
13519         var align = this.labelAlign || this.parentLabelAlign();
13520         
13521         cfg.cn = combobox;
13522         
13523         if(this.fieldLabel.length){
13524             
13525             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13526             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13527             
13528             cfg.cn = [
13529                 {
13530                     tag: 'label',
13531                     cls : 'control-label ' + lw,
13532                     html : this.fieldLabel
13533
13534                 },
13535                 {
13536                     cls : cw, 
13537                     cn: [
13538                         combobox
13539                     ]
13540                 }
13541             ];
13542         }
13543         
13544         var settings = this;
13545         
13546         ['xs','sm','md','lg'].map(function(size){
13547             if (settings[size]) {
13548                 cfg.cls += ' col-' + size + '-' + settings[size];
13549             }
13550         });
13551         
13552         return cfg;
13553     },
13554     
13555     initTouchView : function()
13556     {
13557         this.renderTouchView();
13558         
13559         this.touchViewEl.on('scroll', function(){
13560             this.el.dom.scrollTop = 0;
13561         }, this);
13562         
13563         this.inputEl().on("click", this.showTouchView, this);
13564         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13565         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13566         
13567         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13568         
13569         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13570         this.store.on('load', this.onTouchViewLoad, this);
13571         this.store.on('loadexception', this.onTouchViewLoadException, this);
13572         
13573         if(this.hiddenName){
13574             
13575             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13576             
13577             this.hiddenField.dom.value =
13578                 this.hiddenValue !== undefined ? this.hiddenValue :
13579                 this.value !== undefined ? this.value : '';
13580         
13581             this.el.dom.removeAttribute('name');
13582             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13583         }
13584         
13585         if(this.multiple){
13586             this.choices = this.el.select('ul.select2-choices', true).first();
13587             this.searchField = this.el.select('ul li.select2-search-field', true).first();
13588         }
13589         
13590         if(this.removable && !this.multiple){
13591             var close = this.closeTriggerEl();
13592             if(close){
13593                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13594                 close.on('click', this.removeBtnClick, this, close);
13595             }
13596         }
13597         
13598         return;
13599         
13600         
13601     },
13602     
13603     renderTouchView : function()
13604     {
13605         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13606         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13607         
13608         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13609         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13610         
13611         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13612         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13613         this.touchViewBodyEl.setStyle('overflow', 'auto');
13614         
13615         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13616         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13617         
13618         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13619         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13620         
13621     },
13622     
13623     showTouchView : function()
13624     {
13625         this.touchViewHeaderEl.hide();
13626
13627         if(this.fieldLabel.length){
13628             this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13629             this.touchViewHeaderEl.show();
13630         }
13631
13632         this.touchViewEl.show();
13633
13634         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13635         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13636
13637         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13638
13639         if(this.fieldLabel.length){
13640             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13641         }
13642         
13643         this.touchViewBodyEl.setHeight(bodyHeight);
13644
13645         if(this.animate){
13646             var _this = this;
13647             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13648         }else{
13649             this.touchViewEl.addClass('in');
13650         }
13651
13652         this.doTouchViewQuery();
13653         
13654     },
13655     
13656     hideTouchView : function()
13657     {
13658         this.touchViewEl.removeClass('in');
13659
13660         if(this.animate){
13661             var _this = this;
13662             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13663         }else{
13664             this.touchViewEl.setStyle('display', 'none');
13665         }
13666         
13667     },
13668     
13669     setTouchViewValue : function()
13670     {
13671         if(this.multiple){
13672             this.clearItem();
13673         
13674             var _this = this;
13675
13676             Roo.each(this.tickItems, function(o){
13677                 this.addItem(o);
13678             }, this);
13679         }
13680         
13681         this.hideTouchView();
13682     },
13683     
13684     doTouchViewQuery : function()
13685     {
13686         var qe = {
13687             query: '',
13688             forceAll: true,
13689             combo: this,
13690             cancel:false
13691         };
13692         
13693         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13694             return false;
13695         }
13696         
13697         if(!this.alwaysQuery || this.mode == 'local'){
13698             this.onTouchViewLoad();
13699             return;
13700         }
13701         
13702         this.store.load();
13703     },
13704     
13705     onTouchViewBeforeLoad : function(combo,opts)
13706     {
13707         return;
13708     },
13709
13710     // private
13711     onTouchViewLoad : function()
13712     {
13713         if(this.store.getCount() < 1){
13714             this.onTouchViewEmptyResults();
13715             return;
13716         }
13717         
13718         this.clearTouchView();
13719         
13720         var rawValue = this.getRawValue();
13721         
13722         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13723         
13724         this.tickItems = [];
13725         
13726         this.store.data.each(function(d, rowIndex){
13727             var row = this.touchViewListGroup.createChild(template);
13728             
13729             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13730                 var cfg = {
13731                     data : d.data,
13732                     html : d.data[this.displayField]
13733                 };
13734                 
13735                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
13736                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
13737                 }
13738             }
13739             
13740             if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
13741                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13742             }
13743             
13744             if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
13745                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13746                 this.tickItems.push(d.data);
13747             }
13748             
13749             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
13750             
13751         }, this);
13752         
13753         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
13754         
13755         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13756
13757         if(this.fieldLabel.length){
13758             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13759         }
13760
13761         var listHeight = this.touchViewListGroup.getHeight();
13762         
13763         var _this = this;
13764         
13765         if(firstChecked && listHeight > bodyHeight){
13766             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
13767         }
13768         
13769     },
13770     
13771     onTouchViewLoadException : function()
13772     {
13773         this.hideTouchView();
13774     },
13775     
13776     onTouchViewEmptyResults : function()
13777     {
13778         this.clearTouchView();
13779         
13780         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
13781         
13782         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
13783         
13784     },
13785     
13786     clearTouchView : function()
13787     {
13788         this.touchViewListGroup.dom.innerHTML = '';
13789     },
13790     
13791     onTouchViewClick : function(e, el, o)
13792     {
13793         e.preventDefault();
13794         
13795         var row = o.row;
13796         var rowIndex = o.rowIndex;
13797         
13798         var r = this.store.getAt(rowIndex);
13799         
13800         if(!this.multiple){
13801             Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
13802                 c.dom.removeAttribute('checked');
13803             }, this);
13804             
13805             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13806         
13807             this.setFromData(r.data);
13808             
13809             var close = this.closeTriggerEl();
13810         
13811             if(close){
13812                 close.show();
13813             }
13814
13815             this.hideTouchView();
13816             
13817             this.fireEvent('select', this, r, rowIndex);
13818             
13819             return;
13820         }
13821         
13822         if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
13823             row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
13824             this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
13825             return;
13826         }
13827         
13828         row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13829         this.addItem(r.data);
13830         this.tickItems.push(r.data);
13831         
13832     }
13833     
13834
13835     /** 
13836     * @cfg {Boolean} grow 
13837     * @hide 
13838     */
13839     /** 
13840     * @cfg {Number} growMin 
13841     * @hide 
13842     */
13843     /** 
13844     * @cfg {Number} growMax 
13845     * @hide 
13846     */
13847     /**
13848      * @hide
13849      * @method autoSize
13850      */
13851 });
13852
13853 Roo.apply(Roo.bootstrap.ComboBox,  {
13854     
13855     header : {
13856         tag: 'div',
13857         cls: 'modal-header',
13858         cn: [
13859             {
13860                 tag: 'h4',
13861                 cls: 'modal-title'
13862             }
13863         ]
13864     },
13865     
13866     body : {
13867         tag: 'div',
13868         cls: 'modal-body',
13869         cn: [
13870             {
13871                 tag: 'ul',
13872                 cls: 'list-group'
13873             }
13874         ]
13875     },
13876     
13877     listItemRadio : {
13878         tag: 'li',
13879         cls: 'list-group-item',
13880         cn: [
13881             {
13882                 tag: 'span',
13883                 cls: 'roo-combobox-list-group-item-value'
13884             },
13885             {
13886                 tag: 'div',
13887                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
13888                 cn: [
13889                     {
13890                         tag: 'input',
13891                         type: 'radio'
13892                     },
13893                     {
13894                         tag: 'label'
13895                     }
13896                 ]
13897             }
13898         ]
13899     },
13900     
13901     listItemCheckbox : {
13902         tag: 'li',
13903         cls: 'list-group-item',
13904         cn: [
13905             {
13906                 tag: 'span',
13907                 cls: 'roo-combobox-list-group-item-value'
13908             },
13909             {
13910                 tag: 'div',
13911                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
13912                 cn: [
13913                     {
13914                         tag: 'input',
13915                         type: 'checkbox'
13916                     },
13917                     {
13918                         tag: 'label'
13919                     }
13920                 ]
13921             }
13922         ]
13923     },
13924     
13925     emptyResult : {
13926         tag: 'div',
13927         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
13928     },
13929     
13930     footer : {
13931         tag: 'div',
13932         cls: 'modal-footer',
13933         cn: [
13934             {
13935                 tag: 'div',
13936                 cls: 'row',
13937                 cn: [
13938                     {
13939                         tag: 'div',
13940                         cls: 'col-xs-6 text-left',
13941                         cn: {
13942                             tag: 'button',
13943                             cls: 'btn btn-danger roo-touch-view-cancel',
13944                             html: 'Cancel'
13945                         }
13946                     },
13947                     {
13948                         tag: 'div',
13949                         cls: 'col-xs-6 text-right',
13950                         cn: {
13951                             tag: 'button',
13952                             cls: 'btn btn-success roo-touch-view-ok',
13953                             html: 'OK'
13954                         }
13955                     }
13956                 ]
13957             }
13958         ]
13959         
13960     }
13961 });
13962
13963 Roo.apply(Roo.bootstrap.ComboBox,  {
13964     
13965     touchViewTemplate : {
13966         tag: 'div',
13967         cls: 'modal fade roo-combobox-touch-view',
13968         cn: [
13969             {
13970                 tag: 'div',
13971                 cls: 'modal-dialog',
13972                 cn: [
13973                     {
13974                         tag: 'div',
13975                         cls: 'modal-content',
13976                         cn: [
13977                             Roo.bootstrap.ComboBox.header,
13978                             Roo.bootstrap.ComboBox.body,
13979                             Roo.bootstrap.ComboBox.footer
13980                         ]
13981                     }
13982                 ]
13983             }
13984         ]
13985     }
13986 });/*
13987  * Based on:
13988  * Ext JS Library 1.1.1
13989  * Copyright(c) 2006-2007, Ext JS, LLC.
13990  *
13991  * Originally Released Under LGPL - original licence link has changed is not relivant.
13992  *
13993  * Fork - LGPL
13994  * <script type="text/javascript">
13995  */
13996
13997 /**
13998  * @class Roo.View
13999  * @extends Roo.util.Observable
14000  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
14001  * This class also supports single and multi selection modes. <br>
14002  * Create a data model bound view:
14003  <pre><code>
14004  var store = new Roo.data.Store(...);
14005
14006  var view = new Roo.View({
14007     el : "my-element",
14008     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
14009  
14010     singleSelect: true,
14011     selectedClass: "ydataview-selected",
14012     store: store
14013  });
14014
14015  // listen for node click?
14016  view.on("click", function(vw, index, node, e){
14017  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14018  });
14019
14020  // load XML data
14021  dataModel.load("foobar.xml");
14022  </code></pre>
14023  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14024  * <br><br>
14025  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14026  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14027  * 
14028  * Note: old style constructor is still suported (container, template, config)
14029  * 
14030  * @constructor
14031  * Create a new View
14032  * @param {Object} config The config object
14033  * 
14034  */
14035 Roo.View = function(config, depreciated_tpl, depreciated_config){
14036     
14037     this.parent = false;
14038     
14039     if (typeof(depreciated_tpl) == 'undefined') {
14040         // new way.. - universal constructor.
14041         Roo.apply(this, config);
14042         this.el  = Roo.get(this.el);
14043     } else {
14044         // old format..
14045         this.el  = Roo.get(config);
14046         this.tpl = depreciated_tpl;
14047         Roo.apply(this, depreciated_config);
14048     }
14049     this.wrapEl  = this.el.wrap().wrap();
14050     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14051     
14052     
14053     if(typeof(this.tpl) == "string"){
14054         this.tpl = new Roo.Template(this.tpl);
14055     } else {
14056         // support xtype ctors..
14057         this.tpl = new Roo.factory(this.tpl, Roo);
14058     }
14059     
14060     
14061     this.tpl.compile();
14062     
14063     /** @private */
14064     this.addEvents({
14065         /**
14066          * @event beforeclick
14067          * Fires before a click is processed. Returns false to cancel the default action.
14068          * @param {Roo.View} this
14069          * @param {Number} index The index of the target node
14070          * @param {HTMLElement} node The target node
14071          * @param {Roo.EventObject} e The raw event object
14072          */
14073             "beforeclick" : true,
14074         /**
14075          * @event click
14076          * Fires when a template node is clicked.
14077          * @param {Roo.View} this
14078          * @param {Number} index The index of the target node
14079          * @param {HTMLElement} node The target node
14080          * @param {Roo.EventObject} e The raw event object
14081          */
14082             "click" : true,
14083         /**
14084          * @event dblclick
14085          * Fires when a template node is double clicked.
14086          * @param {Roo.View} this
14087          * @param {Number} index The index of the target node
14088          * @param {HTMLElement} node The target node
14089          * @param {Roo.EventObject} e The raw event object
14090          */
14091             "dblclick" : true,
14092         /**
14093          * @event contextmenu
14094          * Fires when a template node is right clicked.
14095          * @param {Roo.View} this
14096          * @param {Number} index The index of the target node
14097          * @param {HTMLElement} node The target node
14098          * @param {Roo.EventObject} e The raw event object
14099          */
14100             "contextmenu" : true,
14101         /**
14102          * @event selectionchange
14103          * Fires when the selected nodes change.
14104          * @param {Roo.View} this
14105          * @param {Array} selections Array of the selected nodes
14106          */
14107             "selectionchange" : true,
14108     
14109         /**
14110          * @event beforeselect
14111          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14112          * @param {Roo.View} this
14113          * @param {HTMLElement} node The node to be selected
14114          * @param {Array} selections Array of currently selected nodes
14115          */
14116             "beforeselect" : true,
14117         /**
14118          * @event preparedata
14119          * Fires on every row to render, to allow you to change the data.
14120          * @param {Roo.View} this
14121          * @param {Object} data to be rendered (change this)
14122          */
14123           "preparedata" : true
14124           
14125           
14126         });
14127
14128
14129
14130     this.el.on({
14131         "click": this.onClick,
14132         "dblclick": this.onDblClick,
14133         "contextmenu": this.onContextMenu,
14134         scope:this
14135     });
14136
14137     this.selections = [];
14138     this.nodes = [];
14139     this.cmp = new Roo.CompositeElementLite([]);
14140     if(this.store){
14141         this.store = Roo.factory(this.store, Roo.data);
14142         this.setStore(this.store, true);
14143     }
14144     
14145     if ( this.footer && this.footer.xtype) {
14146            
14147          var fctr = this.wrapEl.appendChild(document.createElement("div"));
14148         
14149         this.footer.dataSource = this.store;
14150         this.footer.container = fctr;
14151         this.footer = Roo.factory(this.footer, Roo);
14152         fctr.insertFirst(this.el);
14153         
14154         // this is a bit insane - as the paging toolbar seems to detach the el..
14155 //        dom.parentNode.parentNode.parentNode
14156          // they get detached?
14157     }
14158     
14159     
14160     Roo.View.superclass.constructor.call(this);
14161     
14162     
14163 };
14164
14165 Roo.extend(Roo.View, Roo.util.Observable, {
14166     
14167      /**
14168      * @cfg {Roo.data.Store} store Data store to load data from.
14169      */
14170     store : false,
14171     
14172     /**
14173      * @cfg {String|Roo.Element} el The container element.
14174      */
14175     el : '',
14176     
14177     /**
14178      * @cfg {String|Roo.Template} tpl The template used by this View 
14179      */
14180     tpl : false,
14181     /**
14182      * @cfg {String} dataName the named area of the template to use as the data area
14183      *                          Works with domtemplates roo-name="name"
14184      */
14185     dataName: false,
14186     /**
14187      * @cfg {String} selectedClass The css class to add to selected nodes
14188      */
14189     selectedClass : "x-view-selected",
14190      /**
14191      * @cfg {String} emptyText The empty text to show when nothing is loaded.
14192      */
14193     emptyText : "",
14194     
14195     /**
14196      * @cfg {String} text to display on mask (default Loading)
14197      */
14198     mask : false,
14199     /**
14200      * @cfg {Boolean} multiSelect Allow multiple selection
14201      */
14202     multiSelect : false,
14203     /**
14204      * @cfg {Boolean} singleSelect Allow single selection
14205      */
14206     singleSelect:  false,
14207     
14208     /**
14209      * @cfg {Boolean} toggleSelect - selecting 
14210      */
14211     toggleSelect : false,
14212     
14213     /**
14214      * @cfg {Boolean} tickable - selecting 
14215      */
14216     tickable : false,
14217     
14218     /**
14219      * Returns the element this view is bound to.
14220      * @return {Roo.Element}
14221      */
14222     getEl : function(){
14223         return this.wrapEl;
14224     },
14225     
14226     
14227
14228     /**
14229      * Refreshes the view. - called by datachanged on the store. - do not call directly.
14230      */
14231     refresh : function(){
14232         //Roo.log('refresh');
14233         var t = this.tpl;
14234         
14235         // if we are using something like 'domtemplate', then
14236         // the what gets used is:
14237         // t.applySubtemplate(NAME, data, wrapping data..)
14238         // the outer template then get' applied with
14239         //     the store 'extra data'
14240         // and the body get's added to the
14241         //      roo-name="data" node?
14242         //      <span class='roo-tpl-{name}'></span> ?????
14243         
14244         
14245         
14246         this.clearSelections();
14247         this.el.update("");
14248         var html = [];
14249         var records = this.store.getRange();
14250         if(records.length < 1) {
14251             
14252             // is this valid??  = should it render a template??
14253             
14254             this.el.update(this.emptyText);
14255             return;
14256         }
14257         var el = this.el;
14258         if (this.dataName) {
14259             this.el.update(t.apply(this.store.meta)); //????
14260             el = this.el.child('.roo-tpl-' + this.dataName);
14261         }
14262         
14263         for(var i = 0, len = records.length; i < len; i++){
14264             var data = this.prepareData(records[i].data, i, records[i]);
14265             this.fireEvent("preparedata", this, data, i, records[i]);
14266             
14267             var d = Roo.apply({}, data);
14268             
14269             if(this.tickable){
14270                 Roo.apply(d, {'roo-id' : Roo.id()});
14271                 
14272                 var _this = this;
14273             
14274                 Roo.each(this.parent.item, function(item){
14275                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14276                         return;
14277                     }
14278                     Roo.apply(d, {'roo-data-checked' : 'checked'});
14279                 });
14280             }
14281             
14282             html[html.length] = Roo.util.Format.trim(
14283                 this.dataName ?
14284                     t.applySubtemplate(this.dataName, d, this.store.meta) :
14285                     t.apply(d)
14286             );
14287         }
14288         
14289         
14290         
14291         el.update(html.join(""));
14292         this.nodes = el.dom.childNodes;
14293         this.updateIndexes(0);
14294     },
14295     
14296
14297     /**
14298      * Function to override to reformat the data that is sent to
14299      * the template for each node.
14300      * DEPRICATED - use the preparedata event handler.
14301      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14302      * a JSON object for an UpdateManager bound view).
14303      */
14304     prepareData : function(data, index, record)
14305     {
14306         this.fireEvent("preparedata", this, data, index, record);
14307         return data;
14308     },
14309
14310     onUpdate : function(ds, record){
14311         // Roo.log('on update');   
14312         this.clearSelections();
14313         var index = this.store.indexOf(record);
14314         var n = this.nodes[index];
14315         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14316         n.parentNode.removeChild(n);
14317         this.updateIndexes(index, index);
14318     },
14319
14320     
14321     
14322 // --------- FIXME     
14323     onAdd : function(ds, records, index)
14324     {
14325         //Roo.log(['on Add', ds, records, index] );        
14326         this.clearSelections();
14327         if(this.nodes.length == 0){
14328             this.refresh();
14329             return;
14330         }
14331         var n = this.nodes[index];
14332         for(var i = 0, len = records.length; i < len; i++){
14333             var d = this.prepareData(records[i].data, i, records[i]);
14334             if(n){
14335                 this.tpl.insertBefore(n, d);
14336             }else{
14337                 
14338                 this.tpl.append(this.el, d);
14339             }
14340         }
14341         this.updateIndexes(index);
14342     },
14343
14344     onRemove : function(ds, record, index){
14345        // Roo.log('onRemove');
14346         this.clearSelections();
14347         var el = this.dataName  ?
14348             this.el.child('.roo-tpl-' + this.dataName) :
14349             this.el; 
14350         
14351         el.dom.removeChild(this.nodes[index]);
14352         this.updateIndexes(index);
14353     },
14354
14355     /**
14356      * Refresh an individual node.
14357      * @param {Number} index
14358      */
14359     refreshNode : function(index){
14360         this.onUpdate(this.store, this.store.getAt(index));
14361     },
14362
14363     updateIndexes : function(startIndex, endIndex){
14364         var ns = this.nodes;
14365         startIndex = startIndex || 0;
14366         endIndex = endIndex || ns.length - 1;
14367         for(var i = startIndex; i <= endIndex; i++){
14368             ns[i].nodeIndex = i;
14369         }
14370     },
14371
14372     /**
14373      * Changes the data store this view uses and refresh the view.
14374      * @param {Store} store
14375      */
14376     setStore : function(store, initial){
14377         if(!initial && this.store){
14378             this.store.un("datachanged", this.refresh);
14379             this.store.un("add", this.onAdd);
14380             this.store.un("remove", this.onRemove);
14381             this.store.un("update", this.onUpdate);
14382             this.store.un("clear", this.refresh);
14383             this.store.un("beforeload", this.onBeforeLoad);
14384             this.store.un("load", this.onLoad);
14385             this.store.un("loadexception", this.onLoad);
14386         }
14387         if(store){
14388           
14389             store.on("datachanged", this.refresh, this);
14390             store.on("add", this.onAdd, this);
14391             store.on("remove", this.onRemove, this);
14392             store.on("update", this.onUpdate, this);
14393             store.on("clear", this.refresh, this);
14394             store.on("beforeload", this.onBeforeLoad, this);
14395             store.on("load", this.onLoad, this);
14396             store.on("loadexception", this.onLoad, this);
14397         }
14398         
14399         if(store){
14400             this.refresh();
14401         }
14402     },
14403     /**
14404      * onbeforeLoad - masks the loading area.
14405      *
14406      */
14407     onBeforeLoad : function(store,opts)
14408     {
14409          //Roo.log('onBeforeLoad');   
14410         if (!opts.add) {
14411             this.el.update("");
14412         }
14413         this.el.mask(this.mask ? this.mask : "Loading" ); 
14414     },
14415     onLoad : function ()
14416     {
14417         this.el.unmask();
14418     },
14419     
14420
14421     /**
14422      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14423      * @param {HTMLElement} node
14424      * @return {HTMLElement} The template node
14425      */
14426     findItemFromChild : function(node){
14427         var el = this.dataName  ?
14428             this.el.child('.roo-tpl-' + this.dataName,true) :
14429             this.el.dom; 
14430         
14431         if(!node || node.parentNode == el){
14432                     return node;
14433             }
14434             var p = node.parentNode;
14435             while(p && p != el){
14436             if(p.parentNode == el){
14437                 return p;
14438             }
14439             p = p.parentNode;
14440         }
14441             return null;
14442     },
14443
14444     /** @ignore */
14445     onClick : function(e){
14446         var item = this.findItemFromChild(e.getTarget());
14447         if(item){
14448             var index = this.indexOf(item);
14449             if(this.onItemClick(item, index, e) !== false){
14450                 this.fireEvent("click", this, index, item, e);
14451             }
14452         }else{
14453             this.clearSelections();
14454         }
14455     },
14456
14457     /** @ignore */
14458     onContextMenu : function(e){
14459         var item = this.findItemFromChild(e.getTarget());
14460         if(item){
14461             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14462         }
14463     },
14464
14465     /** @ignore */
14466     onDblClick : function(e){
14467         var item = this.findItemFromChild(e.getTarget());
14468         if(item){
14469             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14470         }
14471     },
14472
14473     onItemClick : function(item, index, e)
14474     {
14475         if(this.fireEvent("beforeclick", this, index, item, e) === false){
14476             return false;
14477         }
14478         if (this.toggleSelect) {
14479             var m = this.isSelected(item) ? 'unselect' : 'select';
14480             //Roo.log(m);
14481             var _t = this;
14482             _t[m](item, true, false);
14483             return true;
14484         }
14485         if(this.multiSelect || this.singleSelect){
14486             if(this.multiSelect && e.shiftKey && this.lastSelection){
14487                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14488             }else{
14489                 this.select(item, this.multiSelect && e.ctrlKey);
14490                 this.lastSelection = item;
14491             }
14492             
14493             if(!this.tickable){
14494                 e.preventDefault();
14495             }
14496             
14497         }
14498         return true;
14499     },
14500
14501     /**
14502      * Get the number of selected nodes.
14503      * @return {Number}
14504      */
14505     getSelectionCount : function(){
14506         return this.selections.length;
14507     },
14508
14509     /**
14510      * Get the currently selected nodes.
14511      * @return {Array} An array of HTMLElements
14512      */
14513     getSelectedNodes : function(){
14514         return this.selections;
14515     },
14516
14517     /**
14518      * Get the indexes of the selected nodes.
14519      * @return {Array}
14520      */
14521     getSelectedIndexes : function(){
14522         var indexes = [], s = this.selections;
14523         for(var i = 0, len = s.length; i < len; i++){
14524             indexes.push(s[i].nodeIndex);
14525         }
14526         return indexes;
14527     },
14528
14529     /**
14530      * Clear all selections
14531      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14532      */
14533     clearSelections : function(suppressEvent){
14534         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14535             this.cmp.elements = this.selections;
14536             this.cmp.removeClass(this.selectedClass);
14537             this.selections = [];
14538             if(!suppressEvent){
14539                 this.fireEvent("selectionchange", this, this.selections);
14540             }
14541         }
14542     },
14543
14544     /**
14545      * Returns true if the passed node is selected
14546      * @param {HTMLElement/Number} node The node or node index
14547      * @return {Boolean}
14548      */
14549     isSelected : function(node){
14550         var s = this.selections;
14551         if(s.length < 1){
14552             return false;
14553         }
14554         node = this.getNode(node);
14555         return s.indexOf(node) !== -1;
14556     },
14557
14558     /**
14559      * Selects nodes.
14560      * @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
14561      * @param {Boolean} keepExisting (optional) true to keep existing selections
14562      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14563      */
14564     select : function(nodeInfo, keepExisting, suppressEvent){
14565         if(nodeInfo instanceof Array){
14566             if(!keepExisting){
14567                 this.clearSelections(true);
14568             }
14569             for(var i = 0, len = nodeInfo.length; i < len; i++){
14570                 this.select(nodeInfo[i], true, true);
14571             }
14572             return;
14573         } 
14574         var node = this.getNode(nodeInfo);
14575         if(!node || this.isSelected(node)){
14576             return; // already selected.
14577         }
14578         if(!keepExisting){
14579             this.clearSelections(true);
14580         }
14581         
14582         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14583             Roo.fly(node).addClass(this.selectedClass);
14584             this.selections.push(node);
14585             if(!suppressEvent){
14586                 this.fireEvent("selectionchange", this, this.selections);
14587             }
14588         }
14589         
14590         
14591     },
14592       /**
14593      * Unselects nodes.
14594      * @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
14595      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14596      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14597      */
14598     unselect : function(nodeInfo, keepExisting, suppressEvent)
14599     {
14600         if(nodeInfo instanceof Array){
14601             Roo.each(this.selections, function(s) {
14602                 this.unselect(s, nodeInfo);
14603             }, this);
14604             return;
14605         }
14606         var node = this.getNode(nodeInfo);
14607         if(!node || !this.isSelected(node)){
14608             //Roo.log("not selected");
14609             return; // not selected.
14610         }
14611         // fireevent???
14612         var ns = [];
14613         Roo.each(this.selections, function(s) {
14614             if (s == node ) {
14615                 Roo.fly(node).removeClass(this.selectedClass);
14616
14617                 return;
14618             }
14619             ns.push(s);
14620         },this);
14621         
14622         this.selections= ns;
14623         this.fireEvent("selectionchange", this, this.selections);
14624     },
14625
14626     /**
14627      * Gets a template node.
14628      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14629      * @return {HTMLElement} The node or null if it wasn't found
14630      */
14631     getNode : function(nodeInfo){
14632         if(typeof nodeInfo == "string"){
14633             return document.getElementById(nodeInfo);
14634         }else if(typeof nodeInfo == "number"){
14635             return this.nodes[nodeInfo];
14636         }
14637         return nodeInfo;
14638     },
14639
14640     /**
14641      * Gets a range template nodes.
14642      * @param {Number} startIndex
14643      * @param {Number} endIndex
14644      * @return {Array} An array of nodes
14645      */
14646     getNodes : function(start, end){
14647         var ns = this.nodes;
14648         start = start || 0;
14649         end = typeof end == "undefined" ? ns.length - 1 : end;
14650         var nodes = [];
14651         if(start <= end){
14652             for(var i = start; i <= end; i++){
14653                 nodes.push(ns[i]);
14654             }
14655         } else{
14656             for(var i = start; i >= end; i--){
14657                 nodes.push(ns[i]);
14658             }
14659         }
14660         return nodes;
14661     },
14662
14663     /**
14664      * Finds the index of the passed node
14665      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14666      * @return {Number} The index of the node or -1
14667      */
14668     indexOf : function(node){
14669         node = this.getNode(node);
14670         if(typeof node.nodeIndex == "number"){
14671             return node.nodeIndex;
14672         }
14673         var ns = this.nodes;
14674         for(var i = 0, len = ns.length; i < len; i++){
14675             if(ns[i] == node){
14676                 return i;
14677             }
14678         }
14679         return -1;
14680     }
14681 });
14682 /*
14683  * - LGPL
14684  *
14685  * based on jquery fullcalendar
14686  * 
14687  */
14688
14689 Roo.bootstrap = Roo.bootstrap || {};
14690 /**
14691  * @class Roo.bootstrap.Calendar
14692  * @extends Roo.bootstrap.Component
14693  * Bootstrap Calendar class
14694  * @cfg {Boolean} loadMask (true|false) default false
14695  * @cfg {Object} header generate the user specific header of the calendar, default false
14696
14697  * @constructor
14698  * Create a new Container
14699  * @param {Object} config The config object
14700  */
14701
14702
14703
14704 Roo.bootstrap.Calendar = function(config){
14705     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14706      this.addEvents({
14707         /**
14708              * @event select
14709              * Fires when a date is selected
14710              * @param {DatePicker} this
14711              * @param {Date} date The selected date
14712              */
14713         'select': true,
14714         /**
14715              * @event monthchange
14716              * Fires when the displayed month changes 
14717              * @param {DatePicker} this
14718              * @param {Date} date The selected month
14719              */
14720         'monthchange': true,
14721         /**
14722              * @event evententer
14723              * Fires when mouse over an event
14724              * @param {Calendar} this
14725              * @param {event} Event
14726              */
14727         'evententer': true,
14728         /**
14729              * @event eventleave
14730              * Fires when the mouse leaves an
14731              * @param {Calendar} this
14732              * @param {event}
14733              */
14734         'eventleave': true,
14735         /**
14736              * @event eventclick
14737              * Fires when the mouse click an
14738              * @param {Calendar} this
14739              * @param {event}
14740              */
14741         'eventclick': true
14742         
14743     });
14744
14745 };
14746
14747 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
14748     
14749      /**
14750      * @cfg {Number} startDay
14751      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
14752      */
14753     startDay : 0,
14754     
14755     loadMask : false,
14756     
14757     header : false,
14758       
14759     getAutoCreate : function(){
14760         
14761         
14762         var fc_button = function(name, corner, style, content ) {
14763             return Roo.apply({},{
14764                 tag : 'span',
14765                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
14766                          (corner.length ?
14767                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
14768                             ''
14769                         ),
14770                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
14771                 unselectable: 'on'
14772             });
14773         };
14774         
14775         var header = {};
14776         
14777         if(!this.header){
14778             header = {
14779                 tag : 'table',
14780                 cls : 'fc-header',
14781                 style : 'width:100%',
14782                 cn : [
14783                     {
14784                         tag: 'tr',
14785                         cn : [
14786                             {
14787                                 tag : 'td',
14788                                 cls : 'fc-header-left',
14789                                 cn : [
14790                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
14791                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
14792                                     { tag: 'span', cls: 'fc-header-space' },
14793                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
14794
14795
14796                                 ]
14797                             },
14798
14799                             {
14800                                 tag : 'td',
14801                                 cls : 'fc-header-center',
14802                                 cn : [
14803                                     {
14804                                         tag: 'span',
14805                                         cls: 'fc-header-title',
14806                                         cn : {
14807                                             tag: 'H2',
14808                                             html : 'month / year'
14809                                         }
14810                                     }
14811
14812                                 ]
14813                             },
14814                             {
14815                                 tag : 'td',
14816                                 cls : 'fc-header-right',
14817                                 cn : [
14818                               /*      fc_button('month', 'left', '', 'month' ),
14819                                     fc_button('week', '', '', 'week' ),
14820                                     fc_button('day', 'right', '', 'day' )
14821                                 */    
14822
14823                                 ]
14824                             }
14825
14826                         ]
14827                     }
14828                 ]
14829             };
14830         }
14831         
14832         header = this.header;
14833         
14834        
14835         var cal_heads = function() {
14836             var ret = [];
14837             // fixme - handle this.
14838             
14839             for (var i =0; i < Date.dayNames.length; i++) {
14840                 var d = Date.dayNames[i];
14841                 ret.push({
14842                     tag: 'th',
14843                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
14844                     html : d.substring(0,3)
14845                 });
14846                 
14847             }
14848             ret[0].cls += ' fc-first';
14849             ret[6].cls += ' fc-last';
14850             return ret;
14851         };
14852         var cal_cell = function(n) {
14853             return  {
14854                 tag: 'td',
14855                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
14856                 cn : [
14857                     {
14858                         cn : [
14859                             {
14860                                 cls: 'fc-day-number',
14861                                 html: 'D'
14862                             },
14863                             {
14864                                 cls: 'fc-day-content',
14865                              
14866                                 cn : [
14867                                      {
14868                                         style: 'position: relative;' // height: 17px;
14869                                     }
14870                                 ]
14871                             }
14872                             
14873                             
14874                         ]
14875                     }
14876                 ]
14877                 
14878             }
14879         };
14880         var cal_rows = function() {
14881             
14882             var ret = [];
14883             for (var r = 0; r < 6; r++) {
14884                 var row= {
14885                     tag : 'tr',
14886                     cls : 'fc-week',
14887                     cn : []
14888                 };
14889                 
14890                 for (var i =0; i < Date.dayNames.length; i++) {
14891                     var d = Date.dayNames[i];
14892                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
14893
14894                 }
14895                 row.cn[0].cls+=' fc-first';
14896                 row.cn[0].cn[0].style = 'min-height:90px';
14897                 row.cn[6].cls+=' fc-last';
14898                 ret.push(row);
14899                 
14900             }
14901             ret[0].cls += ' fc-first';
14902             ret[4].cls += ' fc-prev-last';
14903             ret[5].cls += ' fc-last';
14904             return ret;
14905             
14906         };
14907         
14908         var cal_table = {
14909             tag: 'table',
14910             cls: 'fc-border-separate',
14911             style : 'width:100%',
14912             cellspacing  : 0,
14913             cn : [
14914                 { 
14915                     tag: 'thead',
14916                     cn : [
14917                         { 
14918                             tag: 'tr',
14919                             cls : 'fc-first fc-last',
14920                             cn : cal_heads()
14921                         }
14922                     ]
14923                 },
14924                 { 
14925                     tag: 'tbody',
14926                     cn : cal_rows()
14927                 }
14928                   
14929             ]
14930         };
14931          
14932          var cfg = {
14933             cls : 'fc fc-ltr',
14934             cn : [
14935                 header,
14936                 {
14937                     cls : 'fc-content',
14938                     style : "position: relative;",
14939                     cn : [
14940                         {
14941                             cls : 'fc-view fc-view-month fc-grid',
14942                             style : 'position: relative',
14943                             unselectable : 'on',
14944                             cn : [
14945                                 {
14946                                     cls : 'fc-event-container',
14947                                     style : 'position:absolute;z-index:8;top:0;left:0;'
14948                                 },
14949                                 cal_table
14950                             ]
14951                         }
14952                     ]
14953     
14954                 }
14955            ] 
14956             
14957         };
14958         
14959          
14960         
14961         return cfg;
14962     },
14963     
14964     
14965     initEvents : function()
14966     {
14967         if(!this.store){
14968             throw "can not find store for calendar";
14969         }
14970         
14971         var mark = {
14972             tag: "div",
14973             cls:"x-dlg-mask",
14974             style: "text-align:center",
14975             cn: [
14976                 {
14977                     tag: "div",
14978                     style: "background-color:white;width:50%;margin:250 auto",
14979                     cn: [
14980                         {
14981                             tag: "img",
14982                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
14983                         },
14984                         {
14985                             tag: "span",
14986                             html: "Loading"
14987                         }
14988                         
14989                     ]
14990                 }
14991             ]
14992         };
14993         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
14994         
14995         var size = this.el.select('.fc-content', true).first().getSize();
14996         this.maskEl.setSize(size.width, size.height);
14997         this.maskEl.enableDisplayMode("block");
14998         if(!this.loadMask){
14999             this.maskEl.hide();
15000         }
15001         
15002         this.store = Roo.factory(this.store, Roo.data);
15003         this.store.on('load', this.onLoad, this);
15004         this.store.on('beforeload', this.onBeforeLoad, this);
15005         
15006         this.resize();
15007         
15008         this.cells = this.el.select('.fc-day',true);
15009         //Roo.log(this.cells);
15010         this.textNodes = this.el.query('.fc-day-number');
15011         this.cells.addClassOnOver('fc-state-hover');
15012         
15013         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15014         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15015         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15016         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15017         
15018         this.on('monthchange', this.onMonthChange, this);
15019         
15020         this.update(new Date().clearTime());
15021     },
15022     
15023     resize : function() {
15024         var sz  = this.el.getSize();
15025         
15026         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15027         this.el.select('.fc-day-content div',true).setHeight(34);
15028     },
15029     
15030     
15031     // private
15032     showPrevMonth : function(e){
15033         this.update(this.activeDate.add("mo", -1));
15034     },
15035     showToday : function(e){
15036         this.update(new Date().clearTime());
15037     },
15038     // private
15039     showNextMonth : function(e){
15040         this.update(this.activeDate.add("mo", 1));
15041     },
15042
15043     // private
15044     showPrevYear : function(){
15045         this.update(this.activeDate.add("y", -1));
15046     },
15047
15048     // private
15049     showNextYear : function(){
15050         this.update(this.activeDate.add("y", 1));
15051     },
15052
15053     
15054    // private
15055     update : function(date)
15056     {
15057         var vd = this.activeDate;
15058         this.activeDate = date;
15059 //        if(vd && this.el){
15060 //            var t = date.getTime();
15061 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15062 //                Roo.log('using add remove');
15063 //                
15064 //                this.fireEvent('monthchange', this, date);
15065 //                
15066 //                this.cells.removeClass("fc-state-highlight");
15067 //                this.cells.each(function(c){
15068 //                   if(c.dateValue == t){
15069 //                       c.addClass("fc-state-highlight");
15070 //                       setTimeout(function(){
15071 //                            try{c.dom.firstChild.focus();}catch(e){}
15072 //                       }, 50);
15073 //                       return false;
15074 //                   }
15075 //                   return true;
15076 //                });
15077 //                return;
15078 //            }
15079 //        }
15080         
15081         var days = date.getDaysInMonth();
15082         
15083         var firstOfMonth = date.getFirstDateOfMonth();
15084         var startingPos = firstOfMonth.getDay()-this.startDay;
15085         
15086         if(startingPos < this.startDay){
15087             startingPos += 7;
15088         }
15089         
15090         var pm = date.add(Date.MONTH, -1);
15091         var prevStart = pm.getDaysInMonth()-startingPos;
15092 //        
15093         this.cells = this.el.select('.fc-day',true);
15094         this.textNodes = this.el.query('.fc-day-number');
15095         this.cells.addClassOnOver('fc-state-hover');
15096         
15097         var cells = this.cells.elements;
15098         var textEls = this.textNodes;
15099         
15100         Roo.each(cells, function(cell){
15101             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15102         });
15103         
15104         days += startingPos;
15105
15106         // convert everything to numbers so it's fast
15107         var day = 86400000;
15108         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15109         //Roo.log(d);
15110         //Roo.log(pm);
15111         //Roo.log(prevStart);
15112         
15113         var today = new Date().clearTime().getTime();
15114         var sel = date.clearTime().getTime();
15115         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15116         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15117         var ddMatch = this.disabledDatesRE;
15118         var ddText = this.disabledDatesText;
15119         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15120         var ddaysText = this.disabledDaysText;
15121         var format = this.format;
15122         
15123         var setCellClass = function(cal, cell){
15124             cell.row = 0;
15125             cell.events = [];
15126             cell.more = [];
15127             //Roo.log('set Cell Class');
15128             cell.title = "";
15129             var t = d.getTime();
15130             
15131             //Roo.log(d);
15132             
15133             cell.dateValue = t;
15134             if(t == today){
15135                 cell.className += " fc-today";
15136                 cell.className += " fc-state-highlight";
15137                 cell.title = cal.todayText;
15138             }
15139             if(t == sel){
15140                 // disable highlight in other month..
15141                 //cell.className += " fc-state-highlight";
15142                 
15143             }
15144             // disabling
15145             if(t < min) {
15146                 cell.className = " fc-state-disabled";
15147                 cell.title = cal.minText;
15148                 return;
15149             }
15150             if(t > max) {
15151                 cell.className = " fc-state-disabled";
15152                 cell.title = cal.maxText;
15153                 return;
15154             }
15155             if(ddays){
15156                 if(ddays.indexOf(d.getDay()) != -1){
15157                     cell.title = ddaysText;
15158                     cell.className = " fc-state-disabled";
15159                 }
15160             }
15161             if(ddMatch && format){
15162                 var fvalue = d.dateFormat(format);
15163                 if(ddMatch.test(fvalue)){
15164                     cell.title = ddText.replace("%0", fvalue);
15165                     cell.className = " fc-state-disabled";
15166                 }
15167             }
15168             
15169             if (!cell.initialClassName) {
15170                 cell.initialClassName = cell.dom.className;
15171             }
15172             
15173             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
15174         };
15175
15176         var i = 0;
15177         
15178         for(; i < startingPos; i++) {
15179             textEls[i].innerHTML = (++prevStart);
15180             d.setDate(d.getDate()+1);
15181             
15182             cells[i].className = "fc-past fc-other-month";
15183             setCellClass(this, cells[i]);
15184         }
15185         
15186         var intDay = 0;
15187         
15188         for(; i < days; i++){
15189             intDay = i - startingPos + 1;
15190             textEls[i].innerHTML = (intDay);
15191             d.setDate(d.getDate()+1);
15192             
15193             cells[i].className = ''; // "x-date-active";
15194             setCellClass(this, cells[i]);
15195         }
15196         var extraDays = 0;
15197         
15198         for(; i < 42; i++) {
15199             textEls[i].innerHTML = (++extraDays);
15200             d.setDate(d.getDate()+1);
15201             
15202             cells[i].className = "fc-future fc-other-month";
15203             setCellClass(this, cells[i]);
15204         }
15205         
15206         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15207         
15208         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15209         
15210         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15211         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15212         
15213         if(totalRows != 6){
15214             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15215             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15216         }
15217         
15218         this.fireEvent('monthchange', this, date);
15219         
15220         
15221         /*
15222         if(!this.internalRender){
15223             var main = this.el.dom.firstChild;
15224             var w = main.offsetWidth;
15225             this.el.setWidth(w + this.el.getBorderWidth("lr"));
15226             Roo.fly(main).setWidth(w);
15227             this.internalRender = true;
15228             // opera does not respect the auto grow header center column
15229             // then, after it gets a width opera refuses to recalculate
15230             // without a second pass
15231             if(Roo.isOpera && !this.secondPass){
15232                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15233                 this.secondPass = true;
15234                 this.update.defer(10, this, [date]);
15235             }
15236         }
15237         */
15238         
15239     },
15240     
15241     findCell : function(dt) {
15242         dt = dt.clearTime().getTime();
15243         var ret = false;
15244         this.cells.each(function(c){
15245             //Roo.log("check " +c.dateValue + '?=' + dt);
15246             if(c.dateValue == dt){
15247                 ret = c;
15248                 return false;
15249             }
15250             return true;
15251         });
15252         
15253         return ret;
15254     },
15255     
15256     findCells : function(ev) {
15257         var s = ev.start.clone().clearTime().getTime();
15258        // Roo.log(s);
15259         var e= ev.end.clone().clearTime().getTime();
15260        // Roo.log(e);
15261         var ret = [];
15262         this.cells.each(function(c){
15263              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15264             
15265             if(c.dateValue > e){
15266                 return ;
15267             }
15268             if(c.dateValue < s){
15269                 return ;
15270             }
15271             ret.push(c);
15272         });
15273         
15274         return ret;    
15275     },
15276     
15277 //    findBestRow: function(cells)
15278 //    {
15279 //        var ret = 0;
15280 //        
15281 //        for (var i =0 ; i < cells.length;i++) {
15282 //            ret  = Math.max(cells[i].rows || 0,ret);
15283 //        }
15284 //        return ret;
15285 //        
15286 //    },
15287     
15288     
15289     addItem : function(ev)
15290     {
15291         // look for vertical location slot in
15292         var cells = this.findCells(ev);
15293         
15294 //        ev.row = this.findBestRow(cells);
15295         
15296         // work out the location.
15297         
15298         var crow = false;
15299         var rows = [];
15300         for(var i =0; i < cells.length; i++) {
15301             
15302             cells[i].row = cells[0].row;
15303             
15304             if(i == 0){
15305                 cells[i].row = cells[i].row + 1;
15306             }
15307             
15308             if (!crow) {
15309                 crow = {
15310                     start : cells[i],
15311                     end :  cells[i]
15312                 };
15313                 continue;
15314             }
15315             if (crow.start.getY() == cells[i].getY()) {
15316                 // on same row.
15317                 crow.end = cells[i];
15318                 continue;
15319             }
15320             // different row.
15321             rows.push(crow);
15322             crow = {
15323                 start: cells[i],
15324                 end : cells[i]
15325             };
15326             
15327         }
15328         
15329         rows.push(crow);
15330         ev.els = [];
15331         ev.rows = rows;
15332         ev.cells = cells;
15333         
15334         cells[0].events.push(ev);
15335         
15336         this.calevents.push(ev);
15337     },
15338     
15339     clearEvents: function() {
15340         
15341         if(!this.calevents){
15342             return;
15343         }
15344         
15345         Roo.each(this.cells.elements, function(c){
15346             c.row = 0;
15347             c.events = [];
15348             c.more = [];
15349         });
15350         
15351         Roo.each(this.calevents, function(e) {
15352             Roo.each(e.els, function(el) {
15353                 el.un('mouseenter' ,this.onEventEnter, this);
15354                 el.un('mouseleave' ,this.onEventLeave, this);
15355                 el.remove();
15356             },this);
15357         },this);
15358         
15359         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
15360             e.remove();
15361         });
15362         
15363     },
15364     
15365     renderEvents: function()
15366     {   
15367         var _this = this;
15368         
15369         this.cells.each(function(c) {
15370             
15371             if(c.row < 5){
15372                 return;
15373             }
15374             
15375             var ev = c.events;
15376             
15377             var r = 4;
15378             if(c.row != c.events.length){
15379                 r = 4 - (4 - (c.row - c.events.length));
15380             }
15381             
15382             c.events = ev.slice(0, r);
15383             c.more = ev.slice(r);
15384             
15385             if(c.more.length && c.more.length == 1){
15386                 c.events.push(c.more.pop());
15387             }
15388             
15389             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15390             
15391         });
15392             
15393         this.cells.each(function(c) {
15394             
15395             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15396             
15397             
15398             for (var e = 0; e < c.events.length; e++){
15399                 var ev = c.events[e];
15400                 var rows = ev.rows;
15401                 
15402                 for(var i = 0; i < rows.length; i++) {
15403                 
15404                     // how many rows should it span..
15405
15406                     var  cfg = {
15407                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15408                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15409
15410                         unselectable : "on",
15411                         cn : [
15412                             {
15413                                 cls: 'fc-event-inner',
15414                                 cn : [
15415     //                                {
15416     //                                  tag:'span',
15417     //                                  cls: 'fc-event-time',
15418     //                                  html : cells.length > 1 ? '' : ev.time
15419     //                                },
15420                                     {
15421                                       tag:'span',
15422                                       cls: 'fc-event-title',
15423                                       html : String.format('{0}', ev.title)
15424                                     }
15425
15426
15427                                 ]
15428                             },
15429                             {
15430                                 cls: 'ui-resizable-handle ui-resizable-e',
15431                                 html : '&nbsp;&nbsp;&nbsp'
15432                             }
15433
15434                         ]
15435                     };
15436
15437                     if (i == 0) {
15438                         cfg.cls += ' fc-event-start';
15439                     }
15440                     if ((i+1) == rows.length) {
15441                         cfg.cls += ' fc-event-end';
15442                     }
15443
15444                     var ctr = _this.el.select('.fc-event-container',true).first();
15445                     var cg = ctr.createChild(cfg);
15446
15447                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15448                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15449
15450                     var r = (c.more.length) ? 1 : 0;
15451                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
15452                     cg.setWidth(ebox.right - sbox.x -2);
15453
15454                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15455                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15456                     cg.on('click', _this.onEventClick, _this, ev);
15457
15458                     ev.els.push(cg);
15459                     
15460                 }
15461                 
15462             }
15463             
15464             
15465             if(c.more.length){
15466                 var  cfg = {
15467                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15468                     style : 'position: absolute',
15469                     unselectable : "on",
15470                     cn : [
15471                         {
15472                             cls: 'fc-event-inner',
15473                             cn : [
15474                                 {
15475                                   tag:'span',
15476                                   cls: 'fc-event-title',
15477                                   html : 'More'
15478                                 }
15479
15480
15481                             ]
15482                         },
15483                         {
15484                             cls: 'ui-resizable-handle ui-resizable-e',
15485                             html : '&nbsp;&nbsp;&nbsp'
15486                         }
15487
15488                     ]
15489                 };
15490
15491                 var ctr = _this.el.select('.fc-event-container',true).first();
15492                 var cg = ctr.createChild(cfg);
15493
15494                 var sbox = c.select('.fc-day-content',true).first().getBox();
15495                 var ebox = c.select('.fc-day-content',true).first().getBox();
15496                 //Roo.log(cg);
15497                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
15498                 cg.setWidth(ebox.right - sbox.x -2);
15499
15500                 cg.on('click', _this.onMoreEventClick, _this, c.more);
15501                 
15502             }
15503             
15504         });
15505         
15506         
15507         
15508     },
15509     
15510     onEventEnter: function (e, el,event,d) {
15511         this.fireEvent('evententer', this, el, event);
15512     },
15513     
15514     onEventLeave: function (e, el,event,d) {
15515         this.fireEvent('eventleave', this, el, event);
15516     },
15517     
15518     onEventClick: function (e, el,event,d) {
15519         this.fireEvent('eventclick', this, el, event);
15520     },
15521     
15522     onMonthChange: function () {
15523         this.store.load();
15524     },
15525     
15526     onMoreEventClick: function(e, el, more)
15527     {
15528         var _this = this;
15529         
15530         this.calpopover.placement = 'right';
15531         this.calpopover.setTitle('More');
15532         
15533         this.calpopover.setContent('');
15534         
15535         var ctr = this.calpopover.el.select('.popover-content', true).first();
15536         
15537         Roo.each(more, function(m){
15538             var cfg = {
15539                 cls : 'fc-event-hori fc-event-draggable',
15540                 html : m.title
15541             };
15542             var cg = ctr.createChild(cfg);
15543             
15544             cg.on('click', _this.onEventClick, _this, m);
15545         });
15546         
15547         this.calpopover.show(el);
15548         
15549         
15550     },
15551     
15552     onLoad: function () 
15553     {   
15554         this.calevents = [];
15555         var cal = this;
15556         
15557         if(this.store.getCount() > 0){
15558             this.store.data.each(function(d){
15559                cal.addItem({
15560                     id : d.data.id,
15561                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15562                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15563                     time : d.data.start_time,
15564                     title : d.data.title,
15565                     description : d.data.description,
15566                     venue : d.data.venue
15567                 });
15568             });
15569         }
15570         
15571         this.renderEvents();
15572         
15573         if(this.calevents.length && this.loadMask){
15574             this.maskEl.hide();
15575         }
15576     },
15577     
15578     onBeforeLoad: function()
15579     {
15580         this.clearEvents();
15581         if(this.loadMask){
15582             this.maskEl.show();
15583         }
15584     }
15585 });
15586
15587  
15588  /*
15589  * - LGPL
15590  *
15591  * element
15592  * 
15593  */
15594
15595 /**
15596  * @class Roo.bootstrap.Popover
15597  * @extends Roo.bootstrap.Component
15598  * Bootstrap Popover class
15599  * @cfg {String} html contents of the popover   (or false to use children..)
15600  * @cfg {String} title of popover (or false to hide)
15601  * @cfg {String} placement how it is placed
15602  * @cfg {String} trigger click || hover (or false to trigger manually)
15603  * @cfg {String} over what (parent or false to trigger manually.)
15604  * @cfg {Number} delay - delay before showing
15605  
15606  * @constructor
15607  * Create a new Popover
15608  * @param {Object} config The config object
15609  */
15610
15611 Roo.bootstrap.Popover = function(config){
15612     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15613 };
15614
15615 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
15616     
15617     title: 'Fill in a title',
15618     html: false,
15619     
15620     placement : 'right',
15621     trigger : 'hover', // hover
15622     
15623     delay : 0,
15624     
15625     over: 'parent',
15626     
15627     can_build_overlaid : false,
15628     
15629     getChildContainer : function()
15630     {
15631         return this.el.select('.popover-content',true).first();
15632     },
15633     
15634     getAutoCreate : function(){
15635          Roo.log('make popover?');
15636         var cfg = {
15637            cls : 'popover roo-dynamic',
15638            style: 'display:block',
15639            cn : [
15640                 {
15641                     cls : 'arrow'
15642                 },
15643                 {
15644                     cls : 'popover-inner',
15645                     cn : [
15646                         {
15647                             tag: 'h3',
15648                             cls: 'popover-title',
15649                             html : this.title
15650                         },
15651                         {
15652                             cls : 'popover-content',
15653                             html : this.html
15654                         }
15655                     ]
15656                     
15657                 }
15658            ]
15659         };
15660         
15661         return cfg;
15662     },
15663     setTitle: function(str)
15664     {
15665         this.title = str;
15666         this.el.select('.popover-title',true).first().dom.innerHTML = str;
15667     },
15668     setContent: function(str)
15669     {
15670         this.html = str;
15671         this.el.select('.popover-content',true).first().dom.innerHTML = str;
15672     },
15673     // as it get's added to the bottom of the page.
15674     onRender : function(ct, position)
15675     {
15676         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15677         if(!this.el){
15678             var cfg = Roo.apply({},  this.getAutoCreate());
15679             cfg.id = Roo.id();
15680             
15681             if (this.cls) {
15682                 cfg.cls += ' ' + this.cls;
15683             }
15684             if (this.style) {
15685                 cfg.style = this.style;
15686             }
15687             //Roo.log("adding to ");
15688             this.el = Roo.get(document.body).createChild(cfg, position);
15689             Roo.log(this.el);
15690         }
15691         this.initEvents();
15692     },
15693     
15694     initEvents : function()
15695     {
15696         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15697         this.el.enableDisplayMode('block');
15698         this.el.hide();
15699         if (this.over === false) {
15700             return; 
15701         }
15702         if (this.triggers === false) {
15703             return;
15704         }
15705         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15706         var triggers = this.trigger ? this.trigger.split(' ') : [];
15707         Roo.each(triggers, function(trigger) {
15708         
15709             if (trigger == 'click') {
15710                 on_el.on('click', this.toggle, this);
15711             } else if (trigger != 'manual') {
15712                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
15713                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
15714       
15715                 on_el.on(eventIn  ,this.enter, this);
15716                 on_el.on(eventOut, this.leave, this);
15717             }
15718         }, this);
15719         
15720     },
15721     
15722     
15723     // private
15724     timeout : null,
15725     hoverState : null,
15726     
15727     toggle : function () {
15728         this.hoverState == 'in' ? this.leave() : this.enter();
15729     },
15730     
15731     enter : function () {
15732        
15733     
15734         clearTimeout(this.timeout);
15735     
15736         this.hoverState = 'in';
15737     
15738         if (!this.delay || !this.delay.show) {
15739             this.show();
15740             return;
15741         }
15742         var _t = this;
15743         this.timeout = setTimeout(function () {
15744             if (_t.hoverState == 'in') {
15745                 _t.show();
15746             }
15747         }, this.delay.show)
15748     },
15749     leave : function() {
15750         clearTimeout(this.timeout);
15751     
15752         this.hoverState = 'out';
15753     
15754         if (!this.delay || !this.delay.hide) {
15755             this.hide();
15756             return;
15757         }
15758         var _t = this;
15759         this.timeout = setTimeout(function () {
15760             if (_t.hoverState == 'out') {
15761                 _t.hide();
15762             }
15763         }, this.delay.hide)
15764     },
15765     
15766     show : function (on_el)
15767     {
15768         if (!on_el) {
15769             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15770         }
15771         // set content.
15772         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
15773         if (this.html !== false) {
15774             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
15775         }
15776         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
15777         if (!this.title.length) {
15778             this.el.select('.popover-title',true).hide();
15779         }
15780         
15781         var placement = typeof this.placement == 'function' ?
15782             this.placement.call(this, this.el, on_el) :
15783             this.placement;
15784             
15785         var autoToken = /\s?auto?\s?/i;
15786         var autoPlace = autoToken.test(placement);
15787         if (autoPlace) {
15788             placement = placement.replace(autoToken, '') || 'top';
15789         }
15790         
15791         //this.el.detach()
15792         //this.el.setXY([0,0]);
15793         this.el.show();
15794         this.el.dom.style.display='block';
15795         this.el.addClass(placement);
15796         
15797         //this.el.appendTo(on_el);
15798         
15799         var p = this.getPosition();
15800         var box = this.el.getBox();
15801         
15802         if (autoPlace) {
15803             // fixme..
15804         }
15805         var align = Roo.bootstrap.Popover.alignment[placement];
15806         this.el.alignTo(on_el, align[0],align[1]);
15807         //var arrow = this.el.select('.arrow',true).first();
15808         //arrow.set(align[2], 
15809         
15810         this.el.addClass('in');
15811         
15812         
15813         if (this.el.hasClass('fade')) {
15814             // fade it?
15815         }
15816         
15817     },
15818     hide : function()
15819     {
15820         this.el.setXY([0,0]);
15821         this.el.removeClass('in');
15822         this.el.hide();
15823         this.hoverState = null;
15824         
15825     }
15826     
15827 });
15828
15829 Roo.bootstrap.Popover.alignment = {
15830     'left' : ['r-l', [-10,0], 'right'],
15831     'right' : ['l-r', [10,0], 'left'],
15832     'bottom' : ['t-b', [0,10], 'top'],
15833     'top' : [ 'b-t', [0,-10], 'bottom']
15834 };
15835
15836  /*
15837  * - LGPL
15838  *
15839  * Progress
15840  * 
15841  */
15842
15843 /**
15844  * @class Roo.bootstrap.Progress
15845  * @extends Roo.bootstrap.Component
15846  * Bootstrap Progress class
15847  * @cfg {Boolean} striped striped of the progress bar
15848  * @cfg {Boolean} active animated of the progress bar
15849  * 
15850  * 
15851  * @constructor
15852  * Create a new Progress
15853  * @param {Object} config The config object
15854  */
15855
15856 Roo.bootstrap.Progress = function(config){
15857     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
15858 };
15859
15860 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
15861     
15862     striped : false,
15863     active: false,
15864     
15865     getAutoCreate : function(){
15866         var cfg = {
15867             tag: 'div',
15868             cls: 'progress'
15869         };
15870         
15871         
15872         if(this.striped){
15873             cfg.cls += ' progress-striped';
15874         }
15875       
15876         if(this.active){
15877             cfg.cls += ' active';
15878         }
15879         
15880         
15881         return cfg;
15882     }
15883    
15884 });
15885
15886  
15887
15888  /*
15889  * - LGPL
15890  *
15891  * ProgressBar
15892  * 
15893  */
15894
15895 /**
15896  * @class Roo.bootstrap.ProgressBar
15897  * @extends Roo.bootstrap.Component
15898  * Bootstrap ProgressBar class
15899  * @cfg {Number} aria_valuenow aria-value now
15900  * @cfg {Number} aria_valuemin aria-value min
15901  * @cfg {Number} aria_valuemax aria-value max
15902  * @cfg {String} label label for the progress bar
15903  * @cfg {String} panel (success | info | warning | danger )
15904  * @cfg {String} role role of the progress bar
15905  * @cfg {String} sr_only text
15906  * 
15907  * 
15908  * @constructor
15909  * Create a new ProgressBar
15910  * @param {Object} config The config object
15911  */
15912
15913 Roo.bootstrap.ProgressBar = function(config){
15914     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
15915 };
15916
15917 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
15918     
15919     aria_valuenow : 0,
15920     aria_valuemin : 0,
15921     aria_valuemax : 100,
15922     label : false,
15923     panel : false,
15924     role : false,
15925     sr_only: false,
15926     
15927     getAutoCreate : function()
15928     {
15929         
15930         var cfg = {
15931             tag: 'div',
15932             cls: 'progress-bar',
15933             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
15934         };
15935         
15936         if(this.sr_only){
15937             cfg.cn = {
15938                 tag: 'span',
15939                 cls: 'sr-only',
15940                 html: this.sr_only
15941             }
15942         }
15943         
15944         if(this.role){
15945             cfg.role = this.role;
15946         }
15947         
15948         if(this.aria_valuenow){
15949             cfg['aria-valuenow'] = this.aria_valuenow;
15950         }
15951         
15952         if(this.aria_valuemin){
15953             cfg['aria-valuemin'] = this.aria_valuemin;
15954         }
15955         
15956         if(this.aria_valuemax){
15957             cfg['aria-valuemax'] = this.aria_valuemax;
15958         }
15959         
15960         if(this.label && !this.sr_only){
15961             cfg.html = this.label;
15962         }
15963         
15964         if(this.panel){
15965             cfg.cls += ' progress-bar-' + this.panel;
15966         }
15967         
15968         return cfg;
15969     },
15970     
15971     update : function(aria_valuenow)
15972     {
15973         this.aria_valuenow = aria_valuenow;
15974         
15975         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
15976     }
15977    
15978 });
15979
15980  
15981
15982  /*
15983  * - LGPL
15984  *
15985  * column
15986  * 
15987  */
15988
15989 /**
15990  * @class Roo.bootstrap.TabGroup
15991  * @extends Roo.bootstrap.Column
15992  * Bootstrap Column class
15993  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
15994  * @cfg {Boolean} carousel true to make the group behave like a carousel
15995  * @cfg {Boolean} bullets show bullets for the panels
15996  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
15997  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
15998  * @cfg {Number} timer auto slide timer .. default 0 millisecond
15999  * 
16000  * @constructor
16001  * Create a new TabGroup
16002  * @param {Object} config The config object
16003  */
16004
16005 Roo.bootstrap.TabGroup = function(config){
16006     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16007     if (!this.navId) {
16008         this.navId = Roo.id();
16009     }
16010     this.tabs = [];
16011     Roo.bootstrap.TabGroup.register(this);
16012     
16013 };
16014
16015 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
16016     
16017     carousel : false,
16018     transition : false,
16019     bullets : 0,
16020     timer : 0,
16021     autoslide : false,
16022     slideFn : false,
16023     slideOnTouch : false,
16024     
16025     getAutoCreate : function()
16026     {
16027         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16028         
16029         cfg.cls += ' tab-content';
16030         
16031         Roo.log('get auto create...............');
16032         
16033         if (this.carousel) {
16034             cfg.cls += ' carousel slide';
16035             
16036             cfg.cn = [{
16037                cls : 'carousel-inner'
16038             }];
16039         
16040             if(this.bullets  && !Roo.isTouch){
16041                 
16042                 var bullets = {
16043                     cls : 'carousel-bullets',
16044                     cn : []
16045                 };
16046                
16047                 if(this.bullets_cls){
16048                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16049                 }
16050                  /*
16051                 for (var i = 0; i < this.bullets; i++){
16052                     bullets.cn.push({
16053                         cls : 'bullet bullet-' + i
16054                     });
16055                 }
16056                 */
16057                 bullets.cn.push({
16058                     cls : 'clear'
16059                 });
16060                 
16061                 cfg.cn[0].cn = bullets;
16062             }
16063         }
16064         
16065         return cfg;
16066     },
16067     
16068     initEvents:  function()
16069     {
16070         Roo.log('-------- init events on tab group ---------');
16071         
16072         
16073         
16074         Roo.log(this);
16075         
16076         if(Roo.isTouch && this.slideOnTouch){
16077             this.el.on("touchstart", this.onTouchStart, this);
16078         }
16079         
16080         if(this.autoslide){
16081             var _this = this;
16082             
16083             this.slideFn = window.setInterval(function() {
16084                 _this.showPanelNext();
16085             }, this.timer);
16086         }
16087         
16088     },
16089     
16090     onTouchStart : function(e, el, o)
16091     {
16092         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
16093             return;
16094         }
16095         
16096         this.showPanelNext();
16097     },
16098     
16099     getChildContainer : function()
16100     {
16101         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
16102     },
16103     
16104     /**
16105     * register a Navigation item
16106     * @param {Roo.bootstrap.NavItem} the navitem to add
16107     */
16108     register : function(item)
16109     {
16110         this.tabs.push( item);
16111         item.navId = this.navId; // not really needed..
16112         this.addBullet();
16113     
16114     },
16115     
16116     getActivePanel : function()
16117     {
16118         var r = false;
16119         Roo.each(this.tabs, function(t) {
16120             if (t.active) {
16121                 r = t;
16122                 return false;
16123             }
16124             return null;
16125         });
16126         return r;
16127         
16128     },
16129     getPanelByName : function(n)
16130     {
16131         var r = false;
16132         Roo.each(this.tabs, function(t) {
16133             if (t.tabId == n) {
16134                 r = t;
16135                 return false;
16136             }
16137             return null;
16138         });
16139         return r;
16140     },
16141     indexOfPanel : function(p)
16142     {
16143         var r = false;
16144         Roo.each(this.tabs, function(t,i) {
16145             if (t.tabId == p.tabId) {
16146                 r = i;
16147                 return false;
16148             }
16149             return null;
16150         });
16151         return r;
16152     },
16153     /**
16154      * show a specific panel
16155      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16156      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16157      */
16158     showPanel : function (pan)
16159     {
16160         if(this.transition){
16161             Roo.log("waiting for the transitionend");
16162             return;
16163         }
16164         
16165         if (typeof(pan) == 'number') {
16166             pan = this.tabs[pan];
16167         }
16168         if (typeof(pan) == 'string') {
16169             pan = this.getPanelByName(pan);
16170         }
16171         if (pan.tabId == this.getActivePanel().tabId) {
16172             return true;
16173         }
16174         var cur = this.getActivePanel();
16175         
16176         if (false === cur.fireEvent('beforedeactivate')) {
16177             return false;
16178         }
16179         
16180         if(this.bullets > 0 && !Roo.isTouch){
16181             this.setActiveBullet(this.indexOfPanel(pan));
16182         }
16183         
16184         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16185             
16186             this.transition = true;
16187             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
16188             var lr = dir == 'next' ? 'left' : 'right';
16189             pan.el.addClass(dir); // or prev
16190             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16191             cur.el.addClass(lr); // or right
16192             pan.el.addClass(lr);
16193             
16194             var _this = this;
16195             cur.el.on('transitionend', function() {
16196                 Roo.log("trans end?");
16197                 
16198                 pan.el.removeClass([lr,dir]);
16199                 pan.setActive(true);
16200                 
16201                 cur.el.removeClass([lr]);
16202                 cur.setActive(false);
16203                 
16204                 _this.transition = false;
16205                 
16206             }, this, { single:  true } );
16207             
16208             return true;
16209         }
16210         
16211         cur.setActive(false);
16212         pan.setActive(true);
16213         
16214         return true;
16215         
16216     },
16217     showPanelNext : function()
16218     {
16219         var i = this.indexOfPanel(this.getActivePanel());
16220         
16221         if (i >= this.tabs.length - 1 && !this.autoslide) {
16222             return;
16223         }
16224         
16225         if (i >= this.tabs.length - 1 && this.autoslide) {
16226             i = -1;
16227         }
16228         
16229         this.showPanel(this.tabs[i+1]);
16230     },
16231     
16232     showPanelPrev : function()
16233     {
16234         var i = this.indexOfPanel(this.getActivePanel());
16235         
16236         if (i  < 1 && !this.autoslide) {
16237             return;
16238         }
16239         
16240         if (i < 1 && this.autoslide) {
16241             i = this.tabs.length;
16242         }
16243         
16244         this.showPanel(this.tabs[i-1]);
16245     },
16246     
16247     
16248     addBullet: function()
16249     {
16250         if(!this.bullets || Roo.isTouch){
16251             return;
16252         }
16253         var ctr = this.el.select('.carousel-bullets',true).first();
16254         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16255         var bullet = ctr.createChild({
16256             cls : 'bullet bullet-' + i
16257         },ctr.dom.lastChild);
16258         
16259         
16260         var _this = this;
16261         
16262         bullet.on('click', (function(e, el, o, ii, t){
16263
16264             e.preventDefault();
16265
16266             this.showPanel(ii);
16267
16268             if(this.autoslide && this.slideFn){
16269                 clearInterval(this.slideFn);
16270                 this.slideFn = window.setInterval(function() {
16271                     _this.showPanelNext();
16272                 }, this.timer);
16273             }
16274
16275         }).createDelegate(this, [i, bullet], true));
16276                 
16277         
16278     },
16279      
16280     setActiveBullet : function(i)
16281     {
16282         if(Roo.isTouch){
16283             return;
16284         }
16285         
16286         Roo.each(this.el.select('.bullet', true).elements, function(el){
16287             el.removeClass('selected');
16288         });
16289
16290         var bullet = this.el.select('.bullet-' + i, true).first();
16291         
16292         if(!bullet){
16293             return;
16294         }
16295         
16296         bullet.addClass('selected');
16297     }
16298     
16299     
16300   
16301 });
16302
16303  
16304
16305  
16306  
16307 Roo.apply(Roo.bootstrap.TabGroup, {
16308     
16309     groups: {},
16310      /**
16311     * register a Navigation Group
16312     * @param {Roo.bootstrap.NavGroup} the navgroup to add
16313     */
16314     register : function(navgrp)
16315     {
16316         this.groups[navgrp.navId] = navgrp;
16317         
16318     },
16319     /**
16320     * fetch a Navigation Group based on the navigation ID
16321     * if one does not exist , it will get created.
16322     * @param {string} the navgroup to add
16323     * @returns {Roo.bootstrap.NavGroup} the navgroup 
16324     */
16325     get: function(navId) {
16326         if (typeof(this.groups[navId]) == 'undefined') {
16327             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
16328         }
16329         return this.groups[navId] ;
16330     }
16331     
16332     
16333     
16334 });
16335
16336  /*
16337  * - LGPL
16338  *
16339  * TabPanel
16340  * 
16341  */
16342
16343 /**
16344  * @class Roo.bootstrap.TabPanel
16345  * @extends Roo.bootstrap.Component
16346  * Bootstrap TabPanel class
16347  * @cfg {Boolean} active panel active
16348  * @cfg {String} html panel content
16349  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16350  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16351  * 
16352  * 
16353  * @constructor
16354  * Create a new TabPanel
16355  * @param {Object} config The config object
16356  */
16357
16358 Roo.bootstrap.TabPanel = function(config){
16359     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16360     this.addEvents({
16361         /**
16362              * @event changed
16363              * Fires when the active status changes
16364              * @param {Roo.bootstrap.TabPanel} this
16365              * @param {Boolean} state the new state
16366             
16367          */
16368         'changed': true,
16369         /**
16370              * @event beforedeactivate
16371              * Fires before a tab is de-activated - can be used to do validation on a form.
16372              * @param {Roo.bootstrap.TabPanel} this
16373              * @return {Boolean} false if there is an error
16374             
16375          */
16376         'beforedeactivate': true
16377      });
16378     
16379     this.tabId = this.tabId || Roo.id();
16380   
16381 };
16382
16383 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
16384     
16385     active: false,
16386     html: false,
16387     tabId: false,
16388     navId : false,
16389     
16390     getAutoCreate : function(){
16391         var cfg = {
16392             tag: 'div',
16393             // item is needed for carousel - not sure if it has any effect otherwise
16394             cls: 'tab-pane item',
16395             html: this.html || ''
16396         };
16397         
16398         if(this.active){
16399             cfg.cls += ' active';
16400         }
16401         
16402         if(this.tabId){
16403             cfg.tabId = this.tabId;
16404         }
16405         
16406         
16407         return cfg;
16408     },
16409     
16410     initEvents:  function()
16411     {
16412         Roo.log('-------- init events on tab panel ---------');
16413         
16414         var p = this.parent();
16415         this.navId = this.navId || p.navId;
16416         
16417         if (typeof(this.navId) != 'undefined') {
16418             // not really needed.. but just in case.. parent should be a NavGroup.
16419             var tg = Roo.bootstrap.TabGroup.get(this.navId);
16420             Roo.log(['register', tg, this]);
16421             tg.register(this);
16422             
16423             var i = tg.tabs.length - 1;
16424             
16425             if(this.active && tg.bullets > 0 && i < tg.bullets){
16426                 tg.setActiveBullet(i);
16427             }
16428         }
16429         
16430     },
16431     
16432     
16433     onRender : function(ct, position)
16434     {
16435        // Roo.log("Call onRender: " + this.xtype);
16436         
16437         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16438         
16439         
16440         
16441         
16442         
16443     },
16444     
16445     setActive: function(state)
16446     {
16447         Roo.log("panel - set active " + this.tabId + "=" + state);
16448         
16449         this.active = state;
16450         if (!state) {
16451             this.el.removeClass('active');
16452             
16453         } else  if (!this.el.hasClass('active')) {
16454             this.el.addClass('active');
16455         }
16456         
16457         this.fireEvent('changed', this, state);
16458     }
16459     
16460     
16461 });
16462  
16463
16464  
16465
16466  /*
16467  * - LGPL
16468  *
16469  * DateField
16470  * 
16471  */
16472
16473 /**
16474  * @class Roo.bootstrap.DateField
16475  * @extends Roo.bootstrap.Input
16476  * Bootstrap DateField class
16477  * @cfg {Number} weekStart default 0
16478  * @cfg {String} viewMode default empty, (months|years)
16479  * @cfg {String} minViewMode default empty, (months|years)
16480  * @cfg {Number} startDate default -Infinity
16481  * @cfg {Number} endDate default Infinity
16482  * @cfg {Boolean} todayHighlight default false
16483  * @cfg {Boolean} todayBtn default false
16484  * @cfg {Boolean} calendarWeeks default false
16485  * @cfg {Object} daysOfWeekDisabled default empty
16486  * @cfg {Boolean} singleMode default false (true | false)
16487  * 
16488  * @cfg {Boolean} keyboardNavigation default true
16489  * @cfg {String} language default en
16490  * 
16491  * @constructor
16492  * Create a new DateField
16493  * @param {Object} config The config object
16494  */
16495
16496 Roo.bootstrap.DateField = function(config){
16497     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16498      this.addEvents({
16499             /**
16500              * @event show
16501              * Fires when this field show.
16502              * @param {Roo.bootstrap.DateField} this
16503              * @param {Mixed} date The date value
16504              */
16505             show : true,
16506             /**
16507              * @event show
16508              * Fires when this field hide.
16509              * @param {Roo.bootstrap.DateField} this
16510              * @param {Mixed} date The date value
16511              */
16512             hide : true,
16513             /**
16514              * @event select
16515              * Fires when select a date.
16516              * @param {Roo.bootstrap.DateField} this
16517              * @param {Mixed} date The date value
16518              */
16519             select : true,
16520             /**
16521              * @event beforeselect
16522              * Fires when before select a date.
16523              * @param {Roo.bootstrap.DateField} this
16524              * @param {Mixed} date The date value
16525              */
16526             beforeselect : true
16527         });
16528 };
16529
16530 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
16531     
16532     /**
16533      * @cfg {String} format
16534      * The default date format string which can be overriden for localization support.  The format must be
16535      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16536      */
16537     format : "m/d/y",
16538     /**
16539      * @cfg {String} altFormats
16540      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16541      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16542      */
16543     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16544     
16545     weekStart : 0,
16546     
16547     viewMode : '',
16548     
16549     minViewMode : '',
16550     
16551     todayHighlight : false,
16552     
16553     todayBtn: false,
16554     
16555     language: 'en',
16556     
16557     keyboardNavigation: true,
16558     
16559     calendarWeeks: false,
16560     
16561     startDate: -Infinity,
16562     
16563     endDate: Infinity,
16564     
16565     daysOfWeekDisabled: [],
16566     
16567     _events: [],
16568     
16569     singleMode : false,
16570     
16571     UTCDate: function()
16572     {
16573         return new Date(Date.UTC.apply(Date, arguments));
16574     },
16575     
16576     UTCToday: function()
16577     {
16578         var today = new Date();
16579         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16580     },
16581     
16582     getDate: function() {
16583             var d = this.getUTCDate();
16584             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16585     },
16586     
16587     getUTCDate: function() {
16588             return this.date;
16589     },
16590     
16591     setDate: function(d) {
16592             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16593     },
16594     
16595     setUTCDate: function(d) {
16596             this.date = d;
16597             this.setValue(this.formatDate(this.date));
16598     },
16599         
16600     onRender: function(ct, position)
16601     {
16602         
16603         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16604         
16605         this.language = this.language || 'en';
16606         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16607         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16608         
16609         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16610         this.format = this.format || 'm/d/y';
16611         this.isInline = false;
16612         this.isInput = true;
16613         this.component = this.el.select('.add-on', true).first() || false;
16614         this.component = (this.component && this.component.length === 0) ? false : this.component;
16615         this.hasInput = this.component && this.inputEL().length;
16616         
16617         if (typeof(this.minViewMode === 'string')) {
16618             switch (this.minViewMode) {
16619                 case 'months':
16620                     this.minViewMode = 1;
16621                     break;
16622                 case 'years':
16623                     this.minViewMode = 2;
16624                     break;
16625                 default:
16626                     this.minViewMode = 0;
16627                     break;
16628             }
16629         }
16630         
16631         if (typeof(this.viewMode === 'string')) {
16632             switch (this.viewMode) {
16633                 case 'months':
16634                     this.viewMode = 1;
16635                     break;
16636                 case 'years':
16637                     this.viewMode = 2;
16638                     break;
16639                 default:
16640                     this.viewMode = 0;
16641                     break;
16642             }
16643         }
16644                 
16645         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16646         
16647 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16648         
16649         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16650         
16651         this.picker().on('mousedown', this.onMousedown, this);
16652         this.picker().on('click', this.onClick, this);
16653         
16654         this.picker().addClass('datepicker-dropdown');
16655         
16656         this.startViewMode = this.viewMode;
16657         
16658         if(this.singleMode){
16659             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16660                 v.setVisibilityMode(Roo.Element.DISPLAY);
16661                 v.hide();
16662             });
16663             
16664             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16665                 v.setStyle('width', '189px');
16666             });
16667         }
16668         
16669         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16670             if(!this.calendarWeeks){
16671                 v.remove();
16672                 return;
16673             }
16674             
16675             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16676             v.attr('colspan', function(i, val){
16677                 return parseInt(val) + 1;
16678             });
16679         });
16680                         
16681         
16682         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16683         
16684         this.setStartDate(this.startDate);
16685         this.setEndDate(this.endDate);
16686         
16687         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16688         
16689         this.fillDow();
16690         this.fillMonths();
16691         this.update();
16692         this.showMode();
16693         
16694         if(this.isInline) {
16695             this.show();
16696         }
16697     },
16698     
16699     picker : function()
16700     {
16701         return this.pickerEl;
16702 //        return this.el.select('.datepicker', true).first();
16703     },
16704     
16705     fillDow: function()
16706     {
16707         var dowCnt = this.weekStart;
16708         
16709         var dow = {
16710             tag: 'tr',
16711             cn: [
16712                 
16713             ]
16714         };
16715         
16716         if(this.calendarWeeks){
16717             dow.cn.push({
16718                 tag: 'th',
16719                 cls: 'cw',
16720                 html: '&nbsp;'
16721             })
16722         }
16723         
16724         while (dowCnt < this.weekStart + 7) {
16725             dow.cn.push({
16726                 tag: 'th',
16727                 cls: 'dow',
16728                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16729             });
16730         }
16731         
16732         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16733     },
16734     
16735     fillMonths: function()
16736     {    
16737         var i = 0;
16738         var months = this.picker().select('>.datepicker-months td', true).first();
16739         
16740         months.dom.innerHTML = '';
16741         
16742         while (i < 12) {
16743             var month = {
16744                 tag: 'span',
16745                 cls: 'month',
16746                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16747             };
16748             
16749             months.createChild(month);
16750         }
16751         
16752     },
16753     
16754     update: function()
16755     {
16756         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;
16757         
16758         if (this.date < this.startDate) {
16759             this.viewDate = new Date(this.startDate);
16760         } else if (this.date > this.endDate) {
16761             this.viewDate = new Date(this.endDate);
16762         } else {
16763             this.viewDate = new Date(this.date);
16764         }
16765         
16766         this.fill();
16767     },
16768     
16769     fill: function() 
16770     {
16771         var d = new Date(this.viewDate),
16772                 year = d.getUTCFullYear(),
16773                 month = d.getUTCMonth(),
16774                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16775                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16776                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16777                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16778                 currentDate = this.date && this.date.valueOf(),
16779                 today = this.UTCToday();
16780         
16781         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16782         
16783 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16784         
16785 //        this.picker.select('>tfoot th.today').
16786 //                                              .text(dates[this.language].today)
16787 //                                              .toggle(this.todayBtn !== false);
16788     
16789         this.updateNavArrows();
16790         this.fillMonths();
16791                                                 
16792         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16793         
16794         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16795          
16796         prevMonth.setUTCDate(day);
16797         
16798         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16799         
16800         var nextMonth = new Date(prevMonth);
16801         
16802         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16803         
16804         nextMonth = nextMonth.valueOf();
16805         
16806         var fillMonths = false;
16807         
16808         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16809         
16810         while(prevMonth.valueOf() < nextMonth) {
16811             var clsName = '';
16812             
16813             if (prevMonth.getUTCDay() === this.weekStart) {
16814                 if(fillMonths){
16815                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16816                 }
16817                     
16818                 fillMonths = {
16819                     tag: 'tr',
16820                     cn: []
16821                 };
16822                 
16823                 if(this.calendarWeeks){
16824                     // ISO 8601: First week contains first thursday.
16825                     // ISO also states week starts on Monday, but we can be more abstract here.
16826                     var
16827                     // Start of current week: based on weekstart/current date
16828                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16829                     // Thursday of this week
16830                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16831                     // First Thursday of year, year from thursday
16832                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16833                     // Calendar week: ms between thursdays, div ms per day, div 7 days
16834                     calWeek =  (th - yth) / 864e5 / 7 + 1;
16835                     
16836                     fillMonths.cn.push({
16837                         tag: 'td',
16838                         cls: 'cw',
16839                         html: calWeek
16840                     });
16841                 }
16842             }
16843             
16844             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16845                 clsName += ' old';
16846             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16847                 clsName += ' new';
16848             }
16849             if (this.todayHighlight &&
16850                 prevMonth.getUTCFullYear() == today.getFullYear() &&
16851                 prevMonth.getUTCMonth() == today.getMonth() &&
16852                 prevMonth.getUTCDate() == today.getDate()) {
16853                 clsName += ' today';
16854             }
16855             
16856             if (currentDate && prevMonth.valueOf() === currentDate) {
16857                 clsName += ' active';
16858             }
16859             
16860             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
16861                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
16862                     clsName += ' disabled';
16863             }
16864             
16865             fillMonths.cn.push({
16866                 tag: 'td',
16867                 cls: 'day ' + clsName,
16868                 html: prevMonth.getDate()
16869             });
16870             
16871             prevMonth.setDate(prevMonth.getDate()+1);
16872         }
16873           
16874         var currentYear = this.date && this.date.getUTCFullYear();
16875         var currentMonth = this.date && this.date.getUTCMonth();
16876         
16877         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
16878         
16879         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
16880             v.removeClass('active');
16881             
16882             if(currentYear === year && k === currentMonth){
16883                 v.addClass('active');
16884             }
16885             
16886             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
16887                 v.addClass('disabled');
16888             }
16889             
16890         });
16891         
16892         
16893         year = parseInt(year/10, 10) * 10;
16894         
16895         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
16896         
16897         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
16898         
16899         year -= 1;
16900         for (var i = -1; i < 11; i++) {
16901             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
16902                 tag: 'span',
16903                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
16904                 html: year
16905             });
16906             
16907             year += 1;
16908         }
16909     },
16910     
16911     showMode: function(dir) 
16912     {
16913         if (dir) {
16914             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
16915         }
16916         
16917         Roo.each(this.picker().select('>div',true).elements, function(v){
16918             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16919             v.hide();
16920         });
16921         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
16922     },
16923     
16924     place: function()
16925     {
16926         if(this.isInline) {
16927             return;
16928         }
16929         
16930         this.picker().removeClass(['bottom', 'top']);
16931         
16932         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16933             /*
16934              * place to the top of element!
16935              *
16936              */
16937             
16938             this.picker().addClass('top');
16939             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16940             
16941             return;
16942         }
16943         
16944         this.picker().addClass('bottom');
16945         
16946         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16947     },
16948     
16949     parseDate : function(value)
16950     {
16951         if(!value || value instanceof Date){
16952             return value;
16953         }
16954         var v = Date.parseDate(value, this.format);
16955         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
16956             v = Date.parseDate(value, 'Y-m-d');
16957         }
16958         if(!v && this.altFormats){
16959             if(!this.altFormatsArray){
16960                 this.altFormatsArray = this.altFormats.split("|");
16961             }
16962             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
16963                 v = Date.parseDate(value, this.altFormatsArray[i]);
16964             }
16965         }
16966         return v;
16967     },
16968     
16969     formatDate : function(date, fmt)
16970     {   
16971         return (!date || !(date instanceof Date)) ?
16972         date : date.dateFormat(fmt || this.format);
16973     },
16974     
16975     onFocus : function()
16976     {
16977         Roo.bootstrap.DateField.superclass.onFocus.call(this);
16978         this.show();
16979     },
16980     
16981     onBlur : function()
16982     {
16983         Roo.bootstrap.DateField.superclass.onBlur.call(this);
16984         
16985         var d = this.inputEl().getValue();
16986         
16987         this.setValue(d);
16988                 
16989         this.hide();
16990     },
16991     
16992     show : function()
16993     {
16994         this.picker().show();
16995         this.update();
16996         this.place();
16997         
16998         this.fireEvent('show', this, this.date);
16999     },
17000     
17001     hide : function()
17002     {
17003         if(this.isInline) {
17004             return;
17005         }
17006         this.picker().hide();
17007         this.viewMode = this.startViewMode;
17008         this.showMode();
17009         
17010         this.fireEvent('hide', this, this.date);
17011         
17012     },
17013     
17014     onMousedown: function(e)
17015     {
17016         e.stopPropagation();
17017         e.preventDefault();
17018     },
17019     
17020     keyup: function(e)
17021     {
17022         Roo.bootstrap.DateField.superclass.keyup.call(this);
17023         this.update();
17024     },
17025
17026     setValue: function(v)
17027     {
17028         if(this.fireEvent('beforeselect', this, v) !== false){
17029             var d = new Date(this.parseDate(v) ).clearTime();
17030         
17031             if(isNaN(d.getTime())){
17032                 this.date = this.viewDate = '';
17033                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17034                 return;
17035             }
17036
17037             v = this.formatDate(d);
17038
17039             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17040
17041             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17042
17043             this.update();
17044
17045             this.fireEvent('select', this, this.date);
17046         }
17047     },
17048     
17049     getValue: function()
17050     {
17051         return this.formatDate(this.date);
17052     },
17053     
17054     fireKey: function(e)
17055     {
17056         if (!this.picker().isVisible()){
17057             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17058                 this.show();
17059             }
17060             return;
17061         }
17062         
17063         var dateChanged = false,
17064         dir, day, month,
17065         newDate, newViewDate;
17066         
17067         switch(e.keyCode){
17068             case 27: // escape
17069                 this.hide();
17070                 e.preventDefault();
17071                 break;
17072             case 37: // left
17073             case 39: // right
17074                 if (!this.keyboardNavigation) {
17075                     break;
17076                 }
17077                 dir = e.keyCode == 37 ? -1 : 1;
17078                 
17079                 if (e.ctrlKey){
17080                     newDate = this.moveYear(this.date, dir);
17081                     newViewDate = this.moveYear(this.viewDate, dir);
17082                 } else if (e.shiftKey){
17083                     newDate = this.moveMonth(this.date, dir);
17084                     newViewDate = this.moveMonth(this.viewDate, dir);
17085                 } else {
17086                     newDate = new Date(this.date);
17087                     newDate.setUTCDate(this.date.getUTCDate() + dir);
17088                     newViewDate = new Date(this.viewDate);
17089                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
17090                 }
17091                 if (this.dateWithinRange(newDate)){
17092                     this.date = newDate;
17093                     this.viewDate = newViewDate;
17094                     this.setValue(this.formatDate(this.date));
17095 //                    this.update();
17096                     e.preventDefault();
17097                     dateChanged = true;
17098                 }
17099                 break;
17100             case 38: // up
17101             case 40: // down
17102                 if (!this.keyboardNavigation) {
17103                     break;
17104                 }
17105                 dir = e.keyCode == 38 ? -1 : 1;
17106                 if (e.ctrlKey){
17107                     newDate = this.moveYear(this.date, dir);
17108                     newViewDate = this.moveYear(this.viewDate, dir);
17109                 } else if (e.shiftKey){
17110                     newDate = this.moveMonth(this.date, dir);
17111                     newViewDate = this.moveMonth(this.viewDate, dir);
17112                 } else {
17113                     newDate = new Date(this.date);
17114                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
17115                     newViewDate = new Date(this.viewDate);
17116                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
17117                 }
17118                 if (this.dateWithinRange(newDate)){
17119                     this.date = newDate;
17120                     this.viewDate = newViewDate;
17121                     this.setValue(this.formatDate(this.date));
17122 //                    this.update();
17123                     e.preventDefault();
17124                     dateChanged = true;
17125                 }
17126                 break;
17127             case 13: // enter
17128                 this.setValue(this.formatDate(this.date));
17129                 this.hide();
17130                 e.preventDefault();
17131                 break;
17132             case 9: // tab
17133                 this.setValue(this.formatDate(this.date));
17134                 this.hide();
17135                 break;
17136             case 16: // shift
17137             case 17: // ctrl
17138             case 18: // alt
17139                 break;
17140             default :
17141                 this.hide();
17142                 
17143         }
17144     },
17145     
17146     
17147     onClick: function(e) 
17148     {
17149         e.stopPropagation();
17150         e.preventDefault();
17151         
17152         var target = e.getTarget();
17153         
17154         if(target.nodeName.toLowerCase() === 'i'){
17155             target = Roo.get(target).dom.parentNode;
17156         }
17157         
17158         var nodeName = target.nodeName;
17159         var className = target.className;
17160         var html = target.innerHTML;
17161         //Roo.log(nodeName);
17162         
17163         switch(nodeName.toLowerCase()) {
17164             case 'th':
17165                 switch(className) {
17166                     case 'switch':
17167                         this.showMode(1);
17168                         break;
17169                     case 'prev':
17170                     case 'next':
17171                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17172                         switch(this.viewMode){
17173                                 case 0:
17174                                         this.viewDate = this.moveMonth(this.viewDate, dir);
17175                                         break;
17176                                 case 1:
17177                                 case 2:
17178                                         this.viewDate = this.moveYear(this.viewDate, dir);
17179                                         break;
17180                         }
17181                         this.fill();
17182                         break;
17183                     case 'today':
17184                         var date = new Date();
17185                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17186 //                        this.fill()
17187                         this.setValue(this.formatDate(this.date));
17188                         
17189                         this.hide();
17190                         break;
17191                 }
17192                 break;
17193             case 'span':
17194                 if (className.indexOf('disabled') < 0) {
17195                     this.viewDate.setUTCDate(1);
17196                     if (className.indexOf('month') > -1) {
17197                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17198                     } else {
17199                         var year = parseInt(html, 10) || 0;
17200                         this.viewDate.setUTCFullYear(year);
17201                         
17202                     }
17203                     
17204                     if(this.singleMode){
17205                         this.setValue(this.formatDate(this.viewDate));
17206                         this.hide();
17207                         return;
17208                     }
17209                     
17210                     this.showMode(-1);
17211                     this.fill();
17212                 }
17213                 break;
17214                 
17215             case 'td':
17216                 //Roo.log(className);
17217                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17218                     var day = parseInt(html, 10) || 1;
17219                     var year = this.viewDate.getUTCFullYear(),
17220                         month = this.viewDate.getUTCMonth();
17221
17222                     if (className.indexOf('old') > -1) {
17223                         if(month === 0 ){
17224                             month = 11;
17225                             year -= 1;
17226                         }else{
17227                             month -= 1;
17228                         }
17229                     } else if (className.indexOf('new') > -1) {
17230                         if (month == 11) {
17231                             month = 0;
17232                             year += 1;
17233                         } else {
17234                             month += 1;
17235                         }
17236                     }
17237                     //Roo.log([year,month,day]);
17238                     this.date = this.UTCDate(year, month, day,0,0,0,0);
17239                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17240 //                    this.fill();
17241                     //Roo.log(this.formatDate(this.date));
17242                     this.setValue(this.formatDate(this.date));
17243                     this.hide();
17244                 }
17245                 break;
17246         }
17247     },
17248     
17249     setStartDate: function(startDate)
17250     {
17251         this.startDate = startDate || -Infinity;
17252         if (this.startDate !== -Infinity) {
17253             this.startDate = this.parseDate(this.startDate);
17254         }
17255         this.update();
17256         this.updateNavArrows();
17257     },
17258
17259     setEndDate: function(endDate)
17260     {
17261         this.endDate = endDate || Infinity;
17262         if (this.endDate !== Infinity) {
17263             this.endDate = this.parseDate(this.endDate);
17264         }
17265         this.update();
17266         this.updateNavArrows();
17267     },
17268     
17269     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17270     {
17271         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17272         if (typeof(this.daysOfWeekDisabled) !== 'object') {
17273             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17274         }
17275         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17276             return parseInt(d, 10);
17277         });
17278         this.update();
17279         this.updateNavArrows();
17280     },
17281     
17282     updateNavArrows: function() 
17283     {
17284         if(this.singleMode){
17285             return;
17286         }
17287         
17288         var d = new Date(this.viewDate),
17289         year = d.getUTCFullYear(),
17290         month = d.getUTCMonth();
17291         
17292         Roo.each(this.picker().select('.prev', true).elements, function(v){
17293             v.show();
17294             switch (this.viewMode) {
17295                 case 0:
17296
17297                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
17298                         v.hide();
17299                     }
17300                     break;
17301                 case 1:
17302                 case 2:
17303                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
17304                         v.hide();
17305                     }
17306                     break;
17307             }
17308         });
17309         
17310         Roo.each(this.picker().select('.next', true).elements, function(v){
17311             v.show();
17312             switch (this.viewMode) {
17313                 case 0:
17314
17315                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
17316                         v.hide();
17317                     }
17318                     break;
17319                 case 1:
17320                 case 2:
17321                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
17322                         v.hide();
17323                     }
17324                     break;
17325             }
17326         })
17327     },
17328     
17329     moveMonth: function(date, dir)
17330     {
17331         if (!dir) {
17332             return date;
17333         }
17334         var new_date = new Date(date.valueOf()),
17335         day = new_date.getUTCDate(),
17336         month = new_date.getUTCMonth(),
17337         mag = Math.abs(dir),
17338         new_month, test;
17339         dir = dir > 0 ? 1 : -1;
17340         if (mag == 1){
17341             test = dir == -1
17342             // If going back one month, make sure month is not current month
17343             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
17344             ? function(){
17345                 return new_date.getUTCMonth() == month;
17346             }
17347             // If going forward one month, make sure month is as expected
17348             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
17349             : function(){
17350                 return new_date.getUTCMonth() != new_month;
17351             };
17352             new_month = month + dir;
17353             new_date.setUTCMonth(new_month);
17354             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
17355             if (new_month < 0 || new_month > 11) {
17356                 new_month = (new_month + 12) % 12;
17357             }
17358         } else {
17359             // For magnitudes >1, move one month at a time...
17360             for (var i=0; i<mag; i++) {
17361                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
17362                 new_date = this.moveMonth(new_date, dir);
17363             }
17364             // ...then reset the day, keeping it in the new month
17365             new_month = new_date.getUTCMonth();
17366             new_date.setUTCDate(day);
17367             test = function(){
17368                 return new_month != new_date.getUTCMonth();
17369             };
17370         }
17371         // Common date-resetting loop -- if date is beyond end of month, make it
17372         // end of month
17373         while (test()){
17374             new_date.setUTCDate(--day);
17375             new_date.setUTCMonth(new_month);
17376         }
17377         return new_date;
17378     },
17379
17380     moveYear: function(date, dir)
17381     {
17382         return this.moveMonth(date, dir*12);
17383     },
17384
17385     dateWithinRange: function(date)
17386     {
17387         return date >= this.startDate && date <= this.endDate;
17388     },
17389
17390     
17391     remove: function() 
17392     {
17393         this.picker().remove();
17394     }
17395    
17396 });
17397
17398 Roo.apply(Roo.bootstrap.DateField,  {
17399     
17400     head : {
17401         tag: 'thead',
17402         cn: [
17403         {
17404             tag: 'tr',
17405             cn: [
17406             {
17407                 tag: 'th',
17408                 cls: 'prev',
17409                 html: '<i class="fa fa-arrow-left"/>'
17410             },
17411             {
17412                 tag: 'th',
17413                 cls: 'switch',
17414                 colspan: '5'
17415             },
17416             {
17417                 tag: 'th',
17418                 cls: 'next',
17419                 html: '<i class="fa fa-arrow-right"/>'
17420             }
17421
17422             ]
17423         }
17424         ]
17425     },
17426     
17427     content : {
17428         tag: 'tbody',
17429         cn: [
17430         {
17431             tag: 'tr',
17432             cn: [
17433             {
17434                 tag: 'td',
17435                 colspan: '7'
17436             }
17437             ]
17438         }
17439         ]
17440     },
17441     
17442     footer : {
17443         tag: 'tfoot',
17444         cn: [
17445         {
17446             tag: 'tr',
17447             cn: [
17448             {
17449                 tag: 'th',
17450                 colspan: '7',
17451                 cls: 'today'
17452             }
17453                     
17454             ]
17455         }
17456         ]
17457     },
17458     
17459     dates:{
17460         en: {
17461             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17462             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17463             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17464             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17465             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17466             today: "Today"
17467         }
17468     },
17469     
17470     modes: [
17471     {
17472         clsName: 'days',
17473         navFnc: 'Month',
17474         navStep: 1
17475     },
17476     {
17477         clsName: 'months',
17478         navFnc: 'FullYear',
17479         navStep: 1
17480     },
17481     {
17482         clsName: 'years',
17483         navFnc: 'FullYear',
17484         navStep: 10
17485     }]
17486 });
17487
17488 Roo.apply(Roo.bootstrap.DateField,  {
17489   
17490     template : {
17491         tag: 'div',
17492         cls: 'datepicker dropdown-menu roo-dynamic',
17493         cn: [
17494         {
17495             tag: 'div',
17496             cls: 'datepicker-days',
17497             cn: [
17498             {
17499                 tag: 'table',
17500                 cls: 'table-condensed',
17501                 cn:[
17502                 Roo.bootstrap.DateField.head,
17503                 {
17504                     tag: 'tbody'
17505                 },
17506                 Roo.bootstrap.DateField.footer
17507                 ]
17508             }
17509             ]
17510         },
17511         {
17512             tag: 'div',
17513             cls: 'datepicker-months',
17514             cn: [
17515             {
17516                 tag: 'table',
17517                 cls: 'table-condensed',
17518                 cn:[
17519                 Roo.bootstrap.DateField.head,
17520                 Roo.bootstrap.DateField.content,
17521                 Roo.bootstrap.DateField.footer
17522                 ]
17523             }
17524             ]
17525         },
17526         {
17527             tag: 'div',
17528             cls: 'datepicker-years',
17529             cn: [
17530             {
17531                 tag: 'table',
17532                 cls: 'table-condensed',
17533                 cn:[
17534                 Roo.bootstrap.DateField.head,
17535                 Roo.bootstrap.DateField.content,
17536                 Roo.bootstrap.DateField.footer
17537                 ]
17538             }
17539             ]
17540         }
17541         ]
17542     }
17543 });
17544
17545  
17546
17547  /*
17548  * - LGPL
17549  *
17550  * TimeField
17551  * 
17552  */
17553
17554 /**
17555  * @class Roo.bootstrap.TimeField
17556  * @extends Roo.bootstrap.Input
17557  * Bootstrap DateField class
17558  * 
17559  * 
17560  * @constructor
17561  * Create a new TimeField
17562  * @param {Object} config The config object
17563  */
17564
17565 Roo.bootstrap.TimeField = function(config){
17566     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17567     this.addEvents({
17568             /**
17569              * @event show
17570              * Fires when this field show.
17571              * @param {Roo.bootstrap.DateField} thisthis
17572              * @param {Mixed} date The date value
17573              */
17574             show : true,
17575             /**
17576              * @event show
17577              * Fires when this field hide.
17578              * @param {Roo.bootstrap.DateField} this
17579              * @param {Mixed} date The date value
17580              */
17581             hide : true,
17582             /**
17583              * @event select
17584              * Fires when select a date.
17585              * @param {Roo.bootstrap.DateField} this
17586              * @param {Mixed} date The date value
17587              */
17588             select : true
17589         });
17590 };
17591
17592 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
17593     
17594     /**
17595      * @cfg {String} format
17596      * The default time format string which can be overriden for localization support.  The format must be
17597      * valid according to {@link Date#parseDate} (defaults to 'H:i').
17598      */
17599     format : "H:i",
17600        
17601     onRender: function(ct, position)
17602     {
17603         
17604         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17605                 
17606         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17607         
17608         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17609         
17610         this.pop = this.picker().select('>.datepicker-time',true).first();
17611         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17612         
17613         this.picker().on('mousedown', this.onMousedown, this);
17614         this.picker().on('click', this.onClick, this);
17615         
17616         this.picker().addClass('datepicker-dropdown');
17617     
17618         this.fillTime();
17619         this.update();
17620             
17621         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17622         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17623         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17624         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17625         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17626         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17627
17628     },
17629     
17630     fireKey: function(e){
17631         if (!this.picker().isVisible()){
17632             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17633                 this.show();
17634             }
17635             return;
17636         }
17637
17638         e.preventDefault();
17639         
17640         switch(e.keyCode){
17641             case 27: // escape
17642                 this.hide();
17643                 break;
17644             case 37: // left
17645             case 39: // right
17646                 this.onTogglePeriod();
17647                 break;
17648             case 38: // up
17649                 this.onIncrementMinutes();
17650                 break;
17651             case 40: // down
17652                 this.onDecrementMinutes();
17653                 break;
17654             case 13: // enter
17655             case 9: // tab
17656                 this.setTime();
17657                 break;
17658         }
17659     },
17660     
17661     onClick: function(e) {
17662         e.stopPropagation();
17663         e.preventDefault();
17664     },
17665     
17666     picker : function()
17667     {
17668         return this.el.select('.datepicker', true).first();
17669     },
17670     
17671     fillTime: function()
17672     {    
17673         var time = this.pop.select('tbody', true).first();
17674         
17675         time.dom.innerHTML = '';
17676         
17677         time.createChild({
17678             tag: 'tr',
17679             cn: [
17680                 {
17681                     tag: 'td',
17682                     cn: [
17683                         {
17684                             tag: 'a',
17685                             href: '#',
17686                             cls: 'btn',
17687                             cn: [
17688                                 {
17689                                     tag: 'span',
17690                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
17691                                 }
17692                             ]
17693                         } 
17694                     ]
17695                 },
17696                 {
17697                     tag: 'td',
17698                     cls: 'separator'
17699                 },
17700                 {
17701                     tag: 'td',
17702                     cn: [
17703                         {
17704                             tag: 'a',
17705                             href: '#',
17706                             cls: 'btn',
17707                             cn: [
17708                                 {
17709                                     tag: 'span',
17710                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
17711                                 }
17712                             ]
17713                         }
17714                     ]
17715                 },
17716                 {
17717                     tag: 'td',
17718                     cls: 'separator'
17719                 }
17720             ]
17721         });
17722         
17723         time.createChild({
17724             tag: 'tr',
17725             cn: [
17726                 {
17727                     tag: 'td',
17728                     cn: [
17729                         {
17730                             tag: 'span',
17731                             cls: 'timepicker-hour',
17732                             html: '00'
17733                         }  
17734                     ]
17735                 },
17736                 {
17737                     tag: 'td',
17738                     cls: 'separator',
17739                     html: ':'
17740                 },
17741                 {
17742                     tag: 'td',
17743                     cn: [
17744                         {
17745                             tag: 'span',
17746                             cls: 'timepicker-minute',
17747                             html: '00'
17748                         }  
17749                     ]
17750                 },
17751                 {
17752                     tag: 'td',
17753                     cls: 'separator'
17754                 },
17755                 {
17756                     tag: 'td',
17757                     cn: [
17758                         {
17759                             tag: 'button',
17760                             type: 'button',
17761                             cls: 'btn btn-primary period',
17762                             html: 'AM'
17763                             
17764                         }
17765                     ]
17766                 }
17767             ]
17768         });
17769         
17770         time.createChild({
17771             tag: 'tr',
17772             cn: [
17773                 {
17774                     tag: 'td',
17775                     cn: [
17776                         {
17777                             tag: 'a',
17778                             href: '#',
17779                             cls: 'btn',
17780                             cn: [
17781                                 {
17782                                     tag: 'span',
17783                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
17784                                 }
17785                             ]
17786                         }
17787                     ]
17788                 },
17789                 {
17790                     tag: 'td',
17791                     cls: 'separator'
17792                 },
17793                 {
17794                     tag: 'td',
17795                     cn: [
17796                         {
17797                             tag: 'a',
17798                             href: '#',
17799                             cls: 'btn',
17800                             cn: [
17801                                 {
17802                                     tag: 'span',
17803                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
17804                                 }
17805                             ]
17806                         }
17807                     ]
17808                 },
17809                 {
17810                     tag: 'td',
17811                     cls: 'separator'
17812                 }
17813             ]
17814         });
17815         
17816     },
17817     
17818     update: function()
17819     {
17820         
17821         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17822         
17823         this.fill();
17824     },
17825     
17826     fill: function() 
17827     {
17828         var hours = this.time.getHours();
17829         var minutes = this.time.getMinutes();
17830         var period = 'AM';
17831         
17832         if(hours > 11){
17833             period = 'PM';
17834         }
17835         
17836         if(hours == 0){
17837             hours = 12;
17838         }
17839         
17840         
17841         if(hours > 12){
17842             hours = hours - 12;
17843         }
17844         
17845         if(hours < 10){
17846             hours = '0' + hours;
17847         }
17848         
17849         if(minutes < 10){
17850             minutes = '0' + minutes;
17851         }
17852         
17853         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
17854         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
17855         this.pop.select('button', true).first().dom.innerHTML = period;
17856         
17857     },
17858     
17859     place: function()
17860     {   
17861         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
17862         
17863         var cls = ['bottom'];
17864         
17865         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
17866             cls.pop();
17867             cls.push('top');
17868         }
17869         
17870         cls.push('right');
17871         
17872         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
17873             cls.pop();
17874             cls.push('left');
17875         }
17876         
17877         this.picker().addClass(cls.join('-'));
17878         
17879         var _this = this;
17880         
17881         Roo.each(cls, function(c){
17882             if(c == 'bottom'){
17883                 _this.picker().setTop(_this.inputEl().getHeight());
17884                 return;
17885             }
17886             if(c == 'top'){
17887                 _this.picker().setTop(0 - _this.picker().getHeight());
17888                 return;
17889             }
17890             
17891             if(c == 'left'){
17892                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
17893                 return;
17894             }
17895             if(c == 'right'){
17896                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
17897                 return;
17898             }
17899         });
17900         
17901     },
17902   
17903     onFocus : function()
17904     {
17905         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
17906         this.show();
17907     },
17908     
17909     onBlur : function()
17910     {
17911         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
17912         this.hide();
17913     },
17914     
17915     show : function()
17916     {
17917         this.picker().show();
17918         this.pop.show();
17919         this.update();
17920         this.place();
17921         
17922         this.fireEvent('show', this, this.date);
17923     },
17924     
17925     hide : function()
17926     {
17927         this.picker().hide();
17928         this.pop.hide();
17929         
17930         this.fireEvent('hide', this, this.date);
17931     },
17932     
17933     setTime : function()
17934     {
17935         this.hide();
17936         this.setValue(this.time.format(this.format));
17937         
17938         this.fireEvent('select', this, this.date);
17939         
17940         
17941     },
17942     
17943     onMousedown: function(e){
17944         e.stopPropagation();
17945         e.preventDefault();
17946     },
17947     
17948     onIncrementHours: function()
17949     {
17950         Roo.log('onIncrementHours');
17951         this.time = this.time.add(Date.HOUR, 1);
17952         this.update();
17953         
17954     },
17955     
17956     onDecrementHours: function()
17957     {
17958         Roo.log('onDecrementHours');
17959         this.time = this.time.add(Date.HOUR, -1);
17960         this.update();
17961     },
17962     
17963     onIncrementMinutes: function()
17964     {
17965         Roo.log('onIncrementMinutes');
17966         this.time = this.time.add(Date.MINUTE, 1);
17967         this.update();
17968     },
17969     
17970     onDecrementMinutes: function()
17971     {
17972         Roo.log('onDecrementMinutes');
17973         this.time = this.time.add(Date.MINUTE, -1);
17974         this.update();
17975     },
17976     
17977     onTogglePeriod: function()
17978     {
17979         Roo.log('onTogglePeriod');
17980         this.time = this.time.add(Date.HOUR, 12);
17981         this.update();
17982     }
17983     
17984    
17985 });
17986
17987 Roo.apply(Roo.bootstrap.TimeField,  {
17988     
17989     content : {
17990         tag: 'tbody',
17991         cn: [
17992             {
17993                 tag: 'tr',
17994                 cn: [
17995                 {
17996                     tag: 'td',
17997                     colspan: '7'
17998                 }
17999                 ]
18000             }
18001         ]
18002     },
18003     
18004     footer : {
18005         tag: 'tfoot',
18006         cn: [
18007             {
18008                 tag: 'tr',
18009                 cn: [
18010                 {
18011                     tag: 'th',
18012                     colspan: '7',
18013                     cls: '',
18014                     cn: [
18015                         {
18016                             tag: 'button',
18017                             cls: 'btn btn-info ok',
18018                             html: 'OK'
18019                         }
18020                     ]
18021                 }
18022
18023                 ]
18024             }
18025         ]
18026     }
18027 });
18028
18029 Roo.apply(Roo.bootstrap.TimeField,  {
18030   
18031     template : {
18032         tag: 'div',
18033         cls: 'datepicker dropdown-menu',
18034         cn: [
18035             {
18036                 tag: 'div',
18037                 cls: 'datepicker-time',
18038                 cn: [
18039                 {
18040                     tag: 'table',
18041                     cls: 'table-condensed',
18042                     cn:[
18043                     Roo.bootstrap.TimeField.content,
18044                     Roo.bootstrap.TimeField.footer
18045                     ]
18046                 }
18047                 ]
18048             }
18049         ]
18050     }
18051 });
18052
18053  
18054
18055  /*
18056  * - LGPL
18057  *
18058  * MonthField
18059  * 
18060  */
18061
18062 /**
18063  * @class Roo.bootstrap.MonthField
18064  * @extends Roo.bootstrap.Input
18065  * Bootstrap MonthField class
18066  * 
18067  * @cfg {String} language default en
18068  * 
18069  * @constructor
18070  * Create a new MonthField
18071  * @param {Object} config The config object
18072  */
18073
18074 Roo.bootstrap.MonthField = function(config){
18075     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
18076     
18077     this.addEvents({
18078         /**
18079          * @event show
18080          * Fires when this field show.
18081          * @param {Roo.bootstrap.MonthField} this
18082          * @param {Mixed} date The date value
18083          */
18084         show : true,
18085         /**
18086          * @event show
18087          * Fires when this field hide.
18088          * @param {Roo.bootstrap.MonthField} this
18089          * @param {Mixed} date The date value
18090          */
18091         hide : true,
18092         /**
18093          * @event select
18094          * Fires when select a date.
18095          * @param {Roo.bootstrap.MonthField} this
18096          * @param {String} oldvalue The old value
18097          * @param {String} newvalue The new value
18098          */
18099         select : true
18100     });
18101 };
18102
18103 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
18104     
18105     onRender: function(ct, position)
18106     {
18107         
18108         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
18109         
18110         this.language = this.language || 'en';
18111         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
18112         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
18113         
18114         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
18115         this.isInline = false;
18116         this.isInput = true;
18117         this.component = this.el.select('.add-on', true).first() || false;
18118         this.component = (this.component && this.component.length === 0) ? false : this.component;
18119         this.hasInput = this.component && this.inputEL().length;
18120         
18121         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
18122         
18123         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18124         
18125         this.picker().on('mousedown', this.onMousedown, this);
18126         this.picker().on('click', this.onClick, this);
18127         
18128         this.picker().addClass('datepicker-dropdown');
18129         
18130         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18131             v.setStyle('width', '189px');
18132         });
18133         
18134         this.fillMonths();
18135         
18136         this.update();
18137         
18138         if(this.isInline) {
18139             this.show();
18140         }
18141         
18142     },
18143     
18144     setValue: function(v, suppressEvent)
18145     {   
18146         var o = this.getValue();
18147         
18148         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
18149         
18150         this.update();
18151
18152         if(suppressEvent !== true){
18153             this.fireEvent('select', this, o, v);
18154         }
18155         
18156     },
18157     
18158     getValue: function()
18159     {
18160         return this.value;
18161     },
18162     
18163     onClick: function(e) 
18164     {
18165         e.stopPropagation();
18166         e.preventDefault();
18167         
18168         var target = e.getTarget();
18169         
18170         if(target.nodeName.toLowerCase() === 'i'){
18171             target = Roo.get(target).dom.parentNode;
18172         }
18173         
18174         var nodeName = target.nodeName;
18175         var className = target.className;
18176         var html = target.innerHTML;
18177         
18178         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18179             return;
18180         }
18181         
18182         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18183         
18184         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18185         
18186         this.hide();
18187                         
18188     },
18189     
18190     picker : function()
18191     {
18192         return this.pickerEl;
18193     },
18194     
18195     fillMonths: function()
18196     {    
18197         var i = 0;
18198         var months = this.picker().select('>.datepicker-months td', true).first();
18199         
18200         months.dom.innerHTML = '';
18201         
18202         while (i < 12) {
18203             var month = {
18204                 tag: 'span',
18205                 cls: 'month',
18206                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18207             };
18208             
18209             months.createChild(month);
18210         }
18211         
18212     },
18213     
18214     update: function()
18215     {
18216         var _this = this;
18217         
18218         if(typeof(this.vIndex) == 'undefined' && this.value.length){
18219             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18220         }
18221         
18222         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18223             e.removeClass('active');
18224             
18225             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18226                 e.addClass('active');
18227             }
18228         })
18229     },
18230     
18231     place: function()
18232     {
18233         if(this.isInline) {
18234             return;
18235         }
18236         
18237         this.picker().removeClass(['bottom', 'top']);
18238         
18239         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18240             /*
18241              * place to the top of element!
18242              *
18243              */
18244             
18245             this.picker().addClass('top');
18246             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18247             
18248             return;
18249         }
18250         
18251         this.picker().addClass('bottom');
18252         
18253         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18254     },
18255     
18256     onFocus : function()
18257     {
18258         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18259         this.show();
18260     },
18261     
18262     onBlur : function()
18263     {
18264         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18265         
18266         var d = this.inputEl().getValue();
18267         
18268         this.setValue(d);
18269                 
18270         this.hide();
18271     },
18272     
18273     show : function()
18274     {
18275         this.picker().show();
18276         this.picker().select('>.datepicker-months', true).first().show();
18277         this.update();
18278         this.place();
18279         
18280         this.fireEvent('show', this, this.date);
18281     },
18282     
18283     hide : function()
18284     {
18285         if(this.isInline) {
18286             return;
18287         }
18288         this.picker().hide();
18289         this.fireEvent('hide', this, this.date);
18290         
18291     },
18292     
18293     onMousedown: function(e)
18294     {
18295         e.stopPropagation();
18296         e.preventDefault();
18297     },
18298     
18299     keyup: function(e)
18300     {
18301         Roo.bootstrap.MonthField.superclass.keyup.call(this);
18302         this.update();
18303     },
18304
18305     fireKey: function(e)
18306     {
18307         if (!this.picker().isVisible()){
18308             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
18309                 this.show();
18310             }
18311             return;
18312         }
18313         
18314         var dir;
18315         
18316         switch(e.keyCode){
18317             case 27: // escape
18318                 this.hide();
18319                 e.preventDefault();
18320                 break;
18321             case 37: // left
18322             case 39: // right
18323                 dir = e.keyCode == 37 ? -1 : 1;
18324                 
18325                 this.vIndex = this.vIndex + dir;
18326                 
18327                 if(this.vIndex < 0){
18328                     this.vIndex = 0;
18329                 }
18330                 
18331                 if(this.vIndex > 11){
18332                     this.vIndex = 11;
18333                 }
18334                 
18335                 if(isNaN(this.vIndex)){
18336                     this.vIndex = 0;
18337                 }
18338                 
18339                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18340                 
18341                 break;
18342             case 38: // up
18343             case 40: // down
18344                 
18345                 dir = e.keyCode == 38 ? -1 : 1;
18346                 
18347                 this.vIndex = this.vIndex + dir * 4;
18348                 
18349                 if(this.vIndex < 0){
18350                     this.vIndex = 0;
18351                 }
18352                 
18353                 if(this.vIndex > 11){
18354                     this.vIndex = 11;
18355                 }
18356                 
18357                 if(isNaN(this.vIndex)){
18358                     this.vIndex = 0;
18359                 }
18360                 
18361                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18362                 break;
18363                 
18364             case 13: // enter
18365                 
18366                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18367                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18368                 }
18369                 
18370                 this.hide();
18371                 e.preventDefault();
18372                 break;
18373             case 9: // tab
18374                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18375                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18376                 }
18377                 this.hide();
18378                 break;
18379             case 16: // shift
18380             case 17: // ctrl
18381             case 18: // alt
18382                 break;
18383             default :
18384                 this.hide();
18385                 
18386         }
18387     },
18388     
18389     remove: function() 
18390     {
18391         this.picker().remove();
18392     }
18393    
18394 });
18395
18396 Roo.apply(Roo.bootstrap.MonthField,  {
18397     
18398     content : {
18399         tag: 'tbody',
18400         cn: [
18401         {
18402             tag: 'tr',
18403             cn: [
18404             {
18405                 tag: 'td',
18406                 colspan: '7'
18407             }
18408             ]
18409         }
18410         ]
18411     },
18412     
18413     dates:{
18414         en: {
18415             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18416             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18417         }
18418     }
18419 });
18420
18421 Roo.apply(Roo.bootstrap.MonthField,  {
18422   
18423     template : {
18424         tag: 'div',
18425         cls: 'datepicker dropdown-menu roo-dynamic',
18426         cn: [
18427             {
18428                 tag: 'div',
18429                 cls: 'datepicker-months',
18430                 cn: [
18431                 {
18432                     tag: 'table',
18433                     cls: 'table-condensed',
18434                     cn:[
18435                         Roo.bootstrap.DateField.content
18436                     ]
18437                 }
18438                 ]
18439             }
18440         ]
18441     }
18442 });
18443
18444  
18445
18446  
18447  /*
18448  * - LGPL
18449  *
18450  * CheckBox
18451  * 
18452  */
18453
18454 /**
18455  * @class Roo.bootstrap.CheckBox
18456  * @extends Roo.bootstrap.Input
18457  * Bootstrap CheckBox class
18458  * 
18459  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18460  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18461  * @cfg {String} boxLabel The text that appears beside the checkbox
18462  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18463  * @cfg {Boolean} checked initnal the element
18464  * @cfg {Boolean} inline inline the element (default false)
18465  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18466  * 
18467  * @constructor
18468  * Create a new CheckBox
18469  * @param {Object} config The config object
18470  */
18471
18472 Roo.bootstrap.CheckBox = function(config){
18473     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18474    
18475     this.addEvents({
18476         /**
18477         * @event check
18478         * Fires when the element is checked or unchecked.
18479         * @param {Roo.bootstrap.CheckBox} this This input
18480         * @param {Boolean} checked The new checked value
18481         */
18482        check : true
18483     });
18484     
18485 };
18486
18487 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
18488   
18489     inputType: 'checkbox',
18490     inputValue: 1,
18491     valueOff: 0,
18492     boxLabel: false,
18493     checked: false,
18494     weight : false,
18495     inline: false,
18496     
18497     getAutoCreate : function()
18498     {
18499         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18500         
18501         var id = Roo.id();
18502         
18503         var cfg = {};
18504         
18505         cfg.cls = 'form-group ' + this.inputType; //input-group
18506         
18507         if(this.inline){
18508             cfg.cls += ' ' + this.inputType + '-inline';
18509         }
18510         
18511         var input =  {
18512             tag: 'input',
18513             id : id,
18514             type : this.inputType,
18515             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18516             cls : 'roo-' + this.inputType, //'form-box',
18517             placeholder : this.placeholder || ''
18518             
18519         };
18520         
18521         if (this.weight) { // Validity check?
18522             cfg.cls += " " + this.inputType + "-" + this.weight;
18523         }
18524         
18525         if (this.disabled) {
18526             input.disabled=true;
18527         }
18528         
18529         if(this.checked){
18530             input.checked = this.checked;
18531         }
18532         
18533         if (this.name) {
18534             input.name = this.name;
18535         }
18536         
18537         if (this.size) {
18538             input.cls += ' input-' + this.size;
18539         }
18540         
18541         var settings=this;
18542         
18543         ['xs','sm','md','lg'].map(function(size){
18544             if (settings[size]) {
18545                 cfg.cls += ' col-' + size + '-' + settings[size];
18546             }
18547         });
18548         
18549         var inputblock = input;
18550          
18551         if (this.before || this.after) {
18552             
18553             inputblock = {
18554                 cls : 'input-group',
18555                 cn :  [] 
18556             };
18557             
18558             if (this.before) {
18559                 inputblock.cn.push({
18560                     tag :'span',
18561                     cls : 'input-group-addon',
18562                     html : this.before
18563                 });
18564             }
18565             
18566             inputblock.cn.push(input);
18567             
18568             if (this.after) {
18569                 inputblock.cn.push({
18570                     tag :'span',
18571                     cls : 'input-group-addon',
18572                     html : this.after
18573                 });
18574             }
18575             
18576         }
18577         
18578         if (align ==='left' && this.fieldLabel.length) {
18579                 Roo.log("left and has label");
18580                 cfg.cn = [
18581                     
18582                     {
18583                         tag: 'label',
18584                         'for' :  id,
18585                         cls : 'control-label col-md-' + this.labelWidth,
18586                         html : this.fieldLabel
18587                         
18588                     },
18589                     {
18590                         cls : "col-md-" + (12 - this.labelWidth), 
18591                         cn: [
18592                             inputblock
18593                         ]
18594                     }
18595                     
18596                 ];
18597         } else if ( this.fieldLabel.length) {
18598                 Roo.log(" label");
18599                 cfg.cn = [
18600                    
18601                     {
18602                         tag: this.boxLabel ? 'span' : 'label',
18603                         'for': id,
18604                         cls: 'control-label box-input-label',
18605                         //cls : 'input-group-addon',
18606                         html : this.fieldLabel
18607                         
18608                     },
18609                     
18610                     inputblock
18611                     
18612                 ];
18613
18614         } else {
18615             
18616                 Roo.log(" no label && no align");
18617                 cfg.cn = [  inputblock ] ;
18618                 
18619                 
18620         }
18621         if(this.boxLabel){
18622              var boxLabelCfg = {
18623                 tag: 'label',
18624                 //'for': id, // box label is handled by onclick - so no for...
18625                 cls: 'box-label',
18626                 html: this.boxLabel
18627             };
18628             
18629             if(this.tooltip){
18630                 boxLabelCfg.tooltip = this.tooltip;
18631             }
18632              
18633             cfg.cn.push(boxLabelCfg);
18634         }
18635         
18636         
18637        
18638         return cfg;
18639         
18640     },
18641     
18642     /**
18643      * return the real input element.
18644      */
18645     inputEl: function ()
18646     {
18647         return this.el.select('input.roo-' + this.inputType,true).first();
18648     },
18649     
18650     labelEl: function()
18651     {
18652         return this.el.select('label.control-label',true).first();
18653     },
18654     /* depricated... */
18655     
18656     label: function()
18657     {
18658         return this.labelEl();
18659     },
18660     
18661     initEvents : function()
18662     {
18663 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18664         
18665         this.inputEl().on('click', this.onClick,  this);
18666         
18667         if (this.boxLabel) { 
18668             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
18669         }
18670         
18671         this.startValue = this.getValue();
18672         
18673         if(this.groupId){
18674             Roo.bootstrap.CheckBox.register(this);
18675         }
18676     },
18677     
18678     onClick : function()
18679     {   
18680         this.setChecked(!this.checked);
18681     },
18682     
18683     setChecked : function(state,suppressEvent)
18684     {
18685         this.startValue = this.getValue();
18686         
18687         if(this.inputType == 'radio'){
18688             
18689             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18690                 e.dom.checked = false;
18691             });
18692             
18693             this.inputEl().dom.checked = true;
18694             
18695             this.inputEl().dom.value = this.inputValue;
18696             
18697             if(suppressEvent !== true){
18698                 this.fireEvent('check', this, true);
18699             }
18700             
18701             this.validate();
18702             
18703             return;
18704         }
18705         
18706         this.checked = state;
18707         
18708         this.inputEl().dom.checked = state;
18709         
18710         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18711         
18712         if(suppressEvent !== true){
18713             this.fireEvent('check', this, state);
18714         }
18715         
18716         this.validate();
18717     },
18718     
18719     getValue : function()
18720     {
18721         if(this.inputType == 'radio'){
18722             return this.getGroupValue();
18723         }
18724         
18725         return this.inputEl().getValue();
18726         
18727     },
18728     
18729     getGroupValue : function()
18730     {
18731         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18732             return '';
18733         }
18734         
18735         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18736     },
18737     
18738     setValue : function(v,suppressEvent)
18739     {
18740         if(this.inputType == 'radio'){
18741             this.setGroupValue(v, suppressEvent);
18742             return;
18743         }
18744         
18745         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18746         
18747         this.validate();
18748     },
18749     
18750     setGroupValue : function(v, suppressEvent)
18751     {
18752         this.startValue = this.getValue();
18753         
18754         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18755             e.dom.checked = false;
18756             
18757             if(e.dom.value == v){
18758                 e.dom.checked = true;
18759             }
18760         });
18761         
18762         if(suppressEvent !== true){
18763             this.fireEvent('check', this, true);
18764         }
18765
18766         this.validate();
18767         
18768         return;
18769     },
18770     
18771     validate : function()
18772     {
18773         if(
18774                 this.disabled || 
18775                 (this.inputType == 'radio' && this.validateRadio()) ||
18776                 (this.inputType == 'checkbox' && this.validateCheckbox())
18777         ){
18778             this.markValid();
18779             return true;
18780         }
18781         
18782         this.markInvalid();
18783         return false;
18784     },
18785     
18786     validateRadio : function()
18787     {
18788         var valid = false;
18789         
18790         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18791             if(!e.dom.checked){
18792                 return;
18793             }
18794             
18795             valid = true;
18796             
18797             return false;
18798         });
18799         
18800         return valid;
18801     },
18802     
18803     validateCheckbox : function()
18804     {
18805         if(!this.groupId){
18806             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18807         }
18808         
18809         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18810         
18811         if(!group){
18812             return false;
18813         }
18814         
18815         var r = false;
18816         
18817         for(var i in group){
18818             if(r){
18819                 break;
18820             }
18821             
18822             r = (group[i].getValue() == group[i].inputValue) ? true : false;
18823         }
18824         
18825         return r;
18826     },
18827     
18828     /**
18829      * Mark this field as valid
18830      */
18831     markValid : function()
18832     {
18833         if(this.allowBlank){
18834             return;
18835         }
18836         
18837         var _this = this;
18838         
18839         this.fireEvent('valid', this);
18840         
18841         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18842         
18843         if(this.groupId){
18844             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18845         }
18846         
18847         if(label){
18848             label.markValid();
18849         }
18850         
18851         if(this.inputType == 'radio'){
18852             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18853                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18854                 e.findParent('.form-group', false, true).addClass(_this.validClass);
18855             });
18856             
18857             return;
18858         }
18859         
18860         if(!this.groupId){
18861             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18862             this.el.findParent('.form-group', false, true).addClass(this.validClass);
18863             return;
18864         }
18865         
18866         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18867             
18868         if(!group){
18869             return;
18870         }
18871         
18872         for(var i in group){
18873             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18874             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
18875         }
18876     },
18877     
18878      /**
18879      * Mark this field as invalid
18880      * @param {String} msg The validation message
18881      */
18882     markInvalid : function(msg)
18883     {
18884         if(this.allowBlank){
18885             return;
18886         }
18887         
18888         var _this = this;
18889         
18890         this.fireEvent('invalid', this, msg);
18891         
18892         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18893         
18894         if(this.groupId){
18895             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18896         }
18897         
18898         if(label){
18899             label.markInvalid();
18900         }
18901             
18902         if(this.inputType == 'radio'){
18903             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18904                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18905                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
18906             });
18907             
18908             return;
18909         }
18910         
18911         if(!this.groupId){
18912             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18913             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
18914             return;
18915         }
18916         
18917         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18918         
18919         if(!group){
18920             return;
18921         }
18922         
18923         for(var i in group){
18924             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18925             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
18926         }
18927         
18928     }
18929     
18930 });
18931
18932 Roo.apply(Roo.bootstrap.CheckBox, {
18933     
18934     groups: {},
18935     
18936      /**
18937     * register a CheckBox Group
18938     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
18939     */
18940     register : function(checkbox)
18941     {
18942         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
18943             this.groups[checkbox.groupId] = {};
18944         }
18945         
18946         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
18947             return;
18948         }
18949         
18950         this.groups[checkbox.groupId][checkbox.name] = checkbox;
18951         
18952     },
18953     /**
18954     * fetch a CheckBox Group based on the group ID
18955     * @param {string} the group ID
18956     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
18957     */
18958     get: function(groupId) {
18959         if (typeof(this.groups[groupId]) == 'undefined') {
18960             return false;
18961         }
18962         
18963         return this.groups[groupId] ;
18964     }
18965     
18966     
18967 });
18968 /*
18969  * - LGPL
18970  *
18971  * Radio
18972  *
18973  *
18974  * not inline
18975  *<div class="radio">
18976   <label>
18977     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
18978     Option one is this and that&mdash;be sure to include why it's great
18979   </label>
18980 </div>
18981  *
18982  *
18983  *inline
18984  *<span>
18985  *<label class="radio-inline">fieldLabel</label>
18986  *<label class="radio-inline">
18987   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
18988 </label>
18989 <span>
18990  * 
18991  * 
18992  */
18993
18994 /**
18995  * @class Roo.bootstrap.Radio
18996  * @extends Roo.bootstrap.CheckBox
18997  * Bootstrap Radio class
18998
18999  * @constructor
19000  * Create a new Radio
19001  * @param {Object} config The config object
19002  */
19003
19004 Roo.bootstrap.Radio = function(config){
19005     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
19006    
19007 };
19008
19009 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
19010     
19011     inputType: 'radio',
19012     inputValue: '',
19013     valueOff: '',
19014     
19015     getAutoCreate : function()
19016     {
19017         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19018         align = align || 'left'; // default...
19019         
19020         
19021         
19022         var id = Roo.id();
19023         
19024         var cfg = {
19025                 tag : this.inline ? 'span' : 'div',
19026                 cls : '',
19027                 cn : []
19028         };
19029         
19030         var inline = this.inline ? ' radio-inline' : '';
19031         
19032         var lbl = {
19033                 tag: 'label' ,
19034                 // does not need for, as we wrap the input with it..
19035                 'for' : id,
19036                 cls : 'control-label box-label' + inline,
19037                 cn : []
19038         };
19039         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
19040         
19041         var fieldLabel = {
19042             tag: 'label' ,
19043             //cls : 'control-label' + inline,
19044             html : this.fieldLabel,
19045             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
19046         };
19047         
19048  
19049         
19050         
19051         var input =  {
19052             tag: 'input',
19053             id : id,
19054             type : this.inputType,
19055             //value : (!this.checked) ? this.valueOff : this.inputValue,
19056             value : this.inputValue,
19057             cls : 'roo-radio',
19058             placeholder : this.placeholder || '' // ?? needed????
19059             
19060         };
19061         if (this.weight) { // Validity check?
19062             input.cls += " radio-" + this.weight;
19063         }
19064         if (this.disabled) {
19065             input.disabled=true;
19066         }
19067         
19068         if(this.checked){
19069             input.checked = this.checked;
19070         }
19071         
19072         if (this.name) {
19073             input.name = this.name;
19074         }
19075         
19076         if (this.size) {
19077             input.cls += ' input-' + this.size;
19078         }
19079         
19080         //?? can span's inline have a width??
19081         
19082         var settings=this;
19083         ['xs','sm','md','lg'].map(function(size){
19084             if (settings[size]) {
19085                 cfg.cls += ' col-' + size + '-' + settings[size];
19086             }
19087         });
19088         
19089         var inputblock = input;
19090         
19091         if (this.before || this.after) {
19092             
19093             inputblock = {
19094                 cls : 'input-group',
19095                 tag : 'span',
19096                 cn :  [] 
19097             };
19098             if (this.before) {
19099                 inputblock.cn.push({
19100                     tag :'span',
19101                     cls : 'input-group-addon',
19102                     html : this.before
19103                 });
19104             }
19105             inputblock.cn.push(input);
19106             if (this.after) {
19107                 inputblock.cn.push({
19108                     tag :'span',
19109                     cls : 'input-group-addon',
19110                     html : this.after
19111                 });
19112             }
19113             
19114         };
19115         
19116         
19117         if (this.fieldLabel && this.fieldLabel.length) {
19118             cfg.cn.push(fieldLabel);
19119         }
19120        
19121         // normal bootstrap puts the input inside the label.
19122         // however with our styled version - it has to go after the input.
19123        
19124         //lbl.cn.push(inputblock);
19125         
19126         var lblwrap =  {
19127             tag: 'span',
19128             cls: 'radio' + inline,
19129             cn: [
19130                 inputblock,
19131                 lbl
19132             ]
19133         };
19134         
19135         cfg.cn.push( lblwrap);
19136         
19137         if(this.boxLabel){
19138             lbl.cn.push({
19139                 tag: 'span',
19140                 html: this.boxLabel
19141             })
19142         }
19143          
19144         
19145         return cfg;
19146         
19147     },
19148     
19149     initEvents : function()
19150     {
19151 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19152         
19153         this.inputEl().on('click', this.onClick,  this);
19154         if (this.boxLabel) {
19155             //Roo.log('find label');
19156             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
19157         }
19158         
19159     },
19160     
19161     inputEl: function ()
19162     {
19163         return this.el.select('input.roo-radio',true).first();
19164     },
19165     onClick : function()
19166     {   
19167         Roo.log("click");
19168         this.setChecked(true);
19169     },
19170     
19171     setChecked : function(state,suppressEvent)
19172     {
19173         if(state){
19174             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19175                 v.dom.checked = false;
19176             });
19177         }
19178         Roo.log(this.inputEl().dom);
19179         this.checked = state;
19180         this.inputEl().dom.checked = state;
19181         
19182         if(suppressEvent !== true){
19183             this.fireEvent('check', this, state);
19184         }
19185         
19186         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19187         
19188     },
19189     
19190     getGroupValue : function()
19191     {
19192         var value = '';
19193         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19194             if(v.dom.checked == true){
19195                 value = v.dom.value;
19196             }
19197         });
19198         
19199         return value;
19200     },
19201     
19202     /**
19203      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
19204      * @return {Mixed} value The field value
19205      */
19206     getValue : function(){
19207         return this.getGroupValue();
19208     }
19209     
19210 });
19211
19212  
19213 //<script type="text/javascript">
19214
19215 /*
19216  * Based  Ext JS Library 1.1.1
19217  * Copyright(c) 2006-2007, Ext JS, LLC.
19218  * LGPL
19219  *
19220  */
19221  
19222 /**
19223  * @class Roo.HtmlEditorCore
19224  * @extends Roo.Component
19225  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
19226  *
19227  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
19228  */
19229
19230 Roo.HtmlEditorCore = function(config){
19231     
19232     
19233     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
19234     
19235     
19236     this.addEvents({
19237         /**
19238          * @event initialize
19239          * Fires when the editor is fully initialized (including the iframe)
19240          * @param {Roo.HtmlEditorCore} this
19241          */
19242         initialize: true,
19243         /**
19244          * @event activate
19245          * Fires when the editor is first receives the focus. Any insertion must wait
19246          * until after this event.
19247          * @param {Roo.HtmlEditorCore} this
19248          */
19249         activate: true,
19250          /**
19251          * @event beforesync
19252          * Fires before the textarea is updated with content from the editor iframe. Return false
19253          * to cancel the sync.
19254          * @param {Roo.HtmlEditorCore} this
19255          * @param {String} html
19256          */
19257         beforesync: true,
19258          /**
19259          * @event beforepush
19260          * Fires before the iframe editor is updated with content from the textarea. Return false
19261          * to cancel the push.
19262          * @param {Roo.HtmlEditorCore} this
19263          * @param {String} html
19264          */
19265         beforepush: true,
19266          /**
19267          * @event sync
19268          * Fires when the textarea is updated with content from the editor iframe.
19269          * @param {Roo.HtmlEditorCore} this
19270          * @param {String} html
19271          */
19272         sync: true,
19273          /**
19274          * @event push
19275          * Fires when the iframe editor is updated with content from the textarea.
19276          * @param {Roo.HtmlEditorCore} this
19277          * @param {String} html
19278          */
19279         push: true,
19280         
19281         /**
19282          * @event editorevent
19283          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19284          * @param {Roo.HtmlEditorCore} this
19285          */
19286         editorevent: true
19287         
19288     });
19289     
19290     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
19291     
19292     // defaults : white / black...
19293     this.applyBlacklists();
19294     
19295     
19296     
19297 };
19298
19299
19300 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
19301
19302
19303      /**
19304      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
19305      */
19306     
19307     owner : false,
19308     
19309      /**
19310      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
19311      *                        Roo.resizable.
19312      */
19313     resizable : false,
19314      /**
19315      * @cfg {Number} height (in pixels)
19316      */   
19317     height: 300,
19318    /**
19319      * @cfg {Number} width (in pixels)
19320      */   
19321     width: 500,
19322     
19323     /**
19324      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19325      * 
19326      */
19327     stylesheets: false,
19328     
19329     // id of frame..
19330     frameId: false,
19331     
19332     // private properties
19333     validationEvent : false,
19334     deferHeight: true,
19335     initialized : false,
19336     activated : false,
19337     sourceEditMode : false,
19338     onFocus : Roo.emptyFn,
19339     iframePad:3,
19340     hideMode:'offsets',
19341     
19342     clearUp: true,
19343     
19344     // blacklist + whitelisted elements..
19345     black: false,
19346     white: false,
19347      
19348     
19349
19350     /**
19351      * Protected method that will not generally be called directly. It
19352      * is called when the editor initializes the iframe with HTML contents. Override this method if you
19353      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
19354      */
19355     getDocMarkup : function(){
19356         // body styles..
19357         var st = '';
19358         
19359         // inherit styels from page...?? 
19360         if (this.stylesheets === false) {
19361             
19362             Roo.get(document.head).select('style').each(function(node) {
19363                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19364             });
19365             
19366             Roo.get(document.head).select('link').each(function(node) { 
19367                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19368             });
19369             
19370         } else if (!this.stylesheets.length) {
19371                 // simple..
19372                 st = '<style type="text/css">' +
19373                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19374                    '</style>';
19375         } else { 
19376             
19377         }
19378         
19379         st +=  '<style type="text/css">' +
19380             'IMG { cursor: pointer } ' +
19381         '</style>';
19382
19383         
19384         return '<html><head>' + st  +
19385             //<style type="text/css">' +
19386             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19387             //'</style>' +
19388             ' </head><body class="roo-htmleditor-body"></body></html>';
19389     },
19390
19391     // private
19392     onRender : function(ct, position)
19393     {
19394         var _t = this;
19395         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19396         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19397         
19398         
19399         this.el.dom.style.border = '0 none';
19400         this.el.dom.setAttribute('tabIndex', -1);
19401         this.el.addClass('x-hidden hide');
19402         
19403         
19404         
19405         if(Roo.isIE){ // fix IE 1px bogus margin
19406             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19407         }
19408        
19409         
19410         this.frameId = Roo.id();
19411         
19412          
19413         
19414         var iframe = this.owner.wrap.createChild({
19415             tag: 'iframe',
19416             cls: 'form-control', // bootstrap..
19417             id: this.frameId,
19418             name: this.frameId,
19419             frameBorder : 'no',
19420             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
19421         }, this.el
19422         );
19423         
19424         
19425         this.iframe = iframe.dom;
19426
19427          this.assignDocWin();
19428         
19429         this.doc.designMode = 'on';
19430        
19431         this.doc.open();
19432         this.doc.write(this.getDocMarkup());
19433         this.doc.close();
19434
19435         
19436         var task = { // must defer to wait for browser to be ready
19437             run : function(){
19438                 //console.log("run task?" + this.doc.readyState);
19439                 this.assignDocWin();
19440                 if(this.doc.body || this.doc.readyState == 'complete'){
19441                     try {
19442                         this.doc.designMode="on";
19443                     } catch (e) {
19444                         return;
19445                     }
19446                     Roo.TaskMgr.stop(task);
19447                     this.initEditor.defer(10, this);
19448                 }
19449             },
19450             interval : 10,
19451             duration: 10000,
19452             scope: this
19453         };
19454         Roo.TaskMgr.start(task);
19455
19456     },
19457
19458     // private
19459     onResize : function(w, h)
19460     {
19461          Roo.log('resize: ' +w + ',' + h );
19462         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19463         if(!this.iframe){
19464             return;
19465         }
19466         if(typeof w == 'number'){
19467             
19468             this.iframe.style.width = w + 'px';
19469         }
19470         if(typeof h == 'number'){
19471             
19472             this.iframe.style.height = h + 'px';
19473             if(this.doc){
19474                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19475             }
19476         }
19477         
19478     },
19479
19480     /**
19481      * Toggles the editor between standard and source edit mode.
19482      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19483      */
19484     toggleSourceEdit : function(sourceEditMode){
19485         
19486         this.sourceEditMode = sourceEditMode === true;
19487         
19488         if(this.sourceEditMode){
19489  
19490             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
19491             
19492         }else{
19493             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19494             //this.iframe.className = '';
19495             this.deferFocus();
19496         }
19497         //this.setSize(this.owner.wrap.getSize());
19498         //this.fireEvent('editmodechange', this, this.sourceEditMode);
19499     },
19500
19501     
19502   
19503
19504     /**
19505      * Protected method that will not generally be called directly. If you need/want
19506      * custom HTML cleanup, this is the method you should override.
19507      * @param {String} html The HTML to be cleaned
19508      * return {String} The cleaned HTML
19509      */
19510     cleanHtml : function(html){
19511         html = String(html);
19512         if(html.length > 5){
19513             if(Roo.isSafari){ // strip safari nonsense
19514                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19515             }
19516         }
19517         if(html == '&nbsp;'){
19518             html = '';
19519         }
19520         return html;
19521     },
19522
19523     /**
19524      * HTML Editor -> Textarea
19525      * Protected method that will not generally be called directly. Syncs the contents
19526      * of the editor iframe with the textarea.
19527      */
19528     syncValue : function(){
19529         if(this.initialized){
19530             var bd = (this.doc.body || this.doc.documentElement);
19531             //this.cleanUpPaste(); -- this is done else where and causes havoc..
19532             var html = bd.innerHTML;
19533             if(Roo.isSafari){
19534                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19535                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19536                 if(m && m[1]){
19537                     html = '<div style="'+m[0]+'">' + html + '</div>';
19538                 }
19539             }
19540             html = this.cleanHtml(html);
19541             // fix up the special chars.. normaly like back quotes in word...
19542             // however we do not want to do this with chinese..
19543             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19544                 var cc = b.charCodeAt();
19545                 if (
19546                     (cc >= 0x4E00 && cc < 0xA000 ) ||
19547                     (cc >= 0x3400 && cc < 0x4E00 ) ||
19548                     (cc >= 0xf900 && cc < 0xfb00 )
19549                 ) {
19550                         return b;
19551                 }
19552                 return "&#"+cc+";" 
19553             });
19554             if(this.owner.fireEvent('beforesync', this, html) !== false){
19555                 this.el.dom.value = html;
19556                 this.owner.fireEvent('sync', this, html);
19557             }
19558         }
19559     },
19560
19561     /**
19562      * Protected method that will not generally be called directly. Pushes the value of the textarea
19563      * into the iframe editor.
19564      */
19565     pushValue : function(){
19566         if(this.initialized){
19567             var v = this.el.dom.value.trim();
19568             
19569 //            if(v.length < 1){
19570 //                v = '&#160;';
19571 //            }
19572             
19573             if(this.owner.fireEvent('beforepush', this, v) !== false){
19574                 var d = (this.doc.body || this.doc.documentElement);
19575                 d.innerHTML = v;
19576                 this.cleanUpPaste();
19577                 this.el.dom.value = d.innerHTML;
19578                 this.owner.fireEvent('push', this, v);
19579             }
19580         }
19581     },
19582
19583     // private
19584     deferFocus : function(){
19585         this.focus.defer(10, this);
19586     },
19587
19588     // doc'ed in Field
19589     focus : function(){
19590         if(this.win && !this.sourceEditMode){
19591             this.win.focus();
19592         }else{
19593             this.el.focus();
19594         }
19595     },
19596     
19597     assignDocWin: function()
19598     {
19599         var iframe = this.iframe;
19600         
19601          if(Roo.isIE){
19602             this.doc = iframe.contentWindow.document;
19603             this.win = iframe.contentWindow;
19604         } else {
19605 //            if (!Roo.get(this.frameId)) {
19606 //                return;
19607 //            }
19608 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19609 //            this.win = Roo.get(this.frameId).dom.contentWindow;
19610             
19611             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19612                 return;
19613             }
19614             
19615             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19616             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19617         }
19618     },
19619     
19620     // private
19621     initEditor : function(){
19622         //console.log("INIT EDITOR");
19623         this.assignDocWin();
19624         
19625         
19626         
19627         this.doc.designMode="on";
19628         this.doc.open();
19629         this.doc.write(this.getDocMarkup());
19630         this.doc.close();
19631         
19632         var dbody = (this.doc.body || this.doc.documentElement);
19633         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19634         // this copies styles from the containing element into thsi one..
19635         // not sure why we need all of this..
19636         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19637         
19638         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19639         //ss['background-attachment'] = 'fixed'; // w3c
19640         dbody.bgProperties = 'fixed'; // ie
19641         //Roo.DomHelper.applyStyles(dbody, ss);
19642         Roo.EventManager.on(this.doc, {
19643             //'mousedown': this.onEditorEvent,
19644             'mouseup': this.onEditorEvent,
19645             'dblclick': this.onEditorEvent,
19646             'click': this.onEditorEvent,
19647             'keyup': this.onEditorEvent,
19648             buffer:100,
19649             scope: this
19650         });
19651         if(Roo.isGecko){
19652             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19653         }
19654         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19655             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19656         }
19657         this.initialized = true;
19658
19659         this.owner.fireEvent('initialize', this);
19660         this.pushValue();
19661     },
19662
19663     // private
19664     onDestroy : function(){
19665         
19666         
19667         
19668         if(this.rendered){
19669             
19670             //for (var i =0; i < this.toolbars.length;i++) {
19671             //    // fixme - ask toolbars for heights?
19672             //    this.toolbars[i].onDestroy();
19673            // }
19674             
19675             //this.wrap.dom.innerHTML = '';
19676             //this.wrap.remove();
19677         }
19678     },
19679
19680     // private
19681     onFirstFocus : function(){
19682         
19683         this.assignDocWin();
19684         
19685         
19686         this.activated = true;
19687          
19688     
19689         if(Roo.isGecko){ // prevent silly gecko errors
19690             this.win.focus();
19691             var s = this.win.getSelection();
19692             if(!s.focusNode || s.focusNode.nodeType != 3){
19693                 var r = s.getRangeAt(0);
19694                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19695                 r.collapse(true);
19696                 this.deferFocus();
19697             }
19698             try{
19699                 this.execCmd('useCSS', true);
19700                 this.execCmd('styleWithCSS', false);
19701             }catch(e){}
19702         }
19703         this.owner.fireEvent('activate', this);
19704     },
19705
19706     // private
19707     adjustFont: function(btn){
19708         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19709         //if(Roo.isSafari){ // safari
19710         //    adjust *= 2;
19711        // }
19712         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19713         if(Roo.isSafari){ // safari
19714             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19715             v =  (v < 10) ? 10 : v;
19716             v =  (v > 48) ? 48 : v;
19717             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19718             
19719         }
19720         
19721         
19722         v = Math.max(1, v+adjust);
19723         
19724         this.execCmd('FontSize', v  );
19725     },
19726
19727     onEditorEvent : function(e)
19728     {
19729         this.owner.fireEvent('editorevent', this, e);
19730       //  this.updateToolbar();
19731         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19732     },
19733
19734     insertTag : function(tg)
19735     {
19736         // could be a bit smarter... -> wrap the current selected tRoo..
19737         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19738             
19739             range = this.createRange(this.getSelection());
19740             var wrappingNode = this.doc.createElement(tg.toLowerCase());
19741             wrappingNode.appendChild(range.extractContents());
19742             range.insertNode(wrappingNode);
19743
19744             return;
19745             
19746             
19747             
19748         }
19749         this.execCmd("formatblock",   tg);
19750         
19751     },
19752     
19753     insertText : function(txt)
19754     {
19755         
19756         
19757         var range = this.createRange();
19758         range.deleteContents();
19759                //alert(Sender.getAttribute('label'));
19760                
19761         range.insertNode(this.doc.createTextNode(txt));
19762     } ,
19763     
19764      
19765
19766     /**
19767      * Executes a Midas editor command on the editor document and performs necessary focus and
19768      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19769      * @param {String} cmd The Midas command
19770      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19771      */
19772     relayCmd : function(cmd, value){
19773         this.win.focus();
19774         this.execCmd(cmd, value);
19775         this.owner.fireEvent('editorevent', this);
19776         //this.updateToolbar();
19777         this.owner.deferFocus();
19778     },
19779
19780     /**
19781      * Executes a Midas editor command directly on the editor document.
19782      * For visual commands, you should use {@link #relayCmd} instead.
19783      * <b>This should only be called after the editor is initialized.</b>
19784      * @param {String} cmd The Midas command
19785      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19786      */
19787     execCmd : function(cmd, value){
19788         this.doc.execCommand(cmd, false, value === undefined ? null : value);
19789         this.syncValue();
19790     },
19791  
19792  
19793    
19794     /**
19795      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19796      * to insert tRoo.
19797      * @param {String} text | dom node.. 
19798      */
19799     insertAtCursor : function(text)
19800     {
19801         
19802         
19803         
19804         if(!this.activated){
19805             return;
19806         }
19807         /*
19808         if(Roo.isIE){
19809             this.win.focus();
19810             var r = this.doc.selection.createRange();
19811             if(r){
19812                 r.collapse(true);
19813                 r.pasteHTML(text);
19814                 this.syncValue();
19815                 this.deferFocus();
19816             
19817             }
19818             return;
19819         }
19820         */
19821         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19822             this.win.focus();
19823             
19824             
19825             // from jquery ui (MIT licenced)
19826             var range, node;
19827             var win = this.win;
19828             
19829             if (win.getSelection && win.getSelection().getRangeAt) {
19830                 range = win.getSelection().getRangeAt(0);
19831                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19832                 range.insertNode(node);
19833             } else if (win.document.selection && win.document.selection.createRange) {
19834                 // no firefox support
19835                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19836                 win.document.selection.createRange().pasteHTML(txt);
19837             } else {
19838                 // no firefox support
19839                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19840                 this.execCmd('InsertHTML', txt);
19841             } 
19842             
19843             this.syncValue();
19844             
19845             this.deferFocus();
19846         }
19847     },
19848  // private
19849     mozKeyPress : function(e){
19850         if(e.ctrlKey){
19851             var c = e.getCharCode(), cmd;
19852           
19853             if(c > 0){
19854                 c = String.fromCharCode(c).toLowerCase();
19855                 switch(c){
19856                     case 'b':
19857                         cmd = 'bold';
19858                         break;
19859                     case 'i':
19860                         cmd = 'italic';
19861                         break;
19862                     
19863                     case 'u':
19864                         cmd = 'underline';
19865                         break;
19866                     
19867                     case 'v':
19868                         this.cleanUpPaste.defer(100, this);
19869                         return;
19870                         
19871                 }
19872                 if(cmd){
19873                     this.win.focus();
19874                     this.execCmd(cmd);
19875                     this.deferFocus();
19876                     e.preventDefault();
19877                 }
19878                 
19879             }
19880         }
19881     },
19882
19883     // private
19884     fixKeys : function(){ // load time branching for fastest keydown performance
19885         if(Roo.isIE){
19886             return function(e){
19887                 var k = e.getKey(), r;
19888                 if(k == e.TAB){
19889                     e.stopEvent();
19890                     r = this.doc.selection.createRange();
19891                     if(r){
19892                         r.collapse(true);
19893                         r.pasteHTML('&#160;&#160;&#160;&#160;');
19894                         this.deferFocus();
19895                     }
19896                     return;
19897                 }
19898                 
19899                 if(k == e.ENTER){
19900                     r = this.doc.selection.createRange();
19901                     if(r){
19902                         var target = r.parentElement();
19903                         if(!target || target.tagName.toLowerCase() != 'li'){
19904                             e.stopEvent();
19905                             r.pasteHTML('<br />');
19906                             r.collapse(false);
19907                             r.select();
19908                         }
19909                     }
19910                 }
19911                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19912                     this.cleanUpPaste.defer(100, this);
19913                     return;
19914                 }
19915                 
19916                 
19917             };
19918         }else if(Roo.isOpera){
19919             return function(e){
19920                 var k = e.getKey();
19921                 if(k == e.TAB){
19922                     e.stopEvent();
19923                     this.win.focus();
19924                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
19925                     this.deferFocus();
19926                 }
19927                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19928                     this.cleanUpPaste.defer(100, this);
19929                     return;
19930                 }
19931                 
19932             };
19933         }else if(Roo.isSafari){
19934             return function(e){
19935                 var k = e.getKey();
19936                 
19937                 if(k == e.TAB){
19938                     e.stopEvent();
19939                     this.execCmd('InsertText','\t');
19940                     this.deferFocus();
19941                     return;
19942                 }
19943                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19944                     this.cleanUpPaste.defer(100, this);
19945                     return;
19946                 }
19947                 
19948              };
19949         }
19950     }(),
19951     
19952     getAllAncestors: function()
19953     {
19954         var p = this.getSelectedNode();
19955         var a = [];
19956         if (!p) {
19957             a.push(p); // push blank onto stack..
19958             p = this.getParentElement();
19959         }
19960         
19961         
19962         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
19963             a.push(p);
19964             p = p.parentNode;
19965         }
19966         a.push(this.doc.body);
19967         return a;
19968     },
19969     lastSel : false,
19970     lastSelNode : false,
19971     
19972     
19973     getSelection : function() 
19974     {
19975         this.assignDocWin();
19976         return Roo.isIE ? this.doc.selection : this.win.getSelection();
19977     },
19978     
19979     getSelectedNode: function() 
19980     {
19981         // this may only work on Gecko!!!
19982         
19983         // should we cache this!!!!
19984         
19985         
19986         
19987          
19988         var range = this.createRange(this.getSelection()).cloneRange();
19989         
19990         if (Roo.isIE) {
19991             var parent = range.parentElement();
19992             while (true) {
19993                 var testRange = range.duplicate();
19994                 testRange.moveToElementText(parent);
19995                 if (testRange.inRange(range)) {
19996                     break;
19997                 }
19998                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
19999                     break;
20000                 }
20001                 parent = parent.parentElement;
20002             }
20003             return parent;
20004         }
20005         
20006         // is ancestor a text element.
20007         var ac =  range.commonAncestorContainer;
20008         if (ac.nodeType == 3) {
20009             ac = ac.parentNode;
20010         }
20011         
20012         var ar = ac.childNodes;
20013          
20014         var nodes = [];
20015         var other_nodes = [];
20016         var has_other_nodes = false;
20017         for (var i=0;i<ar.length;i++) {
20018             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
20019                 continue;
20020             }
20021             // fullly contained node.
20022             
20023             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
20024                 nodes.push(ar[i]);
20025                 continue;
20026             }
20027             
20028             // probably selected..
20029             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
20030                 other_nodes.push(ar[i]);
20031                 continue;
20032             }
20033             // outer..
20034             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
20035                 continue;
20036             }
20037             
20038             
20039             has_other_nodes = true;
20040         }
20041         if (!nodes.length && other_nodes.length) {
20042             nodes= other_nodes;
20043         }
20044         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
20045             return false;
20046         }
20047         
20048         return nodes[0];
20049     },
20050     createRange: function(sel)
20051     {
20052         // this has strange effects when using with 
20053         // top toolbar - not sure if it's a great idea.
20054         //this.editor.contentWindow.focus();
20055         if (typeof sel != "undefined") {
20056             try {
20057                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
20058             } catch(e) {
20059                 return this.doc.createRange();
20060             }
20061         } else {
20062             return this.doc.createRange();
20063         }
20064     },
20065     getParentElement: function()
20066     {
20067         
20068         this.assignDocWin();
20069         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
20070         
20071         var range = this.createRange(sel);
20072          
20073         try {
20074             var p = range.commonAncestorContainer;
20075             while (p.nodeType == 3) { // text node
20076                 p = p.parentNode;
20077             }
20078             return p;
20079         } catch (e) {
20080             return null;
20081         }
20082     
20083     },
20084     /***
20085      *
20086      * Range intersection.. the hard stuff...
20087      *  '-1' = before
20088      *  '0' = hits..
20089      *  '1' = after.
20090      *         [ -- selected range --- ]
20091      *   [fail]                        [fail]
20092      *
20093      *    basically..
20094      *      if end is before start or  hits it. fail.
20095      *      if start is after end or hits it fail.
20096      *
20097      *   if either hits (but other is outside. - then it's not 
20098      *   
20099      *    
20100      **/
20101     
20102     
20103     // @see http://www.thismuchiknow.co.uk/?p=64.
20104     rangeIntersectsNode : function(range, node)
20105     {
20106         var nodeRange = node.ownerDocument.createRange();
20107         try {
20108             nodeRange.selectNode(node);
20109         } catch (e) {
20110             nodeRange.selectNodeContents(node);
20111         }
20112     
20113         var rangeStartRange = range.cloneRange();
20114         rangeStartRange.collapse(true);
20115     
20116         var rangeEndRange = range.cloneRange();
20117         rangeEndRange.collapse(false);
20118     
20119         var nodeStartRange = nodeRange.cloneRange();
20120         nodeStartRange.collapse(true);
20121     
20122         var nodeEndRange = nodeRange.cloneRange();
20123         nodeEndRange.collapse(false);
20124     
20125         return rangeStartRange.compareBoundaryPoints(
20126                  Range.START_TO_START, nodeEndRange) == -1 &&
20127                rangeEndRange.compareBoundaryPoints(
20128                  Range.START_TO_START, nodeStartRange) == 1;
20129         
20130          
20131     },
20132     rangeCompareNode : function(range, node)
20133     {
20134         var nodeRange = node.ownerDocument.createRange();
20135         try {
20136             nodeRange.selectNode(node);
20137         } catch (e) {
20138             nodeRange.selectNodeContents(node);
20139         }
20140         
20141         
20142         range.collapse(true);
20143     
20144         nodeRange.collapse(true);
20145      
20146         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
20147         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
20148          
20149         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
20150         
20151         var nodeIsBefore   =  ss == 1;
20152         var nodeIsAfter    = ee == -1;
20153         
20154         if (nodeIsBefore && nodeIsAfter) {
20155             return 0; // outer
20156         }
20157         if (!nodeIsBefore && nodeIsAfter) {
20158             return 1; //right trailed.
20159         }
20160         
20161         if (nodeIsBefore && !nodeIsAfter) {
20162             return 2;  // left trailed.
20163         }
20164         // fully contined.
20165         return 3;
20166     },
20167
20168     // private? - in a new class?
20169     cleanUpPaste :  function()
20170     {
20171         // cleans up the whole document..
20172         Roo.log('cleanuppaste');
20173         
20174         this.cleanUpChildren(this.doc.body);
20175         var clean = this.cleanWordChars(this.doc.body.innerHTML);
20176         if (clean != this.doc.body.innerHTML) {
20177             this.doc.body.innerHTML = clean;
20178         }
20179         
20180     },
20181     
20182     cleanWordChars : function(input) {// change the chars to hex code
20183         var he = Roo.HtmlEditorCore;
20184         
20185         var output = input;
20186         Roo.each(he.swapCodes, function(sw) { 
20187             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
20188             
20189             output = output.replace(swapper, sw[1]);
20190         });
20191         
20192         return output;
20193     },
20194     
20195     
20196     cleanUpChildren : function (n)
20197     {
20198         if (!n.childNodes.length) {
20199             return;
20200         }
20201         for (var i = n.childNodes.length-1; i > -1 ; i--) {
20202            this.cleanUpChild(n.childNodes[i]);
20203         }
20204     },
20205     
20206     
20207         
20208     
20209     cleanUpChild : function (node)
20210     {
20211         var ed = this;
20212         //console.log(node);
20213         if (node.nodeName == "#text") {
20214             // clean up silly Windows -- stuff?
20215             return; 
20216         }
20217         if (node.nodeName == "#comment") {
20218             node.parentNode.removeChild(node);
20219             // clean up silly Windows -- stuff?
20220             return; 
20221         }
20222         var lcname = node.tagName.toLowerCase();
20223         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
20224         // whitelist of tags..
20225         
20226         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
20227             // remove node.
20228             node.parentNode.removeChild(node);
20229             return;
20230             
20231         }
20232         
20233         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
20234         
20235         // remove <a name=....> as rendering on yahoo mailer is borked with this.
20236         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
20237         
20238         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
20239         //    remove_keep_children = true;
20240         //}
20241         
20242         if (remove_keep_children) {
20243             this.cleanUpChildren(node);
20244             // inserts everything just before this node...
20245             while (node.childNodes.length) {
20246                 var cn = node.childNodes[0];
20247                 node.removeChild(cn);
20248                 node.parentNode.insertBefore(cn, node);
20249             }
20250             node.parentNode.removeChild(node);
20251             return;
20252         }
20253         
20254         if (!node.attributes || !node.attributes.length) {
20255             this.cleanUpChildren(node);
20256             return;
20257         }
20258         
20259         function cleanAttr(n,v)
20260         {
20261             
20262             if (v.match(/^\./) || v.match(/^\//)) {
20263                 return;
20264             }
20265             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
20266                 return;
20267             }
20268             if (v.match(/^#/)) {
20269                 return;
20270             }
20271 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
20272             node.removeAttribute(n);
20273             
20274         }
20275         
20276         var cwhite = this.cwhite;
20277         var cblack = this.cblack;
20278             
20279         function cleanStyle(n,v)
20280         {
20281             if (v.match(/expression/)) { //XSS?? should we even bother..
20282                 node.removeAttribute(n);
20283                 return;
20284             }
20285             
20286             var parts = v.split(/;/);
20287             var clean = [];
20288             
20289             Roo.each(parts, function(p) {
20290                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
20291                 if (!p.length) {
20292                     return true;
20293                 }
20294                 var l = p.split(':').shift().replace(/\s+/g,'');
20295                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
20296                 
20297                 if ( cwhite.length && cblack.indexOf(l) > -1) {
20298 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20299                     //node.removeAttribute(n);
20300                     return true;
20301                 }
20302                 //Roo.log()
20303                 // only allow 'c whitelisted system attributes'
20304                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
20305 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20306                     //node.removeAttribute(n);
20307                     return true;
20308                 }
20309                 
20310                 
20311                  
20312                 
20313                 clean.push(p);
20314                 return true;
20315             });
20316             if (clean.length) { 
20317                 node.setAttribute(n, clean.join(';'));
20318             } else {
20319                 node.removeAttribute(n);
20320             }
20321             
20322         }
20323         
20324         
20325         for (var i = node.attributes.length-1; i > -1 ; i--) {
20326             var a = node.attributes[i];
20327             //console.log(a);
20328             
20329             if (a.name.toLowerCase().substr(0,2)=='on')  {
20330                 node.removeAttribute(a.name);
20331                 continue;
20332             }
20333             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
20334                 node.removeAttribute(a.name);
20335                 continue;
20336             }
20337             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
20338                 cleanAttr(a.name,a.value); // fixme..
20339                 continue;
20340             }
20341             if (a.name == 'style') {
20342                 cleanStyle(a.name,a.value);
20343                 continue;
20344             }
20345             /// clean up MS crap..
20346             // tecnically this should be a list of valid class'es..
20347             
20348             
20349             if (a.name == 'class') {
20350                 if (a.value.match(/^Mso/)) {
20351                     node.className = '';
20352                 }
20353                 
20354                 if (a.value.match(/body/)) {
20355                     node.className = '';
20356                 }
20357                 continue;
20358             }
20359             
20360             // style cleanup!?
20361             // class cleanup?
20362             
20363         }
20364         
20365         
20366         this.cleanUpChildren(node);
20367         
20368         
20369     },
20370     
20371     /**
20372      * Clean up MS wordisms...
20373      */
20374     cleanWord : function(node)
20375     {
20376         
20377         
20378         if (!node) {
20379             this.cleanWord(this.doc.body);
20380             return;
20381         }
20382         if (node.nodeName == "#text") {
20383             // clean up silly Windows -- stuff?
20384             return; 
20385         }
20386         if (node.nodeName == "#comment") {
20387             node.parentNode.removeChild(node);
20388             // clean up silly Windows -- stuff?
20389             return; 
20390         }
20391         
20392         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
20393             node.parentNode.removeChild(node);
20394             return;
20395         }
20396         
20397         // remove - but keep children..
20398         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20399             while (node.childNodes.length) {
20400                 var cn = node.childNodes[0];
20401                 node.removeChild(cn);
20402                 node.parentNode.insertBefore(cn, node);
20403             }
20404             node.parentNode.removeChild(node);
20405             this.iterateChildren(node, this.cleanWord);
20406             return;
20407         }
20408         // clean styles
20409         if (node.className.length) {
20410             
20411             var cn = node.className.split(/\W+/);
20412             var cna = [];
20413             Roo.each(cn, function(cls) {
20414                 if (cls.match(/Mso[a-zA-Z]+/)) {
20415                     return;
20416                 }
20417                 cna.push(cls);
20418             });
20419             node.className = cna.length ? cna.join(' ') : '';
20420             if (!cna.length) {
20421                 node.removeAttribute("class");
20422             }
20423         }
20424         
20425         if (node.hasAttribute("lang")) {
20426             node.removeAttribute("lang");
20427         }
20428         
20429         if (node.hasAttribute("style")) {
20430             
20431             var styles = node.getAttribute("style").split(";");
20432             var nstyle = [];
20433             Roo.each(styles, function(s) {
20434                 if (!s.match(/:/)) {
20435                     return;
20436                 }
20437                 var kv = s.split(":");
20438                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20439                     return;
20440                 }
20441                 // what ever is left... we allow.
20442                 nstyle.push(s);
20443             });
20444             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20445             if (!nstyle.length) {
20446                 node.removeAttribute('style');
20447             }
20448         }
20449         this.iterateChildren(node, this.cleanWord);
20450         
20451         
20452         
20453     },
20454     /**
20455      * iterateChildren of a Node, calling fn each time, using this as the scole..
20456      * @param {DomNode} node node to iterate children of.
20457      * @param {Function} fn method of this class to call on each item.
20458      */
20459     iterateChildren : function(node, fn)
20460     {
20461         if (!node.childNodes.length) {
20462                 return;
20463         }
20464         for (var i = node.childNodes.length-1; i > -1 ; i--) {
20465            fn.call(this, node.childNodes[i])
20466         }
20467     },
20468     
20469     
20470     /**
20471      * cleanTableWidths.
20472      *
20473      * Quite often pasting from word etc.. results in tables with column and widths.
20474      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20475      *
20476      */
20477     cleanTableWidths : function(node)
20478     {
20479          
20480          
20481         if (!node) {
20482             this.cleanTableWidths(this.doc.body);
20483             return;
20484         }
20485         
20486         // ignore list...
20487         if (node.nodeName == "#text" || node.nodeName == "#comment") {
20488             return; 
20489         }
20490         Roo.log(node.tagName);
20491         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20492             this.iterateChildren(node, this.cleanTableWidths);
20493             return;
20494         }
20495         if (node.hasAttribute('width')) {
20496             node.removeAttribute('width');
20497         }
20498         
20499          
20500         if (node.hasAttribute("style")) {
20501             // pretty basic...
20502             
20503             var styles = node.getAttribute("style").split(";");
20504             var nstyle = [];
20505             Roo.each(styles, function(s) {
20506                 if (!s.match(/:/)) {
20507                     return;
20508                 }
20509                 var kv = s.split(":");
20510                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20511                     return;
20512                 }
20513                 // what ever is left... we allow.
20514                 nstyle.push(s);
20515             });
20516             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20517             if (!nstyle.length) {
20518                 node.removeAttribute('style');
20519             }
20520         }
20521         
20522         this.iterateChildren(node, this.cleanTableWidths);
20523         
20524         
20525     },
20526     
20527     
20528     
20529     
20530     domToHTML : function(currentElement, depth, nopadtext) {
20531         
20532         depth = depth || 0;
20533         nopadtext = nopadtext || false;
20534     
20535         if (!currentElement) {
20536             return this.domToHTML(this.doc.body);
20537         }
20538         
20539         //Roo.log(currentElement);
20540         var j;
20541         var allText = false;
20542         var nodeName = currentElement.nodeName;
20543         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20544         
20545         if  (nodeName == '#text') {
20546             
20547             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20548         }
20549         
20550         
20551         var ret = '';
20552         if (nodeName != 'BODY') {
20553              
20554             var i = 0;
20555             // Prints the node tagName, such as <A>, <IMG>, etc
20556             if (tagName) {
20557                 var attr = [];
20558                 for(i = 0; i < currentElement.attributes.length;i++) {
20559                     // quoting?
20560                     var aname = currentElement.attributes.item(i).name;
20561                     if (!currentElement.attributes.item(i).value.length) {
20562                         continue;
20563                     }
20564                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20565                 }
20566                 
20567                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20568             } 
20569             else {
20570                 
20571                 // eack
20572             }
20573         } else {
20574             tagName = false;
20575         }
20576         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20577             return ret;
20578         }
20579         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20580             nopadtext = true;
20581         }
20582         
20583         
20584         // Traverse the tree
20585         i = 0;
20586         var currentElementChild = currentElement.childNodes.item(i);
20587         var allText = true;
20588         var innerHTML  = '';
20589         lastnode = '';
20590         while (currentElementChild) {
20591             // Formatting code (indent the tree so it looks nice on the screen)
20592             var nopad = nopadtext;
20593             if (lastnode == 'SPAN') {
20594                 nopad  = true;
20595             }
20596             // text
20597             if  (currentElementChild.nodeName == '#text') {
20598                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20599                 toadd = nopadtext ? toadd : toadd.trim();
20600                 if (!nopad && toadd.length > 80) {
20601                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
20602                 }
20603                 innerHTML  += toadd;
20604                 
20605                 i++;
20606                 currentElementChild = currentElement.childNodes.item(i);
20607                 lastNode = '';
20608                 continue;
20609             }
20610             allText = false;
20611             
20612             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
20613                 
20614             // Recursively traverse the tree structure of the child node
20615             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
20616             lastnode = currentElementChild.nodeName;
20617             i++;
20618             currentElementChild=currentElement.childNodes.item(i);
20619         }
20620         
20621         ret += innerHTML;
20622         
20623         if (!allText) {
20624                 // The remaining code is mostly for formatting the tree
20625             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
20626         }
20627         
20628         
20629         if (tagName) {
20630             ret+= "</"+tagName+">";
20631         }
20632         return ret;
20633         
20634     },
20635         
20636     applyBlacklists : function()
20637     {
20638         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
20639         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
20640         
20641         this.white = [];
20642         this.black = [];
20643         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20644             if (b.indexOf(tag) > -1) {
20645                 return;
20646             }
20647             this.white.push(tag);
20648             
20649         }, this);
20650         
20651         Roo.each(w, function(tag) {
20652             if (b.indexOf(tag) > -1) {
20653                 return;
20654             }
20655             if (this.white.indexOf(tag) > -1) {
20656                 return;
20657             }
20658             this.white.push(tag);
20659             
20660         }, this);
20661         
20662         
20663         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20664             if (w.indexOf(tag) > -1) {
20665                 return;
20666             }
20667             this.black.push(tag);
20668             
20669         }, this);
20670         
20671         Roo.each(b, function(tag) {
20672             if (w.indexOf(tag) > -1) {
20673                 return;
20674             }
20675             if (this.black.indexOf(tag) > -1) {
20676                 return;
20677             }
20678             this.black.push(tag);
20679             
20680         }, this);
20681         
20682         
20683         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
20684         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
20685         
20686         this.cwhite = [];
20687         this.cblack = [];
20688         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20689             if (b.indexOf(tag) > -1) {
20690                 return;
20691             }
20692             this.cwhite.push(tag);
20693             
20694         }, this);
20695         
20696         Roo.each(w, function(tag) {
20697             if (b.indexOf(tag) > -1) {
20698                 return;
20699             }
20700             if (this.cwhite.indexOf(tag) > -1) {
20701                 return;
20702             }
20703             this.cwhite.push(tag);
20704             
20705         }, this);
20706         
20707         
20708         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20709             if (w.indexOf(tag) > -1) {
20710                 return;
20711             }
20712             this.cblack.push(tag);
20713             
20714         }, this);
20715         
20716         Roo.each(b, function(tag) {
20717             if (w.indexOf(tag) > -1) {
20718                 return;
20719             }
20720             if (this.cblack.indexOf(tag) > -1) {
20721                 return;
20722             }
20723             this.cblack.push(tag);
20724             
20725         }, this);
20726     },
20727     
20728     setStylesheets : function(stylesheets)
20729     {
20730         if(typeof(stylesheets) == 'string'){
20731             Roo.get(this.iframe.contentDocument.head).createChild({
20732                 tag : 'link',
20733                 rel : 'stylesheet',
20734                 type : 'text/css',
20735                 href : stylesheets
20736             });
20737             
20738             return;
20739         }
20740         var _this = this;
20741      
20742         Roo.each(stylesheets, function(s) {
20743             if(!s.length){
20744                 return;
20745             }
20746             
20747             Roo.get(_this.iframe.contentDocument.head).createChild({
20748                 tag : 'link',
20749                 rel : 'stylesheet',
20750                 type : 'text/css',
20751                 href : s
20752             });
20753         });
20754
20755         
20756     },
20757     
20758     removeStylesheets : function()
20759     {
20760         var _this = this;
20761         
20762         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20763             s.remove();
20764         });
20765     }
20766     
20767     // hide stuff that is not compatible
20768     /**
20769      * @event blur
20770      * @hide
20771      */
20772     /**
20773      * @event change
20774      * @hide
20775      */
20776     /**
20777      * @event focus
20778      * @hide
20779      */
20780     /**
20781      * @event specialkey
20782      * @hide
20783      */
20784     /**
20785      * @cfg {String} fieldClass @hide
20786      */
20787     /**
20788      * @cfg {String} focusClass @hide
20789      */
20790     /**
20791      * @cfg {String} autoCreate @hide
20792      */
20793     /**
20794      * @cfg {String} inputType @hide
20795      */
20796     /**
20797      * @cfg {String} invalidClass @hide
20798      */
20799     /**
20800      * @cfg {String} invalidText @hide
20801      */
20802     /**
20803      * @cfg {String} msgFx @hide
20804      */
20805     /**
20806      * @cfg {String} validateOnBlur @hide
20807      */
20808 });
20809
20810 Roo.HtmlEditorCore.white = [
20811         'area', 'br', 'img', 'input', 'hr', 'wbr',
20812         
20813        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
20814        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
20815        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
20816        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
20817        'table',   'ul',         'xmp', 
20818        
20819        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
20820       'thead',   'tr', 
20821      
20822       'dir', 'menu', 'ol', 'ul', 'dl',
20823        
20824       'embed',  'object'
20825 ];
20826
20827
20828 Roo.HtmlEditorCore.black = [
20829     //    'embed',  'object', // enable - backend responsiblity to clean thiese
20830         'applet', // 
20831         'base',   'basefont', 'bgsound', 'blink',  'body', 
20832         'frame',  'frameset', 'head',    'html',   'ilayer', 
20833         'iframe', 'layer',  'link',     'meta',    'object',   
20834         'script', 'style' ,'title',  'xml' // clean later..
20835 ];
20836 Roo.HtmlEditorCore.clean = [
20837     'script', 'style', 'title', 'xml'
20838 ];
20839 Roo.HtmlEditorCore.remove = [
20840     'font'
20841 ];
20842 // attributes..
20843
20844 Roo.HtmlEditorCore.ablack = [
20845     'on'
20846 ];
20847     
20848 Roo.HtmlEditorCore.aclean = [ 
20849     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
20850 ];
20851
20852 // protocols..
20853 Roo.HtmlEditorCore.pwhite= [
20854         'http',  'https',  'mailto'
20855 ];
20856
20857 // white listed style attributes.
20858 Roo.HtmlEditorCore.cwhite= [
20859       //  'text-align', /// default is to allow most things..
20860       
20861          
20862 //        'font-size'//??
20863 ];
20864
20865 // black listed style attributes.
20866 Roo.HtmlEditorCore.cblack= [
20867       //  'font-size' -- this can be set by the project 
20868 ];
20869
20870
20871 Roo.HtmlEditorCore.swapCodes   =[ 
20872     [    8211, "--" ], 
20873     [    8212, "--" ], 
20874     [    8216,  "'" ],  
20875     [    8217, "'" ],  
20876     [    8220, '"' ],  
20877     [    8221, '"' ],  
20878     [    8226, "*" ],  
20879     [    8230, "..." ]
20880 ]; 
20881
20882     /*
20883  * - LGPL
20884  *
20885  * HtmlEditor
20886  * 
20887  */
20888
20889 /**
20890  * @class Roo.bootstrap.HtmlEditor
20891  * @extends Roo.bootstrap.TextArea
20892  * Bootstrap HtmlEditor class
20893
20894  * @constructor
20895  * Create a new HtmlEditor
20896  * @param {Object} config The config object
20897  */
20898
20899 Roo.bootstrap.HtmlEditor = function(config){
20900     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
20901     if (!this.toolbars) {
20902         this.toolbars = [];
20903     }
20904     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
20905     this.addEvents({
20906             /**
20907              * @event initialize
20908              * Fires when the editor is fully initialized (including the iframe)
20909              * @param {HtmlEditor} this
20910              */
20911             initialize: true,
20912             /**
20913              * @event activate
20914              * Fires when the editor is first receives the focus. Any insertion must wait
20915              * until after this event.
20916              * @param {HtmlEditor} this
20917              */
20918             activate: true,
20919              /**
20920              * @event beforesync
20921              * Fires before the textarea is updated with content from the editor iframe. Return false
20922              * to cancel the sync.
20923              * @param {HtmlEditor} this
20924              * @param {String} html
20925              */
20926             beforesync: true,
20927              /**
20928              * @event beforepush
20929              * Fires before the iframe editor is updated with content from the textarea. Return false
20930              * to cancel the push.
20931              * @param {HtmlEditor} this
20932              * @param {String} html
20933              */
20934             beforepush: true,
20935              /**
20936              * @event sync
20937              * Fires when the textarea is updated with content from the editor iframe.
20938              * @param {HtmlEditor} this
20939              * @param {String} html
20940              */
20941             sync: true,
20942              /**
20943              * @event push
20944              * Fires when the iframe editor is updated with content from the textarea.
20945              * @param {HtmlEditor} this
20946              * @param {String} html
20947              */
20948             push: true,
20949              /**
20950              * @event editmodechange
20951              * Fires when the editor switches edit modes
20952              * @param {HtmlEditor} this
20953              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
20954              */
20955             editmodechange: true,
20956             /**
20957              * @event editorevent
20958              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20959              * @param {HtmlEditor} this
20960              */
20961             editorevent: true,
20962             /**
20963              * @event firstfocus
20964              * Fires when on first focus - needed by toolbars..
20965              * @param {HtmlEditor} this
20966              */
20967             firstfocus: true,
20968             /**
20969              * @event autosave
20970              * Auto save the htmlEditor value as a file into Events
20971              * @param {HtmlEditor} this
20972              */
20973             autosave: true,
20974             /**
20975              * @event savedpreview
20976              * preview the saved version of htmlEditor
20977              * @param {HtmlEditor} this
20978              */
20979             savedpreview: true
20980         });
20981 };
20982
20983
20984 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
20985     
20986     
20987       /**
20988      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
20989      */
20990     toolbars : false,
20991    
20992      /**
20993      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20994      *                        Roo.resizable.
20995      */
20996     resizable : false,
20997      /**
20998      * @cfg {Number} height (in pixels)
20999      */   
21000     height: 300,
21001    /**
21002      * @cfg {Number} width (in pixels)
21003      */   
21004     width: false,
21005     
21006     /**
21007      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21008      * 
21009      */
21010     stylesheets: false,
21011     
21012     // id of frame..
21013     frameId: false,
21014     
21015     // private properties
21016     validationEvent : false,
21017     deferHeight: true,
21018     initialized : false,
21019     activated : false,
21020     
21021     onFocus : Roo.emptyFn,
21022     iframePad:3,
21023     hideMode:'offsets',
21024     
21025     
21026     tbContainer : false,
21027     
21028     toolbarContainer :function() {
21029         return this.wrap.select('.x-html-editor-tb',true).first();
21030     },
21031
21032     /**
21033      * Protected method that will not generally be called directly. It
21034      * is called when the editor creates its toolbar. Override this method if you need to
21035      * add custom toolbar buttons.
21036      * @param {HtmlEditor} editor
21037      */
21038     createToolbar : function(){
21039         
21040         Roo.log("create toolbars");
21041         
21042         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
21043         this.toolbars[0].render(this.toolbarContainer());
21044         
21045         return;
21046         
21047 //        if (!editor.toolbars || !editor.toolbars.length) {
21048 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
21049 //        }
21050 //        
21051 //        for (var i =0 ; i < editor.toolbars.length;i++) {
21052 //            editor.toolbars[i] = Roo.factory(
21053 //                    typeof(editor.toolbars[i]) == 'string' ?
21054 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
21055 //                Roo.bootstrap.HtmlEditor);
21056 //            editor.toolbars[i].init(editor);
21057 //        }
21058     },
21059
21060      
21061     // private
21062     onRender : function(ct, position)
21063     {
21064        // Roo.log("Call onRender: " + this.xtype);
21065         var _t = this;
21066         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
21067       
21068         this.wrap = this.inputEl().wrap({
21069             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
21070         });
21071         
21072         this.editorcore.onRender(ct, position);
21073          
21074         if (this.resizable) {
21075             this.resizeEl = new Roo.Resizable(this.wrap, {
21076                 pinned : true,
21077                 wrap: true,
21078                 dynamic : true,
21079                 minHeight : this.height,
21080                 height: this.height,
21081                 handles : this.resizable,
21082                 width: this.width,
21083                 listeners : {
21084                     resize : function(r, w, h) {
21085                         _t.onResize(w,h); // -something
21086                     }
21087                 }
21088             });
21089             
21090         }
21091         this.createToolbar(this);
21092        
21093         
21094         if(!this.width && this.resizable){
21095             this.setSize(this.wrap.getSize());
21096         }
21097         if (this.resizeEl) {
21098             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
21099             // should trigger onReize..
21100         }
21101         
21102     },
21103
21104     // private
21105     onResize : function(w, h)
21106     {
21107         Roo.log('resize: ' +w + ',' + h );
21108         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
21109         var ew = false;
21110         var eh = false;
21111         
21112         if(this.inputEl() ){
21113             if(typeof w == 'number'){
21114                 var aw = w - this.wrap.getFrameWidth('lr');
21115                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
21116                 ew = aw;
21117             }
21118             if(typeof h == 'number'){
21119                  var tbh = -11;  // fixme it needs to tool bar size!
21120                 for (var i =0; i < this.toolbars.length;i++) {
21121                     // fixme - ask toolbars for heights?
21122                     tbh += this.toolbars[i].el.getHeight();
21123                     //if (this.toolbars[i].footer) {
21124                     //    tbh += this.toolbars[i].footer.el.getHeight();
21125                     //}
21126                 }
21127               
21128                 
21129                 
21130                 
21131                 
21132                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
21133                 ah -= 5; // knock a few pixes off for look..
21134                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
21135                 var eh = ah;
21136             }
21137         }
21138         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
21139         this.editorcore.onResize(ew,eh);
21140         
21141     },
21142
21143     /**
21144      * Toggles the editor between standard and source edit mode.
21145      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21146      */
21147     toggleSourceEdit : function(sourceEditMode)
21148     {
21149         this.editorcore.toggleSourceEdit(sourceEditMode);
21150         
21151         if(this.editorcore.sourceEditMode){
21152             Roo.log('editor - showing textarea');
21153             
21154 //            Roo.log('in');
21155 //            Roo.log(this.syncValue());
21156             this.syncValue();
21157             this.inputEl().removeClass(['hide', 'x-hidden']);
21158             this.inputEl().dom.removeAttribute('tabIndex');
21159             this.inputEl().focus();
21160         }else{
21161             Roo.log('editor - hiding textarea');
21162 //            Roo.log('out')
21163 //            Roo.log(this.pushValue()); 
21164             this.pushValue();
21165             
21166             this.inputEl().addClass(['hide', 'x-hidden']);
21167             this.inputEl().dom.setAttribute('tabIndex', -1);
21168             //this.deferFocus();
21169         }
21170          
21171         if(this.resizable){
21172             this.setSize(this.wrap.getSize());
21173         }
21174         
21175         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
21176     },
21177  
21178     // private (for BoxComponent)
21179     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21180
21181     // private (for BoxComponent)
21182     getResizeEl : function(){
21183         return this.wrap;
21184     },
21185
21186     // private (for BoxComponent)
21187     getPositionEl : function(){
21188         return this.wrap;
21189     },
21190
21191     // private
21192     initEvents : function(){
21193         this.originalValue = this.getValue();
21194     },
21195
21196 //    /**
21197 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21198 //     * @method
21199 //     */
21200 //    markInvalid : Roo.emptyFn,
21201 //    /**
21202 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21203 //     * @method
21204 //     */
21205 //    clearInvalid : Roo.emptyFn,
21206
21207     setValue : function(v){
21208         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
21209         this.editorcore.pushValue();
21210     },
21211
21212      
21213     // private
21214     deferFocus : function(){
21215         this.focus.defer(10, this);
21216     },
21217
21218     // doc'ed in Field
21219     focus : function(){
21220         this.editorcore.focus();
21221         
21222     },
21223       
21224
21225     // private
21226     onDestroy : function(){
21227         
21228         
21229         
21230         if(this.rendered){
21231             
21232             for (var i =0; i < this.toolbars.length;i++) {
21233                 // fixme - ask toolbars for heights?
21234                 this.toolbars[i].onDestroy();
21235             }
21236             
21237             this.wrap.dom.innerHTML = '';
21238             this.wrap.remove();
21239         }
21240     },
21241
21242     // private
21243     onFirstFocus : function(){
21244         //Roo.log("onFirstFocus");
21245         this.editorcore.onFirstFocus();
21246          for (var i =0; i < this.toolbars.length;i++) {
21247             this.toolbars[i].onFirstFocus();
21248         }
21249         
21250     },
21251     
21252     // private
21253     syncValue : function()
21254     {   
21255         this.editorcore.syncValue();
21256     },
21257     
21258     pushValue : function()
21259     {   
21260         this.editorcore.pushValue();
21261     }
21262      
21263     
21264     // hide stuff that is not compatible
21265     /**
21266      * @event blur
21267      * @hide
21268      */
21269     /**
21270      * @event change
21271      * @hide
21272      */
21273     /**
21274      * @event focus
21275      * @hide
21276      */
21277     /**
21278      * @event specialkey
21279      * @hide
21280      */
21281     /**
21282      * @cfg {String} fieldClass @hide
21283      */
21284     /**
21285      * @cfg {String} focusClass @hide
21286      */
21287     /**
21288      * @cfg {String} autoCreate @hide
21289      */
21290     /**
21291      * @cfg {String} inputType @hide
21292      */
21293     /**
21294      * @cfg {String} invalidClass @hide
21295      */
21296     /**
21297      * @cfg {String} invalidText @hide
21298      */
21299     /**
21300      * @cfg {String} msgFx @hide
21301      */
21302     /**
21303      * @cfg {String} validateOnBlur @hide
21304      */
21305 });
21306  
21307     
21308    
21309    
21310    
21311       
21312 Roo.namespace('Roo.bootstrap.htmleditor');
21313 /**
21314  * @class Roo.bootstrap.HtmlEditorToolbar1
21315  * Basic Toolbar
21316  * 
21317  * Usage:
21318  *
21319  new Roo.bootstrap.HtmlEditor({
21320     ....
21321     toolbars : [
21322         new Roo.bootstrap.HtmlEditorToolbar1({
21323             disable : { fonts: 1 , format: 1, ..., ... , ...],
21324             btns : [ .... ]
21325         })
21326     }
21327      
21328  * 
21329  * @cfg {Object} disable List of elements to disable..
21330  * @cfg {Array} btns List of additional buttons.
21331  * 
21332  * 
21333  * NEEDS Extra CSS? 
21334  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
21335  */
21336  
21337 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
21338 {
21339     
21340     Roo.apply(this, config);
21341     
21342     // default disabled, based on 'good practice'..
21343     this.disable = this.disable || {};
21344     Roo.applyIf(this.disable, {
21345         fontSize : true,
21346         colors : true,
21347         specialElements : true
21348     });
21349     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
21350     
21351     this.editor = config.editor;
21352     this.editorcore = config.editor.editorcore;
21353     
21354     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
21355     
21356     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
21357     // dont call parent... till later.
21358 }
21359 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
21360      
21361     bar : true,
21362     
21363     editor : false,
21364     editorcore : false,
21365     
21366     
21367     formats : [
21368         "p" ,  
21369         "h1","h2","h3","h4","h5","h6", 
21370         "pre", "code", 
21371         "abbr", "acronym", "address", "cite", "samp", "var",
21372         'div','span'
21373     ],
21374     
21375     onRender : function(ct, position)
21376     {
21377        // Roo.log("Call onRender: " + this.xtype);
21378         
21379        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
21380        Roo.log(this.el);
21381        this.el.dom.style.marginBottom = '0';
21382        var _this = this;
21383        var editorcore = this.editorcore;
21384        var editor= this.editor;
21385        
21386        var children = [];
21387        var btn = function(id,cmd , toggle, handler){
21388        
21389             var  event = toggle ? 'toggle' : 'click';
21390        
21391             var a = {
21392                 size : 'sm',
21393                 xtype: 'Button',
21394                 xns: Roo.bootstrap,
21395                 glyphicon : id,
21396                 cmd : id || cmd,
21397                 enableToggle:toggle !== false,
21398                 //html : 'submit'
21399                 pressed : toggle ? false : null,
21400                 listeners : {}
21401             };
21402             a.listeners[toggle ? 'toggle' : 'click'] = function() {
21403                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
21404             };
21405             children.push(a);
21406             return a;
21407        }
21408         
21409         var style = {
21410                 xtype: 'Button',
21411                 size : 'sm',
21412                 xns: Roo.bootstrap,
21413                 glyphicon : 'font',
21414                 //html : 'submit'
21415                 menu : {
21416                     xtype: 'Menu',
21417                     xns: Roo.bootstrap,
21418                     items:  []
21419                 }
21420         };
21421         Roo.each(this.formats, function(f) {
21422             style.menu.items.push({
21423                 xtype :'MenuItem',
21424                 xns: Roo.bootstrap,
21425                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21426                 tagname : f,
21427                 listeners : {
21428                     click : function()
21429                     {
21430                         editorcore.insertTag(this.tagname);
21431                         editor.focus();
21432                     }
21433                 }
21434                 
21435             });
21436         });
21437          children.push(style);   
21438             
21439             
21440         btn('bold',false,true);
21441         btn('italic',false,true);
21442         btn('align-left', 'justifyleft',true);
21443         btn('align-center', 'justifycenter',true);
21444         btn('align-right' , 'justifyright',true);
21445         btn('link', false, false, function(btn) {
21446             //Roo.log("create link?");
21447             var url = prompt(this.createLinkText, this.defaultLinkValue);
21448             if(url && url != 'http:/'+'/'){
21449                 this.editorcore.relayCmd('createlink', url);
21450             }
21451         }),
21452         btn('list','insertunorderedlist',true);
21453         btn('pencil', false,true, function(btn){
21454                 Roo.log(this);
21455                 
21456                 this.toggleSourceEdit(btn.pressed);
21457         });
21458         /*
21459         var cog = {
21460                 xtype: 'Button',
21461                 size : 'sm',
21462                 xns: Roo.bootstrap,
21463                 glyphicon : 'cog',
21464                 //html : 'submit'
21465                 menu : {
21466                     xtype: 'Menu',
21467                     xns: Roo.bootstrap,
21468                     items:  []
21469                 }
21470         };
21471         
21472         cog.menu.items.push({
21473             xtype :'MenuItem',
21474             xns: Roo.bootstrap,
21475             html : Clean styles,
21476             tagname : f,
21477             listeners : {
21478                 click : function()
21479                 {
21480                     editorcore.insertTag(this.tagname);
21481                     editor.focus();
21482                 }
21483             }
21484             
21485         });
21486        */
21487         
21488          
21489        this.xtype = 'NavSimplebar';
21490         
21491         for(var i=0;i< children.length;i++) {
21492             
21493             this.buttons.add(this.addxtypeChild(children[i]));
21494             
21495         }
21496         
21497         editor.on('editorevent', this.updateToolbar, this);
21498     },
21499     onBtnClick : function(id)
21500     {
21501        this.editorcore.relayCmd(id);
21502        this.editorcore.focus();
21503     },
21504     
21505     /**
21506      * Protected method that will not generally be called directly. It triggers
21507      * a toolbar update by reading the markup state of the current selection in the editor.
21508      */
21509     updateToolbar: function(){
21510
21511         if(!this.editorcore.activated){
21512             this.editor.onFirstFocus(); // is this neeed?
21513             return;
21514         }
21515
21516         var btns = this.buttons; 
21517         var doc = this.editorcore.doc;
21518         btns.get('bold').setActive(doc.queryCommandState('bold'));
21519         btns.get('italic').setActive(doc.queryCommandState('italic'));
21520         //btns.get('underline').setActive(doc.queryCommandState('underline'));
21521         
21522         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21523         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21524         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21525         
21526         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21527         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21528          /*
21529         
21530         var ans = this.editorcore.getAllAncestors();
21531         if (this.formatCombo) {
21532             
21533             
21534             var store = this.formatCombo.store;
21535             this.formatCombo.setValue("");
21536             for (var i =0; i < ans.length;i++) {
21537                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21538                     // select it..
21539                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21540                     break;
21541                 }
21542             }
21543         }
21544         
21545         
21546         
21547         // hides menus... - so this cant be on a menu...
21548         Roo.bootstrap.MenuMgr.hideAll();
21549         */
21550         Roo.bootstrap.MenuMgr.hideAll();
21551         //this.editorsyncValue();
21552     },
21553     onFirstFocus: function() {
21554         this.buttons.each(function(item){
21555            item.enable();
21556         });
21557     },
21558     toggleSourceEdit : function(sourceEditMode){
21559         
21560           
21561         if(sourceEditMode){
21562             Roo.log("disabling buttons");
21563            this.buttons.each( function(item){
21564                 if(item.cmd != 'pencil'){
21565                     item.disable();
21566                 }
21567             });
21568           
21569         }else{
21570             Roo.log("enabling buttons");
21571             if(this.editorcore.initialized){
21572                 this.buttons.each( function(item){
21573                     item.enable();
21574                 });
21575             }
21576             
21577         }
21578         Roo.log("calling toggole on editor");
21579         // tell the editor that it's been pressed..
21580         this.editor.toggleSourceEdit(sourceEditMode);
21581        
21582     }
21583 });
21584
21585
21586
21587
21588
21589 /**
21590  * @class Roo.bootstrap.Table.AbstractSelectionModel
21591  * @extends Roo.util.Observable
21592  * Abstract base class for grid SelectionModels.  It provides the interface that should be
21593  * implemented by descendant classes.  This class should not be directly instantiated.
21594  * @constructor
21595  */
21596 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21597     this.locked = false;
21598     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21599 };
21600
21601
21602 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
21603     /** @ignore Called by the grid automatically. Do not call directly. */
21604     init : function(grid){
21605         this.grid = grid;
21606         this.initEvents();
21607     },
21608
21609     /**
21610      * Locks the selections.
21611      */
21612     lock : function(){
21613         this.locked = true;
21614     },
21615
21616     /**
21617      * Unlocks the selections.
21618      */
21619     unlock : function(){
21620         this.locked = false;
21621     },
21622
21623     /**
21624      * Returns true if the selections are locked.
21625      * @return {Boolean}
21626      */
21627     isLocked : function(){
21628         return this.locked;
21629     }
21630 });
21631 /**
21632  * @extends Roo.bootstrap.Table.AbstractSelectionModel
21633  * @class Roo.bootstrap.Table.RowSelectionModel
21634  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21635  * It supports multiple selections and keyboard selection/navigation. 
21636  * @constructor
21637  * @param {Object} config
21638  */
21639
21640 Roo.bootstrap.Table.RowSelectionModel = function(config){
21641     Roo.apply(this, config);
21642     this.selections = new Roo.util.MixedCollection(false, function(o){
21643         return o.id;
21644     });
21645
21646     this.last = false;
21647     this.lastActive = false;
21648
21649     this.addEvents({
21650         /**
21651              * @event selectionchange
21652              * Fires when the selection changes
21653              * @param {SelectionModel} this
21654              */
21655             "selectionchange" : true,
21656         /**
21657              * @event afterselectionchange
21658              * Fires after the selection changes (eg. by key press or clicking)
21659              * @param {SelectionModel} this
21660              */
21661             "afterselectionchange" : true,
21662         /**
21663              * @event beforerowselect
21664              * Fires when a row is selected being selected, return false to cancel.
21665              * @param {SelectionModel} this
21666              * @param {Number} rowIndex The selected index
21667              * @param {Boolean} keepExisting False if other selections will be cleared
21668              */
21669             "beforerowselect" : true,
21670         /**
21671              * @event rowselect
21672              * Fires when a row is selected.
21673              * @param {SelectionModel} this
21674              * @param {Number} rowIndex The selected index
21675              * @param {Roo.data.Record} r The record
21676              */
21677             "rowselect" : true,
21678         /**
21679              * @event rowdeselect
21680              * Fires when a row is deselected.
21681              * @param {SelectionModel} this
21682              * @param {Number} rowIndex The selected index
21683              */
21684         "rowdeselect" : true
21685     });
21686     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21687     this.locked = false;
21688 };
21689
21690 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
21691     /**
21692      * @cfg {Boolean} singleSelect
21693      * True to allow selection of only one row at a time (defaults to false)
21694      */
21695     singleSelect : false,
21696
21697     // private
21698     initEvents : function(){
21699
21700         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21701             this.grid.on("mousedown", this.handleMouseDown, this);
21702         }else{ // allow click to work like normal
21703             this.grid.on("rowclick", this.handleDragableRowClick, this);
21704         }
21705
21706         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21707             "up" : function(e){
21708                 if(!e.shiftKey){
21709                     this.selectPrevious(e.shiftKey);
21710                 }else if(this.last !== false && this.lastActive !== false){
21711                     var last = this.last;
21712                     this.selectRange(this.last,  this.lastActive-1);
21713                     this.grid.getView().focusRow(this.lastActive);
21714                     if(last !== false){
21715                         this.last = last;
21716                     }
21717                 }else{
21718                     this.selectFirstRow();
21719                 }
21720                 this.fireEvent("afterselectionchange", this);
21721             },
21722             "down" : function(e){
21723                 if(!e.shiftKey){
21724                     this.selectNext(e.shiftKey);
21725                 }else if(this.last !== false && this.lastActive !== false){
21726                     var last = this.last;
21727                     this.selectRange(this.last,  this.lastActive+1);
21728                     this.grid.getView().focusRow(this.lastActive);
21729                     if(last !== false){
21730                         this.last = last;
21731                     }
21732                 }else{
21733                     this.selectFirstRow();
21734                 }
21735                 this.fireEvent("afterselectionchange", this);
21736             },
21737             scope: this
21738         });
21739
21740         var view = this.grid.view;
21741         view.on("refresh", this.onRefresh, this);
21742         view.on("rowupdated", this.onRowUpdated, this);
21743         view.on("rowremoved", this.onRemove, this);
21744     },
21745
21746     // private
21747     onRefresh : function(){
21748         var ds = this.grid.dataSource, i, v = this.grid.view;
21749         var s = this.selections;
21750         s.each(function(r){
21751             if((i = ds.indexOfId(r.id)) != -1){
21752                 v.onRowSelect(i);
21753             }else{
21754                 s.remove(r);
21755             }
21756         });
21757     },
21758
21759     // private
21760     onRemove : function(v, index, r){
21761         this.selections.remove(r);
21762     },
21763
21764     // private
21765     onRowUpdated : function(v, index, r){
21766         if(this.isSelected(r)){
21767             v.onRowSelect(index);
21768         }
21769     },
21770
21771     /**
21772      * Select records.
21773      * @param {Array} records The records to select
21774      * @param {Boolean} keepExisting (optional) True to keep existing selections
21775      */
21776     selectRecords : function(records, keepExisting){
21777         if(!keepExisting){
21778             this.clearSelections();
21779         }
21780         var ds = this.grid.dataSource;
21781         for(var i = 0, len = records.length; i < len; i++){
21782             this.selectRow(ds.indexOf(records[i]), true);
21783         }
21784     },
21785
21786     /**
21787      * Gets the number of selected rows.
21788      * @return {Number}
21789      */
21790     getCount : function(){
21791         return this.selections.length;
21792     },
21793
21794     /**
21795      * Selects the first row in the grid.
21796      */
21797     selectFirstRow : function(){
21798         this.selectRow(0);
21799     },
21800
21801     /**
21802      * Select the last row.
21803      * @param {Boolean} keepExisting (optional) True to keep existing selections
21804      */
21805     selectLastRow : function(keepExisting){
21806         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21807     },
21808
21809     /**
21810      * Selects the row immediately following the last selected row.
21811      * @param {Boolean} keepExisting (optional) True to keep existing selections
21812      */
21813     selectNext : function(keepExisting){
21814         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21815             this.selectRow(this.last+1, keepExisting);
21816             this.grid.getView().focusRow(this.last);
21817         }
21818     },
21819
21820     /**
21821      * Selects the row that precedes the last selected row.
21822      * @param {Boolean} keepExisting (optional) True to keep existing selections
21823      */
21824     selectPrevious : function(keepExisting){
21825         if(this.last){
21826             this.selectRow(this.last-1, keepExisting);
21827             this.grid.getView().focusRow(this.last);
21828         }
21829     },
21830
21831     /**
21832      * Returns the selected records
21833      * @return {Array} Array of selected records
21834      */
21835     getSelections : function(){
21836         return [].concat(this.selections.items);
21837     },
21838
21839     /**
21840      * Returns the first selected record.
21841      * @return {Record}
21842      */
21843     getSelected : function(){
21844         return this.selections.itemAt(0);
21845     },
21846
21847
21848     /**
21849      * Clears all selections.
21850      */
21851     clearSelections : function(fast){
21852         if(this.locked) {
21853             return;
21854         }
21855         if(fast !== true){
21856             var ds = this.grid.dataSource;
21857             var s = this.selections;
21858             s.each(function(r){
21859                 this.deselectRow(ds.indexOfId(r.id));
21860             }, this);
21861             s.clear();
21862         }else{
21863             this.selections.clear();
21864         }
21865         this.last = false;
21866     },
21867
21868
21869     /**
21870      * Selects all rows.
21871      */
21872     selectAll : function(){
21873         if(this.locked) {
21874             return;
21875         }
21876         this.selections.clear();
21877         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
21878             this.selectRow(i, true);
21879         }
21880     },
21881
21882     /**
21883      * Returns True if there is a selection.
21884      * @return {Boolean}
21885      */
21886     hasSelection : function(){
21887         return this.selections.length > 0;
21888     },
21889
21890     /**
21891      * Returns True if the specified row is selected.
21892      * @param {Number/Record} record The record or index of the record to check
21893      * @return {Boolean}
21894      */
21895     isSelected : function(index){
21896         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
21897         return (r && this.selections.key(r.id) ? true : false);
21898     },
21899
21900     /**
21901      * Returns True if the specified record id is selected.
21902      * @param {String} id The id of record to check
21903      * @return {Boolean}
21904      */
21905     isIdSelected : function(id){
21906         return (this.selections.key(id) ? true : false);
21907     },
21908
21909     // private
21910     handleMouseDown : function(e, t){
21911         var view = this.grid.getView(), rowIndex;
21912         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
21913             return;
21914         };
21915         if(e.shiftKey && this.last !== false){
21916             var last = this.last;
21917             this.selectRange(last, rowIndex, e.ctrlKey);
21918             this.last = last; // reset the last
21919             view.focusRow(rowIndex);
21920         }else{
21921             var isSelected = this.isSelected(rowIndex);
21922             if(e.button !== 0 && isSelected){
21923                 view.focusRow(rowIndex);
21924             }else if(e.ctrlKey && isSelected){
21925                 this.deselectRow(rowIndex);
21926             }else if(!isSelected){
21927                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
21928                 view.focusRow(rowIndex);
21929             }
21930         }
21931         this.fireEvent("afterselectionchange", this);
21932     },
21933     // private
21934     handleDragableRowClick :  function(grid, rowIndex, e) 
21935     {
21936         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
21937             this.selectRow(rowIndex, false);
21938             grid.view.focusRow(rowIndex);
21939              this.fireEvent("afterselectionchange", this);
21940         }
21941     },
21942     
21943     /**
21944      * Selects multiple rows.
21945      * @param {Array} rows Array of the indexes of the row to select
21946      * @param {Boolean} keepExisting (optional) True to keep existing selections
21947      */
21948     selectRows : function(rows, keepExisting){
21949         if(!keepExisting){
21950             this.clearSelections();
21951         }
21952         for(var i = 0, len = rows.length; i < len; i++){
21953             this.selectRow(rows[i], true);
21954         }
21955     },
21956
21957     /**
21958      * Selects a range of rows. All rows in between startRow and endRow are also selected.
21959      * @param {Number} startRow The index of the first row in the range
21960      * @param {Number} endRow The index of the last row in the range
21961      * @param {Boolean} keepExisting (optional) True to retain existing selections
21962      */
21963     selectRange : function(startRow, endRow, keepExisting){
21964         if(this.locked) {
21965             return;
21966         }
21967         if(!keepExisting){
21968             this.clearSelections();
21969         }
21970         if(startRow <= endRow){
21971             for(var i = startRow; i <= endRow; i++){
21972                 this.selectRow(i, true);
21973             }
21974         }else{
21975             for(var i = startRow; i >= endRow; i--){
21976                 this.selectRow(i, true);
21977             }
21978         }
21979     },
21980
21981     /**
21982      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
21983      * @param {Number} startRow The index of the first row in the range
21984      * @param {Number} endRow The index of the last row in the range
21985      */
21986     deselectRange : function(startRow, endRow, preventViewNotify){
21987         if(this.locked) {
21988             return;
21989         }
21990         for(var i = startRow; i <= endRow; i++){
21991             this.deselectRow(i, preventViewNotify);
21992         }
21993     },
21994
21995     /**
21996      * Selects a row.
21997      * @param {Number} row The index of the row to select
21998      * @param {Boolean} keepExisting (optional) True to keep existing selections
21999      */
22000     selectRow : function(index, keepExisting, preventViewNotify){
22001         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
22002             return;
22003         }
22004         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
22005             if(!keepExisting || this.singleSelect){
22006                 this.clearSelections();
22007             }
22008             var r = this.grid.dataSource.getAt(index);
22009             this.selections.add(r);
22010             this.last = this.lastActive = index;
22011             if(!preventViewNotify){
22012                 this.grid.getView().onRowSelect(index);
22013             }
22014             this.fireEvent("rowselect", this, index, r);
22015             this.fireEvent("selectionchange", this);
22016         }
22017     },
22018
22019     /**
22020      * Deselects a row.
22021      * @param {Number} row The index of the row to deselect
22022      */
22023     deselectRow : function(index, preventViewNotify){
22024         if(this.locked) {
22025             return;
22026         }
22027         if(this.last == index){
22028             this.last = false;
22029         }
22030         if(this.lastActive == index){
22031             this.lastActive = false;
22032         }
22033         var r = this.grid.dataSource.getAt(index);
22034         this.selections.remove(r);
22035         if(!preventViewNotify){
22036             this.grid.getView().onRowDeselect(index);
22037         }
22038         this.fireEvent("rowdeselect", this, index);
22039         this.fireEvent("selectionchange", this);
22040     },
22041
22042     // private
22043     restoreLast : function(){
22044         if(this._last){
22045             this.last = this._last;
22046         }
22047     },
22048
22049     // private
22050     acceptsNav : function(row, col, cm){
22051         return !cm.isHidden(col) && cm.isCellEditable(col, row);
22052     },
22053
22054     // private
22055     onEditorKey : function(field, e){
22056         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22057         if(k == e.TAB){
22058             e.stopEvent();
22059             ed.completeEdit();
22060             if(e.shiftKey){
22061                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22062             }else{
22063                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22064             }
22065         }else if(k == e.ENTER && !e.ctrlKey){
22066             e.stopEvent();
22067             ed.completeEdit();
22068             if(e.shiftKey){
22069                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22070             }else{
22071                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22072             }
22073         }else if(k == e.ESC){
22074             ed.cancelEdit();
22075         }
22076         if(newCell){
22077             g.startEditing(newCell[0], newCell[1]);
22078         }
22079     }
22080 });/*
22081  * Based on:
22082  * Ext JS Library 1.1.1
22083  * Copyright(c) 2006-2007, Ext JS, LLC.
22084  *
22085  * Originally Released Under LGPL - original licence link has changed is not relivant.
22086  *
22087  * Fork - LGPL
22088  * <script type="text/javascript">
22089  */
22090  
22091 /**
22092  * @class Roo.bootstrap.PagingToolbar
22093  * @extends Roo.bootstrap.NavSimplebar
22094  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
22095  * @constructor
22096  * Create a new PagingToolbar
22097  * @param {Object} config The config object
22098  * @param {Roo.data.Store} store
22099  */
22100 Roo.bootstrap.PagingToolbar = function(config)
22101 {
22102     // old args format still supported... - xtype is prefered..
22103         // created from xtype...
22104     
22105     this.ds = config.dataSource;
22106     
22107     if (config.store && !this.ds) {
22108         this.store= Roo.factory(config.store, Roo.data);
22109         this.ds = this.store;
22110         this.ds.xmodule = this.xmodule || false;
22111     }
22112     
22113     this.toolbarItems = [];
22114     if (config.items) {
22115         this.toolbarItems = config.items;
22116     }
22117     
22118     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
22119     
22120     this.cursor = 0;
22121     
22122     if (this.ds) { 
22123         this.bind(this.ds);
22124     }
22125     
22126     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
22127     
22128 };
22129
22130 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
22131     /**
22132      * @cfg {Roo.data.Store} dataSource
22133      * The underlying data store providing the paged data
22134      */
22135     /**
22136      * @cfg {String/HTMLElement/Element} container
22137      * container The id or element that will contain the toolbar
22138      */
22139     /**
22140      * @cfg {Boolean} displayInfo
22141      * True to display the displayMsg (defaults to false)
22142      */
22143     /**
22144      * @cfg {Number} pageSize
22145      * The number of records to display per page (defaults to 20)
22146      */
22147     pageSize: 20,
22148     /**
22149      * @cfg {String} displayMsg
22150      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
22151      */
22152     displayMsg : 'Displaying {0} - {1} of {2}',
22153     /**
22154      * @cfg {String} emptyMsg
22155      * The message to display when no records are found (defaults to "No data to display")
22156      */
22157     emptyMsg : 'No data to display',
22158     /**
22159      * Customizable piece of the default paging text (defaults to "Page")
22160      * @type String
22161      */
22162     beforePageText : "Page",
22163     /**
22164      * Customizable piece of the default paging text (defaults to "of %0")
22165      * @type String
22166      */
22167     afterPageText : "of {0}",
22168     /**
22169      * Customizable piece of the default paging text (defaults to "First Page")
22170      * @type String
22171      */
22172     firstText : "First Page",
22173     /**
22174      * Customizable piece of the default paging text (defaults to "Previous Page")
22175      * @type String
22176      */
22177     prevText : "Previous Page",
22178     /**
22179      * Customizable piece of the default paging text (defaults to "Next Page")
22180      * @type String
22181      */
22182     nextText : "Next Page",
22183     /**
22184      * Customizable piece of the default paging text (defaults to "Last Page")
22185      * @type String
22186      */
22187     lastText : "Last Page",
22188     /**
22189      * Customizable piece of the default paging text (defaults to "Refresh")
22190      * @type String
22191      */
22192     refreshText : "Refresh",
22193
22194     buttons : false,
22195     // private
22196     onRender : function(ct, position) 
22197     {
22198         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
22199         this.navgroup.parentId = this.id;
22200         this.navgroup.onRender(this.el, null);
22201         // add the buttons to the navgroup
22202         
22203         if(this.displayInfo){
22204             Roo.log(this.el.select('ul.navbar-nav',true).first());
22205             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
22206             this.displayEl = this.el.select('.x-paging-info', true).first();
22207 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
22208 //            this.displayEl = navel.el.select('span',true).first();
22209         }
22210         
22211         var _this = this;
22212         
22213         if(this.buttons){
22214             Roo.each(_this.buttons, function(e){ // this might need to use render????
22215                Roo.factory(e).onRender(_this.el, null);
22216             });
22217         }
22218             
22219         Roo.each(_this.toolbarItems, function(e) {
22220             _this.navgroup.addItem(e);
22221         });
22222         
22223         
22224         this.first = this.navgroup.addItem({
22225             tooltip: this.firstText,
22226             cls: "prev",
22227             icon : 'fa fa-backward',
22228             disabled: true,
22229             preventDefault: true,
22230             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
22231         });
22232         
22233         this.prev =  this.navgroup.addItem({
22234             tooltip: this.prevText,
22235             cls: "prev",
22236             icon : 'fa fa-step-backward',
22237             disabled: true,
22238             preventDefault: true,
22239             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
22240         });
22241     //this.addSeparator();
22242         
22243         
22244         var field = this.navgroup.addItem( {
22245             tagtype : 'span',
22246             cls : 'x-paging-position',
22247             
22248             html : this.beforePageText  +
22249                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22250                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
22251          } ); //?? escaped?
22252         
22253         this.field = field.el.select('input', true).first();
22254         this.field.on("keydown", this.onPagingKeydown, this);
22255         this.field.on("focus", function(){this.dom.select();});
22256     
22257     
22258         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
22259         //this.field.setHeight(18);
22260         //this.addSeparator();
22261         this.next = this.navgroup.addItem({
22262             tooltip: this.nextText,
22263             cls: "next",
22264             html : ' <i class="fa fa-step-forward">',
22265             disabled: true,
22266             preventDefault: true,
22267             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
22268         });
22269         this.last = this.navgroup.addItem({
22270             tooltip: this.lastText,
22271             icon : 'fa fa-forward',
22272             cls: "next",
22273             disabled: true,
22274             preventDefault: true,
22275             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
22276         });
22277     //this.addSeparator();
22278         this.loading = this.navgroup.addItem({
22279             tooltip: this.refreshText,
22280             icon: 'fa fa-refresh',
22281             preventDefault: true,
22282             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
22283         });
22284         
22285     },
22286
22287     // private
22288     updateInfo : function(){
22289         if(this.displayEl){
22290             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
22291             var msg = count == 0 ?
22292                 this.emptyMsg :
22293                 String.format(
22294                     this.displayMsg,
22295                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
22296                 );
22297             this.displayEl.update(msg);
22298         }
22299     },
22300
22301     // private
22302     onLoad : function(ds, r, o){
22303        this.cursor = o.params ? o.params.start : 0;
22304        var d = this.getPageData(),
22305             ap = d.activePage,
22306             ps = d.pages;
22307         
22308        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
22309        this.field.dom.value = ap;
22310        this.first.setDisabled(ap == 1);
22311        this.prev.setDisabled(ap == 1);
22312        this.next.setDisabled(ap == ps);
22313        this.last.setDisabled(ap == ps);
22314        this.loading.enable();
22315        this.updateInfo();
22316     },
22317
22318     // private
22319     getPageData : function(){
22320         var total = this.ds.getTotalCount();
22321         return {
22322             total : total,
22323             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
22324             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
22325         };
22326     },
22327
22328     // private
22329     onLoadError : function(){
22330         this.loading.enable();
22331     },
22332
22333     // private
22334     onPagingKeydown : function(e){
22335         var k = e.getKey();
22336         var d = this.getPageData();
22337         if(k == e.RETURN){
22338             var v = this.field.dom.value, pageNum;
22339             if(!v || isNaN(pageNum = parseInt(v, 10))){
22340                 this.field.dom.value = d.activePage;
22341                 return;
22342             }
22343             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
22344             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22345             e.stopEvent();
22346         }
22347         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))
22348         {
22349           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
22350           this.field.dom.value = pageNum;
22351           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
22352           e.stopEvent();
22353         }
22354         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22355         {
22356           var v = this.field.dom.value, pageNum; 
22357           var increment = (e.shiftKey) ? 10 : 1;
22358           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
22359                 increment *= -1;
22360           }
22361           if(!v || isNaN(pageNum = parseInt(v, 10))) {
22362             this.field.dom.value = d.activePage;
22363             return;
22364           }
22365           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
22366           {
22367             this.field.dom.value = parseInt(v, 10) + increment;
22368             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
22369             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22370           }
22371           e.stopEvent();
22372         }
22373     },
22374
22375     // private
22376     beforeLoad : function(){
22377         if(this.loading){
22378             this.loading.disable();
22379         }
22380     },
22381
22382     // private
22383     onClick : function(which){
22384         
22385         var ds = this.ds;
22386         if (!ds) {
22387             return;
22388         }
22389         
22390         switch(which){
22391             case "first":
22392                 ds.load({params:{start: 0, limit: this.pageSize}});
22393             break;
22394             case "prev":
22395                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
22396             break;
22397             case "next":
22398                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
22399             break;
22400             case "last":
22401                 var total = ds.getTotalCount();
22402                 var extra = total % this.pageSize;
22403                 var lastStart = extra ? (total - extra) : total-this.pageSize;
22404                 ds.load({params:{start: lastStart, limit: this.pageSize}});
22405             break;
22406             case "refresh":
22407                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22408             break;
22409         }
22410     },
22411
22412     /**
22413      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22414      * @param {Roo.data.Store} store The data store to unbind
22415      */
22416     unbind : function(ds){
22417         ds.un("beforeload", this.beforeLoad, this);
22418         ds.un("load", this.onLoad, this);
22419         ds.un("loadexception", this.onLoadError, this);
22420         ds.un("remove", this.updateInfo, this);
22421         ds.un("add", this.updateInfo, this);
22422         this.ds = undefined;
22423     },
22424
22425     /**
22426      * Binds the paging toolbar to the specified {@link Roo.data.Store}
22427      * @param {Roo.data.Store} store The data store to bind
22428      */
22429     bind : function(ds){
22430         ds.on("beforeload", this.beforeLoad, this);
22431         ds.on("load", this.onLoad, this);
22432         ds.on("loadexception", this.onLoadError, this);
22433         ds.on("remove", this.updateInfo, this);
22434         ds.on("add", this.updateInfo, this);
22435         this.ds = ds;
22436     }
22437 });/*
22438  * - LGPL
22439  *
22440  * element
22441  * 
22442  */
22443
22444 /**
22445  * @class Roo.bootstrap.MessageBar
22446  * @extends Roo.bootstrap.Component
22447  * Bootstrap MessageBar class
22448  * @cfg {String} html contents of the MessageBar
22449  * @cfg {String} weight (info | success | warning | danger) default info
22450  * @cfg {String} beforeClass insert the bar before the given class
22451  * @cfg {Boolean} closable (true | false) default false
22452  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22453  * 
22454  * @constructor
22455  * Create a new Element
22456  * @param {Object} config The config object
22457  */
22458
22459 Roo.bootstrap.MessageBar = function(config){
22460     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22461 };
22462
22463 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
22464     
22465     html: '',
22466     weight: 'info',
22467     closable: false,
22468     fixed: false,
22469     beforeClass: 'bootstrap-sticky-wrap',
22470     
22471     getAutoCreate : function(){
22472         
22473         var cfg = {
22474             tag: 'div',
22475             cls: 'alert alert-dismissable alert-' + this.weight,
22476             cn: [
22477                 {
22478                     tag: 'span',
22479                     cls: 'message',
22480                     html: this.html || ''
22481                 }
22482             ]
22483         };
22484         
22485         if(this.fixed){
22486             cfg.cls += ' alert-messages-fixed';
22487         }
22488         
22489         if(this.closable){
22490             cfg.cn.push({
22491                 tag: 'button',
22492                 cls: 'close',
22493                 html: 'x'
22494             });
22495         }
22496         
22497         return cfg;
22498     },
22499     
22500     onRender : function(ct, position)
22501     {
22502         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22503         
22504         if(!this.el){
22505             var cfg = Roo.apply({},  this.getAutoCreate());
22506             cfg.id = Roo.id();
22507             
22508             if (this.cls) {
22509                 cfg.cls += ' ' + this.cls;
22510             }
22511             if (this.style) {
22512                 cfg.style = this.style;
22513             }
22514             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22515             
22516             this.el.setVisibilityMode(Roo.Element.DISPLAY);
22517         }
22518         
22519         this.el.select('>button.close').on('click', this.hide, this);
22520         
22521     },
22522     
22523     show : function()
22524     {
22525         if (!this.rendered) {
22526             this.render();
22527         }
22528         
22529         this.el.show();
22530         
22531         this.fireEvent('show', this);
22532         
22533     },
22534     
22535     hide : function()
22536     {
22537         if (!this.rendered) {
22538             this.render();
22539         }
22540         
22541         this.el.hide();
22542         
22543         this.fireEvent('hide', this);
22544     },
22545     
22546     update : function()
22547     {
22548 //        var e = this.el.dom.firstChild;
22549 //        
22550 //        if(this.closable){
22551 //            e = e.nextSibling;
22552 //        }
22553 //        
22554 //        e.data = this.html || '';
22555
22556         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22557     }
22558    
22559 });
22560
22561  
22562
22563      /*
22564  * - LGPL
22565  *
22566  * Graph
22567  * 
22568  */
22569
22570
22571 /**
22572  * @class Roo.bootstrap.Graph
22573  * @extends Roo.bootstrap.Component
22574  * Bootstrap Graph class
22575 > Prameters
22576  -sm {number} sm 4
22577  -md {number} md 5
22578  @cfg {String} graphtype  bar | vbar | pie
22579  @cfg {number} g_x coodinator | centre x (pie)
22580  @cfg {number} g_y coodinator | centre y (pie)
22581  @cfg {number} g_r radius (pie)
22582  @cfg {number} g_height height of the chart (respected by all elements in the set)
22583  @cfg {number} g_width width of the chart (respected by all elements in the set)
22584  @cfg {Object} title The title of the chart
22585     
22586  -{Array}  values
22587  -opts (object) options for the chart 
22588      o {
22589      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22590      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22591      o vgutter (number)
22592      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.
22593      o stacked (boolean) whether or not to tread values as in a stacked bar chart
22594      o to
22595      o stretch (boolean)
22596      o }
22597  -opts (object) options for the pie
22598      o{
22599      o cut
22600      o startAngle (number)
22601      o endAngle (number)
22602      } 
22603  *
22604  * @constructor
22605  * Create a new Input
22606  * @param {Object} config The config object
22607  */
22608
22609 Roo.bootstrap.Graph = function(config){
22610     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22611     
22612     this.addEvents({
22613         // img events
22614         /**
22615          * @event click
22616          * The img click event for the img.
22617          * @param {Roo.EventObject} e
22618          */
22619         "click" : true
22620     });
22621 };
22622
22623 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
22624     
22625     sm: 4,
22626     md: 5,
22627     graphtype: 'bar',
22628     g_height: 250,
22629     g_width: 400,
22630     g_x: 50,
22631     g_y: 50,
22632     g_r: 30,
22633     opts:{
22634         //g_colors: this.colors,
22635         g_type: 'soft',
22636         g_gutter: '20%'
22637
22638     },
22639     title : false,
22640
22641     getAutoCreate : function(){
22642         
22643         var cfg = {
22644             tag: 'div',
22645             html : null
22646         };
22647         
22648         
22649         return  cfg;
22650     },
22651
22652     onRender : function(ct,position){
22653         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22654         this.raphael = Raphael(this.el.dom);
22655         
22656                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22657                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22658                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22659                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22660                 /*
22661                 r.text(160, 10, "Single Series Chart").attr(txtattr);
22662                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22663                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22664                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22665                 
22666                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22667                 r.barchart(330, 10, 300, 220, data1);
22668                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22669                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22670                 */
22671                 
22672                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22673                 // r.barchart(30, 30, 560, 250,  xdata, {
22674                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22675                 //     axis : "0 0 1 1",
22676                 //     axisxlabels :  xdata
22677                 //     //yvalues : cols,
22678                    
22679                 // });
22680 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22681 //        
22682 //        this.load(null,xdata,{
22683 //                axis : "0 0 1 1",
22684 //                axisxlabels :  xdata
22685 //                });
22686
22687     },
22688
22689     load : function(graphtype,xdata,opts){
22690         this.raphael.clear();
22691         if(!graphtype) {
22692             graphtype = this.graphtype;
22693         }
22694         if(!opts){
22695             opts = this.opts;
22696         }
22697         var r = this.raphael,
22698             fin = function () {
22699                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22700             },
22701             fout = function () {
22702                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22703             },
22704             pfin = function() {
22705                 this.sector.stop();
22706                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22707
22708                 if (this.label) {
22709                     this.label[0].stop();
22710                     this.label[0].attr({ r: 7.5 });
22711                     this.label[1].attr({ "font-weight": 800 });
22712                 }
22713             },
22714             pfout = function() {
22715                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22716
22717                 if (this.label) {
22718                     this.label[0].animate({ r: 5 }, 500, "bounce");
22719                     this.label[1].attr({ "font-weight": 400 });
22720                 }
22721             };
22722
22723         switch(graphtype){
22724             case 'bar':
22725                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22726                 break;
22727             case 'hbar':
22728                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22729                 break;
22730             case 'pie':
22731 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
22732 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22733 //            
22734                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22735                 
22736                 break;
22737
22738         }
22739         
22740         if(this.title){
22741             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22742         }
22743         
22744     },
22745     
22746     setTitle: function(o)
22747     {
22748         this.title = o;
22749     },
22750     
22751     initEvents: function() {
22752         
22753         if(!this.href){
22754             this.el.on('click', this.onClick, this);
22755         }
22756     },
22757     
22758     onClick : function(e)
22759     {
22760         Roo.log('img onclick');
22761         this.fireEvent('click', this, e);
22762     }
22763    
22764 });
22765
22766  
22767 /*
22768  * - LGPL
22769  *
22770  * numberBox
22771  * 
22772  */
22773 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22774
22775 /**
22776  * @class Roo.bootstrap.dash.NumberBox
22777  * @extends Roo.bootstrap.Component
22778  * Bootstrap NumberBox class
22779  * @cfg {String} headline Box headline
22780  * @cfg {String} content Box content
22781  * @cfg {String} icon Box icon
22782  * @cfg {String} footer Footer text
22783  * @cfg {String} fhref Footer href
22784  * 
22785  * @constructor
22786  * Create a new NumberBox
22787  * @param {Object} config The config object
22788  */
22789
22790
22791 Roo.bootstrap.dash.NumberBox = function(config){
22792     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22793     
22794 };
22795
22796 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
22797     
22798     headline : '',
22799     content : '',
22800     icon : '',
22801     footer : '',
22802     fhref : '',
22803     ficon : '',
22804     
22805     getAutoCreate : function(){
22806         
22807         var cfg = {
22808             tag : 'div',
22809             cls : 'small-box ',
22810             cn : [
22811                 {
22812                     tag : 'div',
22813                     cls : 'inner',
22814                     cn :[
22815                         {
22816                             tag : 'h3',
22817                             cls : 'roo-headline',
22818                             html : this.headline
22819                         },
22820                         {
22821                             tag : 'p',
22822                             cls : 'roo-content',
22823                             html : this.content
22824                         }
22825                     ]
22826                 }
22827             ]
22828         };
22829         
22830         if(this.icon){
22831             cfg.cn.push({
22832                 tag : 'div',
22833                 cls : 'icon',
22834                 cn :[
22835                     {
22836                         tag : 'i',
22837                         cls : 'ion ' + this.icon
22838                     }
22839                 ]
22840             });
22841         }
22842         
22843         if(this.footer){
22844             var footer = {
22845                 tag : 'a',
22846                 cls : 'small-box-footer',
22847                 href : this.fhref || '#',
22848                 html : this.footer
22849             };
22850             
22851             cfg.cn.push(footer);
22852             
22853         }
22854         
22855         return  cfg;
22856     },
22857
22858     onRender : function(ct,position){
22859         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
22860
22861
22862        
22863                 
22864     },
22865
22866     setHeadline: function (value)
22867     {
22868         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
22869     },
22870     
22871     setFooter: function (value, href)
22872     {
22873         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
22874         
22875         if(href){
22876             this.el.select('a.small-box-footer',true).first().attr('href', href);
22877         }
22878         
22879     },
22880
22881     setContent: function (value)
22882     {
22883         this.el.select('.roo-content',true).first().dom.innerHTML = value;
22884     },
22885
22886     initEvents: function() 
22887     {   
22888         
22889     }
22890     
22891 });
22892
22893  
22894 /*
22895  * - LGPL
22896  *
22897  * TabBox
22898  * 
22899  */
22900 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22901
22902 /**
22903  * @class Roo.bootstrap.dash.TabBox
22904  * @extends Roo.bootstrap.Component
22905  * Bootstrap TabBox class
22906  * @cfg {String} title Title of the TabBox
22907  * @cfg {String} icon Icon of the TabBox
22908  * @cfg {Boolean} showtabs (true|false) show the tabs default true
22909  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
22910  * 
22911  * @constructor
22912  * Create a new TabBox
22913  * @param {Object} config The config object
22914  */
22915
22916
22917 Roo.bootstrap.dash.TabBox = function(config){
22918     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
22919     this.addEvents({
22920         // raw events
22921         /**
22922          * @event addpane
22923          * When a pane is added
22924          * @param {Roo.bootstrap.dash.TabPane} pane
22925          */
22926         "addpane" : true,
22927         /**
22928          * @event activatepane
22929          * When a pane is activated
22930          * @param {Roo.bootstrap.dash.TabPane} pane
22931          */
22932         "activatepane" : true
22933         
22934          
22935     });
22936     
22937     this.panes = [];
22938 };
22939
22940 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
22941
22942     title : '',
22943     icon : false,
22944     showtabs : true,
22945     tabScrollable : false,
22946     
22947     getChildContainer : function()
22948     {
22949         return this.el.select('.tab-content', true).first();
22950     },
22951     
22952     getAutoCreate : function(){
22953         
22954         var header = {
22955             tag: 'li',
22956             cls: 'pull-left header',
22957             html: this.title,
22958             cn : []
22959         };
22960         
22961         if(this.icon){
22962             header.cn.push({
22963                 tag: 'i',
22964                 cls: 'fa ' + this.icon
22965             });
22966         }
22967         
22968         var h = {
22969             tag: 'ul',
22970             cls: 'nav nav-tabs pull-right',
22971             cn: [
22972                 header
22973             ]
22974         };
22975         
22976         if(this.tabScrollable){
22977             h = {
22978                 tag: 'div',
22979                 cls: 'tab-header',
22980                 cn: [
22981                     {
22982                         tag: 'ul',
22983                         cls: 'nav nav-tabs pull-right',
22984                         cn: [
22985                             header
22986                         ]
22987                     }
22988                 ]
22989             };
22990         }
22991         
22992         var cfg = {
22993             tag: 'div',
22994             cls: 'nav-tabs-custom',
22995             cn: [
22996                 h,
22997                 {
22998                     tag: 'div',
22999                     cls: 'tab-content no-padding',
23000                     cn: []
23001                 }
23002             ]
23003         };
23004
23005         return  cfg;
23006     },
23007     initEvents : function()
23008     {
23009         //Roo.log('add add pane handler');
23010         this.on('addpane', this.onAddPane, this);
23011     },
23012      /**
23013      * Updates the box title
23014      * @param {String} html to set the title to.
23015      */
23016     setTitle : function(value)
23017     {
23018         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23019     },
23020     onAddPane : function(pane)
23021     {
23022         this.panes.push(pane);
23023         //Roo.log('addpane');
23024         //Roo.log(pane);
23025         // tabs are rendere left to right..
23026         if(!this.showtabs){
23027             return;
23028         }
23029         
23030         var ctr = this.el.select('.nav-tabs', true).first();
23031          
23032          
23033         var existing = ctr.select('.nav-tab',true);
23034         var qty = existing.getCount();;
23035         
23036         
23037         var tab = ctr.createChild({
23038             tag : 'li',
23039             cls : 'nav-tab' + (qty ? '' : ' active'),
23040             cn : [
23041                 {
23042                     tag : 'a',
23043                     href:'#',
23044                     html : pane.title
23045                 }
23046             ]
23047         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23048         pane.tab = tab;
23049         
23050         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23051         if (!qty) {
23052             pane.el.addClass('active');
23053         }
23054         
23055                 
23056     },
23057     onTabClick : function(ev,un,ob,pane)
23058     {
23059         //Roo.log('tab - prev default');
23060         ev.preventDefault();
23061         
23062         
23063         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23064         pane.tab.addClass('active');
23065         //Roo.log(pane.title);
23066         this.getChildContainer().select('.tab-pane',true).removeClass('active');
23067         // technically we should have a deactivate event.. but maybe add later.
23068         // and it should not de-activate the selected tab...
23069         this.fireEvent('activatepane', pane);
23070         pane.el.addClass('active');
23071         pane.fireEvent('activate');
23072         
23073         
23074     },
23075     
23076     getActivePane : function()
23077     {
23078         var r = false;
23079         Roo.each(this.panes, function(p) {
23080             if(p.el.hasClass('active')){
23081                 r = p;
23082                 return false;
23083             }
23084             
23085             return;
23086         });
23087         
23088         return r;
23089     }
23090     
23091     
23092 });
23093
23094  
23095 /*
23096  * - LGPL
23097  *
23098  * Tab pane
23099  * 
23100  */
23101 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23102 /**
23103  * @class Roo.bootstrap.TabPane
23104  * @extends Roo.bootstrap.Component
23105  * Bootstrap TabPane class
23106  * @cfg {Boolean} active (false | true) Default false
23107  * @cfg {String} title title of panel
23108
23109  * 
23110  * @constructor
23111  * Create a new TabPane
23112  * @param {Object} config The config object
23113  */
23114
23115 Roo.bootstrap.dash.TabPane = function(config){
23116     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
23117     
23118     this.addEvents({
23119         // raw events
23120         /**
23121          * @event activate
23122          * When a pane is activated
23123          * @param {Roo.bootstrap.dash.TabPane} pane
23124          */
23125         "activate" : true
23126          
23127     });
23128 };
23129
23130 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
23131     
23132     active : false,
23133     title : '',
23134     
23135     // the tabBox that this is attached to.
23136     tab : false,
23137      
23138     getAutoCreate : function() 
23139     {
23140         var cfg = {
23141             tag: 'div',
23142             cls: 'tab-pane'
23143         };
23144         
23145         if(this.active){
23146             cfg.cls += ' active';
23147         }
23148         
23149         return cfg;
23150     },
23151     initEvents  : function()
23152     {
23153         //Roo.log('trigger add pane handler');
23154         this.parent().fireEvent('addpane', this)
23155     },
23156     
23157      /**
23158      * Updates the tab title 
23159      * @param {String} html to set the title to.
23160      */
23161     setTitle: function(str)
23162     {
23163         if (!this.tab) {
23164             return;
23165         }
23166         this.title = str;
23167         this.tab.select('a', true).first().dom.innerHTML = str;
23168         
23169     }
23170     
23171     
23172     
23173 });
23174
23175  
23176
23177
23178  /*
23179  * - LGPL
23180  *
23181  * menu
23182  * 
23183  */
23184 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23185
23186 /**
23187  * @class Roo.bootstrap.menu.Menu
23188  * @extends Roo.bootstrap.Component
23189  * Bootstrap Menu class - container for Menu
23190  * @cfg {String} html Text of the menu
23191  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
23192  * @cfg {String} icon Font awesome icon
23193  * @cfg {String} pos Menu align to (top | bottom) default bottom
23194  * 
23195  * 
23196  * @constructor
23197  * Create a new Menu
23198  * @param {Object} config The config object
23199  */
23200
23201
23202 Roo.bootstrap.menu.Menu = function(config){
23203     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
23204     
23205     this.addEvents({
23206         /**
23207          * @event beforeshow
23208          * Fires before this menu is displayed
23209          * @param {Roo.bootstrap.menu.Menu} this
23210          */
23211         beforeshow : true,
23212         /**
23213          * @event beforehide
23214          * Fires before this menu is hidden
23215          * @param {Roo.bootstrap.menu.Menu} this
23216          */
23217         beforehide : true,
23218         /**
23219          * @event show
23220          * Fires after this menu is displayed
23221          * @param {Roo.bootstrap.menu.Menu} this
23222          */
23223         show : true,
23224         /**
23225          * @event hide
23226          * Fires after this menu is hidden
23227          * @param {Roo.bootstrap.menu.Menu} this
23228          */
23229         hide : true,
23230         /**
23231          * @event click
23232          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
23233          * @param {Roo.bootstrap.menu.Menu} this
23234          * @param {Roo.EventObject} e
23235          */
23236         click : true
23237     });
23238     
23239 };
23240
23241 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
23242     
23243     submenu : false,
23244     html : '',
23245     weight : 'default',
23246     icon : false,
23247     pos : 'bottom',
23248     
23249     
23250     getChildContainer : function() {
23251         if(this.isSubMenu){
23252             return this.el;
23253         }
23254         
23255         return this.el.select('ul.dropdown-menu', true).first();  
23256     },
23257     
23258     getAutoCreate : function()
23259     {
23260         var text = [
23261             {
23262                 tag : 'span',
23263                 cls : 'roo-menu-text',
23264                 html : this.html
23265             }
23266         ];
23267         
23268         if(this.icon){
23269             text.unshift({
23270                 tag : 'i',
23271                 cls : 'fa ' + this.icon
23272             })
23273         }
23274         
23275         
23276         var cfg = {
23277             tag : 'div',
23278             cls : 'btn-group',
23279             cn : [
23280                 {
23281                     tag : 'button',
23282                     cls : 'dropdown-button btn btn-' + this.weight,
23283                     cn : text
23284                 },
23285                 {
23286                     tag : 'button',
23287                     cls : 'dropdown-toggle btn btn-' + this.weight,
23288                     cn : [
23289                         {
23290                             tag : 'span',
23291                             cls : 'caret'
23292                         }
23293                     ]
23294                 },
23295                 {
23296                     tag : 'ul',
23297                     cls : 'dropdown-menu'
23298                 }
23299             ]
23300             
23301         };
23302         
23303         if(this.pos == 'top'){
23304             cfg.cls += ' dropup';
23305         }
23306         
23307         if(this.isSubMenu){
23308             cfg = {
23309                 tag : 'ul',
23310                 cls : 'dropdown-menu'
23311             }
23312         }
23313         
23314         return cfg;
23315     },
23316     
23317     onRender : function(ct, position)
23318     {
23319         this.isSubMenu = ct.hasClass('dropdown-submenu');
23320         
23321         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
23322     },
23323     
23324     initEvents : function() 
23325     {
23326         if(this.isSubMenu){
23327             return;
23328         }
23329         
23330         this.hidden = true;
23331         
23332         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
23333         this.triggerEl.on('click', this.onTriggerPress, this);
23334         
23335         this.buttonEl = this.el.select('button.dropdown-button', true).first();
23336         this.buttonEl.on('click', this.onClick, this);
23337         
23338     },
23339     
23340     list : function()
23341     {
23342         if(this.isSubMenu){
23343             return this.el;
23344         }
23345         
23346         return this.el.select('ul.dropdown-menu', true).first();
23347     },
23348     
23349     onClick : function(e)
23350     {
23351         this.fireEvent("click", this, e);
23352     },
23353     
23354     onTriggerPress  : function(e)
23355     {   
23356         if (this.isVisible()) {
23357             this.hide();
23358         } else {
23359             this.show();
23360         }
23361     },
23362     
23363     isVisible : function(){
23364         return !this.hidden;
23365     },
23366     
23367     show : function()
23368     {
23369         this.fireEvent("beforeshow", this);
23370         
23371         this.hidden = false;
23372         this.el.addClass('open');
23373         
23374         Roo.get(document).on("mouseup", this.onMouseUp, this);
23375         
23376         this.fireEvent("show", this);
23377         
23378         
23379     },
23380     
23381     hide : function()
23382     {
23383         this.fireEvent("beforehide", this);
23384         
23385         this.hidden = true;
23386         this.el.removeClass('open');
23387         
23388         Roo.get(document).un("mouseup", this.onMouseUp);
23389         
23390         this.fireEvent("hide", this);
23391     },
23392     
23393     onMouseUp : function()
23394     {
23395         this.hide();
23396     }
23397     
23398 });
23399
23400  
23401  /*
23402  * - LGPL
23403  *
23404  * menu item
23405  * 
23406  */
23407 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23408
23409 /**
23410  * @class Roo.bootstrap.menu.Item
23411  * @extends Roo.bootstrap.Component
23412  * Bootstrap MenuItem class
23413  * @cfg {Boolean} submenu (true | false) default false
23414  * @cfg {String} html text of the item
23415  * @cfg {String} href the link
23416  * @cfg {Boolean} disable (true | false) default false
23417  * @cfg {Boolean} preventDefault (true | false) default true
23418  * @cfg {String} icon Font awesome icon
23419  * @cfg {String} pos Submenu align to (left | right) default right 
23420  * 
23421  * 
23422  * @constructor
23423  * Create a new Item
23424  * @param {Object} config The config object
23425  */
23426
23427
23428 Roo.bootstrap.menu.Item = function(config){
23429     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23430     this.addEvents({
23431         /**
23432          * @event mouseover
23433          * Fires when the mouse is hovering over this menu
23434          * @param {Roo.bootstrap.menu.Item} this
23435          * @param {Roo.EventObject} e
23436          */
23437         mouseover : true,
23438         /**
23439          * @event mouseout
23440          * Fires when the mouse exits this menu
23441          * @param {Roo.bootstrap.menu.Item} this
23442          * @param {Roo.EventObject} e
23443          */
23444         mouseout : true,
23445         // raw events
23446         /**
23447          * @event click
23448          * The raw click event for the entire grid.
23449          * @param {Roo.EventObject} e
23450          */
23451         click : true
23452     });
23453 };
23454
23455 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
23456     
23457     submenu : false,
23458     href : '',
23459     html : '',
23460     preventDefault: true,
23461     disable : false,
23462     icon : false,
23463     pos : 'right',
23464     
23465     getAutoCreate : function()
23466     {
23467         var text = [
23468             {
23469                 tag : 'span',
23470                 cls : 'roo-menu-item-text',
23471                 html : this.html
23472             }
23473         ];
23474         
23475         if(this.icon){
23476             text.unshift({
23477                 tag : 'i',
23478                 cls : 'fa ' + this.icon
23479             })
23480         }
23481         
23482         var cfg = {
23483             tag : 'li',
23484             cn : [
23485                 {
23486                     tag : 'a',
23487                     href : this.href || '#',
23488                     cn : text
23489                 }
23490             ]
23491         };
23492         
23493         if(this.disable){
23494             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23495         }
23496         
23497         if(this.submenu){
23498             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23499             
23500             if(this.pos == 'left'){
23501                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23502             }
23503         }
23504         
23505         return cfg;
23506     },
23507     
23508     initEvents : function() 
23509     {
23510         this.el.on('mouseover', this.onMouseOver, this);
23511         this.el.on('mouseout', this.onMouseOut, this);
23512         
23513         this.el.select('a', true).first().on('click', this.onClick, this);
23514         
23515     },
23516     
23517     onClick : function(e)
23518     {
23519         if(this.preventDefault){
23520             e.preventDefault();
23521         }
23522         
23523         this.fireEvent("click", this, e);
23524     },
23525     
23526     onMouseOver : function(e)
23527     {
23528         if(this.submenu && this.pos == 'left'){
23529             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23530         }
23531         
23532         this.fireEvent("mouseover", this, e);
23533     },
23534     
23535     onMouseOut : function(e)
23536     {
23537         this.fireEvent("mouseout", this, e);
23538     }
23539 });
23540
23541  
23542
23543  /*
23544  * - LGPL
23545  *
23546  * menu separator
23547  * 
23548  */
23549 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23550
23551 /**
23552  * @class Roo.bootstrap.menu.Separator
23553  * @extends Roo.bootstrap.Component
23554  * Bootstrap Separator class
23555  * 
23556  * @constructor
23557  * Create a new Separator
23558  * @param {Object} config The config object
23559  */
23560
23561
23562 Roo.bootstrap.menu.Separator = function(config){
23563     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23564 };
23565
23566 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
23567     
23568     getAutoCreate : function(){
23569         var cfg = {
23570             tag : 'li',
23571             cls: 'divider'
23572         };
23573         
23574         return cfg;
23575     }
23576    
23577 });
23578
23579  
23580
23581  /*
23582  * - LGPL
23583  *
23584  * Tooltip
23585  * 
23586  */
23587
23588 /**
23589  * @class Roo.bootstrap.Tooltip
23590  * Bootstrap Tooltip class
23591  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23592  * to determine which dom element triggers the tooltip.
23593  * 
23594  * It needs to add support for additional attributes like tooltip-position
23595  * 
23596  * @constructor
23597  * Create a new Toolti
23598  * @param {Object} config The config object
23599  */
23600
23601 Roo.bootstrap.Tooltip = function(config){
23602     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23603 };
23604
23605 Roo.apply(Roo.bootstrap.Tooltip, {
23606     /**
23607      * @function init initialize tooltip monitoring.
23608      * @static
23609      */
23610     currentEl : false,
23611     currentTip : false,
23612     currentRegion : false,
23613     
23614     //  init : delay?
23615     
23616     init : function()
23617     {
23618         Roo.get(document).on('mouseover', this.enter ,this);
23619         Roo.get(document).on('mouseout', this.leave, this);
23620          
23621         
23622         this.currentTip = new Roo.bootstrap.Tooltip();
23623     },
23624     
23625     enter : function(ev)
23626     {
23627         var dom = ev.getTarget();
23628         
23629         //Roo.log(['enter',dom]);
23630         var el = Roo.fly(dom);
23631         if (this.currentEl) {
23632             //Roo.log(dom);
23633             //Roo.log(this.currentEl);
23634             //Roo.log(this.currentEl.contains(dom));
23635             if (this.currentEl == el) {
23636                 return;
23637             }
23638             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23639                 return;
23640             }
23641
23642         }
23643         
23644         if (this.currentTip.el) {
23645             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
23646         }    
23647         //Roo.log(ev);
23648         var bindEl = el;
23649         
23650         // you can not look for children, as if el is the body.. then everythign is the child..
23651         if (!el.attr('tooltip')) { //
23652             if (!el.select("[tooltip]").elements.length) {
23653                 return;
23654             }
23655             // is the mouse over this child...?
23656             bindEl = el.select("[tooltip]").first();
23657             var xy = ev.getXY();
23658             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23659                 //Roo.log("not in region.");
23660                 return;
23661             }
23662             //Roo.log("child element over..");
23663             
23664         }
23665         this.currentEl = bindEl;
23666         this.currentTip.bind(bindEl);
23667         this.currentRegion = Roo.lib.Region.getRegion(dom);
23668         this.currentTip.enter();
23669         
23670     },
23671     leave : function(ev)
23672     {
23673         var dom = ev.getTarget();
23674         //Roo.log(['leave',dom]);
23675         if (!this.currentEl) {
23676             return;
23677         }
23678         
23679         
23680         if (dom != this.currentEl.dom) {
23681             return;
23682         }
23683         var xy = ev.getXY();
23684         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
23685             return;
23686         }
23687         // only activate leave if mouse cursor is outside... bounding box..
23688         
23689         
23690         
23691         
23692         if (this.currentTip) {
23693             this.currentTip.leave();
23694         }
23695         //Roo.log('clear currentEl');
23696         this.currentEl = false;
23697         
23698         
23699     },
23700     alignment : {
23701         'left' : ['r-l', [-2,0], 'right'],
23702         'right' : ['l-r', [2,0], 'left'],
23703         'bottom' : ['t-b', [0,2], 'top'],
23704         'top' : [ 'b-t', [0,-2], 'bottom']
23705     }
23706     
23707 });
23708
23709
23710 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
23711     
23712     
23713     bindEl : false,
23714     
23715     delay : null, // can be { show : 300 , hide: 500}
23716     
23717     timeout : null,
23718     
23719     hoverState : null, //???
23720     
23721     placement : 'bottom', 
23722     
23723     getAutoCreate : function(){
23724     
23725         var cfg = {
23726            cls : 'tooltip',
23727            role : 'tooltip',
23728            cn : [
23729                 {
23730                     cls : 'tooltip-arrow'
23731                 },
23732                 {
23733                     cls : 'tooltip-inner'
23734                 }
23735            ]
23736         };
23737         
23738         return cfg;
23739     },
23740     bind : function(el)
23741     {
23742         this.bindEl = el;
23743     },
23744       
23745     
23746     enter : function () {
23747        
23748         if (this.timeout != null) {
23749             clearTimeout(this.timeout);
23750         }
23751         
23752         this.hoverState = 'in';
23753          //Roo.log("enter - show");
23754         if (!this.delay || !this.delay.show) {
23755             this.show();
23756             return;
23757         }
23758         var _t = this;
23759         this.timeout = setTimeout(function () {
23760             if (_t.hoverState == 'in') {
23761                 _t.show();
23762             }
23763         }, this.delay.show);
23764     },
23765     leave : function()
23766     {
23767         clearTimeout(this.timeout);
23768     
23769         this.hoverState = 'out';
23770          if (!this.delay || !this.delay.hide) {
23771             this.hide();
23772             return;
23773         }
23774        
23775         var _t = this;
23776         this.timeout = setTimeout(function () {
23777             //Roo.log("leave - timeout");
23778             
23779             if (_t.hoverState == 'out') {
23780                 _t.hide();
23781                 Roo.bootstrap.Tooltip.currentEl = false;
23782             }
23783         }, delay);
23784     },
23785     
23786     show : function ()
23787     {
23788         if (!this.el) {
23789             this.render(document.body);
23790         }
23791         // set content.
23792         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23793         
23794         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23795         
23796         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23797         
23798         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23799         
23800         var placement = typeof this.placement == 'function' ?
23801             this.placement.call(this, this.el, on_el) :
23802             this.placement;
23803             
23804         var autoToken = /\s?auto?\s?/i;
23805         var autoPlace = autoToken.test(placement);
23806         if (autoPlace) {
23807             placement = placement.replace(autoToken, '') || 'top';
23808         }
23809         
23810         //this.el.detach()
23811         //this.el.setXY([0,0]);
23812         this.el.show();
23813         //this.el.dom.style.display='block';
23814         
23815         //this.el.appendTo(on_el);
23816         
23817         var p = this.getPosition();
23818         var box = this.el.getBox();
23819         
23820         if (autoPlace) {
23821             // fixme..
23822         }
23823         
23824         var align = Roo.bootstrap.Tooltip.alignment[placement];
23825         
23826         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
23827         
23828         if(placement == 'top' || placement == 'bottom'){
23829             if(xy[0] < 0){
23830                 placement = 'right';
23831             }
23832             
23833             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
23834                 placement = 'left';
23835             }
23836         }
23837         
23838         align = Roo.bootstrap.Tooltip.alignment[placement];
23839         
23840         this.el.alignTo(this.bindEl, align[0],align[1]);
23841         //var arrow = this.el.select('.arrow',true).first();
23842         //arrow.set(align[2], 
23843         
23844         this.el.addClass(placement);
23845         
23846         this.el.addClass('in fade');
23847         
23848         this.hoverState = null;
23849         
23850         if (this.el.hasClass('fade')) {
23851             // fade it?
23852         }
23853         
23854     },
23855     hide : function()
23856     {
23857          
23858         if (!this.el) {
23859             return;
23860         }
23861         //this.el.setXY([0,0]);
23862         this.el.removeClass('in');
23863         //this.el.hide();
23864         
23865     }
23866     
23867 });
23868  
23869
23870  /*
23871  * - LGPL
23872  *
23873  * Location Picker
23874  * 
23875  */
23876
23877 /**
23878  * @class Roo.bootstrap.LocationPicker
23879  * @extends Roo.bootstrap.Component
23880  * Bootstrap LocationPicker class
23881  * @cfg {Number} latitude Position when init default 0
23882  * @cfg {Number} longitude Position when init default 0
23883  * @cfg {Number} zoom default 15
23884  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
23885  * @cfg {Boolean} mapTypeControl default false
23886  * @cfg {Boolean} disableDoubleClickZoom default false
23887  * @cfg {Boolean} scrollwheel default true
23888  * @cfg {Boolean} streetViewControl default false
23889  * @cfg {Number} radius default 0
23890  * @cfg {String} locationName
23891  * @cfg {Boolean} draggable default true
23892  * @cfg {Boolean} enableAutocomplete default false
23893  * @cfg {Boolean} enableReverseGeocode default true
23894  * @cfg {String} markerTitle
23895  * 
23896  * @constructor
23897  * Create a new LocationPicker
23898  * @param {Object} config The config object
23899  */
23900
23901
23902 Roo.bootstrap.LocationPicker = function(config){
23903     
23904     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
23905     
23906     this.addEvents({
23907         /**
23908          * @event initial
23909          * Fires when the picker initialized.
23910          * @param {Roo.bootstrap.LocationPicker} this
23911          * @param {Google Location} location
23912          */
23913         initial : true,
23914         /**
23915          * @event positionchanged
23916          * Fires when the picker position changed.
23917          * @param {Roo.bootstrap.LocationPicker} this
23918          * @param {Google Location} location
23919          */
23920         positionchanged : true,
23921         /**
23922          * @event resize
23923          * Fires when the map resize.
23924          * @param {Roo.bootstrap.LocationPicker} this
23925          */
23926         resize : true,
23927         /**
23928          * @event show
23929          * Fires when the map show.
23930          * @param {Roo.bootstrap.LocationPicker} this
23931          */
23932         show : true,
23933         /**
23934          * @event hide
23935          * Fires when the map hide.
23936          * @param {Roo.bootstrap.LocationPicker} this
23937          */
23938         hide : true,
23939         /**
23940          * @event mapClick
23941          * Fires when click the map.
23942          * @param {Roo.bootstrap.LocationPicker} this
23943          * @param {Map event} e
23944          */
23945         mapClick : true,
23946         /**
23947          * @event mapRightClick
23948          * Fires when right click the map.
23949          * @param {Roo.bootstrap.LocationPicker} this
23950          * @param {Map event} e
23951          */
23952         mapRightClick : true,
23953         /**
23954          * @event markerClick
23955          * Fires when click the marker.
23956          * @param {Roo.bootstrap.LocationPicker} this
23957          * @param {Map event} e
23958          */
23959         markerClick : true,
23960         /**
23961          * @event markerRightClick
23962          * Fires when right click the marker.
23963          * @param {Roo.bootstrap.LocationPicker} this
23964          * @param {Map event} e
23965          */
23966         markerRightClick : true,
23967         /**
23968          * @event OverlayViewDraw
23969          * Fires when OverlayView Draw
23970          * @param {Roo.bootstrap.LocationPicker} this
23971          */
23972         OverlayViewDraw : true,
23973         /**
23974          * @event OverlayViewOnAdd
23975          * Fires when OverlayView Draw
23976          * @param {Roo.bootstrap.LocationPicker} this
23977          */
23978         OverlayViewOnAdd : true,
23979         /**
23980          * @event OverlayViewOnRemove
23981          * Fires when OverlayView Draw
23982          * @param {Roo.bootstrap.LocationPicker} this
23983          */
23984         OverlayViewOnRemove : true,
23985         /**
23986          * @event OverlayViewShow
23987          * Fires when OverlayView Draw
23988          * @param {Roo.bootstrap.LocationPicker} this
23989          * @param {Pixel} cpx
23990          */
23991         OverlayViewShow : true,
23992         /**
23993          * @event OverlayViewHide
23994          * Fires when OverlayView Draw
23995          * @param {Roo.bootstrap.LocationPicker} this
23996          */
23997         OverlayViewHide : true,
23998         /**
23999          * @event loadexception
24000          * Fires when load google lib failed.
24001          * @param {Roo.bootstrap.LocationPicker} this
24002          */
24003         loadexception : true
24004     });
24005         
24006 };
24007
24008 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
24009     
24010     gMapContext: false,
24011     
24012     latitude: 0,
24013     longitude: 0,
24014     zoom: 15,
24015     mapTypeId: false,
24016     mapTypeControl: false,
24017     disableDoubleClickZoom: false,
24018     scrollwheel: true,
24019     streetViewControl: false,
24020     radius: 0,
24021     locationName: '',
24022     draggable: true,
24023     enableAutocomplete: false,
24024     enableReverseGeocode: true,
24025     markerTitle: '',
24026     
24027     getAutoCreate: function()
24028     {
24029
24030         var cfg = {
24031             tag: 'div',
24032             cls: 'roo-location-picker'
24033         };
24034         
24035         return cfg
24036     },
24037     
24038     initEvents: function(ct, position)
24039     {       
24040         if(!this.el.getWidth() || this.isApplied()){
24041             return;
24042         }
24043         
24044         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24045         
24046         this.initial();
24047     },
24048     
24049     initial: function()
24050     {
24051         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24052             this.fireEvent('loadexception', this);
24053             return;
24054         }
24055         
24056         if(!this.mapTypeId){
24057             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24058         }
24059         
24060         this.gMapContext = this.GMapContext();
24061         
24062         this.initOverlayView();
24063         
24064         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24065         
24066         var _this = this;
24067                 
24068         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24069             _this.setPosition(_this.gMapContext.marker.position);
24070         });
24071         
24072         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
24073             _this.fireEvent('mapClick', this, event);
24074             
24075         });
24076
24077         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
24078             _this.fireEvent('mapRightClick', this, event);
24079             
24080         });
24081         
24082         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
24083             _this.fireEvent('markerClick', this, event);
24084             
24085         });
24086
24087         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
24088             _this.fireEvent('markerRightClick', this, event);
24089             
24090         });
24091         
24092         this.setPosition(this.gMapContext.location);
24093         
24094         this.fireEvent('initial', this, this.gMapContext.location);
24095     },
24096     
24097     initOverlayView: function()
24098     {
24099         var _this = this;
24100         
24101         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
24102             
24103             draw: function()
24104             {
24105                 _this.fireEvent('OverlayViewDraw', _this);
24106             },
24107             
24108             onAdd: function()
24109             {
24110                 _this.fireEvent('OverlayViewOnAdd', _this);
24111             },
24112             
24113             onRemove: function()
24114             {
24115                 _this.fireEvent('OverlayViewOnRemove', _this);
24116             },
24117             
24118             show: function(cpx)
24119             {
24120                 _this.fireEvent('OverlayViewShow', _this, cpx);
24121             },
24122             
24123             hide: function()
24124             {
24125                 _this.fireEvent('OverlayViewHide', _this);
24126             }
24127             
24128         });
24129     },
24130     
24131     fromLatLngToContainerPixel: function(event)
24132     {
24133         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
24134     },
24135     
24136     isApplied: function() 
24137     {
24138         return this.getGmapContext() == false ? false : true;
24139     },
24140     
24141     getGmapContext: function() 
24142     {
24143         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
24144     },
24145     
24146     GMapContext: function() 
24147     {
24148         var position = new google.maps.LatLng(this.latitude, this.longitude);
24149         
24150         var _map = new google.maps.Map(this.el.dom, {
24151             center: position,
24152             zoom: this.zoom,
24153             mapTypeId: this.mapTypeId,
24154             mapTypeControl: this.mapTypeControl,
24155             disableDoubleClickZoom: this.disableDoubleClickZoom,
24156             scrollwheel: this.scrollwheel,
24157             streetViewControl: this.streetViewControl,
24158             locationName: this.locationName,
24159             draggable: this.draggable,
24160             enableAutocomplete: this.enableAutocomplete,
24161             enableReverseGeocode: this.enableReverseGeocode
24162         });
24163         
24164         var _marker = new google.maps.Marker({
24165             position: position,
24166             map: _map,
24167             title: this.markerTitle,
24168             draggable: this.draggable
24169         });
24170         
24171         return {
24172             map: _map,
24173             marker: _marker,
24174             circle: null,
24175             location: position,
24176             radius: this.radius,
24177             locationName: this.locationName,
24178             addressComponents: {
24179                 formatted_address: null,
24180                 addressLine1: null,
24181                 addressLine2: null,
24182                 streetName: null,
24183                 streetNumber: null,
24184                 city: null,
24185                 district: null,
24186                 state: null,
24187                 stateOrProvince: null
24188             },
24189             settings: this,
24190             domContainer: this.el.dom,
24191             geodecoder: new google.maps.Geocoder()
24192         };
24193     },
24194     
24195     drawCircle: function(center, radius, options) 
24196     {
24197         if (this.gMapContext.circle != null) {
24198             this.gMapContext.circle.setMap(null);
24199         }
24200         if (radius > 0) {
24201             radius *= 1;
24202             options = Roo.apply({}, options, {
24203                 strokeColor: "#0000FF",
24204                 strokeOpacity: .35,
24205                 strokeWeight: 2,
24206                 fillColor: "#0000FF",
24207                 fillOpacity: .2
24208             });
24209             
24210             options.map = this.gMapContext.map;
24211             options.radius = radius;
24212             options.center = center;
24213             this.gMapContext.circle = new google.maps.Circle(options);
24214             return this.gMapContext.circle;
24215         }
24216         
24217         return null;
24218     },
24219     
24220     setPosition: function(location) 
24221     {
24222         this.gMapContext.location = location;
24223         this.gMapContext.marker.setPosition(location);
24224         this.gMapContext.map.panTo(location);
24225         this.drawCircle(location, this.gMapContext.radius, {});
24226         
24227         var _this = this;
24228         
24229         if (this.gMapContext.settings.enableReverseGeocode) {
24230             this.gMapContext.geodecoder.geocode({
24231                 latLng: this.gMapContext.location
24232             }, function(results, status) {
24233                 
24234                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
24235                     _this.gMapContext.locationName = results[0].formatted_address;
24236                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
24237                     
24238                     _this.fireEvent('positionchanged', this, location);
24239                 }
24240             });
24241             
24242             return;
24243         }
24244         
24245         this.fireEvent('positionchanged', this, location);
24246     },
24247     
24248     resize: function()
24249     {
24250         google.maps.event.trigger(this.gMapContext.map, "resize");
24251         
24252         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24253         
24254         this.fireEvent('resize', this);
24255     },
24256     
24257     setPositionByLatLng: function(latitude, longitude)
24258     {
24259         this.setPosition(new google.maps.LatLng(latitude, longitude));
24260     },
24261     
24262     getCurrentPosition: function() 
24263     {
24264         return {
24265             latitude: this.gMapContext.location.lat(),
24266             longitude: this.gMapContext.location.lng()
24267         };
24268     },
24269     
24270     getAddressName: function() 
24271     {
24272         return this.gMapContext.locationName;
24273     },
24274     
24275     getAddressComponents: function() 
24276     {
24277         return this.gMapContext.addressComponents;
24278     },
24279     
24280     address_component_from_google_geocode: function(address_components) 
24281     {
24282         var result = {};
24283         
24284         for (var i = 0; i < address_components.length; i++) {
24285             var component = address_components[i];
24286             if (component.types.indexOf("postal_code") >= 0) {
24287                 result.postalCode = component.short_name;
24288             } else if (component.types.indexOf("street_number") >= 0) {
24289                 result.streetNumber = component.short_name;
24290             } else if (component.types.indexOf("route") >= 0) {
24291                 result.streetName = component.short_name;
24292             } else if (component.types.indexOf("neighborhood") >= 0) {
24293                 result.city = component.short_name;
24294             } else if (component.types.indexOf("locality") >= 0) {
24295                 result.city = component.short_name;
24296             } else if (component.types.indexOf("sublocality") >= 0) {
24297                 result.district = component.short_name;
24298             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
24299                 result.stateOrProvince = component.short_name;
24300             } else if (component.types.indexOf("country") >= 0) {
24301                 result.country = component.short_name;
24302             }
24303         }
24304         
24305         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
24306         result.addressLine2 = "";
24307         return result;
24308     },
24309     
24310     setZoomLevel: function(zoom)
24311     {
24312         this.gMapContext.map.setZoom(zoom);
24313     },
24314     
24315     show: function()
24316     {
24317         if(!this.el){
24318             return;
24319         }
24320         
24321         this.el.show();
24322         
24323         this.resize();
24324         
24325         this.fireEvent('show', this);
24326     },
24327     
24328     hide: function()
24329     {
24330         if(!this.el){
24331             return;
24332         }
24333         
24334         this.el.hide();
24335         
24336         this.fireEvent('hide', this);
24337     }
24338     
24339 });
24340
24341 Roo.apply(Roo.bootstrap.LocationPicker, {
24342     
24343     OverlayView : function(map, options)
24344     {
24345         options = options || {};
24346         
24347         this.setMap(map);
24348     }
24349     
24350     
24351 });/*
24352  * - LGPL
24353  *
24354  * Alert
24355  * 
24356  */
24357
24358 /**
24359  * @class Roo.bootstrap.Alert
24360  * @extends Roo.bootstrap.Component
24361  * Bootstrap Alert class
24362  * @cfg {String} title The title of alert
24363  * @cfg {String} html The content of alert
24364  * @cfg {String} weight (  success | info | warning | danger )
24365  * @cfg {String} faicon font-awesomeicon
24366  * 
24367  * @constructor
24368  * Create a new alert
24369  * @param {Object} config The config object
24370  */
24371
24372
24373 Roo.bootstrap.Alert = function(config){
24374     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
24375     
24376 };
24377
24378 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
24379     
24380     title: '',
24381     html: '',
24382     weight: false,
24383     faicon: false,
24384     
24385     getAutoCreate : function()
24386     {
24387         
24388         var cfg = {
24389             tag : 'div',
24390             cls : 'alert',
24391             cn : [
24392                 {
24393                     tag : 'i',
24394                     cls : 'roo-alert-icon'
24395                     
24396                 },
24397                 {
24398                     tag : 'b',
24399                     cls : 'roo-alert-title',
24400                     html : this.title
24401                 },
24402                 {
24403                     tag : 'span',
24404                     cls : 'roo-alert-text',
24405                     html : this.html
24406                 }
24407             ]
24408         };
24409         
24410         if(this.faicon){
24411             cfg.cn[0].cls += ' fa ' + this.faicon;
24412         }
24413         
24414         if(this.weight){
24415             cfg.cls += ' alert-' + this.weight;
24416         }
24417         
24418         return cfg;
24419     },
24420     
24421     initEvents: function() 
24422     {
24423         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24424     },
24425     
24426     setTitle : function(str)
24427     {
24428         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24429     },
24430     
24431     setText : function(str)
24432     {
24433         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24434     },
24435     
24436     setWeight : function(weight)
24437     {
24438         if(this.weight){
24439             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24440         }
24441         
24442         this.weight = weight;
24443         
24444         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24445     },
24446     
24447     setIcon : function(icon)
24448     {
24449         if(this.faicon){
24450             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24451         }
24452         
24453         this.faicon = icon;
24454         
24455         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24456     },
24457     
24458     hide: function() 
24459     {
24460         this.el.hide();   
24461     },
24462     
24463     show: function() 
24464     {  
24465         this.el.show();   
24466     }
24467     
24468 });
24469
24470  
24471 /*
24472 * Licence: LGPL
24473 */
24474
24475 /**
24476  * @class Roo.bootstrap.UploadCropbox
24477  * @extends Roo.bootstrap.Component
24478  * Bootstrap UploadCropbox class
24479  * @cfg {String} emptyText show when image has been loaded
24480  * @cfg {String} rotateNotify show when image too small to rotate
24481  * @cfg {Number} errorTimeout default 3000
24482  * @cfg {Number} minWidth default 300
24483  * @cfg {Number} minHeight default 300
24484  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24485  * @cfg {Boolean} isDocument (true|false) default false
24486  * @cfg {String} url action url
24487  * @cfg {String} paramName default 'imageUpload'
24488  * @cfg {String} method default POST
24489  * @cfg {Boolean} loadMask (true|false) default true
24490  * @cfg {Boolean} loadingText default 'Loading...'
24491  * 
24492  * @constructor
24493  * Create a new UploadCropbox
24494  * @param {Object} config The config object
24495  */
24496
24497 Roo.bootstrap.UploadCropbox = function(config){
24498     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
24499     
24500     this.addEvents({
24501         /**
24502          * @event beforeselectfile
24503          * Fire before select file
24504          * @param {Roo.bootstrap.UploadCropbox} this
24505          */
24506         "beforeselectfile" : true,
24507         /**
24508          * @event initial
24509          * Fire after initEvent
24510          * @param {Roo.bootstrap.UploadCropbox} this
24511          */
24512         "initial" : true,
24513         /**
24514          * @event crop
24515          * Fire after initEvent
24516          * @param {Roo.bootstrap.UploadCropbox} this
24517          * @param {String} data
24518          */
24519         "crop" : true,
24520         /**
24521          * @event prepare
24522          * Fire when preparing the file data
24523          * @param {Roo.bootstrap.UploadCropbox} this
24524          * @param {Object} file
24525          */
24526         "prepare" : true,
24527         /**
24528          * @event exception
24529          * Fire when get exception
24530          * @param {Roo.bootstrap.UploadCropbox} this
24531          * @param {XMLHttpRequest} xhr
24532          */
24533         "exception" : true,
24534         /**
24535          * @event beforeloadcanvas
24536          * Fire before load the canvas
24537          * @param {Roo.bootstrap.UploadCropbox} this
24538          * @param {String} src
24539          */
24540         "beforeloadcanvas" : true,
24541         /**
24542          * @event trash
24543          * Fire when trash image
24544          * @param {Roo.bootstrap.UploadCropbox} this
24545          */
24546         "trash" : true,
24547         /**
24548          * @event download
24549          * Fire when download the image
24550          * @param {Roo.bootstrap.UploadCropbox} this
24551          */
24552         "download" : true,
24553         /**
24554          * @event footerbuttonclick
24555          * Fire when footerbuttonclick
24556          * @param {Roo.bootstrap.UploadCropbox} this
24557          * @param {String} type
24558          */
24559         "footerbuttonclick" : true,
24560         /**
24561          * @event resize
24562          * Fire when resize
24563          * @param {Roo.bootstrap.UploadCropbox} this
24564          */
24565         "resize" : true,
24566         /**
24567          * @event rotate
24568          * Fire when rotate the image
24569          * @param {Roo.bootstrap.UploadCropbox} this
24570          * @param {String} pos
24571          */
24572         "rotate" : true,
24573         /**
24574          * @event inspect
24575          * Fire when inspect the file
24576          * @param {Roo.bootstrap.UploadCropbox} this
24577          * @param {Object} file
24578          */
24579         "inspect" : true,
24580         /**
24581          * @event upload
24582          * Fire when xhr upload the file
24583          * @param {Roo.bootstrap.UploadCropbox} this
24584          * @param {Object} data
24585          */
24586         "upload" : true,
24587         /**
24588          * @event arrange
24589          * Fire when arrange the file data
24590          * @param {Roo.bootstrap.UploadCropbox} this
24591          * @param {Object} formData
24592          */
24593         "arrange" : true
24594     });
24595     
24596     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24597 };
24598
24599 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
24600     
24601     emptyText : 'Click to upload image',
24602     rotateNotify : 'Image is too small to rotate',
24603     errorTimeout : 3000,
24604     scale : 0,
24605     baseScale : 1,
24606     rotate : 0,
24607     dragable : false,
24608     pinching : false,
24609     mouseX : 0,
24610     mouseY : 0,
24611     cropData : false,
24612     minWidth : 300,
24613     minHeight : 300,
24614     file : false,
24615     exif : {},
24616     baseRotate : 1,
24617     cropType : 'image/jpeg',
24618     buttons : false,
24619     canvasLoaded : false,
24620     isDocument : false,
24621     method : 'POST',
24622     paramName : 'imageUpload',
24623     loadMask : true,
24624     loadingText : 'Loading...',
24625     maskEl : false,
24626     
24627     getAutoCreate : function()
24628     {
24629         var cfg = {
24630             tag : 'div',
24631             cls : 'roo-upload-cropbox',
24632             cn : [
24633                 {
24634                     tag : 'input',
24635                     cls : 'roo-upload-cropbox-selector',
24636                     type : 'file'
24637                 },
24638                 {
24639                     tag : 'div',
24640                     cls : 'roo-upload-cropbox-body',
24641                     style : 'cursor:pointer',
24642                     cn : [
24643                         {
24644                             tag : 'div',
24645                             cls : 'roo-upload-cropbox-preview'
24646                         },
24647                         {
24648                             tag : 'div',
24649                             cls : 'roo-upload-cropbox-thumb'
24650                         },
24651                         {
24652                             tag : 'div',
24653                             cls : 'roo-upload-cropbox-empty-notify',
24654                             html : this.emptyText
24655                         },
24656                         {
24657                             tag : 'div',
24658                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
24659                             html : this.rotateNotify
24660                         }
24661                     ]
24662                 },
24663                 {
24664                     tag : 'div',
24665                     cls : 'roo-upload-cropbox-footer',
24666                     cn : {
24667                         tag : 'div',
24668                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24669                         cn : []
24670                     }
24671                 }
24672             ]
24673         };
24674         
24675         return cfg;
24676     },
24677     
24678     onRender : function(ct, position)
24679     {
24680         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
24681         
24682         if (this.buttons.length) {
24683             
24684             Roo.each(this.buttons, function(bb) {
24685                 
24686                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
24687                 
24688                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
24689                 
24690             }, this);
24691         }
24692         
24693         if(this.loadMask){
24694             this.maskEl = this.el;
24695         }
24696     },
24697     
24698     initEvents : function()
24699     {
24700         this.urlAPI = (window.createObjectURL && window) || 
24701                                 (window.URL && URL.revokeObjectURL && URL) || 
24702                                 (window.webkitURL && webkitURL);
24703                         
24704         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
24705         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24706         
24707         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
24708         this.selectorEl.hide();
24709         
24710         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
24711         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24712         
24713         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
24714         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24715         this.thumbEl.hide();
24716         
24717         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
24718         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24719         
24720         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
24721         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24722         this.errorEl.hide();
24723         
24724         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
24725         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24726         this.footerEl.hide();
24727         
24728         this.setThumbBoxSize();
24729         
24730         this.bind();
24731         
24732         this.resize();
24733         
24734         this.fireEvent('initial', this);
24735     },
24736
24737     bind : function()
24738     {
24739         var _this = this;
24740         
24741         window.addEventListener("resize", function() { _this.resize(); } );
24742         
24743         this.bodyEl.on('click', this.beforeSelectFile, this);
24744         
24745         if(Roo.isTouch){
24746             this.bodyEl.on('touchstart', this.onTouchStart, this);
24747             this.bodyEl.on('touchmove', this.onTouchMove, this);
24748             this.bodyEl.on('touchend', this.onTouchEnd, this);
24749         }
24750         
24751         if(!Roo.isTouch){
24752             this.bodyEl.on('mousedown', this.onMouseDown, this);
24753             this.bodyEl.on('mousemove', this.onMouseMove, this);
24754             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
24755             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
24756             Roo.get(document).on('mouseup', this.onMouseUp, this);
24757         }
24758         
24759         this.selectorEl.on('change', this.onFileSelected, this);
24760     },
24761     
24762     reset : function()
24763     {    
24764         this.scale = 0;
24765         this.baseScale = 1;
24766         this.rotate = 0;
24767         this.baseRotate = 1;
24768         this.dragable = false;
24769         this.pinching = false;
24770         this.mouseX = 0;
24771         this.mouseY = 0;
24772         this.cropData = false;
24773         this.notifyEl.dom.innerHTML = this.emptyText;
24774         
24775         this.selectorEl.dom.value = '';
24776         
24777     },
24778     
24779     resize : function()
24780     {
24781         if(this.fireEvent('resize', this) != false){
24782             this.setThumbBoxPosition();
24783             this.setCanvasPosition();
24784         }
24785     },
24786     
24787     onFooterButtonClick : function(e, el, o, type)
24788     {
24789         switch (type) {
24790             case 'rotate-left' :
24791                 this.onRotateLeft(e);
24792                 break;
24793             case 'rotate-right' :
24794                 this.onRotateRight(e);
24795                 break;
24796             case 'picture' :
24797                 this.beforeSelectFile(e);
24798                 break;
24799             case 'trash' :
24800                 this.trash(e);
24801                 break;
24802             case 'crop' :
24803                 this.crop(e);
24804                 break;
24805             case 'download' :
24806                 this.download(e);
24807                 break;
24808             default :
24809                 break;
24810         }
24811         
24812         this.fireEvent('footerbuttonclick', this, type);
24813     },
24814     
24815     beforeSelectFile : function(e)
24816     {
24817         e.preventDefault();
24818         
24819         if(this.fireEvent('beforeselectfile', this) != false){
24820             this.selectorEl.dom.click();
24821         }
24822     },
24823     
24824     onFileSelected : function(e)
24825     {
24826         e.preventDefault();
24827         
24828         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
24829             return;
24830         }
24831         
24832         var file = this.selectorEl.dom.files[0];
24833         
24834         if(this.fireEvent('inspect', this, file) != false){
24835             this.prepare(file);
24836         }
24837         
24838     },
24839     
24840     trash : function(e)
24841     {
24842         this.fireEvent('trash', this);
24843     },
24844     
24845     download : function(e)
24846     {
24847         this.fireEvent('download', this);
24848     },
24849     
24850     loadCanvas : function(src)
24851     {   
24852         if(this.fireEvent('beforeloadcanvas', this, src) != false){
24853             
24854             this.reset();
24855             
24856             this.imageEl = document.createElement('img');
24857             
24858             var _this = this;
24859             
24860             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
24861             
24862             this.imageEl.src = src;
24863         }
24864     },
24865     
24866     onLoadCanvas : function()
24867     {   
24868         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
24869         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
24870         
24871         this.bodyEl.un('click', this.beforeSelectFile, this);
24872         
24873         this.notifyEl.hide();
24874         this.thumbEl.show();
24875         this.footerEl.show();
24876         
24877         this.baseRotateLevel();
24878         
24879         if(this.isDocument){
24880             this.setThumbBoxSize();
24881         }
24882         
24883         this.setThumbBoxPosition();
24884         
24885         this.baseScaleLevel();
24886         
24887         this.draw();
24888         
24889         this.resize();
24890         
24891         this.canvasLoaded = true;
24892         
24893         if(this.loadMask){
24894             this.maskEl.unmask();
24895         }
24896         
24897     },
24898     
24899     setCanvasPosition : function()
24900     {   
24901         if(!this.canvasEl){
24902             return;
24903         }
24904         
24905         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
24906         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
24907         
24908         this.previewEl.setLeft(pw);
24909         this.previewEl.setTop(ph);
24910         
24911     },
24912     
24913     onMouseDown : function(e)
24914     {   
24915         e.stopEvent();
24916         
24917         this.dragable = true;
24918         this.pinching = false;
24919         
24920         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
24921             this.dragable = false;
24922             return;
24923         }
24924         
24925         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24926         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24927         
24928     },
24929     
24930     onMouseMove : function(e)
24931     {   
24932         e.stopEvent();
24933         
24934         if(!this.canvasLoaded){
24935             return;
24936         }
24937         
24938         if (!this.dragable){
24939             return;
24940         }
24941         
24942         var minX = Math.ceil(this.thumbEl.getLeft(true));
24943         var minY = Math.ceil(this.thumbEl.getTop(true));
24944         
24945         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
24946         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
24947         
24948         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24949         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24950         
24951         x = x - this.mouseX;
24952         y = y - this.mouseY;
24953         
24954         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
24955         var bgY = Math.ceil(y + this.previewEl.getTop(true));
24956         
24957         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
24958         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
24959         
24960         this.previewEl.setLeft(bgX);
24961         this.previewEl.setTop(bgY);
24962         
24963         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24964         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24965     },
24966     
24967     onMouseUp : function(e)
24968     {   
24969         e.stopEvent();
24970         
24971         this.dragable = false;
24972     },
24973     
24974     onMouseWheel : function(e)
24975     {   
24976         e.stopEvent();
24977         
24978         this.startScale = this.scale;
24979         
24980         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
24981         
24982         if(!this.zoomable()){
24983             this.scale = this.startScale;
24984             return;
24985         }
24986         
24987         this.draw();
24988         
24989         return;
24990     },
24991     
24992     zoomable : function()
24993     {
24994         var minScale = this.thumbEl.getWidth() / this.minWidth;
24995         
24996         if(this.minWidth < this.minHeight){
24997             minScale = this.thumbEl.getHeight() / this.minHeight;
24998         }
24999         
25000         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
25001         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
25002         
25003         if(
25004                 this.isDocument &&
25005                 (this.rotate == 0 || this.rotate == 180) && 
25006                 (
25007                     width > this.imageEl.OriginWidth || 
25008                     height > this.imageEl.OriginHeight ||
25009                     (width < this.minWidth && height < this.minHeight)
25010                 )
25011         ){
25012             return false;
25013         }
25014         
25015         if(
25016                 this.isDocument &&
25017                 (this.rotate == 90 || this.rotate == 270) && 
25018                 (
25019                     width > this.imageEl.OriginWidth || 
25020                     height > this.imageEl.OriginHeight ||
25021                     (width < this.minHeight && height < this.minWidth)
25022                 )
25023         ){
25024             return false;
25025         }
25026         
25027         if(
25028                 !this.isDocument &&
25029                 (this.rotate == 0 || this.rotate == 180) && 
25030                 (
25031                     width < this.minWidth || 
25032                     width > this.imageEl.OriginWidth || 
25033                     height < this.minHeight || 
25034                     height > this.imageEl.OriginHeight
25035                 )
25036         ){
25037             return false;
25038         }
25039         
25040         if(
25041                 !this.isDocument &&
25042                 (this.rotate == 90 || this.rotate == 270) && 
25043                 (
25044                     width < this.minHeight || 
25045                     width > this.imageEl.OriginWidth || 
25046                     height < this.minWidth || 
25047                     height > this.imageEl.OriginHeight
25048                 )
25049         ){
25050             return false;
25051         }
25052         
25053         return true;
25054         
25055     },
25056     
25057     onRotateLeft : function(e)
25058     {   
25059         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25060             
25061             var minScale = this.thumbEl.getWidth() / this.minWidth;
25062             
25063             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25064             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25065             
25066             this.startScale = this.scale;
25067             
25068             while (this.getScaleLevel() < minScale){
25069             
25070                 this.scale = this.scale + 1;
25071                 
25072                 if(!this.zoomable()){
25073                     break;
25074                 }
25075                 
25076                 if(
25077                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25078                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25079                 ){
25080                     continue;
25081                 }
25082                 
25083                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25084
25085                 this.draw();
25086                 
25087                 return;
25088             }
25089             
25090             this.scale = this.startScale;
25091             
25092             this.onRotateFail();
25093             
25094             return false;
25095         }
25096         
25097         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25098
25099         if(this.isDocument){
25100             this.setThumbBoxSize();
25101             this.setThumbBoxPosition();
25102             this.setCanvasPosition();
25103         }
25104         
25105         this.draw();
25106         
25107         this.fireEvent('rotate', this, 'left');
25108         
25109     },
25110     
25111     onRotateRight : function(e)
25112     {
25113         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25114             
25115             var minScale = this.thumbEl.getWidth() / this.minWidth;
25116         
25117             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25118             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25119             
25120             this.startScale = this.scale;
25121             
25122             while (this.getScaleLevel() < minScale){
25123             
25124                 this.scale = this.scale + 1;
25125                 
25126                 if(!this.zoomable()){
25127                     break;
25128                 }
25129                 
25130                 if(
25131                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25132                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25133                 ){
25134                     continue;
25135                 }
25136                 
25137                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25138
25139                 this.draw();
25140                 
25141                 return;
25142             }
25143             
25144             this.scale = this.startScale;
25145             
25146             this.onRotateFail();
25147             
25148             return false;
25149         }
25150         
25151         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25152
25153         if(this.isDocument){
25154             this.setThumbBoxSize();
25155             this.setThumbBoxPosition();
25156             this.setCanvasPosition();
25157         }
25158         
25159         this.draw();
25160         
25161         this.fireEvent('rotate', this, 'right');
25162     },
25163     
25164     onRotateFail : function()
25165     {
25166         this.errorEl.show(true);
25167         
25168         var _this = this;
25169         
25170         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
25171     },
25172     
25173     draw : function()
25174     {
25175         this.previewEl.dom.innerHTML = '';
25176         
25177         var canvasEl = document.createElement("canvas");
25178         
25179         var contextEl = canvasEl.getContext("2d");
25180         
25181         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25182         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25183         var center = this.imageEl.OriginWidth / 2;
25184         
25185         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
25186             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25187             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25188             center = this.imageEl.OriginHeight / 2;
25189         }
25190         
25191         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
25192         
25193         contextEl.translate(center, center);
25194         contextEl.rotate(this.rotate * Math.PI / 180);
25195
25196         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25197         
25198         this.canvasEl = document.createElement("canvas");
25199         
25200         this.contextEl = this.canvasEl.getContext("2d");
25201         
25202         switch (this.rotate) {
25203             case 0 :
25204                 
25205                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25206                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25207                 
25208                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25209                 
25210                 break;
25211             case 90 : 
25212                 
25213                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25214                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25215                 
25216                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25217                     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);
25218                     break;
25219                 }
25220                 
25221                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25222                 
25223                 break;
25224             case 180 :
25225                 
25226                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25227                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25228                 
25229                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25230                     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);
25231                     break;
25232                 }
25233                 
25234                 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);
25235                 
25236                 break;
25237             case 270 :
25238                 
25239                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25240                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25241         
25242                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25243                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25244                     break;
25245                 }
25246                 
25247                 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);
25248                 
25249                 break;
25250             default : 
25251                 break;
25252         }
25253         
25254         this.previewEl.appendChild(this.canvasEl);
25255         
25256         this.setCanvasPosition();
25257     },
25258     
25259     crop : function()
25260     {
25261         if(!this.canvasLoaded){
25262             return;
25263         }
25264         
25265         var imageCanvas = document.createElement("canvas");
25266         
25267         var imageContext = imageCanvas.getContext("2d");
25268         
25269         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25270         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25271         
25272         var center = imageCanvas.width / 2;
25273         
25274         imageContext.translate(center, center);
25275         
25276         imageContext.rotate(this.rotate * Math.PI / 180);
25277         
25278         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25279         
25280         var canvas = document.createElement("canvas");
25281         
25282         var context = canvas.getContext("2d");
25283                 
25284         canvas.width = this.minWidth;
25285         canvas.height = this.minHeight;
25286
25287         switch (this.rotate) {
25288             case 0 :
25289                 
25290                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25291                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25292                 
25293                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25294                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25295                 
25296                 var targetWidth = this.minWidth - 2 * x;
25297                 var targetHeight = this.minHeight - 2 * y;
25298                 
25299                 var scale = 1;
25300                 
25301                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25302                     scale = targetWidth / width;
25303                 }
25304                 
25305                 if(x > 0 && y == 0){
25306                     scale = targetHeight / height;
25307                 }
25308                 
25309                 if(x > 0 && y > 0){
25310                     scale = targetWidth / width;
25311                     
25312                     if(width < height){
25313                         scale = targetHeight / height;
25314                     }
25315                 }
25316                 
25317                 context.scale(scale, scale);
25318                 
25319                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25320                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25321
25322                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25323                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25324
25325                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25326                 
25327                 break;
25328             case 90 : 
25329                 
25330                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25331                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25332                 
25333                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25334                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25335                 
25336                 var targetWidth = this.minWidth - 2 * x;
25337                 var targetHeight = this.minHeight - 2 * y;
25338                 
25339                 var scale = 1;
25340                 
25341                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25342                     scale = targetWidth / width;
25343                 }
25344                 
25345                 if(x > 0 && y == 0){
25346                     scale = targetHeight / height;
25347                 }
25348                 
25349                 if(x > 0 && y > 0){
25350                     scale = targetWidth / width;
25351                     
25352                     if(width < height){
25353                         scale = targetHeight / height;
25354                     }
25355                 }
25356                 
25357                 context.scale(scale, scale);
25358                 
25359                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25360                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25361
25362                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25363                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25364                 
25365                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25366                 
25367                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25368                 
25369                 break;
25370             case 180 :
25371                 
25372                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25373                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25374                 
25375                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25376                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25377                 
25378                 var targetWidth = this.minWidth - 2 * x;
25379                 var targetHeight = this.minHeight - 2 * y;
25380                 
25381                 var scale = 1;
25382                 
25383                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25384                     scale = targetWidth / width;
25385                 }
25386                 
25387                 if(x > 0 && y == 0){
25388                     scale = targetHeight / height;
25389                 }
25390                 
25391                 if(x > 0 && y > 0){
25392                     scale = targetWidth / width;
25393                     
25394                     if(width < height){
25395                         scale = targetHeight / height;
25396                     }
25397                 }
25398                 
25399                 context.scale(scale, scale);
25400                 
25401                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25402                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25403
25404                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25405                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25406
25407                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25408                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25409                 
25410                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25411                 
25412                 break;
25413             case 270 :
25414                 
25415                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25416                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25417                 
25418                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25419                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25420                 
25421                 var targetWidth = this.minWidth - 2 * x;
25422                 var targetHeight = this.minHeight - 2 * y;
25423                 
25424                 var scale = 1;
25425                 
25426                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25427                     scale = targetWidth / width;
25428                 }
25429                 
25430                 if(x > 0 && y == 0){
25431                     scale = targetHeight / height;
25432                 }
25433                 
25434                 if(x > 0 && y > 0){
25435                     scale = targetWidth / width;
25436                     
25437                     if(width < height){
25438                         scale = targetHeight / height;
25439                     }
25440                 }
25441                 
25442                 context.scale(scale, scale);
25443                 
25444                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25445                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25446
25447                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25448                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25449                 
25450                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25451                 
25452                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25453                 
25454                 break;
25455             default : 
25456                 break;
25457         }
25458         
25459         this.cropData = canvas.toDataURL(this.cropType);
25460         
25461         if(this.fireEvent('crop', this, this.cropData) !== false){
25462             this.process(this.file, this.cropData);
25463         }
25464         
25465         return;
25466         
25467     },
25468     
25469     setThumbBoxSize : function()
25470     {
25471         var width, height;
25472         
25473         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
25474             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
25475             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
25476             
25477             this.minWidth = width;
25478             this.minHeight = height;
25479             
25480             if(this.rotate == 90 || this.rotate == 270){
25481                 this.minWidth = height;
25482                 this.minHeight = width;
25483             }
25484         }
25485         
25486         height = 300;
25487         width = Math.ceil(this.minWidth * height / this.minHeight);
25488         
25489         if(this.minWidth > this.minHeight){
25490             width = 300;
25491             height = Math.ceil(this.minHeight * width / this.minWidth);
25492         }
25493         
25494         this.thumbEl.setStyle({
25495             width : width + 'px',
25496             height : height + 'px'
25497         });
25498
25499         return;
25500             
25501     },
25502     
25503     setThumbBoxPosition : function()
25504     {
25505         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
25506         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
25507         
25508         this.thumbEl.setLeft(x);
25509         this.thumbEl.setTop(y);
25510         
25511     },
25512     
25513     baseRotateLevel : function()
25514     {
25515         this.baseRotate = 1;
25516         
25517         if(
25518                 typeof(this.exif) != 'undefined' &&
25519                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
25520                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
25521         ){
25522             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
25523         }
25524         
25525         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
25526         
25527     },
25528     
25529     baseScaleLevel : function()
25530     {
25531         var width, height;
25532         
25533         if(this.isDocument){
25534             
25535             if(this.baseRotate == 6 || this.baseRotate == 8){
25536             
25537                 height = this.thumbEl.getHeight();
25538                 this.baseScale = height / this.imageEl.OriginWidth;
25539
25540                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
25541                     width = this.thumbEl.getWidth();
25542                     this.baseScale = width / this.imageEl.OriginHeight;
25543                 }
25544
25545                 return;
25546             }
25547
25548             height = this.thumbEl.getHeight();
25549             this.baseScale = height / this.imageEl.OriginHeight;
25550
25551             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
25552                 width = this.thumbEl.getWidth();
25553                 this.baseScale = width / this.imageEl.OriginWidth;
25554             }
25555
25556             return;
25557         }
25558         
25559         if(this.baseRotate == 6 || this.baseRotate == 8){
25560             
25561             width = this.thumbEl.getHeight();
25562             this.baseScale = width / this.imageEl.OriginHeight;
25563             
25564             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
25565                 height = this.thumbEl.getWidth();
25566                 this.baseScale = height / this.imageEl.OriginHeight;
25567             }
25568             
25569             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25570                 height = this.thumbEl.getWidth();
25571                 this.baseScale = height / this.imageEl.OriginHeight;
25572                 
25573                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
25574                     width = this.thumbEl.getHeight();
25575                     this.baseScale = width / this.imageEl.OriginWidth;
25576                 }
25577             }
25578             
25579             return;
25580         }
25581         
25582         width = this.thumbEl.getWidth();
25583         this.baseScale = width / this.imageEl.OriginWidth;
25584         
25585         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
25586             height = this.thumbEl.getHeight();
25587             this.baseScale = height / this.imageEl.OriginHeight;
25588         }
25589         
25590         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25591             
25592             height = this.thumbEl.getHeight();
25593             this.baseScale = height / this.imageEl.OriginHeight;
25594             
25595             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
25596                 width = this.thumbEl.getWidth();
25597                 this.baseScale = width / this.imageEl.OriginWidth;
25598             }
25599             
25600         }
25601         
25602         return;
25603     },
25604     
25605     getScaleLevel : function()
25606     {
25607         return this.baseScale * Math.pow(1.1, this.scale);
25608     },
25609     
25610     onTouchStart : function(e)
25611     {
25612         if(!this.canvasLoaded){
25613             this.beforeSelectFile(e);
25614             return;
25615         }
25616         
25617         var touches = e.browserEvent.touches;
25618         
25619         if(!touches){
25620             return;
25621         }
25622         
25623         if(touches.length == 1){
25624             this.onMouseDown(e);
25625             return;
25626         }
25627         
25628         if(touches.length != 2){
25629             return;
25630         }
25631         
25632         var coords = [];
25633         
25634         for(var i = 0, finger; finger = touches[i]; i++){
25635             coords.push(finger.pageX, finger.pageY);
25636         }
25637         
25638         var x = Math.pow(coords[0] - coords[2], 2);
25639         var y = Math.pow(coords[1] - coords[3], 2);
25640         
25641         this.startDistance = Math.sqrt(x + y);
25642         
25643         this.startScale = this.scale;
25644         
25645         this.pinching = true;
25646         this.dragable = false;
25647         
25648     },
25649     
25650     onTouchMove : function(e)
25651     {
25652         if(!this.pinching && !this.dragable){
25653             return;
25654         }
25655         
25656         var touches = e.browserEvent.touches;
25657         
25658         if(!touches){
25659             return;
25660         }
25661         
25662         if(this.dragable){
25663             this.onMouseMove(e);
25664             return;
25665         }
25666         
25667         var coords = [];
25668         
25669         for(var i = 0, finger; finger = touches[i]; i++){
25670             coords.push(finger.pageX, finger.pageY);
25671         }
25672         
25673         var x = Math.pow(coords[0] - coords[2], 2);
25674         var y = Math.pow(coords[1] - coords[3], 2);
25675         
25676         this.endDistance = Math.sqrt(x + y);
25677         
25678         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
25679         
25680         if(!this.zoomable()){
25681             this.scale = this.startScale;
25682             return;
25683         }
25684         
25685         this.draw();
25686         
25687     },
25688     
25689     onTouchEnd : function(e)
25690     {
25691         this.pinching = false;
25692         this.dragable = false;
25693         
25694     },
25695     
25696     process : function(file, crop)
25697     {
25698         if(this.loadMask){
25699             this.maskEl.mask(this.loadingText);
25700         }
25701         
25702         this.xhr = new XMLHttpRequest();
25703         
25704         file.xhr = this.xhr;
25705
25706         this.xhr.open(this.method, this.url, true);
25707         
25708         var headers = {
25709             "Accept": "application/json",
25710             "Cache-Control": "no-cache",
25711             "X-Requested-With": "XMLHttpRequest"
25712         };
25713         
25714         for (var headerName in headers) {
25715             var headerValue = headers[headerName];
25716             if (headerValue) {
25717                 this.xhr.setRequestHeader(headerName, headerValue);
25718             }
25719         }
25720         
25721         var _this = this;
25722         
25723         this.xhr.onload = function()
25724         {
25725             _this.xhrOnLoad(_this.xhr);
25726         }
25727         
25728         this.xhr.onerror = function()
25729         {
25730             _this.xhrOnError(_this.xhr);
25731         }
25732         
25733         var formData = new FormData();
25734
25735         formData.append('returnHTML', 'NO');
25736         
25737         if(crop){
25738             formData.append('crop', crop);
25739         }
25740         
25741         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
25742             formData.append(this.paramName, file, file.name);
25743         }
25744         
25745         if(typeof(file.filename) != 'undefined'){
25746             formData.append('filename', file.filename);
25747         }
25748         
25749         if(typeof(file.mimetype) != 'undefined'){
25750             formData.append('mimetype', file.mimetype);
25751         }
25752         
25753         if(this.fireEvent('arrange', this, formData) != false){
25754             this.xhr.send(formData);
25755         };
25756     },
25757     
25758     xhrOnLoad : function(xhr)
25759     {
25760         if(this.loadMask){
25761             this.maskEl.unmask();
25762         }
25763         
25764         if (xhr.readyState !== 4) {
25765             this.fireEvent('exception', this, xhr);
25766             return;
25767         }
25768
25769         var response = Roo.decode(xhr.responseText);
25770         
25771         if(!response.success){
25772             this.fireEvent('exception', this, xhr);
25773             return;
25774         }
25775         
25776         var response = Roo.decode(xhr.responseText);
25777         
25778         this.fireEvent('upload', this, response);
25779         
25780     },
25781     
25782     xhrOnError : function()
25783     {
25784         if(this.loadMask){
25785             this.maskEl.unmask();
25786         }
25787         
25788         Roo.log('xhr on error');
25789         
25790         var response = Roo.decode(xhr.responseText);
25791           
25792         Roo.log(response);
25793         
25794     },
25795     
25796     prepare : function(file)
25797     {   
25798         if(this.loadMask){
25799             this.maskEl.mask(this.loadingText);
25800         }
25801         
25802         this.file = false;
25803         this.exif = {};
25804         
25805         if(typeof(file) === 'string'){
25806             this.loadCanvas(file);
25807             return;
25808         }
25809         
25810         if(!file || !this.urlAPI){
25811             return;
25812         }
25813         
25814         this.file = file;
25815         this.cropType = file.type;
25816         
25817         var _this = this;
25818         
25819         if(this.fireEvent('prepare', this, this.file) != false){
25820             
25821             var reader = new FileReader();
25822             
25823             reader.onload = function (e) {
25824                 if (e.target.error) {
25825                     Roo.log(e.target.error);
25826                     return;
25827                 }
25828                 
25829                 var buffer = e.target.result,
25830                     dataView = new DataView(buffer),
25831                     offset = 2,
25832                     maxOffset = dataView.byteLength - 4,
25833                     markerBytes,
25834                     markerLength;
25835                 
25836                 if (dataView.getUint16(0) === 0xffd8) {
25837                     while (offset < maxOffset) {
25838                         markerBytes = dataView.getUint16(offset);
25839                         
25840                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
25841                             markerLength = dataView.getUint16(offset + 2) + 2;
25842                             if (offset + markerLength > dataView.byteLength) {
25843                                 Roo.log('Invalid meta data: Invalid segment size.');
25844                                 break;
25845                             }
25846                             
25847                             if(markerBytes == 0xffe1){
25848                                 _this.parseExifData(
25849                                     dataView,
25850                                     offset,
25851                                     markerLength
25852                                 );
25853                             }
25854                             
25855                             offset += markerLength;
25856                             
25857                             continue;
25858                         }
25859                         
25860                         break;
25861                     }
25862                     
25863                 }
25864                 
25865                 var url = _this.urlAPI.createObjectURL(_this.file);
25866                 
25867                 _this.loadCanvas(url);
25868                 
25869                 return;
25870             }
25871             
25872             reader.readAsArrayBuffer(this.file);
25873             
25874         }
25875         
25876     },
25877     
25878     parseExifData : function(dataView, offset, length)
25879     {
25880         var tiffOffset = offset + 10,
25881             littleEndian,
25882             dirOffset;
25883     
25884         if (dataView.getUint32(offset + 4) !== 0x45786966) {
25885             // No Exif data, might be XMP data instead
25886             return;
25887         }
25888         
25889         // Check for the ASCII code for "Exif" (0x45786966):
25890         if (dataView.getUint32(offset + 4) !== 0x45786966) {
25891             // No Exif data, might be XMP data instead
25892             return;
25893         }
25894         if (tiffOffset + 8 > dataView.byteLength) {
25895             Roo.log('Invalid Exif data: Invalid segment size.');
25896             return;
25897         }
25898         // Check for the two null bytes:
25899         if (dataView.getUint16(offset + 8) !== 0x0000) {
25900             Roo.log('Invalid Exif data: Missing byte alignment offset.');
25901             return;
25902         }
25903         // Check the byte alignment:
25904         switch (dataView.getUint16(tiffOffset)) {
25905         case 0x4949:
25906             littleEndian = true;
25907             break;
25908         case 0x4D4D:
25909             littleEndian = false;
25910             break;
25911         default:
25912             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
25913             return;
25914         }
25915         // Check for the TIFF tag marker (0x002A):
25916         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
25917             Roo.log('Invalid Exif data: Missing TIFF marker.');
25918             return;
25919         }
25920         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
25921         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
25922         
25923         this.parseExifTags(
25924             dataView,
25925             tiffOffset,
25926             tiffOffset + dirOffset,
25927             littleEndian
25928         );
25929     },
25930     
25931     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
25932     {
25933         var tagsNumber,
25934             dirEndOffset,
25935             i;
25936         if (dirOffset + 6 > dataView.byteLength) {
25937             Roo.log('Invalid Exif data: Invalid directory offset.');
25938             return;
25939         }
25940         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
25941         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
25942         if (dirEndOffset + 4 > dataView.byteLength) {
25943             Roo.log('Invalid Exif data: Invalid directory size.');
25944             return;
25945         }
25946         for (i = 0; i < tagsNumber; i += 1) {
25947             this.parseExifTag(
25948                 dataView,
25949                 tiffOffset,
25950                 dirOffset + 2 + 12 * i, // tag offset
25951                 littleEndian
25952             );
25953         }
25954         // Return the offset to the next directory:
25955         return dataView.getUint32(dirEndOffset, littleEndian);
25956     },
25957     
25958     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
25959     {
25960         var tag = dataView.getUint16(offset, littleEndian);
25961         
25962         this.exif[tag] = this.getExifValue(
25963             dataView,
25964             tiffOffset,
25965             offset,
25966             dataView.getUint16(offset + 2, littleEndian), // tag type
25967             dataView.getUint32(offset + 4, littleEndian), // tag length
25968             littleEndian
25969         );
25970     },
25971     
25972     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
25973     {
25974         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
25975             tagSize,
25976             dataOffset,
25977             values,
25978             i,
25979             str,
25980             c;
25981     
25982         if (!tagType) {
25983             Roo.log('Invalid Exif data: Invalid tag type.');
25984             return;
25985         }
25986         
25987         tagSize = tagType.size * length;
25988         // Determine if the value is contained in the dataOffset bytes,
25989         // or if the value at the dataOffset is a pointer to the actual data:
25990         dataOffset = tagSize > 4 ?
25991                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
25992         if (dataOffset + tagSize > dataView.byteLength) {
25993             Roo.log('Invalid Exif data: Invalid data offset.');
25994             return;
25995         }
25996         if (length === 1) {
25997             return tagType.getValue(dataView, dataOffset, littleEndian);
25998         }
25999         values = [];
26000         for (i = 0; i < length; i += 1) {
26001             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
26002         }
26003         
26004         if (tagType.ascii) {
26005             str = '';
26006             // Concatenate the chars:
26007             for (i = 0; i < values.length; i += 1) {
26008                 c = values[i];
26009                 // Ignore the terminating NULL byte(s):
26010                 if (c === '\u0000') {
26011                     break;
26012                 }
26013                 str += c;
26014             }
26015             return str;
26016         }
26017         return values;
26018     }
26019     
26020 });
26021
26022 Roo.apply(Roo.bootstrap.UploadCropbox, {
26023     tags : {
26024         'Orientation': 0x0112
26025     },
26026     
26027     Orientation: {
26028             1: 0, //'top-left',
26029 //            2: 'top-right',
26030             3: 180, //'bottom-right',
26031 //            4: 'bottom-left',
26032 //            5: 'left-top',
26033             6: 90, //'right-top',
26034 //            7: 'right-bottom',
26035             8: 270 //'left-bottom'
26036     },
26037     
26038     exifTagTypes : {
26039         // byte, 8-bit unsigned int:
26040         1: {
26041             getValue: function (dataView, dataOffset) {
26042                 return dataView.getUint8(dataOffset);
26043             },
26044             size: 1
26045         },
26046         // ascii, 8-bit byte:
26047         2: {
26048             getValue: function (dataView, dataOffset) {
26049                 return String.fromCharCode(dataView.getUint8(dataOffset));
26050             },
26051             size: 1,
26052             ascii: true
26053         },
26054         // short, 16 bit int:
26055         3: {
26056             getValue: function (dataView, dataOffset, littleEndian) {
26057                 return dataView.getUint16(dataOffset, littleEndian);
26058             },
26059             size: 2
26060         },
26061         // long, 32 bit int:
26062         4: {
26063             getValue: function (dataView, dataOffset, littleEndian) {
26064                 return dataView.getUint32(dataOffset, littleEndian);
26065             },
26066             size: 4
26067         },
26068         // rational = two long values, first is numerator, second is denominator:
26069         5: {
26070             getValue: function (dataView, dataOffset, littleEndian) {
26071                 return dataView.getUint32(dataOffset, littleEndian) /
26072                     dataView.getUint32(dataOffset + 4, littleEndian);
26073             },
26074             size: 8
26075         },
26076         // slong, 32 bit signed int:
26077         9: {
26078             getValue: function (dataView, dataOffset, littleEndian) {
26079                 return dataView.getInt32(dataOffset, littleEndian);
26080             },
26081             size: 4
26082         },
26083         // srational, two slongs, first is numerator, second is denominator:
26084         10: {
26085             getValue: function (dataView, dataOffset, littleEndian) {
26086                 return dataView.getInt32(dataOffset, littleEndian) /
26087                     dataView.getInt32(dataOffset + 4, littleEndian);
26088             },
26089             size: 8
26090         }
26091     },
26092     
26093     footer : {
26094         STANDARD : [
26095             {
26096                 tag : 'div',
26097                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26098                 action : 'rotate-left',
26099                 cn : [
26100                     {
26101                         tag : 'button',
26102                         cls : 'btn btn-default',
26103                         html : '<i class="fa fa-undo"></i>'
26104                     }
26105                 ]
26106             },
26107             {
26108                 tag : 'div',
26109                 cls : 'btn-group roo-upload-cropbox-picture',
26110                 action : 'picture',
26111                 cn : [
26112                     {
26113                         tag : 'button',
26114                         cls : 'btn btn-default',
26115                         html : '<i class="fa fa-picture-o"></i>'
26116                     }
26117                 ]
26118             },
26119             {
26120                 tag : 'div',
26121                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26122                 action : 'rotate-right',
26123                 cn : [
26124                     {
26125                         tag : 'button',
26126                         cls : 'btn btn-default',
26127                         html : '<i class="fa fa-repeat"></i>'
26128                     }
26129                 ]
26130             }
26131         ],
26132         DOCUMENT : [
26133             {
26134                 tag : 'div',
26135                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26136                 action : 'rotate-left',
26137                 cn : [
26138                     {
26139                         tag : 'button',
26140                         cls : 'btn btn-default',
26141                         html : '<i class="fa fa-undo"></i>'
26142                     }
26143                 ]
26144             },
26145             {
26146                 tag : 'div',
26147                 cls : 'btn-group roo-upload-cropbox-download',
26148                 action : 'download',
26149                 cn : [
26150                     {
26151                         tag : 'button',
26152                         cls : 'btn btn-default',
26153                         html : '<i class="fa fa-download"></i>'
26154                     }
26155                 ]
26156             },
26157             {
26158                 tag : 'div',
26159                 cls : 'btn-group roo-upload-cropbox-crop',
26160                 action : 'crop',
26161                 cn : [
26162                     {
26163                         tag : 'button',
26164                         cls : 'btn btn-default',
26165                         html : '<i class="fa fa-crop"></i>'
26166                     }
26167                 ]
26168             },
26169             {
26170                 tag : 'div',
26171                 cls : 'btn-group roo-upload-cropbox-trash',
26172                 action : 'trash',
26173                 cn : [
26174                     {
26175                         tag : 'button',
26176                         cls : 'btn btn-default',
26177                         html : '<i class="fa fa-trash"></i>'
26178                     }
26179                 ]
26180             },
26181             {
26182                 tag : 'div',
26183                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26184                 action : 'rotate-right',
26185                 cn : [
26186                     {
26187                         tag : 'button',
26188                         cls : 'btn btn-default',
26189                         html : '<i class="fa fa-repeat"></i>'
26190                     }
26191                 ]
26192             }
26193         ],
26194         ROTATOR : [
26195             {
26196                 tag : 'div',
26197                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26198                 action : 'rotate-left',
26199                 cn : [
26200                     {
26201                         tag : 'button',
26202                         cls : 'btn btn-default',
26203                         html : '<i class="fa fa-undo"></i>'
26204                     }
26205                 ]
26206             },
26207             {
26208                 tag : 'div',
26209                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26210                 action : 'rotate-right',
26211                 cn : [
26212                     {
26213                         tag : 'button',
26214                         cls : 'btn btn-default',
26215                         html : '<i class="fa fa-repeat"></i>'
26216                     }
26217                 ]
26218             }
26219         ]
26220     }
26221 });
26222
26223 /*
26224 * Licence: LGPL
26225 */
26226
26227 /**
26228  * @class Roo.bootstrap.DocumentManager
26229  * @extends Roo.bootstrap.Component
26230  * Bootstrap DocumentManager class
26231  * @cfg {String} paramName default 'imageUpload'
26232  * @cfg {String} method default POST
26233  * @cfg {String} url action url
26234  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
26235  * @cfg {Boolean} multiple multiple upload default true
26236  * @cfg {Number} thumbSize default 300
26237  * @cfg {String} fieldLabel
26238  * @cfg {Number} labelWidth default 4
26239  * @cfg {String} labelAlign (left|top) default left
26240  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
26241  * 
26242  * @constructor
26243  * Create a new DocumentManager
26244  * @param {Object} config The config object
26245  */
26246
26247 Roo.bootstrap.DocumentManager = function(config){
26248     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26249     
26250     this.addEvents({
26251         /**
26252          * @event initial
26253          * Fire when initial the DocumentManager
26254          * @param {Roo.bootstrap.DocumentManager} this
26255          */
26256         "initial" : true,
26257         /**
26258          * @event inspect
26259          * inspect selected file
26260          * @param {Roo.bootstrap.DocumentManager} this
26261          * @param {File} file
26262          */
26263         "inspect" : true,
26264         /**
26265          * @event exception
26266          * Fire when xhr load exception
26267          * @param {Roo.bootstrap.DocumentManager} this
26268          * @param {XMLHttpRequest} xhr
26269          */
26270         "exception" : true,
26271         /**
26272          * @event prepare
26273          * prepare the form data
26274          * @param {Roo.bootstrap.DocumentManager} this
26275          * @param {Object} formData
26276          */
26277         "prepare" : true,
26278         /**
26279          * @event remove
26280          * Fire when remove the file
26281          * @param {Roo.bootstrap.DocumentManager} this
26282          * @param {Object} file
26283          */
26284         "remove" : true,
26285         /**
26286          * @event refresh
26287          * Fire after refresh the file
26288          * @param {Roo.bootstrap.DocumentManager} this
26289          */
26290         "refresh" : true,
26291         /**
26292          * @event click
26293          * Fire after click the image
26294          * @param {Roo.bootstrap.DocumentManager} this
26295          * @param {Object} file
26296          */
26297         "click" : true,
26298         /**
26299          * @event edit
26300          * Fire when upload a image and editable set to true
26301          * @param {Roo.bootstrap.DocumentManager} this
26302          * @param {Object} file
26303          */
26304         "edit" : true,
26305         /**
26306          * @event beforeselectfile
26307          * Fire before select file
26308          * @param {Roo.bootstrap.DocumentManager} this
26309          */
26310         "beforeselectfile" : true,
26311         /**
26312          * @event process
26313          * Fire before process file
26314          * @param {Roo.bootstrap.DocumentManager} this
26315          * @param {Object} file
26316          */
26317         "process" : true
26318         
26319     });
26320 };
26321
26322 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
26323     
26324     boxes : 0,
26325     inputName : '',
26326     thumbSize : 300,
26327     multiple : true,
26328     files : [],
26329     method : 'POST',
26330     url : '',
26331     paramName : 'imageUpload',
26332     fieldLabel : '',
26333     labelWidth : 4,
26334     labelAlign : 'left',
26335     editable : true,
26336     delegates : [],
26337     
26338     
26339     xhr : false, 
26340     
26341     getAutoCreate : function()
26342     {   
26343         var managerWidget = {
26344             tag : 'div',
26345             cls : 'roo-document-manager',
26346             cn : [
26347                 {
26348                     tag : 'input',
26349                     cls : 'roo-document-manager-selector',
26350                     type : 'file'
26351                 },
26352                 {
26353                     tag : 'div',
26354                     cls : 'roo-document-manager-uploader',
26355                     cn : [
26356                         {
26357                             tag : 'div',
26358                             cls : 'roo-document-manager-upload-btn',
26359                             html : '<i class="fa fa-plus"></i>'
26360                         }
26361                     ]
26362                     
26363                 }
26364             ]
26365         };
26366         
26367         var content = [
26368             {
26369                 tag : 'div',
26370                 cls : 'column col-md-12',
26371                 cn : managerWidget
26372             }
26373         ];
26374         
26375         if(this.fieldLabel.length){
26376             
26377             content = [
26378                 {
26379                     tag : 'div',
26380                     cls : 'column col-md-12',
26381                     html : this.fieldLabel
26382                 },
26383                 {
26384                     tag : 'div',
26385                     cls : 'column col-md-12',
26386                     cn : managerWidget
26387                 }
26388             ];
26389
26390             if(this.labelAlign == 'left'){
26391                 content = [
26392                     {
26393                         tag : 'div',
26394                         cls : 'column col-md-' + this.labelWidth,
26395                         html : this.fieldLabel
26396                     },
26397                     {
26398                         tag : 'div',
26399                         cls : 'column col-md-' + (12 - this.labelWidth),
26400                         cn : managerWidget
26401                     }
26402                 ];
26403                 
26404             }
26405         }
26406         
26407         var cfg = {
26408             tag : 'div',
26409             cls : 'row clearfix',
26410             cn : content
26411         };
26412         
26413         return cfg;
26414         
26415     },
26416     
26417     initEvents : function()
26418     {
26419         this.managerEl = this.el.select('.roo-document-manager', true).first();
26420         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26421         
26422         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
26423         this.selectorEl.hide();
26424         
26425         if(this.multiple){
26426             this.selectorEl.attr('multiple', 'multiple');
26427         }
26428         
26429         this.selectorEl.on('change', this.onFileSelected, this);
26430         
26431         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
26432         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26433         
26434         this.uploader.on('click', this.onUploaderClick, this);
26435         
26436         this.renderProgressDialog();
26437         
26438         var _this = this;
26439         
26440         window.addEventListener("resize", function() { _this.refresh(); } );
26441         
26442         this.fireEvent('initial', this);
26443     },
26444     
26445     renderProgressDialog : function()
26446     {
26447         var _this = this;
26448         
26449         this.progressDialog = new Roo.bootstrap.Modal({
26450             cls : 'roo-document-manager-progress-dialog',
26451             allow_close : false,
26452             title : '',
26453             buttons : [
26454                 {
26455                     name  :'cancel',
26456                     weight : 'danger',
26457                     html : 'Cancel'
26458                 }
26459             ], 
26460             listeners : { 
26461                 btnclick : function() {
26462                     _this.uploadCancel();
26463                     this.hide();
26464                 }
26465             }
26466         });
26467          
26468         this.progressDialog.render(Roo.get(document.body));
26469          
26470         this.progress = new Roo.bootstrap.Progress({
26471             cls : 'roo-document-manager-progress',
26472             active : true,
26473             striped : true
26474         });
26475         
26476         this.progress.render(this.progressDialog.getChildContainer());
26477         
26478         this.progressBar = new Roo.bootstrap.ProgressBar({
26479             cls : 'roo-document-manager-progress-bar',
26480             aria_valuenow : 0,
26481             aria_valuemin : 0,
26482             aria_valuemax : 12,
26483             panel : 'success'
26484         });
26485         
26486         this.progressBar.render(this.progress.getChildContainer());
26487     },
26488     
26489     onUploaderClick : function(e)
26490     {
26491         e.preventDefault();
26492      
26493         if(this.fireEvent('beforeselectfile', this) != false){
26494             this.selectorEl.dom.click();
26495         }
26496         
26497     },
26498     
26499     onFileSelected : function(e)
26500     {
26501         e.preventDefault();
26502         
26503         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26504             return;
26505         }
26506         
26507         Roo.each(this.selectorEl.dom.files, function(file){
26508             if(this.fireEvent('inspect', this, file) != false){
26509                 this.files.push(file);
26510             }
26511         }, this);
26512         
26513         this.queue();
26514         
26515     },
26516     
26517     queue : function()
26518     {
26519         this.selectorEl.dom.value = '';
26520         
26521         if(!this.files.length){
26522             return;
26523         }
26524         
26525         if(this.boxes > 0 && this.files.length > this.boxes){
26526             this.files = this.files.slice(0, this.boxes);
26527         }
26528         
26529         this.uploader.show();
26530         
26531         if(this.boxes > 0 && this.files.length > this.boxes - 1){
26532             this.uploader.hide();
26533         }
26534         
26535         var _this = this;
26536         
26537         var files = [];
26538         
26539         var docs = [];
26540         
26541         Roo.each(this.files, function(file){
26542             
26543             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26544                 var f = this.renderPreview(file);
26545                 files.push(f);
26546                 return;
26547             }
26548             
26549             if(file.type.indexOf('image') != -1){
26550                 this.delegates.push(
26551                     (function(){
26552                         _this.process(file);
26553                     }).createDelegate(this)
26554                 );
26555         
26556                 return;
26557             }
26558             
26559             docs.push(
26560                 (function(){
26561                     _this.process(file);
26562                 }).createDelegate(this)
26563             );
26564             
26565         }, this);
26566         
26567         this.files = files;
26568         
26569         this.delegates = this.delegates.concat(docs);
26570         
26571         if(!this.delegates.length){
26572             this.refresh();
26573             return;
26574         }
26575         
26576         this.progressBar.aria_valuemax = this.delegates.length;
26577         
26578         this.arrange();
26579         
26580         return;
26581     },
26582     
26583     arrange : function()
26584     {
26585         if(!this.delegates.length){
26586             this.progressDialog.hide();
26587             this.refresh();
26588             return;
26589         }
26590         
26591         var delegate = this.delegates.shift();
26592         
26593         this.progressDialog.show();
26594         
26595         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
26596         
26597         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
26598         
26599         delegate();
26600     },
26601     
26602     refresh : function()
26603     {
26604         this.uploader.show();
26605         
26606         if(this.boxes > 0 && this.files.length > this.boxes - 1){
26607             this.uploader.hide();
26608         }
26609         
26610         Roo.isTouch ? this.closable(false) : this.closable(true);
26611         
26612         this.fireEvent('refresh', this);
26613     },
26614     
26615     onRemove : function(e, el, o)
26616     {
26617         e.preventDefault();
26618         
26619         this.fireEvent('remove', this, o);
26620         
26621     },
26622     
26623     remove : function(o)
26624     {
26625         var files = [];
26626         
26627         Roo.each(this.files, function(file){
26628             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
26629                 files.push(file);
26630                 return;
26631             }
26632
26633             o.target.remove();
26634
26635         }, this);
26636         
26637         this.files = files;
26638         
26639         this.refresh();
26640     },
26641     
26642     clear : function()
26643     {
26644         Roo.each(this.files, function(file){
26645             if(!file.target){
26646                 return;
26647             }
26648             
26649             file.target.remove();
26650
26651         }, this);
26652         
26653         this.files = [];
26654         
26655         this.refresh();
26656     },
26657     
26658     onClick : function(e, el, o)
26659     {
26660         e.preventDefault();
26661         
26662         this.fireEvent('click', this, o);
26663         
26664     },
26665     
26666     closable : function(closable)
26667     {
26668         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
26669             
26670             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26671             
26672             if(closable){
26673                 el.show();
26674                 return;
26675             }
26676             
26677             el.hide();
26678             
26679         }, this);
26680     },
26681     
26682     xhrOnLoad : function(xhr)
26683     {
26684         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26685             el.remove();
26686         }, this);
26687         
26688         if (xhr.readyState !== 4) {
26689             this.arrange();
26690             this.fireEvent('exception', this, xhr);
26691             return;
26692         }
26693
26694         var response = Roo.decode(xhr.responseText);
26695         
26696         if(!response.success){
26697             this.arrange();
26698             this.fireEvent('exception', this, xhr);
26699             return;
26700         }
26701         
26702         var file = this.renderPreview(response.data);
26703         
26704         this.files.push(file);
26705         
26706         this.arrange();
26707         
26708     },
26709     
26710     xhrOnError : function()
26711     {
26712         Roo.log('xhr on error');
26713         
26714         var response = Roo.decode(xhr.responseText);
26715           
26716         Roo.log(response);
26717         
26718         this.arrange();
26719     },
26720     
26721     process : function(file)
26722     {
26723         if(this.fireEvent('process', this, file) !== false){
26724             if(this.editable && file.type.indexOf('image') != -1){
26725                 this.fireEvent('edit', this, file);
26726                 return;
26727             }
26728
26729             this.uploadStart(file, false);
26730
26731             return;
26732         }
26733         
26734     },
26735     
26736     uploadStart : function(file, crop)
26737     {
26738         this.xhr = new XMLHttpRequest();
26739         
26740         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26741             this.arrange();
26742             return;
26743         }
26744         
26745         file.xhr = this.xhr;
26746             
26747         this.managerEl.createChild({
26748             tag : 'div',
26749             cls : 'roo-document-manager-loading',
26750             cn : [
26751                 {
26752                     tag : 'div',
26753                     tooltip : file.name,
26754                     cls : 'roo-document-manager-thumb',
26755                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26756                 }
26757             ]
26758
26759         });
26760
26761         this.xhr.open(this.method, this.url, true);
26762         
26763         var headers = {
26764             "Accept": "application/json",
26765             "Cache-Control": "no-cache",
26766             "X-Requested-With": "XMLHttpRequest"
26767         };
26768         
26769         for (var headerName in headers) {
26770             var headerValue = headers[headerName];
26771             if (headerValue) {
26772                 this.xhr.setRequestHeader(headerName, headerValue);
26773             }
26774         }
26775         
26776         var _this = this;
26777         
26778         this.xhr.onload = function()
26779         {
26780             _this.xhrOnLoad(_this.xhr);
26781         }
26782         
26783         this.xhr.onerror = function()
26784         {
26785             _this.xhrOnError(_this.xhr);
26786         }
26787         
26788         var formData = new FormData();
26789
26790         formData.append('returnHTML', 'NO');
26791         
26792         if(crop){
26793             formData.append('crop', crop);
26794         }
26795         
26796         formData.append(this.paramName, file, file.name);
26797         
26798         if(this.fireEvent('prepare', this, formData) != false){
26799             this.xhr.send(formData);
26800         };
26801     },
26802     
26803     uploadCancel : function()
26804     {
26805         if (this.xhr) {
26806             this.xhr.abort();
26807         }
26808         
26809         
26810         this.delegates = [];
26811         
26812         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26813             el.remove();
26814         }, this);
26815         
26816         this.arrange();
26817     },
26818     
26819     renderPreview : function(file)
26820     {
26821         if(typeof(file.target) != 'undefined' && file.target){
26822             return file;
26823         }
26824         
26825         var previewEl = this.managerEl.createChild({
26826             tag : 'div',
26827             cls : 'roo-document-manager-preview',
26828             cn : [
26829                 {
26830                     tag : 'div',
26831                     tooltip : file.filename,
26832                     cls : 'roo-document-manager-thumb',
26833                     html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
26834                 },
26835                 {
26836                     tag : 'button',
26837                     cls : 'close',
26838                     html : '<i class="fa fa-times-circle"></i>'
26839                 }
26840             ]
26841         });
26842
26843         var close = previewEl.select('button.close', true).first();
26844
26845         close.on('click', this.onRemove, this, file);
26846
26847         file.target = previewEl;
26848
26849         var image = previewEl.select('img', true).first();
26850         
26851         var _this = this;
26852         
26853         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
26854         
26855         image.on('click', this.onClick, this, file);
26856         
26857         return file;
26858         
26859     },
26860     
26861     onPreviewLoad : function(file, image)
26862     {
26863         if(typeof(file.target) == 'undefined' || !file.target){
26864             return;
26865         }
26866         
26867         var width = image.dom.naturalWidth || image.dom.width;
26868         var height = image.dom.naturalHeight || image.dom.height;
26869         
26870         if(width > height){
26871             file.target.addClass('wide');
26872             return;
26873         }
26874         
26875         file.target.addClass('tall');
26876         return;
26877         
26878     },
26879     
26880     uploadFromSource : function(file, crop)
26881     {
26882         this.xhr = new XMLHttpRequest();
26883         
26884         this.managerEl.createChild({
26885             tag : 'div',
26886             cls : 'roo-document-manager-loading',
26887             cn : [
26888                 {
26889                     tag : 'div',
26890                     tooltip : file.name,
26891                     cls : 'roo-document-manager-thumb',
26892                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26893                 }
26894             ]
26895
26896         });
26897
26898         this.xhr.open(this.method, this.url, true);
26899         
26900         var headers = {
26901             "Accept": "application/json",
26902             "Cache-Control": "no-cache",
26903             "X-Requested-With": "XMLHttpRequest"
26904         };
26905         
26906         for (var headerName in headers) {
26907             var headerValue = headers[headerName];
26908             if (headerValue) {
26909                 this.xhr.setRequestHeader(headerName, headerValue);
26910             }
26911         }
26912         
26913         var _this = this;
26914         
26915         this.xhr.onload = function()
26916         {
26917             _this.xhrOnLoad(_this.xhr);
26918         }
26919         
26920         this.xhr.onerror = function()
26921         {
26922             _this.xhrOnError(_this.xhr);
26923         }
26924         
26925         var formData = new FormData();
26926
26927         formData.append('returnHTML', 'NO');
26928         
26929         formData.append('crop', crop);
26930         
26931         if(typeof(file.filename) != 'undefined'){
26932             formData.append('filename', file.filename);
26933         }
26934         
26935         if(typeof(file.mimetype) != 'undefined'){
26936             formData.append('mimetype', file.mimetype);
26937         }
26938         
26939         if(this.fireEvent('prepare', this, formData) != false){
26940             this.xhr.send(formData);
26941         };
26942     }
26943 });
26944
26945 /*
26946 * Licence: LGPL
26947 */
26948
26949 /**
26950  * @class Roo.bootstrap.DocumentViewer
26951  * @extends Roo.bootstrap.Component
26952  * Bootstrap DocumentViewer class
26953  * 
26954  * @constructor
26955  * Create a new DocumentViewer
26956  * @param {Object} config The config object
26957  */
26958
26959 Roo.bootstrap.DocumentViewer = function(config){
26960     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
26961     
26962     this.addEvents({
26963         /**
26964          * @event initial
26965          * Fire after initEvent
26966          * @param {Roo.bootstrap.DocumentViewer} this
26967          */
26968         "initial" : true,
26969         /**
26970          * @event click
26971          * Fire after click
26972          * @param {Roo.bootstrap.DocumentViewer} this
26973          */
26974         "click" : true,
26975         /**
26976          * @event trash
26977          * Fire after trash button
26978          * @param {Roo.bootstrap.DocumentViewer} this
26979          */
26980         "trash" : true
26981         
26982     });
26983 };
26984
26985 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
26986     
26987     getAutoCreate : function()
26988     {
26989         var cfg = {
26990             tag : 'div',
26991             cls : 'roo-document-viewer',
26992             cn : [
26993                 {
26994                     tag : 'div',
26995                     cls : 'roo-document-viewer-body',
26996                     cn : [
26997                         {
26998                             tag : 'div',
26999                             cls : 'roo-document-viewer-thumb',
27000                             cn : [
27001                                 {
27002                                     tag : 'img',
27003                                     cls : 'roo-document-viewer-image'
27004                                 }
27005                             ]
27006                         }
27007                     ]
27008                 },
27009                 {
27010                     tag : 'div',
27011                     cls : 'roo-document-viewer-footer',
27012                     cn : {
27013                         tag : 'div',
27014                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
27015                         cn : [
27016                             {
27017                                 tag : 'div',
27018                                 cls : 'btn-group',
27019                                 cn : [
27020                                     {
27021                                         tag : 'button',
27022                                         cls : 'btn btn-default roo-document-viewer-trash',
27023                                         html : '<i class="fa fa-trash"></i>'
27024                                     }
27025                                 ]
27026                             }
27027                         ]
27028                     }
27029                 }
27030             ]
27031         };
27032         
27033         return cfg;
27034     },
27035     
27036     initEvents : function()
27037     {
27038         
27039         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27040         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27041         
27042         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27043         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27044         
27045         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27046         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27047         
27048         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27049         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27050         
27051         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27052         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27053         
27054         this.bodyEl.on('click', this.onClick, this);
27055         
27056         this.trashBtn.on('click', this.onTrash, this);
27057         
27058     },
27059     
27060     initial : function()
27061     {
27062 //        this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27063         
27064         
27065         this.fireEvent('initial', this);
27066         
27067     },
27068     
27069     onClick : function(e)
27070     {
27071         e.preventDefault();
27072         
27073         this.fireEvent('click', this);
27074     },
27075     
27076     onTrash : function(e)
27077     {
27078         e.preventDefault();
27079         
27080         this.fireEvent('trash', this);
27081     }
27082     
27083 });
27084 /*
27085  * - LGPL
27086  *
27087  * nav progress bar
27088  * 
27089  */
27090
27091 /**
27092  * @class Roo.bootstrap.NavProgressBar
27093  * @extends Roo.bootstrap.Component
27094  * Bootstrap NavProgressBar class
27095  * 
27096  * @constructor
27097  * Create a new nav progress bar
27098  * @param {Object} config The config object
27099  */
27100
27101 Roo.bootstrap.NavProgressBar = function(config){
27102     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
27103
27104     this.bullets = this.bullets || [];
27105    
27106 //    Roo.bootstrap.NavProgressBar.register(this);
27107      this.addEvents({
27108         /**
27109              * @event changed
27110              * Fires when the active item changes
27111              * @param {Roo.bootstrap.NavProgressBar} this
27112              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
27113              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
27114          */
27115         'changed': true
27116      });
27117     
27118 };
27119
27120 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
27121     
27122     bullets : [],
27123     barItems : [],
27124     
27125     getAutoCreate : function()
27126     {
27127         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
27128         
27129         cfg = {
27130             tag : 'div',
27131             cls : 'roo-navigation-bar-group',
27132             cn : [
27133                 {
27134                     tag : 'div',
27135                     cls : 'roo-navigation-top-bar'
27136                 },
27137                 {
27138                     tag : 'div',
27139                     cls : 'roo-navigation-bullets-bar',
27140                     cn : [
27141                         {
27142                             tag : 'ul',
27143                             cls : 'roo-navigation-bar'
27144                         }
27145                     ]
27146                 },
27147                 
27148                 {
27149                     tag : 'div',
27150                     cls : 'roo-navigation-bottom-bar'
27151                 }
27152             ]
27153             
27154         };
27155         
27156         return cfg;
27157         
27158     },
27159     
27160     initEvents: function() 
27161     {
27162         
27163     },
27164     
27165     onRender : function(ct, position) 
27166     {
27167         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27168         
27169         if(this.bullets.length){
27170             Roo.each(this.bullets, function(b){
27171                this.addItem(b);
27172             }, this);
27173         }
27174         
27175         this.format();
27176         
27177     },
27178     
27179     addItem : function(cfg)
27180     {
27181         var item = new Roo.bootstrap.NavProgressItem(cfg);
27182         
27183         item.parentId = this.id;
27184         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
27185         
27186         if(cfg.html){
27187             var top = new Roo.bootstrap.Element({
27188                 tag : 'div',
27189                 cls : 'roo-navigation-bar-text'
27190             });
27191             
27192             var bottom = new Roo.bootstrap.Element({
27193                 tag : 'div',
27194                 cls : 'roo-navigation-bar-text'
27195             });
27196             
27197             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
27198             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
27199             
27200             var topText = new Roo.bootstrap.Element({
27201                 tag : 'span',
27202                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
27203             });
27204             
27205             var bottomText = new Roo.bootstrap.Element({
27206                 tag : 'span',
27207                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
27208             });
27209             
27210             topText.onRender(top.el, null);
27211             bottomText.onRender(bottom.el, null);
27212             
27213             item.topEl = top;
27214             item.bottomEl = bottom;
27215         }
27216         
27217         this.barItems.push(item);
27218         
27219         return item;
27220     },
27221     
27222     getActive : function()
27223     {
27224         var active = false;
27225         
27226         Roo.each(this.barItems, function(v){
27227             
27228             if (!v.isActive()) {
27229                 return;
27230             }
27231             
27232             active = v;
27233             return false;
27234             
27235         });
27236         
27237         return active;
27238     },
27239     
27240     setActiveItem : function(item)
27241     {
27242         var prev = false;
27243         
27244         Roo.each(this.barItems, function(v){
27245             if (v.rid == item.rid) {
27246                 return ;
27247             }
27248             
27249             if (v.isActive()) {
27250                 v.setActive(false);
27251                 prev = v;
27252             }
27253         });
27254
27255         item.setActive(true);
27256         
27257         this.fireEvent('changed', this, item, prev);
27258     },
27259     
27260     getBarItem: function(rid)
27261     {
27262         var ret = false;
27263         
27264         Roo.each(this.barItems, function(e) {
27265             if (e.rid != rid) {
27266                 return;
27267             }
27268             
27269             ret =  e;
27270             return false;
27271         });
27272         
27273         return ret;
27274     },
27275     
27276     indexOfItem : function(item)
27277     {
27278         var index = false;
27279         
27280         Roo.each(this.barItems, function(v, i){
27281             
27282             if (v.rid != item.rid) {
27283                 return;
27284             }
27285             
27286             index = i;
27287             return false
27288         });
27289         
27290         return index;
27291     },
27292     
27293     setActiveNext : function()
27294     {
27295         var i = this.indexOfItem(this.getActive());
27296         
27297         if (i > this.barItems.length) {
27298             return;
27299         }
27300         
27301         this.setActiveItem(this.barItems[i+1]);
27302     },
27303     
27304     setActivePrev : function()
27305     {
27306         var i = this.indexOfItem(this.getActive());
27307         
27308         if (i  < 1) {
27309             return;
27310         }
27311         
27312         this.setActiveItem(this.barItems[i-1]);
27313     },
27314     
27315     format : function()
27316     {
27317         if(!this.barItems.length){
27318             return;
27319         }
27320      
27321         var width = 100 / this.barItems.length;
27322         
27323         Roo.each(this.barItems, function(i){
27324             i.el.setStyle('width', width + '%');
27325             i.topEl.el.setStyle('width', width + '%');
27326             i.bottomEl.el.setStyle('width', width + '%');
27327         }, this);
27328         
27329     }
27330     
27331 });
27332 /*
27333  * - LGPL
27334  *
27335  * Nav Progress Item
27336  * 
27337  */
27338
27339 /**
27340  * @class Roo.bootstrap.NavProgressItem
27341  * @extends Roo.bootstrap.Component
27342  * Bootstrap NavProgressItem class
27343  * @cfg {String} rid the reference id
27344  * @cfg {Boolean} active (true|false) Is item active default false
27345  * @cfg {Boolean} disabled (true|false) Is item active default false
27346  * @cfg {String} html
27347  * @cfg {String} position (top|bottom) text position default bottom
27348  * @cfg {String} icon show icon instead of number
27349  * 
27350  * @constructor
27351  * Create a new NavProgressItem
27352  * @param {Object} config The config object
27353  */
27354 Roo.bootstrap.NavProgressItem = function(config){
27355     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
27356     this.addEvents({
27357         // raw events
27358         /**
27359          * @event click
27360          * The raw click event for the entire grid.
27361          * @param {Roo.bootstrap.NavProgressItem} this
27362          * @param {Roo.EventObject} e
27363          */
27364         "click" : true
27365     });
27366    
27367 };
27368
27369 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
27370     
27371     rid : '',
27372     active : false,
27373     disabled : false,
27374     html : '',
27375     position : 'bottom',
27376     icon : false,
27377     
27378     getAutoCreate : function()
27379     {
27380         var iconCls = 'roo-navigation-bar-item-icon';
27381         
27382         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
27383         
27384         var cfg = {
27385             tag: 'li',
27386             cls: 'roo-navigation-bar-item',
27387             cn : [
27388                 {
27389                     tag : 'i',
27390                     cls : iconCls
27391                 }
27392             ]
27393         };
27394         
27395         if(this.active){
27396             cfg.cls += ' active';
27397         }
27398         if(this.disabled){
27399             cfg.cls += ' disabled';
27400         }
27401         
27402         return cfg;
27403     },
27404     
27405     disable : function()
27406     {
27407         this.setDisabled(true);
27408     },
27409     
27410     enable : function()
27411     {
27412         this.setDisabled(false);
27413     },
27414     
27415     initEvents: function() 
27416     {
27417         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
27418         
27419         this.iconEl.on('click', this.onClick, this);
27420     },
27421     
27422     onClick : function(e)
27423     {
27424         e.preventDefault();
27425         
27426         if(this.disabled){
27427             return;
27428         }
27429         
27430         if(this.fireEvent('click', this, e) === false){
27431             return;
27432         };
27433         
27434         this.parent().setActiveItem(this);
27435     },
27436     
27437     isActive: function () 
27438     {
27439         return this.active;
27440     },
27441     
27442     setActive : function(state)
27443     {
27444         if(this.active == state){
27445             return;
27446         }
27447         
27448         this.active = state;
27449         
27450         if (state) {
27451             this.el.addClass('active');
27452             return;
27453         }
27454         
27455         this.el.removeClass('active');
27456         
27457         return;
27458     },
27459     
27460     setDisabled : function(state)
27461     {
27462         if(this.disabled == state){
27463             return;
27464         }
27465         
27466         this.disabled = state;
27467         
27468         if (state) {
27469             this.el.addClass('disabled');
27470             return;
27471         }
27472         
27473         this.el.removeClass('disabled');
27474     },
27475     
27476     tooltipEl : function()
27477     {
27478         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
27479     }
27480 });
27481  
27482
27483  /*
27484  * - LGPL
27485  *
27486  * FieldLabel
27487  * 
27488  */
27489
27490 /**
27491  * @class Roo.bootstrap.FieldLabel
27492  * @extends Roo.bootstrap.Component
27493  * Bootstrap FieldLabel class
27494  * @cfg {String} html contents of the element
27495  * @cfg {String} tag tag of the element default label
27496  * @cfg {String} cls class of the element
27497  * @cfg {String} target label target 
27498  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
27499  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
27500  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
27501  * @cfg {String} iconTooltip default "This field is required"
27502  * 
27503  * @constructor
27504  * Create a new FieldLabel
27505  * @param {Object} config The config object
27506  */
27507
27508 Roo.bootstrap.FieldLabel = function(config){
27509     Roo.bootstrap.Element.superclass.constructor.call(this, config);
27510     
27511     this.addEvents({
27512             /**
27513              * @event invalid
27514              * Fires after the field has been marked as invalid.
27515              * @param {Roo.form.FieldLabel} this
27516              * @param {String} msg The validation message
27517              */
27518             invalid : true,
27519             /**
27520              * @event valid
27521              * Fires after the field has been validated with no errors.
27522              * @param {Roo.form.FieldLabel} this
27523              */
27524             valid : true
27525         });
27526 };
27527
27528 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
27529     
27530     tag: 'label',
27531     cls: '',
27532     html: '',
27533     target: '',
27534     allowBlank : true,
27535     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
27536     validClass : 'text-success fa fa-lg fa-check',
27537     iconTooltip : 'This field is required',
27538     
27539     getAutoCreate : function(){
27540         
27541         var cfg = {
27542             tag : this.tag,
27543             cls : 'roo-bootstrap-field-label ' + this.cls,
27544             for : this.target,
27545             cn : [
27546                 {
27547                     tag : 'i',
27548                     cls : '',
27549                     tooltip : this.iconTooltip
27550                 },
27551                 {
27552                     tag : 'span',
27553                     html : this.html
27554                 }
27555             ] 
27556         };
27557         
27558         return cfg;
27559     },
27560     
27561     initEvents: function() 
27562     {
27563         Roo.bootstrap.Element.superclass.initEvents.call(this);
27564         
27565         this.iconEl = this.el.select('i', true).first();
27566         
27567         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
27568         
27569         Roo.bootstrap.FieldLabel.register(this);
27570     },
27571     
27572     /**
27573      * Mark this field as valid
27574      */
27575     markValid : function()
27576     {
27577         this.iconEl.show();
27578         
27579         this.iconEl.removeClass(this.invalidClass);
27580         
27581         this.iconEl.addClass(this.validClass);
27582         
27583         this.fireEvent('valid', this);
27584     },
27585     
27586     /**
27587      * Mark this field as invalid
27588      * @param {String} msg The validation message
27589      */
27590     markInvalid : function(msg)
27591     {
27592         this.iconEl.show();
27593         
27594         this.iconEl.removeClass(this.validClass);
27595         
27596         this.iconEl.addClass(this.invalidClass);
27597         
27598         this.fireEvent('invalid', this, msg);
27599     }
27600     
27601    
27602 });
27603
27604 Roo.apply(Roo.bootstrap.FieldLabel, {
27605     
27606     groups: {},
27607     
27608      /**
27609     * register a FieldLabel Group
27610     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
27611     */
27612     register : function(label)
27613     {
27614         if(this.groups.hasOwnProperty(label.target)){
27615             return;
27616         }
27617      
27618         this.groups[label.target] = label;
27619         
27620     },
27621     /**
27622     * fetch a FieldLabel Group based on the target
27623     * @param {string} target
27624     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
27625     */
27626     get: function(target) {
27627         if (typeof(this.groups[target]) == 'undefined') {
27628             return false;
27629         }
27630         
27631         return this.groups[target] ;
27632     }
27633 });
27634
27635  
27636
27637  /*
27638  * - LGPL
27639  *
27640  * page DateSplitField.
27641  * 
27642  */
27643
27644
27645 /**
27646  * @class Roo.bootstrap.DateSplitField
27647  * @extends Roo.bootstrap.Component
27648  * Bootstrap DateSplitField class
27649  * @cfg {string} fieldLabel - the label associated
27650  * @cfg {Number} labelWidth set the width of label (0-12)
27651  * @cfg {String} labelAlign (top|left)
27652  * @cfg {Boolean} dayAllowBlank (true|false) default false
27653  * @cfg {Boolean} monthAllowBlank (true|false) default false
27654  * @cfg {Boolean} yearAllowBlank (true|false) default false
27655  * @cfg {string} dayPlaceholder 
27656  * @cfg {string} monthPlaceholder
27657  * @cfg {string} yearPlaceholder
27658  * @cfg {string} dayFormat default 'd'
27659  * @cfg {string} monthFormat default 'm'
27660  * @cfg {string} yearFormat default 'Y'
27661
27662  *     
27663  * @constructor
27664  * Create a new DateSplitField
27665  * @param {Object} config The config object
27666  */
27667
27668 Roo.bootstrap.DateSplitField = function(config){
27669     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
27670     
27671     this.addEvents({
27672         // raw events
27673          /**
27674          * @event years
27675          * getting the data of years
27676          * @param {Roo.bootstrap.DateSplitField} this
27677          * @param {Object} years
27678          */
27679         "years" : true,
27680         /**
27681          * @event days
27682          * getting the data of days
27683          * @param {Roo.bootstrap.DateSplitField} this
27684          * @param {Object} days
27685          */
27686         "days" : true,
27687         /**
27688          * @event invalid
27689          * Fires after the field has been marked as invalid.
27690          * @param {Roo.form.Field} this
27691          * @param {String} msg The validation message
27692          */
27693         invalid : true,
27694        /**
27695          * @event valid
27696          * Fires after the field has been validated with no errors.
27697          * @param {Roo.form.Field} this
27698          */
27699         valid : true
27700     });
27701 };
27702
27703 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
27704     
27705     fieldLabel : '',
27706     labelAlign : 'top',
27707     labelWidth : 3,
27708     dayAllowBlank : false,
27709     monthAllowBlank : false,
27710     yearAllowBlank : false,
27711     dayPlaceholder : '',
27712     monthPlaceholder : '',
27713     yearPlaceholder : '',
27714     dayFormat : 'd',
27715     monthFormat : 'm',
27716     yearFormat : 'Y',
27717     isFormField : true,
27718     
27719     getAutoCreate : function()
27720     {
27721         var cfg = {
27722             tag : 'div',
27723             cls : 'row roo-date-split-field-group',
27724             cn : [
27725                 {
27726                     tag : 'input',
27727                     type : 'hidden',
27728                     cls : 'form-hidden-field roo-date-split-field-group-value',
27729                     name : this.name
27730                 }
27731             ]
27732         };
27733         
27734         if(this.fieldLabel){
27735             cfg.cn.push({
27736                 tag : 'div',
27737                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
27738                 cn : [
27739                     {
27740                         tag : 'label',
27741                         html : this.fieldLabel
27742                     }
27743                 ]
27744             });
27745         }
27746         
27747         Roo.each(['day', 'month', 'year'], function(t){
27748             cfg.cn.push({
27749                 tag : 'div',
27750                 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
27751             });
27752         }, this);
27753         
27754         return cfg;
27755     },
27756     
27757     inputEl: function ()
27758     {
27759         return this.el.select('.roo-date-split-field-group-value', true).first();
27760     },
27761     
27762     onRender : function(ct, position) 
27763     {
27764         var _this = this;
27765         
27766         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27767         
27768         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
27769         
27770         this.dayField = new Roo.bootstrap.ComboBox({
27771             allowBlank : this.dayAllowBlank,
27772             alwaysQuery : true,
27773             displayField : 'value',
27774             editable : false,
27775             fieldLabel : '',
27776             forceSelection : true,
27777             mode : 'local',
27778             placeholder : this.dayPlaceholder,
27779             selectOnFocus : true,
27780             tpl : '<div class="select2-result"><b>{value}</b></div>',
27781             triggerAction : 'all',
27782             typeAhead : true,
27783             valueField : 'value',
27784             store : new Roo.data.SimpleStore({
27785                 data : (function() {    
27786                     var days = [];
27787                     _this.fireEvent('days', _this, days);
27788                     return days;
27789                 })(),
27790                 fields : [ 'value' ]
27791             }),
27792             listeners : {
27793                 select : function (_self, record, index)
27794                 {
27795                     _this.setValue(_this.getValue());
27796                 }
27797             }
27798         });
27799
27800         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
27801         
27802         this.monthField = new Roo.bootstrap.MonthField({
27803             after : '<i class=\"fa fa-calendar\"></i>',
27804             allowBlank : this.monthAllowBlank,
27805             placeholder : this.monthPlaceholder,
27806             readOnly : true,
27807             listeners : {
27808                 render : function (_self)
27809                 {
27810                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
27811                         e.preventDefault();
27812                         _self.focus();
27813                     });
27814                 },
27815                 select : function (_self, oldvalue, newvalue)
27816                 {
27817                     _this.setValue(_this.getValue());
27818                 }
27819             }
27820         });
27821         
27822         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
27823         
27824         this.yearField = new Roo.bootstrap.ComboBox({
27825             allowBlank : this.yearAllowBlank,
27826             alwaysQuery : true,
27827             displayField : 'value',
27828             editable : false,
27829             fieldLabel : '',
27830             forceSelection : true,
27831             mode : 'local',
27832             placeholder : this.yearPlaceholder,
27833             selectOnFocus : true,
27834             tpl : '<div class="select2-result"><b>{value}</b></div>',
27835             triggerAction : 'all',
27836             typeAhead : true,
27837             valueField : 'value',
27838             store : new Roo.data.SimpleStore({
27839                 data : (function() {
27840                     var years = [];
27841                     _this.fireEvent('years', _this, years);
27842                     return years;
27843                 })(),
27844                 fields : [ 'value' ]
27845             }),
27846             listeners : {
27847                 select : function (_self, record, index)
27848                 {
27849                     _this.setValue(_this.getValue());
27850                 }
27851             }
27852         });
27853
27854         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
27855     },
27856     
27857     setValue : function(v, format)
27858     {
27859         this.inputEl.dom.value = v;
27860         
27861         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
27862         
27863         var d = Date.parseDate(v, f);
27864         
27865         if(!d){
27866             this.validate();
27867             return;
27868         }
27869         
27870         this.setDay(d.format(this.dayFormat));
27871         this.setMonth(d.format(this.monthFormat));
27872         this.setYear(d.format(this.yearFormat));
27873         
27874         this.validate();
27875         
27876         return;
27877     },
27878     
27879     setDay : function(v)
27880     {
27881         this.dayField.setValue(v);
27882         this.inputEl.dom.value = this.getValue();
27883         this.validate();
27884         return;
27885     },
27886     
27887     setMonth : function(v)
27888     {
27889         this.monthField.setValue(v, true);
27890         this.inputEl.dom.value = this.getValue();
27891         this.validate();
27892         return;
27893     },
27894     
27895     setYear : function(v)
27896     {
27897         this.yearField.setValue(v);
27898         this.inputEl.dom.value = this.getValue();
27899         this.validate();
27900         return;
27901     },
27902     
27903     getDay : function()
27904     {
27905         return this.dayField.getValue();
27906     },
27907     
27908     getMonth : function()
27909     {
27910         return this.monthField.getValue();
27911     },
27912     
27913     getYear : function()
27914     {
27915         return this.yearField.getValue();
27916     },
27917     
27918     getValue : function()
27919     {
27920         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
27921         
27922         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
27923         
27924         return date;
27925     },
27926     
27927     reset : function()
27928     {
27929         this.setDay('');
27930         this.setMonth('');
27931         this.setYear('');
27932         this.inputEl.dom.value = '';
27933         this.validate();
27934         return;
27935     },
27936     
27937     validate : function()
27938     {
27939         var d = this.dayField.validate();
27940         var m = this.monthField.validate();
27941         var y = this.yearField.validate();
27942         
27943         var valid = true;
27944         
27945         if(
27946                 (!this.dayAllowBlank && !d) ||
27947                 (!this.monthAllowBlank && !m) ||
27948                 (!this.yearAllowBlank && !y)
27949         ){
27950             valid = false;
27951         }
27952         
27953         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
27954             return valid;
27955         }
27956         
27957         if(valid){
27958             this.markValid();
27959             return valid;
27960         }
27961         
27962         this.markInvalid();
27963         
27964         return valid;
27965     },
27966     
27967     markValid : function()
27968     {
27969         
27970         var label = this.el.select('label', true).first();
27971         var icon = this.el.select('i.fa-star', true).first();
27972
27973         if(label && icon){
27974             icon.remove();
27975         }
27976         
27977         this.fireEvent('valid', this);
27978     },
27979     
27980      /**
27981      * Mark this field as invalid
27982      * @param {String} msg The validation message
27983      */
27984     markInvalid : function(msg)
27985     {
27986         
27987         var label = this.el.select('label', true).first();
27988         var icon = this.el.select('i.fa-star', true).first();
27989
27990         if(label && !icon){
27991             this.el.select('.roo-date-split-field-label', true).createChild({
27992                 tag : 'i',
27993                 cls : 'text-danger fa fa-lg fa-star',
27994                 tooltip : 'This field is required',
27995                 style : 'margin-right:5px;'
27996             }, label, true);
27997         }
27998         
27999         this.fireEvent('invalid', this, msg);
28000     },
28001     
28002     clearInvalid : function()
28003     {
28004         var label = this.el.select('label', true).first();
28005         var icon = this.el.select('i.fa-star', true).first();
28006
28007         if(label && icon){
28008             icon.remove();
28009         }
28010         
28011         this.fireEvent('valid', this);
28012     },
28013     
28014     getName: function()
28015     {
28016         return this.name;
28017     }
28018     
28019 });
28020
28021