sync
[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     });
11510     
11511     this.item = [];
11512     this.tickItems = [];
11513     
11514     this.selectedIndex = -1;
11515     if(this.mode == 'local'){
11516         if(config.queryDelay === undefined){
11517             this.queryDelay = 10;
11518         }
11519         if(config.minChars === undefined){
11520             this.minChars = 0;
11521         }
11522     }
11523 };
11524
11525 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11526      
11527     /**
11528      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11529      * rendering into an Roo.Editor, defaults to false)
11530      */
11531     /**
11532      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11533      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11534      */
11535     /**
11536      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11537      */
11538     /**
11539      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11540      * the dropdown list (defaults to undefined, with no header element)
11541      */
11542
11543      /**
11544      * @cfg {String/Roo.Template} tpl The template to use to render the output
11545      */
11546      
11547      /**
11548      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11549      */
11550     listWidth: undefined,
11551     /**
11552      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11553      * mode = 'remote' or 'text' if mode = 'local')
11554      */
11555     displayField: undefined,
11556     
11557     /**
11558      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11559      * mode = 'remote' or 'value' if mode = 'local'). 
11560      * Note: use of a valueField requires the user make a selection
11561      * in order for a value to be mapped.
11562      */
11563     valueField: undefined,
11564     
11565     
11566     /**
11567      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11568      * field's data value (defaults to the underlying DOM element's name)
11569      */
11570     hiddenName: undefined,
11571     /**
11572      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11573      */
11574     listClass: '',
11575     /**
11576      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11577      */
11578     selectedClass: 'active',
11579     
11580     /**
11581      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11582      */
11583     shadow:'sides',
11584     /**
11585      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11586      * anchor positions (defaults to 'tl-bl')
11587      */
11588     listAlign: 'tl-bl?',
11589     /**
11590      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11591      */
11592     maxHeight: 300,
11593     /**
11594      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
11595      * query specified by the allQuery config option (defaults to 'query')
11596      */
11597     triggerAction: 'query',
11598     /**
11599      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11600      * (defaults to 4, does not apply if editable = false)
11601      */
11602     minChars : 4,
11603     /**
11604      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11605      * delay (typeAheadDelay) if it matches a known value (defaults to false)
11606      */
11607     typeAhead: false,
11608     /**
11609      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11610      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11611      */
11612     queryDelay: 500,
11613     /**
11614      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11615      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
11616      */
11617     pageSize: 0,
11618     /**
11619      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
11620      * when editable = true (defaults to false)
11621      */
11622     selectOnFocus:false,
11623     /**
11624      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11625      */
11626     queryParam: 'query',
11627     /**
11628      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
11629      * when mode = 'remote' (defaults to 'Loading...')
11630      */
11631     loadingText: 'Loading...',
11632     /**
11633      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11634      */
11635     resizable: false,
11636     /**
11637      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11638      */
11639     handleHeight : 8,
11640     /**
11641      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11642      * traditional select (defaults to true)
11643      */
11644     editable: true,
11645     /**
11646      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11647      */
11648     allQuery: '',
11649     /**
11650      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11651      */
11652     mode: 'remote',
11653     /**
11654      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11655      * listWidth has a higher value)
11656      */
11657     minListWidth : 70,
11658     /**
11659      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11660      * allow the user to set arbitrary text into the field (defaults to false)
11661      */
11662     forceSelection:false,
11663     /**
11664      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11665      * if typeAhead = true (defaults to 250)
11666      */
11667     typeAheadDelay : 250,
11668     /**
11669      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11670      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11671      */
11672     valueNotFoundText : undefined,
11673     /**
11674      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11675      */
11676     blockFocus : false,
11677     
11678     /**
11679      * @cfg {Boolean} disableClear Disable showing of clear button.
11680      */
11681     disableClear : false,
11682     /**
11683      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
11684      */
11685     alwaysQuery : false,
11686     
11687     /**
11688      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
11689      */
11690     multiple : false,
11691     
11692     /**
11693      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11694      */
11695     invalidClass : "has-warning",
11696     
11697     /**
11698      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11699      */
11700     validClass : "has-success",
11701     
11702     /**
11703      * @cfg {Boolean} specialFilter (true|false) special filter default false
11704      */
11705     specialFilter : false,
11706     
11707     /**
11708      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
11709      */
11710     mobileTouchView : true,
11711     
11712     //private
11713     addicon : false,
11714     editicon: false,
11715     
11716     page: 0,
11717     hasQuery: false,
11718     append: false,
11719     loadNext: false,
11720     autoFocus : true,
11721     tickable : false,
11722     btnPosition : 'right',
11723     triggerList : true,
11724     showToggleBtn : true,
11725     animate : true,
11726     emptyResultText: 'Empty',
11727     triggerText : 'Select',
11728     
11729     // element that contains real text value.. (when hidden is used..)
11730     
11731     getAutoCreate : function()
11732     {
11733         var cfg = false;
11734         
11735         /*
11736          * Touch Devices
11737          */
11738         
11739         if(Roo.isTouch && this.mobileTouchView){
11740             cfg = this.getAutoCreateTouchView();
11741             return cfg;;
11742         }
11743         
11744         /*
11745          *  Normal ComboBox
11746          */
11747         if(!this.tickable){
11748             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11749             return cfg;
11750         }
11751         
11752         /*
11753          *  ComboBox with tickable selections
11754          */
11755              
11756         var align = this.labelAlign || this.parentLabelAlign();
11757         
11758         cfg = {
11759             cls : 'form-group roo-combobox-tickable' //input-group
11760         };
11761         
11762         var buttons = {
11763             tag : 'div',
11764             cls : 'tickable-buttons',
11765             cn : [
11766                 {
11767                     tag : 'button',
11768                     type : 'button',
11769                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11770                     html : this.triggerText
11771                 },
11772                 {
11773                     tag : 'button',
11774                     type : 'button',
11775                     name : 'ok',
11776                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11777                     html : 'Done'
11778                 },
11779                 {
11780                     tag : 'button',
11781                     type : 'button',
11782                     name : 'cancel',
11783                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11784                     html : 'Cancel'
11785                 }
11786             ]
11787         };
11788         
11789         if(this.editable){
11790             buttons.cn.unshift({
11791                 tag: 'input',
11792                 cls: 'select2-search-field-input'
11793             });
11794         }
11795         
11796         var _this = this;
11797         
11798         Roo.each(buttons.cn, function(c){
11799             if (_this.size) {
11800                 c.cls += ' btn-' + _this.size;
11801             }
11802
11803             if (_this.disabled) {
11804                 c.disabled = true;
11805             }
11806         });
11807         
11808         var box = {
11809             tag: 'div',
11810             cn: [
11811                 {
11812                     tag: 'input',
11813                     type : 'hidden',
11814                     cls: 'form-hidden-field'
11815                 },
11816                 {
11817                     tag: 'ul',
11818                     cls: 'select2-choices',
11819                     cn:[
11820                         {
11821                             tag: 'li',
11822                             cls: 'select2-search-field',
11823                             cn: [
11824
11825                                 buttons
11826                             ]
11827                         }
11828                     ]
11829                 }
11830             ]
11831         };
11832         
11833         var combobox = {
11834             cls: 'select2-container input-group select2-container-multi',
11835             cn: [
11836                 box
11837 //                {
11838 //                    tag: 'ul',
11839 //                    cls: 'typeahead typeahead-long dropdown-menu',
11840 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
11841 //                }
11842             ]
11843         };
11844         
11845         if(this.hasFeedback && !this.allowBlank){
11846             
11847             var feedback = {
11848                 tag: 'span',
11849                 cls: 'glyphicon form-control-feedback'
11850             };
11851
11852             combobox.cn.push(feedback);
11853         }
11854         
11855         if (align ==='left' && this.fieldLabel.length) {
11856             
11857                 Roo.log("left and has label");
11858                 cfg.cn = [
11859                     
11860                     {
11861                         tag: 'label',
11862                         'for' :  id,
11863                         cls : 'control-label col-sm-' + this.labelWidth,
11864                         html : this.fieldLabel
11865                         
11866                     },
11867                     {
11868                         cls : "col-sm-" + (12 - this.labelWidth), 
11869                         cn: [
11870                             combobox
11871                         ]
11872                     }
11873                     
11874                 ];
11875         } else if ( this.fieldLabel.length) {
11876                 Roo.log(" label");
11877                  cfg.cn = [
11878                    
11879                     {
11880                         tag: 'label',
11881                         //cls : 'input-group-addon',
11882                         html : this.fieldLabel
11883                         
11884                     },
11885                     
11886                     combobox
11887                     
11888                 ];
11889
11890         } else {
11891             
11892                 Roo.log(" no label && no align");
11893                 cfg = combobox
11894                      
11895                 
11896         }
11897          
11898         var settings=this;
11899         ['xs','sm','md','lg'].map(function(size){
11900             if (settings[size]) {
11901                 cfg.cls += ' col-' + size + '-' + settings[size];
11902             }
11903         });
11904         
11905         return cfg;
11906         
11907     },
11908     
11909     _initEventsCalled : false,
11910     
11911     // private
11912     initEvents: function()
11913     {
11914         
11915         if (this._initEventsCalled) { // as we call render... prevent looping...
11916             return;
11917         }
11918         this._initEventsCalled = true;
11919         
11920         if (!this.store) {
11921             throw "can not find store for combo";
11922         }
11923         
11924         this.store = Roo.factory(this.store, Roo.data);
11925         
11926         // if we are building from html. then this element is so complex, that we can not really
11927         // use the rendered HTML.
11928         // so we have to trash and replace the previous code.
11929         if (Roo.XComponent.build_from_html) {
11930             
11931             // remove this element....
11932             var e = this.el.dom, k=0;
11933             while (e ) { e = e.previousSibling;  ++k;}
11934
11935             this.el.remove();
11936             
11937             this.el=false;
11938             this.rendered = false;
11939             
11940             this.render(this.parent().getChildContainer(true), k);
11941             
11942             
11943             
11944         }
11945         
11946         
11947         /*
11948          * Touch Devices
11949          */
11950         
11951         if(Roo.isTouch && this.mobileTouchView){
11952             this.initTouchView();
11953             return;
11954         }
11955         
11956         if(this.tickable){
11957             this.initTickableEvents();
11958             return;
11959         }
11960         
11961         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11962         
11963         if(this.hiddenName){
11964             
11965             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11966             
11967             this.hiddenField.dom.value =
11968                 this.hiddenValue !== undefined ? this.hiddenValue :
11969                 this.value !== undefined ? this.value : '';
11970
11971             // prevent input submission
11972             this.el.dom.removeAttribute('name');
11973             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11974              
11975              
11976         }
11977         //if(Roo.isGecko){
11978         //    this.el.dom.setAttribute('autocomplete', 'off');
11979         //}
11980         
11981         var cls = 'x-combo-list';
11982         
11983         //this.list = new Roo.Layer({
11984         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
11985         //});
11986         
11987         var _this = this;
11988         
11989         (function(){
11990             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11991             _this.list.setWidth(lw);
11992         }).defer(100);
11993         
11994         this.list.on('mouseover', this.onViewOver, this);
11995         this.list.on('mousemove', this.onViewMove, this);
11996         
11997         this.list.on('scroll', this.onViewScroll, this);
11998         
11999         /*
12000         this.list.swallowEvent('mousewheel');
12001         this.assetHeight = 0;
12002
12003         if(this.title){
12004             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12005             this.assetHeight += this.header.getHeight();
12006         }
12007
12008         this.innerList = this.list.createChild({cls:cls+'-inner'});
12009         this.innerList.on('mouseover', this.onViewOver, this);
12010         this.innerList.on('mousemove', this.onViewMove, this);
12011         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12012         
12013         if(this.allowBlank && !this.pageSize && !this.disableClear){
12014             this.footer = this.list.createChild({cls:cls+'-ft'});
12015             this.pageTb = new Roo.Toolbar(this.footer);
12016            
12017         }
12018         if(this.pageSize){
12019             this.footer = this.list.createChild({cls:cls+'-ft'});
12020             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12021                     {pageSize: this.pageSize});
12022             
12023         }
12024         
12025         if (this.pageTb && this.allowBlank && !this.disableClear) {
12026             var _this = this;
12027             this.pageTb.add(new Roo.Toolbar.Fill(), {
12028                 cls: 'x-btn-icon x-btn-clear',
12029                 text: '&#160;',
12030                 handler: function()
12031                 {
12032                     _this.collapse();
12033                     _this.clearValue();
12034                     _this.onSelect(false, -1);
12035                 }
12036             });
12037         }
12038         if (this.footer) {
12039             this.assetHeight += this.footer.getHeight();
12040         }
12041         */
12042             
12043         if(!this.tpl){
12044             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12045         }
12046
12047         this.view = new Roo.View(this.list, this.tpl, {
12048             singleSelect:true, store: this.store, selectedClass: this.selectedClass
12049         });
12050         //this.view.wrapEl.setDisplayed(false);
12051         this.view.on('click', this.onViewClick, this);
12052         
12053         
12054         
12055         this.store.on('beforeload', this.onBeforeLoad, this);
12056         this.store.on('load', this.onLoad, this);
12057         this.store.on('loadexception', this.onLoadException, this);
12058         /*
12059         if(this.resizable){
12060             this.resizer = new Roo.Resizable(this.list,  {
12061                pinned:true, handles:'se'
12062             });
12063             this.resizer.on('resize', function(r, w, h){
12064                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12065                 this.listWidth = w;
12066                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12067                 this.restrictHeight();
12068             }, this);
12069             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12070         }
12071         */
12072         if(!this.editable){
12073             this.editable = true;
12074             this.setEditable(false);
12075         }
12076         
12077         /*
12078         
12079         if (typeof(this.events.add.listeners) != 'undefined') {
12080             
12081             this.addicon = this.wrap.createChild(
12082                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
12083        
12084             this.addicon.on('click', function(e) {
12085                 this.fireEvent('add', this);
12086             }, this);
12087         }
12088         if (typeof(this.events.edit.listeners) != 'undefined') {
12089             
12090             this.editicon = this.wrap.createChild(
12091                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
12092             if (this.addicon) {
12093                 this.editicon.setStyle('margin-left', '40px');
12094             }
12095             this.editicon.on('click', function(e) {
12096                 
12097                 // we fire even  if inothing is selected..
12098                 this.fireEvent('edit', this, this.lastData );
12099                 
12100             }, this);
12101         }
12102         */
12103         
12104         this.keyNav = new Roo.KeyNav(this.inputEl(), {
12105             "up" : function(e){
12106                 this.inKeyMode = true;
12107                 this.selectPrev();
12108             },
12109
12110             "down" : function(e){
12111                 if(!this.isExpanded()){
12112                     this.onTriggerClick();
12113                 }else{
12114                     this.inKeyMode = true;
12115                     this.selectNext();
12116                 }
12117             },
12118
12119             "enter" : function(e){
12120 //                this.onViewClick();
12121                 //return true;
12122                 this.collapse();
12123                 
12124                 if(this.fireEvent("specialkey", this, e)){
12125                     this.onViewClick(false);
12126                 }
12127                 
12128                 return true;
12129             },
12130
12131             "esc" : function(e){
12132                 this.collapse();
12133             },
12134
12135             "tab" : function(e){
12136                 this.collapse();
12137                 
12138                 if(this.fireEvent("specialkey", this, e)){
12139                     this.onViewClick(false);
12140                 }
12141                 
12142                 return true;
12143             },
12144
12145             scope : this,
12146
12147             doRelay : function(foo, bar, hname){
12148                 if(hname == 'down' || this.scope.isExpanded()){
12149                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12150                 }
12151                 return true;
12152             },
12153
12154             forceKeyDown: true
12155         });
12156         
12157         
12158         this.queryDelay = Math.max(this.queryDelay || 10,
12159                 this.mode == 'local' ? 10 : 250);
12160         
12161         
12162         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12163         
12164         if(this.typeAhead){
12165             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12166         }
12167         if(this.editable !== false){
12168             this.inputEl().on("keyup", this.onKeyUp, this);
12169         }
12170         if(this.forceSelection){
12171             this.inputEl().on('blur', this.doForce, this);
12172         }
12173         
12174         if(this.multiple){
12175             this.choices = this.el.select('ul.select2-choices', true).first();
12176             this.searchField = this.el.select('ul li.select2-search-field', true).first();
12177         }
12178     },
12179     
12180     initTickableEvents: function()
12181     {   
12182         this.createList();
12183         
12184         if(this.hiddenName){
12185             
12186             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12187             
12188             this.hiddenField.dom.value =
12189                 this.hiddenValue !== undefined ? this.hiddenValue :
12190                 this.value !== undefined ? this.value : '';
12191
12192             // prevent input submission
12193             this.el.dom.removeAttribute('name');
12194             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12195              
12196              
12197         }
12198         
12199 //        this.list = this.el.select('ul.dropdown-menu',true).first();
12200         
12201         this.choices = this.el.select('ul.select2-choices', true).first();
12202         this.searchField = this.el.select('ul li.select2-search-field', true).first();
12203         if(this.triggerList){
12204             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12205         }
12206          
12207         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12208         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12209         
12210         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12211         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12212         
12213         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12214         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12215         
12216         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12217         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12218         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12219         
12220         this.okBtn.hide();
12221         this.cancelBtn.hide();
12222         
12223         var _this = this;
12224         
12225         (function(){
12226             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12227             _this.list.setWidth(lw);
12228         }).defer(100);
12229         
12230         this.list.on('mouseover', this.onViewOver, this);
12231         this.list.on('mousemove', this.onViewMove, this);
12232         
12233         this.list.on('scroll', this.onViewScroll, this);
12234         
12235         if(!this.tpl){
12236             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>';
12237         }
12238
12239         this.view = new Roo.View(this.list, this.tpl, {
12240             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12241         });
12242         
12243         //this.view.wrapEl.setDisplayed(false);
12244         this.view.on('click', this.onViewClick, this);
12245         
12246         
12247         
12248         this.store.on('beforeload', this.onBeforeLoad, this);
12249         this.store.on('load', this.onLoad, this);
12250         this.store.on('loadexception', this.onLoadException, this);
12251         
12252         if(this.editable){
12253             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12254                 "up" : function(e){
12255                     this.inKeyMode = true;
12256                     this.selectPrev();
12257                 },
12258
12259                 "down" : function(e){
12260                     this.inKeyMode = true;
12261                     this.selectNext();
12262                 },
12263
12264                 "enter" : function(e){
12265                     if(this.fireEvent("specialkey", this, e)){
12266                         this.onViewClick(false);
12267                     }
12268                     
12269                     return true;
12270                 },
12271
12272                 "esc" : function(e){
12273                     this.onTickableFooterButtonClick(e, false, false);
12274                 },
12275
12276                 "tab" : function(e){
12277                     this.fireEvent("specialkey", this, e);
12278                     
12279                     this.onTickableFooterButtonClick(e, false, false);
12280                     
12281                     return true;
12282                 },
12283
12284                 scope : this,
12285
12286                 doRelay : function(e, fn, key){
12287                     if(this.scope.isExpanded()){
12288                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12289                     }
12290                     return true;
12291                 },
12292
12293                 forceKeyDown: true
12294             });
12295         }
12296         
12297         this.queryDelay = Math.max(this.queryDelay || 10,
12298                 this.mode == 'local' ? 10 : 250);
12299         
12300         
12301         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12302         
12303         if(this.typeAhead){
12304             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12305         }
12306         
12307         if(this.editable !== false){
12308             this.tickableInputEl().on("keyup", this.onKeyUp, this);
12309         }
12310         
12311     },
12312
12313     onDestroy : function(){
12314         if(this.view){
12315             this.view.setStore(null);
12316             this.view.el.removeAllListeners();
12317             this.view.el.remove();
12318             this.view.purgeListeners();
12319         }
12320         if(this.list){
12321             this.list.dom.innerHTML  = '';
12322         }
12323         
12324         if(this.store){
12325             this.store.un('beforeload', this.onBeforeLoad, this);
12326             this.store.un('load', this.onLoad, this);
12327             this.store.un('loadexception', this.onLoadException, this);
12328         }
12329         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12330     },
12331
12332     // private
12333     fireKey : function(e){
12334         if(e.isNavKeyPress() && !this.list.isVisible()){
12335             this.fireEvent("specialkey", this, e);
12336         }
12337     },
12338
12339     // private
12340     onResize: function(w, h){
12341 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12342 //        
12343 //        if(typeof w != 'number'){
12344 //            // we do not handle it!?!?
12345 //            return;
12346 //        }
12347 //        var tw = this.trigger.getWidth();
12348 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
12349 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
12350 //        var x = w - tw;
12351 //        this.inputEl().setWidth( this.adjustWidth('input', x));
12352 //            
12353 //        //this.trigger.setStyle('left', x+'px');
12354 //        
12355 //        if(this.list && this.listWidth === undefined){
12356 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12357 //            this.list.setWidth(lw);
12358 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12359 //        }
12360         
12361     
12362         
12363     },
12364
12365     /**
12366      * Allow or prevent the user from directly editing the field text.  If false is passed,
12367      * the user will only be able to select from the items defined in the dropdown list.  This method
12368      * is the runtime equivalent of setting the 'editable' config option at config time.
12369      * @param {Boolean} value True to allow the user to directly edit the field text
12370      */
12371     setEditable : function(value){
12372         if(value == this.editable){
12373             return;
12374         }
12375         this.editable = value;
12376         if(!value){
12377             this.inputEl().dom.setAttribute('readOnly', true);
12378             this.inputEl().on('mousedown', this.onTriggerClick,  this);
12379             this.inputEl().addClass('x-combo-noedit');
12380         }else{
12381             this.inputEl().dom.setAttribute('readOnly', false);
12382             this.inputEl().un('mousedown', this.onTriggerClick,  this);
12383             this.inputEl().removeClass('x-combo-noedit');
12384         }
12385     },
12386
12387     // private
12388     
12389     onBeforeLoad : function(combo,opts){
12390         if(!this.hasFocus){
12391             return;
12392         }
12393          if (!opts.add) {
12394             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12395          }
12396         this.restrictHeight();
12397         this.selectedIndex = -1;
12398     },
12399
12400     // private
12401     onLoad : function(){
12402         
12403         this.hasQuery = false;
12404         
12405         if(!this.hasFocus){
12406             return;
12407         }
12408         
12409         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12410             this.loading.hide();
12411         }
12412              
12413         if(this.store.getCount() > 0){
12414             this.expand();
12415             this.restrictHeight();
12416             if(this.lastQuery == this.allQuery){
12417                 if(this.editable && !this.tickable){
12418                     this.inputEl().dom.select();
12419                 }
12420                 
12421                 if(
12422                     !this.selectByValue(this.value, true) &&
12423                     this.autoFocus && 
12424                     (
12425                         !this.store.lastOptions ||
12426                         typeof(this.store.lastOptions.add) == 'undefined' || 
12427                         this.store.lastOptions.add != true
12428                     )
12429                 ){
12430                     this.select(0, true);
12431                 }
12432             }else{
12433                 if(this.autoFocus){
12434                     this.selectNext();
12435                 }
12436                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12437                     this.taTask.delay(this.typeAheadDelay);
12438                 }
12439             }
12440         }else{
12441             this.onEmptyResults();
12442         }
12443         
12444         //this.el.focus();
12445     },
12446     // private
12447     onLoadException : function()
12448     {
12449         this.hasQuery = false;
12450         
12451         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12452             this.loading.hide();
12453         }
12454         
12455         if(this.tickable && this.editable){
12456             return;
12457         }
12458         
12459         this.collapse();
12460         // only causes errors at present
12461         //Roo.log(this.store.reader.jsonData);
12462         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12463             // fixme
12464             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12465         //}
12466         
12467         
12468     },
12469     // private
12470     onTypeAhead : function(){
12471         if(this.store.getCount() > 0){
12472             var r = this.store.getAt(0);
12473             var newValue = r.data[this.displayField];
12474             var len = newValue.length;
12475             var selStart = this.getRawValue().length;
12476             
12477             if(selStart != len){
12478                 this.setRawValue(newValue);
12479                 this.selectText(selStart, newValue.length);
12480             }
12481         }
12482     },
12483
12484     // private
12485     onSelect : function(record, index){
12486         
12487         if(this.fireEvent('beforeselect', this, record, index) !== false){
12488         
12489             this.setFromData(index > -1 ? record.data : false);
12490             
12491             this.collapse();
12492             this.fireEvent('select', this, record, index);
12493         }
12494     },
12495
12496     /**
12497      * Returns the currently selected field value or empty string if no value is set.
12498      * @return {String} value The selected value
12499      */
12500     getValue : function(){
12501         
12502         if(this.multiple){
12503             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12504         }
12505         
12506         if(this.valueField){
12507             return typeof this.value != 'undefined' ? this.value : '';
12508         }else{
12509             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12510         }
12511     },
12512
12513     /**
12514      * Clears any text/value currently set in the field
12515      */
12516     clearValue : function(){
12517         if(this.hiddenField){
12518             this.hiddenField.dom.value = '';
12519         }
12520         this.value = '';
12521         this.setRawValue('');
12522         this.lastSelectionText = '';
12523         this.lastData = false;
12524         
12525         var close = this.closeTriggerEl();
12526         
12527         if(close){
12528             close.hide();
12529         }
12530         
12531     },
12532
12533     /**
12534      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
12535      * will be displayed in the field.  If the value does not match the data value of an existing item,
12536      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12537      * Otherwise the field will be blank (although the value will still be set).
12538      * @param {String} value The value to match
12539      */
12540     setValue : function(v){
12541         if(this.multiple){
12542             this.syncValue();
12543             return;
12544         }
12545         
12546         var text = v;
12547         if(this.valueField){
12548             var r = this.findRecord(this.valueField, v);
12549             if(r){
12550                 text = r.data[this.displayField];
12551             }else if(this.valueNotFoundText !== undefined){
12552                 text = this.valueNotFoundText;
12553             }
12554         }
12555         this.lastSelectionText = text;
12556         if(this.hiddenField){
12557             this.hiddenField.dom.value = v;
12558         }
12559         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12560         this.value = v;
12561         
12562         var close = this.closeTriggerEl();
12563         
12564         if(close){
12565             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12566         }
12567     },
12568     /**
12569      * @property {Object} the last set data for the element
12570      */
12571     
12572     lastData : false,
12573     /**
12574      * Sets the value of the field based on a object which is related to the record format for the store.
12575      * @param {Object} value the value to set as. or false on reset?
12576      */
12577     setFromData : function(o){
12578         
12579         if(this.multiple){
12580             this.addItem(o);
12581             return;
12582         }
12583             
12584         var dv = ''; // display value
12585         var vv = ''; // value value..
12586         this.lastData = o;
12587         if (this.displayField) {
12588             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12589         } else {
12590             // this is an error condition!!!
12591             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12592         }
12593         
12594         if(this.valueField){
12595             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12596         }
12597         
12598         var close = this.closeTriggerEl();
12599         
12600         if(close){
12601             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12602         }
12603         
12604         if(this.hiddenField){
12605             this.hiddenField.dom.value = vv;
12606             
12607             this.lastSelectionText = dv;
12608             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12609             this.value = vv;
12610             return;
12611         }
12612         // no hidden field.. - we store the value in 'value', but still display
12613         // display field!!!!
12614         this.lastSelectionText = dv;
12615         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12616         this.value = vv;
12617         
12618         
12619         
12620     },
12621     // private
12622     reset : function(){
12623         // overridden so that last data is reset..
12624         
12625         if(this.multiple){
12626             this.clearItem();
12627             return;
12628         }
12629         
12630         this.setValue(this.originalValue);
12631         this.clearInvalid();
12632         this.lastData = false;
12633         if (this.view) {
12634             this.view.clearSelections();
12635         }
12636     },
12637     // private
12638     findRecord : function(prop, value){
12639         var record;
12640         if(this.store.getCount() > 0){
12641             this.store.each(function(r){
12642                 if(r.data[prop] == value){
12643                     record = r;
12644                     return false;
12645                 }
12646                 return true;
12647             });
12648         }
12649         return record;
12650     },
12651     
12652     getName: function()
12653     {
12654         // returns hidden if it's set..
12655         if (!this.rendered) {return ''};
12656         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
12657         
12658     },
12659     // private
12660     onViewMove : function(e, t){
12661         this.inKeyMode = false;
12662     },
12663
12664     // private
12665     onViewOver : function(e, t){
12666         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12667             return;
12668         }
12669         var item = this.view.findItemFromChild(t);
12670         
12671         if(item){
12672             var index = this.view.indexOf(item);
12673             this.select(index, false);
12674         }
12675     },
12676
12677     // private
12678     onViewClick : function(view, doFocus, el, e)
12679     {
12680         var index = this.view.getSelectedIndexes()[0];
12681         
12682         var r = this.store.getAt(index);
12683         
12684         if(this.tickable){
12685             
12686             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12687                 return;
12688             }
12689             
12690             var rm = false;
12691             var _this = this;
12692             
12693             Roo.each(this.tickItems, function(v,k){
12694                 
12695                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12696                     Roo.log(v);
12697                     _this.tickItems.splice(k, 1);
12698                     
12699                     if(typeof(e) == 'undefined' && view == false){
12700                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12701                     }
12702                     
12703                     rm = true;
12704                     return;
12705                 }
12706             });
12707             
12708             if(rm){
12709                 return;
12710             }
12711             
12712             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
12713                 this.tickItems.push(r.data);
12714             }
12715             
12716             if(typeof(e) == 'undefined' && view == false){
12717                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12718             }
12719                     
12720             return;
12721         }
12722         
12723         if(r){
12724             this.onSelect(r, index);
12725         }
12726         if(doFocus !== false && !this.blockFocus){
12727             this.inputEl().focus();
12728         }
12729     },
12730
12731     // private
12732     restrictHeight : function(){
12733         //this.innerList.dom.style.height = '';
12734         //var inner = this.innerList.dom;
12735         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12736         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12737         //this.list.beginUpdate();
12738         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12739         this.list.alignTo(this.inputEl(), this.listAlign);
12740         this.list.alignTo(this.inputEl(), this.listAlign);
12741         //this.list.endUpdate();
12742     },
12743
12744     // private
12745     onEmptyResults : function(){
12746         
12747         if(this.tickable && this.editable){
12748             this.restrictHeight();
12749             return;
12750         }
12751         
12752         this.collapse();
12753     },
12754
12755     /**
12756      * Returns true if the dropdown list is expanded, else false.
12757      */
12758     isExpanded : function(){
12759         return this.list.isVisible();
12760     },
12761
12762     /**
12763      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12764      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12765      * @param {String} value The data value of the item to select
12766      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12767      * selected item if it is not currently in view (defaults to true)
12768      * @return {Boolean} True if the value matched an item in the list, else false
12769      */
12770     selectByValue : function(v, scrollIntoView){
12771         if(v !== undefined && v !== null){
12772             var r = this.findRecord(this.valueField || this.displayField, v);
12773             if(r){
12774                 this.select(this.store.indexOf(r), scrollIntoView);
12775                 return true;
12776             }
12777         }
12778         return false;
12779     },
12780
12781     /**
12782      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12783      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12784      * @param {Number} index The zero-based index of the list item to select
12785      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12786      * selected item if it is not currently in view (defaults to true)
12787      */
12788     select : function(index, scrollIntoView){
12789         this.selectedIndex = index;
12790         this.view.select(index);
12791         if(scrollIntoView !== false){
12792             var el = this.view.getNode(index);
12793             /*
12794              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12795              */
12796             if(el){
12797                 this.list.scrollChildIntoView(el, false);
12798             }
12799         }
12800     },
12801
12802     // private
12803     selectNext : function(){
12804         var ct = this.store.getCount();
12805         if(ct > 0){
12806             if(this.selectedIndex == -1){
12807                 this.select(0);
12808             }else if(this.selectedIndex < ct-1){
12809                 this.select(this.selectedIndex+1);
12810             }
12811         }
12812     },
12813
12814     // private
12815     selectPrev : function(){
12816         var ct = this.store.getCount();
12817         if(ct > 0){
12818             if(this.selectedIndex == -1){
12819                 this.select(0);
12820             }else if(this.selectedIndex != 0){
12821                 this.select(this.selectedIndex-1);
12822             }
12823         }
12824     },
12825
12826     // private
12827     onKeyUp : function(e){
12828         if(this.editable !== false && !e.isSpecialKey()){
12829             this.lastKey = e.getKey();
12830             this.dqTask.delay(this.queryDelay);
12831         }
12832     },
12833
12834     // private
12835     validateBlur : function(){
12836         return !this.list || !this.list.isVisible();   
12837     },
12838
12839     // private
12840     initQuery : function(){
12841         
12842         var v = this.getRawValue();
12843         
12844         if(this.tickable && this.editable){
12845             v = this.tickableInputEl().getValue();
12846         }
12847         
12848         this.doQuery(v);
12849     },
12850
12851     // private
12852     doForce : function(){
12853         if(this.inputEl().dom.value.length > 0){
12854             this.inputEl().dom.value =
12855                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12856              
12857         }
12858     },
12859
12860     /**
12861      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
12862      * query allowing the query action to be canceled if needed.
12863      * @param {String} query The SQL query to execute
12864      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12865      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
12866      * saved in the current store (defaults to false)
12867      */
12868     doQuery : function(q, forceAll){
12869         
12870         if(q === undefined || q === null){
12871             q = '';
12872         }
12873         var qe = {
12874             query: q,
12875             forceAll: forceAll,
12876             combo: this,
12877             cancel:false
12878         };
12879         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12880             return false;
12881         }
12882         q = qe.query;
12883         
12884         forceAll = qe.forceAll;
12885         if(forceAll === true || (q.length >= this.minChars)){
12886             
12887             this.hasQuery = true;
12888             
12889             if(this.lastQuery != q || this.alwaysQuery){
12890                 this.lastQuery = q;
12891                 if(this.mode == 'local'){
12892                     this.selectedIndex = -1;
12893                     if(forceAll){
12894                         this.store.clearFilter();
12895                     }else{
12896                         
12897                         if(this.specialFilter){
12898                             this.fireEvent('specialfilter', this);
12899                             this.onLoad();
12900                             return;
12901                         }
12902                         
12903                         this.store.filter(this.displayField, q);
12904                     }
12905                     
12906                     this.store.fireEvent("datachanged", this.store);
12907                     
12908                     this.onLoad();
12909                     
12910                     
12911                 }else{
12912                     
12913                     this.store.baseParams[this.queryParam] = q;
12914                     
12915                     var options = {params : this.getParams(q)};
12916                     
12917                     if(this.loadNext){
12918                         options.add = true;
12919                         options.params.start = this.page * this.pageSize;
12920                     }
12921                     
12922                     this.store.load(options);
12923                     
12924                     /*
12925                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
12926                      *  we should expand the list on onLoad
12927                      *  so command out it
12928                      */
12929 //                    this.expand();
12930                 }
12931             }else{
12932                 this.selectedIndex = -1;
12933                 this.onLoad();   
12934             }
12935         }
12936         
12937         this.loadNext = false;
12938     },
12939     
12940     // private
12941     getParams : function(q){
12942         var p = {};
12943         //p[this.queryParam] = q;
12944         
12945         if(this.pageSize){
12946             p.start = 0;
12947             p.limit = this.pageSize;
12948         }
12949         return p;
12950     },
12951
12952     /**
12953      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12954      */
12955     collapse : function(){
12956         if(!this.isExpanded()){
12957             return;
12958         }
12959         
12960         this.list.hide();
12961         
12962         if(this.tickable){
12963             this.hasFocus = false;
12964             this.okBtn.hide();
12965             this.cancelBtn.hide();
12966             this.trigger.show();
12967             
12968             if(this.editable){
12969                 this.tickableInputEl().dom.value = '';
12970                 this.tickableInputEl().blur();
12971             }
12972             
12973         }
12974         
12975         Roo.get(document).un('mousedown', this.collapseIf, this);
12976         Roo.get(document).un('mousewheel', this.collapseIf, this);
12977         if (!this.editable) {
12978             Roo.get(document).un('keydown', this.listKeyPress, this);
12979         }
12980         this.fireEvent('collapse', this);
12981     },
12982
12983     // private
12984     collapseIf : function(e){
12985         var in_combo  = e.within(this.el);
12986         var in_list =  e.within(this.list);
12987         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
12988         
12989         if (in_combo || in_list || is_list) {
12990             //e.stopPropagation();
12991             return;
12992         }
12993         
12994         if(this.tickable){
12995             this.onTickableFooterButtonClick(e, false, false);
12996         }
12997
12998         this.collapse();
12999         
13000     },
13001
13002     /**
13003      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13004      */
13005     expand : function(){
13006        
13007         if(this.isExpanded() || !this.hasFocus){
13008             return;
13009         }
13010         
13011         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13012         this.list.setWidth(lw);
13013         
13014         
13015          Roo.log('expand');
13016         
13017         this.list.show();
13018         
13019         this.restrictHeight();
13020         
13021         if(this.tickable){
13022             
13023             this.tickItems = Roo.apply([], this.item);
13024             
13025             this.okBtn.show();
13026             this.cancelBtn.show();
13027             this.trigger.hide();
13028             
13029             if(this.editable){
13030                 this.tickableInputEl().focus();
13031             }
13032             
13033         }
13034         
13035         Roo.get(document).on('mousedown', this.collapseIf, this);
13036         Roo.get(document).on('mousewheel', this.collapseIf, this);
13037         if (!this.editable) {
13038             Roo.get(document).on('keydown', this.listKeyPress, this);
13039         }
13040         
13041         this.fireEvent('expand', this);
13042     },
13043
13044     // private
13045     // Implements the default empty TriggerField.onTriggerClick function
13046     onTriggerClick : function(e)
13047     {
13048         Roo.log('trigger click');
13049         
13050         if(this.disabled || !this.triggerList){
13051             return;
13052         }
13053         
13054         this.page = 0;
13055         this.loadNext = false;
13056         
13057         if(this.isExpanded()){
13058             this.collapse();
13059             if (!this.blockFocus) {
13060                 this.inputEl().focus();
13061             }
13062             
13063         }else {
13064             this.hasFocus = true;
13065             if(this.triggerAction == 'all') {
13066                 this.doQuery(this.allQuery, true);
13067             } else {
13068                 this.doQuery(this.getRawValue());
13069             }
13070             if (!this.blockFocus) {
13071                 this.inputEl().focus();
13072             }
13073         }
13074     },
13075     
13076     onTickableTriggerClick : function(e)
13077     {
13078         if(this.disabled){
13079             return;
13080         }
13081         
13082         this.page = 0;
13083         this.loadNext = false;
13084         this.hasFocus = true;
13085         
13086         if(this.triggerAction == 'all') {
13087             this.doQuery(this.allQuery, true);
13088         } else {
13089             this.doQuery(this.getRawValue());
13090         }
13091     },
13092     
13093     onSearchFieldClick : function(e)
13094     {
13095         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13096             this.onTickableFooterButtonClick(e, false, false);
13097             return;
13098         }
13099         
13100         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13101             return;
13102         }
13103         
13104         this.page = 0;
13105         this.loadNext = false;
13106         this.hasFocus = true;
13107         
13108         if(this.triggerAction == 'all') {
13109             this.doQuery(this.allQuery, true);
13110         } else {
13111             this.doQuery(this.getRawValue());
13112         }
13113     },
13114     
13115     listKeyPress : function(e)
13116     {
13117         //Roo.log('listkeypress');
13118         // scroll to first matching element based on key pres..
13119         if (e.isSpecialKey()) {
13120             return false;
13121         }
13122         var k = String.fromCharCode(e.getKey()).toUpperCase();
13123         //Roo.log(k);
13124         var match  = false;
13125         var csel = this.view.getSelectedNodes();
13126         var cselitem = false;
13127         if (csel.length) {
13128             var ix = this.view.indexOf(csel[0]);
13129             cselitem  = this.store.getAt(ix);
13130             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13131                 cselitem = false;
13132             }
13133             
13134         }
13135         
13136         this.store.each(function(v) { 
13137             if (cselitem) {
13138                 // start at existing selection.
13139                 if (cselitem.id == v.id) {
13140                     cselitem = false;
13141                 }
13142                 return true;
13143             }
13144                 
13145             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13146                 match = this.store.indexOf(v);
13147                 return false;
13148             }
13149             return true;
13150         }, this);
13151         
13152         if (match === false) {
13153             return true; // no more action?
13154         }
13155         // scroll to?
13156         this.view.select(match);
13157         var sn = Roo.get(this.view.getSelectedNodes()[0]);
13158         sn.scrollIntoView(sn.dom.parentNode, false);
13159     },
13160     
13161     onViewScroll : function(e, t){
13162         
13163         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){
13164             return;
13165         }
13166         
13167         this.hasQuery = true;
13168         
13169         this.loading = this.list.select('.loading', true).first();
13170         
13171         if(this.loading === null){
13172             this.list.createChild({
13173                 tag: 'div',
13174                 cls: 'loading select2-more-results select2-active',
13175                 html: 'Loading more results...'
13176             });
13177             
13178             this.loading = this.list.select('.loading', true).first();
13179             
13180             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13181             
13182             this.loading.hide();
13183         }
13184         
13185         this.loading.show();
13186         
13187         var _combo = this;
13188         
13189         this.page++;
13190         this.loadNext = true;
13191         
13192         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13193         
13194         return;
13195     },
13196     
13197     addItem : function(o)
13198     {   
13199         var dv = ''; // display value
13200         
13201         if (this.displayField) {
13202             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13203         } else {
13204             // this is an error condition!!!
13205             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13206         }
13207         
13208         if(!dv.length){
13209             return;
13210         }
13211         
13212         var choice = this.choices.createChild({
13213             tag: 'li',
13214             cls: 'select2-search-choice',
13215             cn: [
13216                 {
13217                     tag: 'div',
13218                     html: dv
13219                 },
13220                 {
13221                     tag: 'a',
13222                     href: '#',
13223                     cls: 'select2-search-choice-close',
13224                     tabindex: '-1'
13225                 }
13226             ]
13227             
13228         }, this.searchField);
13229         
13230         var close = choice.select('a.select2-search-choice-close', true).first();
13231         
13232         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13233         
13234         this.item.push(o);
13235         
13236         this.lastData = o;
13237         
13238         this.syncValue();
13239         
13240         this.inputEl().dom.value = '';
13241         
13242         this.validate();
13243     },
13244     
13245     onRemoveItem : function(e, _self, o)
13246     {
13247         e.preventDefault();
13248         
13249         this.lastItem = Roo.apply([], this.item);
13250         
13251         var index = this.item.indexOf(o.data) * 1;
13252         
13253         if( index < 0){
13254             Roo.log('not this item?!');
13255             return;
13256         }
13257         
13258         this.item.splice(index, 1);
13259         o.item.remove();
13260         
13261         this.syncValue();
13262         
13263         this.fireEvent('remove', this, e);
13264         
13265         this.validate();
13266         
13267     },
13268     
13269     syncValue : function()
13270     {
13271         if(!this.item.length){
13272             this.clearValue();
13273             return;
13274         }
13275             
13276         var value = [];
13277         var _this = this;
13278         Roo.each(this.item, function(i){
13279             if(_this.valueField){
13280                 value.push(i[_this.valueField]);
13281                 return;
13282             }
13283
13284             value.push(i);
13285         });
13286
13287         this.value = value.join(',');
13288
13289         if(this.hiddenField){
13290             this.hiddenField.dom.value = this.value;
13291         }
13292         
13293         this.store.fireEvent("datachanged", this.store);
13294     },
13295     
13296     clearItem : function()
13297     {
13298         if(!this.multiple){
13299             return;
13300         }
13301         
13302         this.item = [];
13303         
13304         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
13305            c.remove();
13306         });
13307         
13308         this.syncValue();
13309         
13310         this.validate();
13311         
13312         if(this.tickable && !Roo.isTouch){
13313             this.view.refresh();
13314         }
13315     },
13316     
13317     inputEl: function ()
13318     {
13319         if(Roo.isTouch && this.mobileTouchView){
13320             return this.el.select('input.form-control',true).first();
13321         }
13322         
13323         if(this.tickable){
13324             return this.searchField;
13325         }
13326         
13327         return this.el.select('input.form-control',true).first();
13328     },
13329     
13330     
13331     onTickableFooterButtonClick : function(e, btn, el)
13332     {
13333         e.preventDefault();
13334         
13335         this.lastItem = Roo.apply([], this.item);
13336         
13337         if(btn && btn.name == 'cancel'){
13338             this.tickItems = Roo.apply([], this.item);
13339             this.collapse();
13340             return;
13341         }
13342         
13343         this.clearItem();
13344         
13345         var _this = this;
13346         
13347         Roo.each(this.tickItems, function(o){
13348             _this.addItem(o);
13349         });
13350         
13351         this.collapse();
13352         
13353     },
13354     
13355     validate : function()
13356     {
13357         var v = this.getRawValue();
13358         
13359         if(this.multiple){
13360             v = this.getValue();
13361         }
13362         
13363         if(this.disabled || this.allowBlank || v.length){
13364             this.markValid();
13365             return true;
13366         }
13367         
13368         this.markInvalid();
13369         return false;
13370     },
13371     
13372     tickableInputEl : function()
13373     {
13374         if(!this.tickable || !this.editable){
13375             return this.inputEl();
13376         }
13377         
13378         return this.inputEl().select('.select2-search-field-input', true).first();
13379     },
13380     
13381     
13382     getAutoCreateTouchView : function()
13383     {
13384         var id = Roo.id();
13385         
13386         var cfg = {
13387             cls: 'form-group' //input-group
13388         };
13389         
13390         var input =  {
13391             tag: 'input',
13392             id : id,
13393             type : this.inputType,
13394             cls : 'form-control x-combo-noedit',
13395             autocomplete: 'new-password',
13396             placeholder : this.placeholder || '',
13397             readonly : true
13398         };
13399         
13400         if (this.name) {
13401             input.name = this.name;
13402         }
13403         
13404         if (this.size) {
13405             input.cls += ' input-' + this.size;
13406         }
13407         
13408         if (this.disabled) {
13409             input.disabled = true;
13410         }
13411         
13412         var inputblock = {
13413             cls : '',
13414             cn : [
13415                 input
13416             ]
13417         };
13418         
13419         if(this.before){
13420             inputblock.cls += ' input-group';
13421             
13422             inputblock.cn.unshift({
13423                 tag :'span',
13424                 cls : 'input-group-addon',
13425                 html : this.before
13426             });
13427         }
13428         
13429         if(this.removable && !this.multiple){
13430             inputblock.cls += ' roo-removable';
13431             
13432             inputblock.cn.push({
13433                 tag: 'button',
13434                 html : 'x',
13435                 cls : 'roo-combo-removable-btn close'
13436             });
13437         }
13438
13439         if(this.hasFeedback && !this.allowBlank){
13440             
13441             inputblock.cls += ' has-feedback';
13442             
13443             inputblock.cn.push({
13444                 tag: 'span',
13445                 cls: 'glyphicon form-control-feedback'
13446             });
13447             
13448         }
13449         
13450         if (this.after) {
13451             
13452             inputblock.cls += (this.before) ? '' : ' input-group';
13453             
13454             inputblock.cn.push({
13455                 tag :'span',
13456                 cls : 'input-group-addon',
13457                 html : this.after
13458             });
13459         }
13460
13461         var box = {
13462             tag: 'div',
13463             cn: [
13464                 {
13465                     tag: 'input',
13466                     type : 'hidden',
13467                     cls: 'form-hidden-field'
13468                 },
13469                 inputblock
13470             ]
13471             
13472         };
13473         
13474         if(this.multiple){
13475             box = {
13476                 tag: 'div',
13477                 cn: [
13478                     {
13479                         tag: 'input',
13480                         type : 'hidden',
13481                         cls: 'form-hidden-field'
13482                     },
13483                     {
13484                         tag: 'ul',
13485                         cls: 'select2-choices',
13486                         cn:[
13487                             {
13488                                 tag: 'li',
13489                                 cls: 'select2-search-field',
13490                                 cn: [
13491
13492                                     inputblock
13493                                 ]
13494                             }
13495                         ]
13496                     }
13497                 ]
13498             }
13499         };
13500         
13501         var combobox = {
13502             cls: 'select2-container input-group',
13503             cn: [
13504                 box
13505             ]
13506         };
13507         
13508         if(this.multiple){
13509             combobox.cls += ' select2-container-multi';
13510         }
13511         
13512         var align = this.labelAlign || this.parentLabelAlign();
13513         
13514         cfg.cn = combobox;
13515         
13516         if(this.fieldLabel.length){
13517             
13518             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13519             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13520             
13521             cfg.cn = [
13522                 {
13523                     tag: 'label',
13524                     cls : 'control-label ' + lw,
13525                     html : this.fieldLabel
13526
13527                 },
13528                 {
13529                     cls : cw, 
13530                     cn: [
13531                         combobox
13532                     ]
13533                 }
13534             ];
13535         }
13536         
13537         var settings = this;
13538         
13539         ['xs','sm','md','lg'].map(function(size){
13540             if (settings[size]) {
13541                 cfg.cls += ' col-' + size + '-' + settings[size];
13542             }
13543         });
13544         
13545         return cfg;
13546     },
13547     
13548     initTouchView : function()
13549     {
13550         this.renderTouchView();
13551         
13552         this.touchViewEl.on('scroll', function(){
13553             this.el.dom.scrollTop = 0;
13554         }, this);
13555         
13556         this.inputEl().on("click", this.showTouchView, this);
13557         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13558         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13559         
13560         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13561         
13562         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13563         this.store.on('load', this.onTouchViewLoad, this);
13564         this.store.on('loadexception', this.onTouchViewLoadException, this);
13565         
13566         if(this.hiddenName){
13567             
13568             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13569             
13570             this.hiddenField.dom.value =
13571                 this.hiddenValue !== undefined ? this.hiddenValue :
13572                 this.value !== undefined ? this.value : '';
13573         
13574             this.el.dom.removeAttribute('name');
13575             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13576         }
13577         
13578         if(this.multiple){
13579             this.choices = this.el.select('ul.select2-choices', true).first();
13580             this.searchField = this.el.select('ul li.select2-search-field', true).first();
13581         }
13582         
13583         if(this.removable && !this.multiple){
13584             var close = this.closeTriggerEl();
13585             if(close){
13586                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13587                 close.on('click', this.removeBtnClick, this, close);
13588             }
13589         }
13590         
13591         return;
13592         
13593         
13594     },
13595     
13596     renderTouchView : function()
13597     {
13598         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13599         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13600         
13601         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13602         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13603         
13604         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13605         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13606         this.touchViewBodyEl.setStyle('overflow', 'auto');
13607         
13608         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13609         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13610         
13611         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13612         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13613         
13614     },
13615     
13616     showTouchView : function()
13617     {
13618         this.touchViewHeaderEl.hide();
13619
13620         if(this.fieldLabel.length){
13621             this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13622             this.touchViewHeaderEl.show();
13623         }
13624
13625         this.touchViewEl.show();
13626
13627         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13628         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13629
13630         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13631
13632         if(this.fieldLabel.length){
13633             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13634         }
13635         
13636         this.touchViewBodyEl.setHeight(bodyHeight);
13637
13638         if(this.animate){
13639             var _this = this;
13640             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13641         }else{
13642             this.touchViewEl.addClass('in');
13643         }
13644
13645         this.doTouchViewQuery();
13646         
13647     },
13648     
13649     hideTouchView : function()
13650     {
13651         this.touchViewEl.removeClass('in');
13652
13653         if(this.animate){
13654             var _this = this;
13655             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13656         }else{
13657             this.touchViewEl.setStyle('display', 'none');
13658         }
13659         
13660     },
13661     
13662     setTouchViewValue : function()
13663     {
13664         if(this.multiple){
13665             this.clearItem();
13666         
13667             var _this = this;
13668
13669             Roo.each(this.tickItems, function(o){
13670                 this.addItem(o);
13671             }, this);
13672         }
13673         
13674         this.hideTouchView();
13675     },
13676     
13677     doTouchViewQuery : function()
13678     {
13679         var qe = {
13680             query: '',
13681             forceAll: true,
13682             combo: this,
13683             cancel:false
13684         };
13685         
13686         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13687             return false;
13688         }
13689         
13690         if(!this.alwaysQuery || this.mode == 'local'){
13691             this.onTouchViewLoad();
13692             return;
13693         }
13694         
13695         this.store.load();
13696     },
13697     
13698     onTouchViewBeforeLoad : function(combo,opts)
13699     {
13700         return;
13701     },
13702
13703     // private
13704     onTouchViewLoad : function()
13705     {
13706         if(this.store.getCount() < 1){
13707             this.onTouchViewEmptyResults();
13708             return;
13709         }
13710         
13711         this.clearTouchView();
13712         
13713         var rawValue = this.getRawValue();
13714         
13715         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13716         
13717         this.tickItems = [];
13718         
13719         this.store.data.each(function(d, rowIndex){
13720             var row = this.touchViewListGroup.createChild(template);
13721             
13722             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13723                 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = d.data[this.displayField];
13724             }
13725             
13726             if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
13727                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13728             }
13729             
13730             if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
13731                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13732                 this.tickItems.push(d.data);
13733             }
13734             
13735             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
13736             
13737         }, this);
13738         
13739         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
13740         
13741         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13742
13743         if(this.fieldLabel.length){
13744             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13745         }
13746
13747         var listHeight = this.touchViewListGroup.getHeight();
13748         
13749         var _this = this;
13750         
13751         if(firstChecked && listHeight > bodyHeight){
13752             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
13753         }
13754         
13755     },
13756     
13757     onTouchViewLoadException : function()
13758     {
13759         this.hideTouchView();
13760     },
13761     
13762     onTouchViewEmptyResults : function()
13763     {
13764         this.clearTouchView();
13765         
13766         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
13767         
13768         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
13769         
13770     },
13771     
13772     clearTouchView : function()
13773     {
13774         this.touchViewListGroup.dom.innerHTML = '';
13775     },
13776     
13777     onTouchViewClick : function(e, el, o)
13778     {
13779         e.preventDefault();
13780         
13781         var row = o.row;
13782         var rowIndex = o.rowIndex;
13783         
13784         var r = this.store.getAt(rowIndex);
13785         
13786         if(!this.multiple){
13787             Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
13788                 c.dom.removeAttribute('checked');
13789             }, this);
13790             
13791             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13792         
13793             this.setFromData(r.data);
13794             
13795             var close = this.closeTriggerEl();
13796         
13797             if(close){
13798                 close.show();
13799             }
13800
13801             this.hideTouchView();
13802             
13803             this.fireEvent('select', this, r, rowIndex);
13804             
13805             return;
13806         }
13807         
13808         if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
13809             row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
13810             this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
13811             return;
13812         }
13813         
13814         row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13815         this.addItem(r.data);
13816         this.tickItems.push(r.data);
13817         
13818     }
13819     
13820
13821     /** 
13822     * @cfg {Boolean} grow 
13823     * @hide 
13824     */
13825     /** 
13826     * @cfg {Number} growMin 
13827     * @hide 
13828     */
13829     /** 
13830     * @cfg {Number} growMax 
13831     * @hide 
13832     */
13833     /**
13834      * @hide
13835      * @method autoSize
13836      */
13837 });
13838
13839 Roo.apply(Roo.bootstrap.ComboBox,  {
13840     
13841     header : {
13842         tag: 'div',
13843         cls: 'modal-header',
13844         cn: [
13845             {
13846                 tag: 'h4',
13847                 cls: 'modal-title'
13848             }
13849         ]
13850     },
13851     
13852     body : {
13853         tag: 'div',
13854         cls: 'modal-body',
13855         cn: [
13856             {
13857                 tag: 'ul',
13858                 cls: 'list-group'
13859             }
13860         ]
13861     },
13862     
13863     listItemRadio : {
13864         tag: 'li',
13865         cls: 'list-group-item',
13866         cn: [
13867             {
13868                 tag: 'span',
13869                 cls: 'roo-combobox-list-group-item-value'
13870             },
13871             {
13872                 tag: 'div',
13873                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
13874                 cn: [
13875                     {
13876                         tag: 'input',
13877                         type: 'radio'
13878                     },
13879                     {
13880                         tag: 'label'
13881                     }
13882                 ]
13883             }
13884         ]
13885     },
13886     
13887     listItemCheckbox : {
13888         tag: 'li',
13889         cls: 'list-group-item',
13890         cn: [
13891             {
13892                 tag: 'span',
13893                 cls: 'roo-combobox-list-group-item-value'
13894             },
13895             {
13896                 tag: 'div',
13897                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
13898                 cn: [
13899                     {
13900                         tag: 'input',
13901                         type: 'checkbox'
13902                     },
13903                     {
13904                         tag: 'label'
13905                     }
13906                 ]
13907             }
13908         ]
13909     },
13910     
13911     emptyResult : {
13912         tag: 'div',
13913         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
13914     },
13915     
13916     footer : {
13917         tag: 'div',
13918         cls: 'modal-footer',
13919         cn: [
13920             {
13921                 tag: 'div',
13922                 cls: 'row',
13923                 cn: [
13924                     {
13925                         tag: 'div',
13926                         cls: 'col-xs-6 text-left',
13927                         cn: {
13928                             tag: 'button',
13929                             cls: 'btn btn-danger roo-touch-view-cancel',
13930                             html: 'Cancel'
13931                         }
13932                     },
13933                     {
13934                         tag: 'div',
13935                         cls: 'col-xs-6 text-right',
13936                         cn: {
13937                             tag: 'button',
13938                             cls: 'btn btn-success roo-touch-view-ok',
13939                             html: 'OK'
13940                         }
13941                     }
13942                 ]
13943             }
13944         ]
13945         
13946     }
13947 });
13948
13949 Roo.apply(Roo.bootstrap.ComboBox,  {
13950     
13951     touchViewTemplate : {
13952         tag: 'div',
13953         cls: 'modal fade roo-combobox-touch-view',
13954         cn: [
13955             {
13956                 tag: 'div',
13957                 cls: 'modal-dialog',
13958                 cn: [
13959                     {
13960                         tag: 'div',
13961                         cls: 'modal-content',
13962                         cn: [
13963                             Roo.bootstrap.ComboBox.header,
13964                             Roo.bootstrap.ComboBox.body,
13965                             Roo.bootstrap.ComboBox.footer
13966                         ]
13967                     }
13968                 ]
13969             }
13970         ]
13971     }
13972 });/*
13973  * Based on:
13974  * Ext JS Library 1.1.1
13975  * Copyright(c) 2006-2007, Ext JS, LLC.
13976  *
13977  * Originally Released Under LGPL - original licence link has changed is not relivant.
13978  *
13979  * Fork - LGPL
13980  * <script type="text/javascript">
13981  */
13982
13983 /**
13984  * @class Roo.View
13985  * @extends Roo.util.Observable
13986  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
13987  * This class also supports single and multi selection modes. <br>
13988  * Create a data model bound view:
13989  <pre><code>
13990  var store = new Roo.data.Store(...);
13991
13992  var view = new Roo.View({
13993     el : "my-element",
13994     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
13995  
13996     singleSelect: true,
13997     selectedClass: "ydataview-selected",
13998     store: store
13999  });
14000
14001  // listen for node click?
14002  view.on("click", function(vw, index, node, e){
14003  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14004  });
14005
14006  // load XML data
14007  dataModel.load("foobar.xml");
14008  </code></pre>
14009  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14010  * <br><br>
14011  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14012  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14013  * 
14014  * Note: old style constructor is still suported (container, template, config)
14015  * 
14016  * @constructor
14017  * Create a new View
14018  * @param {Object} config The config object
14019  * 
14020  */
14021 Roo.View = function(config, depreciated_tpl, depreciated_config){
14022     
14023     this.parent = false;
14024     
14025     if (typeof(depreciated_tpl) == 'undefined') {
14026         // new way.. - universal constructor.
14027         Roo.apply(this, config);
14028         this.el  = Roo.get(this.el);
14029     } else {
14030         // old format..
14031         this.el  = Roo.get(config);
14032         this.tpl = depreciated_tpl;
14033         Roo.apply(this, depreciated_config);
14034     }
14035     this.wrapEl  = this.el.wrap().wrap();
14036     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14037     
14038     
14039     if(typeof(this.tpl) == "string"){
14040         this.tpl = new Roo.Template(this.tpl);
14041     } else {
14042         // support xtype ctors..
14043         this.tpl = new Roo.factory(this.tpl, Roo);
14044     }
14045     
14046     
14047     this.tpl.compile();
14048     
14049     /** @private */
14050     this.addEvents({
14051         /**
14052          * @event beforeclick
14053          * Fires before a click is processed. Returns false to cancel the default action.
14054          * @param {Roo.View} this
14055          * @param {Number} index The index of the target node
14056          * @param {HTMLElement} node The target node
14057          * @param {Roo.EventObject} e The raw event object
14058          */
14059             "beforeclick" : true,
14060         /**
14061          * @event click
14062          * Fires when a template node is clicked.
14063          * @param {Roo.View} this
14064          * @param {Number} index The index of the target node
14065          * @param {HTMLElement} node The target node
14066          * @param {Roo.EventObject} e The raw event object
14067          */
14068             "click" : true,
14069         /**
14070          * @event dblclick
14071          * Fires when a template node is double clicked.
14072          * @param {Roo.View} this
14073          * @param {Number} index The index of the target node
14074          * @param {HTMLElement} node The target node
14075          * @param {Roo.EventObject} e The raw event object
14076          */
14077             "dblclick" : true,
14078         /**
14079          * @event contextmenu
14080          * Fires when a template node is right clicked.
14081          * @param {Roo.View} this
14082          * @param {Number} index The index of the target node
14083          * @param {HTMLElement} node The target node
14084          * @param {Roo.EventObject} e The raw event object
14085          */
14086             "contextmenu" : true,
14087         /**
14088          * @event selectionchange
14089          * Fires when the selected nodes change.
14090          * @param {Roo.View} this
14091          * @param {Array} selections Array of the selected nodes
14092          */
14093             "selectionchange" : true,
14094     
14095         /**
14096          * @event beforeselect
14097          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14098          * @param {Roo.View} this
14099          * @param {HTMLElement} node The node to be selected
14100          * @param {Array} selections Array of currently selected nodes
14101          */
14102             "beforeselect" : true,
14103         /**
14104          * @event preparedata
14105          * Fires on every row to render, to allow you to change the data.
14106          * @param {Roo.View} this
14107          * @param {Object} data to be rendered (change this)
14108          */
14109           "preparedata" : true
14110           
14111           
14112         });
14113
14114
14115
14116     this.el.on({
14117         "click": this.onClick,
14118         "dblclick": this.onDblClick,
14119         "contextmenu": this.onContextMenu,
14120         scope:this
14121     });
14122
14123     this.selections = [];
14124     this.nodes = [];
14125     this.cmp = new Roo.CompositeElementLite([]);
14126     if(this.store){
14127         this.store = Roo.factory(this.store, Roo.data);
14128         this.setStore(this.store, true);
14129     }
14130     
14131     if ( this.footer && this.footer.xtype) {
14132            
14133          var fctr = this.wrapEl.appendChild(document.createElement("div"));
14134         
14135         this.footer.dataSource = this.store;
14136         this.footer.container = fctr;
14137         this.footer = Roo.factory(this.footer, Roo);
14138         fctr.insertFirst(this.el);
14139         
14140         // this is a bit insane - as the paging toolbar seems to detach the el..
14141 //        dom.parentNode.parentNode.parentNode
14142          // they get detached?
14143     }
14144     
14145     
14146     Roo.View.superclass.constructor.call(this);
14147     
14148     
14149 };
14150
14151 Roo.extend(Roo.View, Roo.util.Observable, {
14152     
14153      /**
14154      * @cfg {Roo.data.Store} store Data store to load data from.
14155      */
14156     store : false,
14157     
14158     /**
14159      * @cfg {String|Roo.Element} el The container element.
14160      */
14161     el : '',
14162     
14163     /**
14164      * @cfg {String|Roo.Template} tpl The template used by this View 
14165      */
14166     tpl : false,
14167     /**
14168      * @cfg {String} dataName the named area of the template to use as the data area
14169      *                          Works with domtemplates roo-name="name"
14170      */
14171     dataName: false,
14172     /**
14173      * @cfg {String} selectedClass The css class to add to selected nodes
14174      */
14175     selectedClass : "x-view-selected",
14176      /**
14177      * @cfg {String} emptyText The empty text to show when nothing is loaded.
14178      */
14179     emptyText : "",
14180     
14181     /**
14182      * @cfg {String} text to display on mask (default Loading)
14183      */
14184     mask : false,
14185     /**
14186      * @cfg {Boolean} multiSelect Allow multiple selection
14187      */
14188     multiSelect : false,
14189     /**
14190      * @cfg {Boolean} singleSelect Allow single selection
14191      */
14192     singleSelect:  false,
14193     
14194     /**
14195      * @cfg {Boolean} toggleSelect - selecting 
14196      */
14197     toggleSelect : false,
14198     
14199     /**
14200      * @cfg {Boolean} tickable - selecting 
14201      */
14202     tickable : false,
14203     
14204     /**
14205      * Returns the element this view is bound to.
14206      * @return {Roo.Element}
14207      */
14208     getEl : function(){
14209         return this.wrapEl;
14210     },
14211     
14212     
14213
14214     /**
14215      * Refreshes the view. - called by datachanged on the store. - do not call directly.
14216      */
14217     refresh : function(){
14218         //Roo.log('refresh');
14219         var t = this.tpl;
14220         
14221         // if we are using something like 'domtemplate', then
14222         // the what gets used is:
14223         // t.applySubtemplate(NAME, data, wrapping data..)
14224         // the outer template then get' applied with
14225         //     the store 'extra data'
14226         // and the body get's added to the
14227         //      roo-name="data" node?
14228         //      <span class='roo-tpl-{name}'></span> ?????
14229         
14230         
14231         
14232         this.clearSelections();
14233         this.el.update("");
14234         var html = [];
14235         var records = this.store.getRange();
14236         if(records.length < 1) {
14237             
14238             // is this valid??  = should it render a template??
14239             
14240             this.el.update(this.emptyText);
14241             return;
14242         }
14243         var el = this.el;
14244         if (this.dataName) {
14245             this.el.update(t.apply(this.store.meta)); //????
14246             el = this.el.child('.roo-tpl-' + this.dataName);
14247         }
14248         
14249         for(var i = 0, len = records.length; i < len; i++){
14250             var data = this.prepareData(records[i].data, i, records[i]);
14251             this.fireEvent("preparedata", this, data, i, records[i]);
14252             
14253             var d = Roo.apply({}, data);
14254             
14255             if(this.tickable){
14256                 Roo.apply(d, {'roo-id' : Roo.id()});
14257                 
14258                 var _this = this;
14259             
14260                 Roo.each(this.parent.item, function(item){
14261                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14262                         return;
14263                     }
14264                     Roo.apply(d, {'roo-data-checked' : 'checked'});
14265                 });
14266             }
14267             
14268             html[html.length] = Roo.util.Format.trim(
14269                 this.dataName ?
14270                     t.applySubtemplate(this.dataName, d, this.store.meta) :
14271                     t.apply(d)
14272             );
14273         }
14274         
14275         
14276         
14277         el.update(html.join(""));
14278         this.nodes = el.dom.childNodes;
14279         this.updateIndexes(0);
14280     },
14281     
14282
14283     /**
14284      * Function to override to reformat the data that is sent to
14285      * the template for each node.
14286      * DEPRICATED - use the preparedata event handler.
14287      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14288      * a JSON object for an UpdateManager bound view).
14289      */
14290     prepareData : function(data, index, record)
14291     {
14292         this.fireEvent("preparedata", this, data, index, record);
14293         return data;
14294     },
14295
14296     onUpdate : function(ds, record){
14297         // Roo.log('on update');   
14298         this.clearSelections();
14299         var index = this.store.indexOf(record);
14300         var n = this.nodes[index];
14301         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14302         n.parentNode.removeChild(n);
14303         this.updateIndexes(index, index);
14304     },
14305
14306     
14307     
14308 // --------- FIXME     
14309     onAdd : function(ds, records, index)
14310     {
14311         //Roo.log(['on Add', ds, records, index] );        
14312         this.clearSelections();
14313         if(this.nodes.length == 0){
14314             this.refresh();
14315             return;
14316         }
14317         var n = this.nodes[index];
14318         for(var i = 0, len = records.length; i < len; i++){
14319             var d = this.prepareData(records[i].data, i, records[i]);
14320             if(n){
14321                 this.tpl.insertBefore(n, d);
14322             }else{
14323                 
14324                 this.tpl.append(this.el, d);
14325             }
14326         }
14327         this.updateIndexes(index);
14328     },
14329
14330     onRemove : function(ds, record, index){
14331        // Roo.log('onRemove');
14332         this.clearSelections();
14333         var el = this.dataName  ?
14334             this.el.child('.roo-tpl-' + this.dataName) :
14335             this.el; 
14336         
14337         el.dom.removeChild(this.nodes[index]);
14338         this.updateIndexes(index);
14339     },
14340
14341     /**
14342      * Refresh an individual node.
14343      * @param {Number} index
14344      */
14345     refreshNode : function(index){
14346         this.onUpdate(this.store, this.store.getAt(index));
14347     },
14348
14349     updateIndexes : function(startIndex, endIndex){
14350         var ns = this.nodes;
14351         startIndex = startIndex || 0;
14352         endIndex = endIndex || ns.length - 1;
14353         for(var i = startIndex; i <= endIndex; i++){
14354             ns[i].nodeIndex = i;
14355         }
14356     },
14357
14358     /**
14359      * Changes the data store this view uses and refresh the view.
14360      * @param {Store} store
14361      */
14362     setStore : function(store, initial){
14363         if(!initial && this.store){
14364             this.store.un("datachanged", this.refresh);
14365             this.store.un("add", this.onAdd);
14366             this.store.un("remove", this.onRemove);
14367             this.store.un("update", this.onUpdate);
14368             this.store.un("clear", this.refresh);
14369             this.store.un("beforeload", this.onBeforeLoad);
14370             this.store.un("load", this.onLoad);
14371             this.store.un("loadexception", this.onLoad);
14372         }
14373         if(store){
14374           
14375             store.on("datachanged", this.refresh, this);
14376             store.on("add", this.onAdd, this);
14377             store.on("remove", this.onRemove, this);
14378             store.on("update", this.onUpdate, this);
14379             store.on("clear", this.refresh, this);
14380             store.on("beforeload", this.onBeforeLoad, this);
14381             store.on("load", this.onLoad, this);
14382             store.on("loadexception", this.onLoad, this);
14383         }
14384         
14385         if(store){
14386             this.refresh();
14387         }
14388     },
14389     /**
14390      * onbeforeLoad - masks the loading area.
14391      *
14392      */
14393     onBeforeLoad : function(store,opts)
14394     {
14395          //Roo.log('onBeforeLoad');   
14396         if (!opts.add) {
14397             this.el.update("");
14398         }
14399         this.el.mask(this.mask ? this.mask : "Loading" ); 
14400     },
14401     onLoad : function ()
14402     {
14403         this.el.unmask();
14404     },
14405     
14406
14407     /**
14408      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14409      * @param {HTMLElement} node
14410      * @return {HTMLElement} The template node
14411      */
14412     findItemFromChild : function(node){
14413         var el = this.dataName  ?
14414             this.el.child('.roo-tpl-' + this.dataName,true) :
14415             this.el.dom; 
14416         
14417         if(!node || node.parentNode == el){
14418                     return node;
14419             }
14420             var p = node.parentNode;
14421             while(p && p != el){
14422             if(p.parentNode == el){
14423                 return p;
14424             }
14425             p = p.parentNode;
14426         }
14427             return null;
14428     },
14429
14430     /** @ignore */
14431     onClick : function(e){
14432         var item = this.findItemFromChild(e.getTarget());
14433         if(item){
14434             var index = this.indexOf(item);
14435             if(this.onItemClick(item, index, e) !== false){
14436                 this.fireEvent("click", this, index, item, e);
14437             }
14438         }else{
14439             this.clearSelections();
14440         }
14441     },
14442
14443     /** @ignore */
14444     onContextMenu : function(e){
14445         var item = this.findItemFromChild(e.getTarget());
14446         if(item){
14447             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14448         }
14449     },
14450
14451     /** @ignore */
14452     onDblClick : function(e){
14453         var item = this.findItemFromChild(e.getTarget());
14454         if(item){
14455             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14456         }
14457     },
14458
14459     onItemClick : function(item, index, e)
14460     {
14461         if(this.fireEvent("beforeclick", this, index, item, e) === false){
14462             return false;
14463         }
14464         if (this.toggleSelect) {
14465             var m = this.isSelected(item) ? 'unselect' : 'select';
14466             //Roo.log(m);
14467             var _t = this;
14468             _t[m](item, true, false);
14469             return true;
14470         }
14471         if(this.multiSelect || this.singleSelect){
14472             if(this.multiSelect && e.shiftKey && this.lastSelection){
14473                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14474             }else{
14475                 this.select(item, this.multiSelect && e.ctrlKey);
14476                 this.lastSelection = item;
14477             }
14478             
14479             if(!this.tickable){
14480                 e.preventDefault();
14481             }
14482             
14483         }
14484         return true;
14485     },
14486
14487     /**
14488      * Get the number of selected nodes.
14489      * @return {Number}
14490      */
14491     getSelectionCount : function(){
14492         return this.selections.length;
14493     },
14494
14495     /**
14496      * Get the currently selected nodes.
14497      * @return {Array} An array of HTMLElements
14498      */
14499     getSelectedNodes : function(){
14500         return this.selections;
14501     },
14502
14503     /**
14504      * Get the indexes of the selected nodes.
14505      * @return {Array}
14506      */
14507     getSelectedIndexes : function(){
14508         var indexes = [], s = this.selections;
14509         for(var i = 0, len = s.length; i < len; i++){
14510             indexes.push(s[i].nodeIndex);
14511         }
14512         return indexes;
14513     },
14514
14515     /**
14516      * Clear all selections
14517      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14518      */
14519     clearSelections : function(suppressEvent){
14520         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14521             this.cmp.elements = this.selections;
14522             this.cmp.removeClass(this.selectedClass);
14523             this.selections = [];
14524             if(!suppressEvent){
14525                 this.fireEvent("selectionchange", this, this.selections);
14526             }
14527         }
14528     },
14529
14530     /**
14531      * Returns true if the passed node is selected
14532      * @param {HTMLElement/Number} node The node or node index
14533      * @return {Boolean}
14534      */
14535     isSelected : function(node){
14536         var s = this.selections;
14537         if(s.length < 1){
14538             return false;
14539         }
14540         node = this.getNode(node);
14541         return s.indexOf(node) !== -1;
14542     },
14543
14544     /**
14545      * Selects nodes.
14546      * @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
14547      * @param {Boolean} keepExisting (optional) true to keep existing selections
14548      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14549      */
14550     select : function(nodeInfo, keepExisting, suppressEvent){
14551         if(nodeInfo instanceof Array){
14552             if(!keepExisting){
14553                 this.clearSelections(true);
14554             }
14555             for(var i = 0, len = nodeInfo.length; i < len; i++){
14556                 this.select(nodeInfo[i], true, true);
14557             }
14558             return;
14559         } 
14560         var node = this.getNode(nodeInfo);
14561         if(!node || this.isSelected(node)){
14562             return; // already selected.
14563         }
14564         if(!keepExisting){
14565             this.clearSelections(true);
14566         }
14567         
14568         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14569             Roo.fly(node).addClass(this.selectedClass);
14570             this.selections.push(node);
14571             if(!suppressEvent){
14572                 this.fireEvent("selectionchange", this, this.selections);
14573             }
14574         }
14575         
14576         
14577     },
14578       /**
14579      * Unselects nodes.
14580      * @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
14581      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14582      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14583      */
14584     unselect : function(nodeInfo, keepExisting, suppressEvent)
14585     {
14586         if(nodeInfo instanceof Array){
14587             Roo.each(this.selections, function(s) {
14588                 this.unselect(s, nodeInfo);
14589             }, this);
14590             return;
14591         }
14592         var node = this.getNode(nodeInfo);
14593         if(!node || !this.isSelected(node)){
14594             //Roo.log("not selected");
14595             return; // not selected.
14596         }
14597         // fireevent???
14598         var ns = [];
14599         Roo.each(this.selections, function(s) {
14600             if (s == node ) {
14601                 Roo.fly(node).removeClass(this.selectedClass);
14602
14603                 return;
14604             }
14605             ns.push(s);
14606         },this);
14607         
14608         this.selections= ns;
14609         this.fireEvent("selectionchange", this, this.selections);
14610     },
14611
14612     /**
14613      * Gets a template node.
14614      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14615      * @return {HTMLElement} The node or null if it wasn't found
14616      */
14617     getNode : function(nodeInfo){
14618         if(typeof nodeInfo == "string"){
14619             return document.getElementById(nodeInfo);
14620         }else if(typeof nodeInfo == "number"){
14621             return this.nodes[nodeInfo];
14622         }
14623         return nodeInfo;
14624     },
14625
14626     /**
14627      * Gets a range template nodes.
14628      * @param {Number} startIndex
14629      * @param {Number} endIndex
14630      * @return {Array} An array of nodes
14631      */
14632     getNodes : function(start, end){
14633         var ns = this.nodes;
14634         start = start || 0;
14635         end = typeof end == "undefined" ? ns.length - 1 : end;
14636         var nodes = [];
14637         if(start <= end){
14638             for(var i = start; i <= end; i++){
14639                 nodes.push(ns[i]);
14640             }
14641         } else{
14642             for(var i = start; i >= end; i--){
14643                 nodes.push(ns[i]);
14644             }
14645         }
14646         return nodes;
14647     },
14648
14649     /**
14650      * Finds the index of the passed node
14651      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14652      * @return {Number} The index of the node or -1
14653      */
14654     indexOf : function(node){
14655         node = this.getNode(node);
14656         if(typeof node.nodeIndex == "number"){
14657             return node.nodeIndex;
14658         }
14659         var ns = this.nodes;
14660         for(var i = 0, len = ns.length; i < len; i++){
14661             if(ns[i] == node){
14662                 return i;
14663             }
14664         }
14665         return -1;
14666     }
14667 });
14668 /*
14669  * - LGPL
14670  *
14671  * based on jquery fullcalendar
14672  * 
14673  */
14674
14675 Roo.bootstrap = Roo.bootstrap || {};
14676 /**
14677  * @class Roo.bootstrap.Calendar
14678  * @extends Roo.bootstrap.Component
14679  * Bootstrap Calendar class
14680  * @cfg {Boolean} loadMask (true|false) default false
14681  * @cfg {Object} header generate the user specific header of the calendar, default false
14682
14683  * @constructor
14684  * Create a new Container
14685  * @param {Object} config The config object
14686  */
14687
14688
14689
14690 Roo.bootstrap.Calendar = function(config){
14691     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14692      this.addEvents({
14693         /**
14694              * @event select
14695              * Fires when a date is selected
14696              * @param {DatePicker} this
14697              * @param {Date} date The selected date
14698              */
14699         'select': true,
14700         /**
14701              * @event monthchange
14702              * Fires when the displayed month changes 
14703              * @param {DatePicker} this
14704              * @param {Date} date The selected month
14705              */
14706         'monthchange': true,
14707         /**
14708              * @event evententer
14709              * Fires when mouse over an event
14710              * @param {Calendar} this
14711              * @param {event} Event
14712              */
14713         'evententer': true,
14714         /**
14715              * @event eventleave
14716              * Fires when the mouse leaves an
14717              * @param {Calendar} this
14718              * @param {event}
14719              */
14720         'eventleave': true,
14721         /**
14722              * @event eventclick
14723              * Fires when the mouse click an
14724              * @param {Calendar} this
14725              * @param {event}
14726              */
14727         'eventclick': true
14728         
14729     });
14730
14731 };
14732
14733 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
14734     
14735      /**
14736      * @cfg {Number} startDay
14737      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
14738      */
14739     startDay : 0,
14740     
14741     loadMask : false,
14742     
14743     header : false,
14744       
14745     getAutoCreate : function(){
14746         
14747         
14748         var fc_button = function(name, corner, style, content ) {
14749             return Roo.apply({},{
14750                 tag : 'span',
14751                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
14752                          (corner.length ?
14753                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
14754                             ''
14755                         ),
14756                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
14757                 unselectable: 'on'
14758             });
14759         };
14760         
14761         var header = {};
14762         
14763         if(!this.header){
14764             header = {
14765                 tag : 'table',
14766                 cls : 'fc-header',
14767                 style : 'width:100%',
14768                 cn : [
14769                     {
14770                         tag: 'tr',
14771                         cn : [
14772                             {
14773                                 tag : 'td',
14774                                 cls : 'fc-header-left',
14775                                 cn : [
14776                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
14777                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
14778                                     { tag: 'span', cls: 'fc-header-space' },
14779                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
14780
14781
14782                                 ]
14783                             },
14784
14785                             {
14786                                 tag : 'td',
14787                                 cls : 'fc-header-center',
14788                                 cn : [
14789                                     {
14790                                         tag: 'span',
14791                                         cls: 'fc-header-title',
14792                                         cn : {
14793                                             tag: 'H2',
14794                                             html : 'month / year'
14795                                         }
14796                                     }
14797
14798                                 ]
14799                             },
14800                             {
14801                                 tag : 'td',
14802                                 cls : 'fc-header-right',
14803                                 cn : [
14804                               /*      fc_button('month', 'left', '', 'month' ),
14805                                     fc_button('week', '', '', 'week' ),
14806                                     fc_button('day', 'right', '', 'day' )
14807                                 */    
14808
14809                                 ]
14810                             }
14811
14812                         ]
14813                     }
14814                 ]
14815             };
14816         }
14817         
14818         header = this.header;
14819         
14820        
14821         var cal_heads = function() {
14822             var ret = [];
14823             // fixme - handle this.
14824             
14825             for (var i =0; i < Date.dayNames.length; i++) {
14826                 var d = Date.dayNames[i];
14827                 ret.push({
14828                     tag: 'th',
14829                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
14830                     html : d.substring(0,3)
14831                 });
14832                 
14833             }
14834             ret[0].cls += ' fc-first';
14835             ret[6].cls += ' fc-last';
14836             return ret;
14837         };
14838         var cal_cell = function(n) {
14839             return  {
14840                 tag: 'td',
14841                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
14842                 cn : [
14843                     {
14844                         cn : [
14845                             {
14846                                 cls: 'fc-day-number',
14847                                 html: 'D'
14848                             },
14849                             {
14850                                 cls: 'fc-day-content',
14851                              
14852                                 cn : [
14853                                      {
14854                                         style: 'position: relative;' // height: 17px;
14855                                     }
14856                                 ]
14857                             }
14858                             
14859                             
14860                         ]
14861                     }
14862                 ]
14863                 
14864             }
14865         };
14866         var cal_rows = function() {
14867             
14868             var ret = [];
14869             for (var r = 0; r < 6; r++) {
14870                 var row= {
14871                     tag : 'tr',
14872                     cls : 'fc-week',
14873                     cn : []
14874                 };
14875                 
14876                 for (var i =0; i < Date.dayNames.length; i++) {
14877                     var d = Date.dayNames[i];
14878                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
14879
14880                 }
14881                 row.cn[0].cls+=' fc-first';
14882                 row.cn[0].cn[0].style = 'min-height:90px';
14883                 row.cn[6].cls+=' fc-last';
14884                 ret.push(row);
14885                 
14886             }
14887             ret[0].cls += ' fc-first';
14888             ret[4].cls += ' fc-prev-last';
14889             ret[5].cls += ' fc-last';
14890             return ret;
14891             
14892         };
14893         
14894         var cal_table = {
14895             tag: 'table',
14896             cls: 'fc-border-separate',
14897             style : 'width:100%',
14898             cellspacing  : 0,
14899             cn : [
14900                 { 
14901                     tag: 'thead',
14902                     cn : [
14903                         { 
14904                             tag: 'tr',
14905                             cls : 'fc-first fc-last',
14906                             cn : cal_heads()
14907                         }
14908                     ]
14909                 },
14910                 { 
14911                     tag: 'tbody',
14912                     cn : cal_rows()
14913                 }
14914                   
14915             ]
14916         };
14917          
14918          var cfg = {
14919             cls : 'fc fc-ltr',
14920             cn : [
14921                 header,
14922                 {
14923                     cls : 'fc-content',
14924                     style : "position: relative;",
14925                     cn : [
14926                         {
14927                             cls : 'fc-view fc-view-month fc-grid',
14928                             style : 'position: relative',
14929                             unselectable : 'on',
14930                             cn : [
14931                                 {
14932                                     cls : 'fc-event-container',
14933                                     style : 'position:absolute;z-index:8;top:0;left:0;'
14934                                 },
14935                                 cal_table
14936                             ]
14937                         }
14938                     ]
14939     
14940                 }
14941            ] 
14942             
14943         };
14944         
14945          
14946         
14947         return cfg;
14948     },
14949     
14950     
14951     initEvents : function()
14952     {
14953         if(!this.store){
14954             throw "can not find store for calendar";
14955         }
14956         
14957         var mark = {
14958             tag: "div",
14959             cls:"x-dlg-mask",
14960             style: "text-align:center",
14961             cn: [
14962                 {
14963                     tag: "div",
14964                     style: "background-color:white;width:50%;margin:250 auto",
14965                     cn: [
14966                         {
14967                             tag: "img",
14968                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
14969                         },
14970                         {
14971                             tag: "span",
14972                             html: "Loading"
14973                         }
14974                         
14975                     ]
14976                 }
14977             ]
14978         };
14979         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
14980         
14981         var size = this.el.select('.fc-content', true).first().getSize();
14982         this.maskEl.setSize(size.width, size.height);
14983         this.maskEl.enableDisplayMode("block");
14984         if(!this.loadMask){
14985             this.maskEl.hide();
14986         }
14987         
14988         this.store = Roo.factory(this.store, Roo.data);
14989         this.store.on('load', this.onLoad, this);
14990         this.store.on('beforeload', this.onBeforeLoad, this);
14991         
14992         this.resize();
14993         
14994         this.cells = this.el.select('.fc-day',true);
14995         //Roo.log(this.cells);
14996         this.textNodes = this.el.query('.fc-day-number');
14997         this.cells.addClassOnOver('fc-state-hover');
14998         
14999         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15000         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15001         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15002         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15003         
15004         this.on('monthchange', this.onMonthChange, this);
15005         
15006         this.update(new Date().clearTime());
15007     },
15008     
15009     resize : function() {
15010         var sz  = this.el.getSize();
15011         
15012         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15013         this.el.select('.fc-day-content div',true).setHeight(34);
15014     },
15015     
15016     
15017     // private
15018     showPrevMonth : function(e){
15019         this.update(this.activeDate.add("mo", -1));
15020     },
15021     showToday : function(e){
15022         this.update(new Date().clearTime());
15023     },
15024     // private
15025     showNextMonth : function(e){
15026         this.update(this.activeDate.add("mo", 1));
15027     },
15028
15029     // private
15030     showPrevYear : function(){
15031         this.update(this.activeDate.add("y", -1));
15032     },
15033
15034     // private
15035     showNextYear : function(){
15036         this.update(this.activeDate.add("y", 1));
15037     },
15038
15039     
15040    // private
15041     update : function(date)
15042     {
15043         var vd = this.activeDate;
15044         this.activeDate = date;
15045 //        if(vd && this.el){
15046 //            var t = date.getTime();
15047 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15048 //                Roo.log('using add remove');
15049 //                
15050 //                this.fireEvent('monthchange', this, date);
15051 //                
15052 //                this.cells.removeClass("fc-state-highlight");
15053 //                this.cells.each(function(c){
15054 //                   if(c.dateValue == t){
15055 //                       c.addClass("fc-state-highlight");
15056 //                       setTimeout(function(){
15057 //                            try{c.dom.firstChild.focus();}catch(e){}
15058 //                       }, 50);
15059 //                       return false;
15060 //                   }
15061 //                   return true;
15062 //                });
15063 //                return;
15064 //            }
15065 //        }
15066         
15067         var days = date.getDaysInMonth();
15068         
15069         var firstOfMonth = date.getFirstDateOfMonth();
15070         var startingPos = firstOfMonth.getDay()-this.startDay;
15071         
15072         if(startingPos < this.startDay){
15073             startingPos += 7;
15074         }
15075         
15076         var pm = date.add(Date.MONTH, -1);
15077         var prevStart = pm.getDaysInMonth()-startingPos;
15078 //        
15079         this.cells = this.el.select('.fc-day',true);
15080         this.textNodes = this.el.query('.fc-day-number');
15081         this.cells.addClassOnOver('fc-state-hover');
15082         
15083         var cells = this.cells.elements;
15084         var textEls = this.textNodes;
15085         
15086         Roo.each(cells, function(cell){
15087             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15088         });
15089         
15090         days += startingPos;
15091
15092         // convert everything to numbers so it's fast
15093         var day = 86400000;
15094         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15095         //Roo.log(d);
15096         //Roo.log(pm);
15097         //Roo.log(prevStart);
15098         
15099         var today = new Date().clearTime().getTime();
15100         var sel = date.clearTime().getTime();
15101         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15102         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15103         var ddMatch = this.disabledDatesRE;
15104         var ddText = this.disabledDatesText;
15105         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15106         var ddaysText = this.disabledDaysText;
15107         var format = this.format;
15108         
15109         var setCellClass = function(cal, cell){
15110             cell.row = 0;
15111             cell.events = [];
15112             cell.more = [];
15113             //Roo.log('set Cell Class');
15114             cell.title = "";
15115             var t = d.getTime();
15116             
15117             //Roo.log(d);
15118             
15119             cell.dateValue = t;
15120             if(t == today){
15121                 cell.className += " fc-today";
15122                 cell.className += " fc-state-highlight";
15123                 cell.title = cal.todayText;
15124             }
15125             if(t == sel){
15126                 // disable highlight in other month..
15127                 //cell.className += " fc-state-highlight";
15128                 
15129             }
15130             // disabling
15131             if(t < min) {
15132                 cell.className = " fc-state-disabled";
15133                 cell.title = cal.minText;
15134                 return;
15135             }
15136             if(t > max) {
15137                 cell.className = " fc-state-disabled";
15138                 cell.title = cal.maxText;
15139                 return;
15140             }
15141             if(ddays){
15142                 if(ddays.indexOf(d.getDay()) != -1){
15143                     cell.title = ddaysText;
15144                     cell.className = " fc-state-disabled";
15145                 }
15146             }
15147             if(ddMatch && format){
15148                 var fvalue = d.dateFormat(format);
15149                 if(ddMatch.test(fvalue)){
15150                     cell.title = ddText.replace("%0", fvalue);
15151                     cell.className = " fc-state-disabled";
15152                 }
15153             }
15154             
15155             if (!cell.initialClassName) {
15156                 cell.initialClassName = cell.dom.className;
15157             }
15158             
15159             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
15160         };
15161
15162         var i = 0;
15163         
15164         for(; i < startingPos; i++) {
15165             textEls[i].innerHTML = (++prevStart);
15166             d.setDate(d.getDate()+1);
15167             
15168             cells[i].className = "fc-past fc-other-month";
15169             setCellClass(this, cells[i]);
15170         }
15171         
15172         var intDay = 0;
15173         
15174         for(; i < days; i++){
15175             intDay = i - startingPos + 1;
15176             textEls[i].innerHTML = (intDay);
15177             d.setDate(d.getDate()+1);
15178             
15179             cells[i].className = ''; // "x-date-active";
15180             setCellClass(this, cells[i]);
15181         }
15182         var extraDays = 0;
15183         
15184         for(; i < 42; i++) {
15185             textEls[i].innerHTML = (++extraDays);
15186             d.setDate(d.getDate()+1);
15187             
15188             cells[i].className = "fc-future fc-other-month";
15189             setCellClass(this, cells[i]);
15190         }
15191         
15192         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15193         
15194         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15195         
15196         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15197         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15198         
15199         if(totalRows != 6){
15200             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15201             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15202         }
15203         
15204         this.fireEvent('monthchange', this, date);
15205         
15206         
15207         /*
15208         if(!this.internalRender){
15209             var main = this.el.dom.firstChild;
15210             var w = main.offsetWidth;
15211             this.el.setWidth(w + this.el.getBorderWidth("lr"));
15212             Roo.fly(main).setWidth(w);
15213             this.internalRender = true;
15214             // opera does not respect the auto grow header center column
15215             // then, after it gets a width opera refuses to recalculate
15216             // without a second pass
15217             if(Roo.isOpera && !this.secondPass){
15218                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15219                 this.secondPass = true;
15220                 this.update.defer(10, this, [date]);
15221             }
15222         }
15223         */
15224         
15225     },
15226     
15227     findCell : function(dt) {
15228         dt = dt.clearTime().getTime();
15229         var ret = false;
15230         this.cells.each(function(c){
15231             //Roo.log("check " +c.dateValue + '?=' + dt);
15232             if(c.dateValue == dt){
15233                 ret = c;
15234                 return false;
15235             }
15236             return true;
15237         });
15238         
15239         return ret;
15240     },
15241     
15242     findCells : function(ev) {
15243         var s = ev.start.clone().clearTime().getTime();
15244        // Roo.log(s);
15245         var e= ev.end.clone().clearTime().getTime();
15246        // Roo.log(e);
15247         var ret = [];
15248         this.cells.each(function(c){
15249              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15250             
15251             if(c.dateValue > e){
15252                 return ;
15253             }
15254             if(c.dateValue < s){
15255                 return ;
15256             }
15257             ret.push(c);
15258         });
15259         
15260         return ret;    
15261     },
15262     
15263 //    findBestRow: function(cells)
15264 //    {
15265 //        var ret = 0;
15266 //        
15267 //        for (var i =0 ; i < cells.length;i++) {
15268 //            ret  = Math.max(cells[i].rows || 0,ret);
15269 //        }
15270 //        return ret;
15271 //        
15272 //    },
15273     
15274     
15275     addItem : function(ev)
15276     {
15277         // look for vertical location slot in
15278         var cells = this.findCells(ev);
15279         
15280 //        ev.row = this.findBestRow(cells);
15281         
15282         // work out the location.
15283         
15284         var crow = false;
15285         var rows = [];
15286         for(var i =0; i < cells.length; i++) {
15287             
15288             cells[i].row = cells[0].row;
15289             
15290             if(i == 0){
15291                 cells[i].row = cells[i].row + 1;
15292             }
15293             
15294             if (!crow) {
15295                 crow = {
15296                     start : cells[i],
15297                     end :  cells[i]
15298                 };
15299                 continue;
15300             }
15301             if (crow.start.getY() == cells[i].getY()) {
15302                 // on same row.
15303                 crow.end = cells[i];
15304                 continue;
15305             }
15306             // different row.
15307             rows.push(crow);
15308             crow = {
15309                 start: cells[i],
15310                 end : cells[i]
15311             };
15312             
15313         }
15314         
15315         rows.push(crow);
15316         ev.els = [];
15317         ev.rows = rows;
15318         ev.cells = cells;
15319         
15320         cells[0].events.push(ev);
15321         
15322         this.calevents.push(ev);
15323     },
15324     
15325     clearEvents: function() {
15326         
15327         if(!this.calevents){
15328             return;
15329         }
15330         
15331         Roo.each(this.cells.elements, function(c){
15332             c.row = 0;
15333             c.events = [];
15334             c.more = [];
15335         });
15336         
15337         Roo.each(this.calevents, function(e) {
15338             Roo.each(e.els, function(el) {
15339                 el.un('mouseenter' ,this.onEventEnter, this);
15340                 el.un('mouseleave' ,this.onEventLeave, this);
15341                 el.remove();
15342             },this);
15343         },this);
15344         
15345         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
15346             e.remove();
15347         });
15348         
15349     },
15350     
15351     renderEvents: function()
15352     {   
15353         var _this = this;
15354         
15355         this.cells.each(function(c) {
15356             
15357             if(c.row < 5){
15358                 return;
15359             }
15360             
15361             var ev = c.events;
15362             
15363             var r = 4;
15364             if(c.row != c.events.length){
15365                 r = 4 - (4 - (c.row - c.events.length));
15366             }
15367             
15368             c.events = ev.slice(0, r);
15369             c.more = ev.slice(r);
15370             
15371             if(c.more.length && c.more.length == 1){
15372                 c.events.push(c.more.pop());
15373             }
15374             
15375             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15376             
15377         });
15378             
15379         this.cells.each(function(c) {
15380             
15381             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15382             
15383             
15384             for (var e = 0; e < c.events.length; e++){
15385                 var ev = c.events[e];
15386                 var rows = ev.rows;
15387                 
15388                 for(var i = 0; i < rows.length; i++) {
15389                 
15390                     // how many rows should it span..
15391
15392                     var  cfg = {
15393                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15394                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15395
15396                         unselectable : "on",
15397                         cn : [
15398                             {
15399                                 cls: 'fc-event-inner',
15400                                 cn : [
15401     //                                {
15402     //                                  tag:'span',
15403     //                                  cls: 'fc-event-time',
15404     //                                  html : cells.length > 1 ? '' : ev.time
15405     //                                },
15406                                     {
15407                                       tag:'span',
15408                                       cls: 'fc-event-title',
15409                                       html : String.format('{0}', ev.title)
15410                                     }
15411
15412
15413                                 ]
15414                             },
15415                             {
15416                                 cls: 'ui-resizable-handle ui-resizable-e',
15417                                 html : '&nbsp;&nbsp;&nbsp'
15418                             }
15419
15420                         ]
15421                     };
15422
15423                     if (i == 0) {
15424                         cfg.cls += ' fc-event-start';
15425                     }
15426                     if ((i+1) == rows.length) {
15427                         cfg.cls += ' fc-event-end';
15428                     }
15429
15430                     var ctr = _this.el.select('.fc-event-container',true).first();
15431                     var cg = ctr.createChild(cfg);
15432
15433                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15434                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15435
15436                     var r = (c.more.length) ? 1 : 0;
15437                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
15438                     cg.setWidth(ebox.right - sbox.x -2);
15439
15440                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15441                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15442                     cg.on('click', _this.onEventClick, _this, ev);
15443
15444                     ev.els.push(cg);
15445                     
15446                 }
15447                 
15448             }
15449             
15450             
15451             if(c.more.length){
15452                 var  cfg = {
15453                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15454                     style : 'position: absolute',
15455                     unselectable : "on",
15456                     cn : [
15457                         {
15458                             cls: 'fc-event-inner',
15459                             cn : [
15460                                 {
15461                                   tag:'span',
15462                                   cls: 'fc-event-title',
15463                                   html : 'More'
15464                                 }
15465
15466
15467                             ]
15468                         },
15469                         {
15470                             cls: 'ui-resizable-handle ui-resizable-e',
15471                             html : '&nbsp;&nbsp;&nbsp'
15472                         }
15473
15474                     ]
15475                 };
15476
15477                 var ctr = _this.el.select('.fc-event-container',true).first();
15478                 var cg = ctr.createChild(cfg);
15479
15480                 var sbox = c.select('.fc-day-content',true).first().getBox();
15481                 var ebox = c.select('.fc-day-content',true).first().getBox();
15482                 //Roo.log(cg);
15483                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
15484                 cg.setWidth(ebox.right - sbox.x -2);
15485
15486                 cg.on('click', _this.onMoreEventClick, _this, c.more);
15487                 
15488             }
15489             
15490         });
15491         
15492         
15493         
15494     },
15495     
15496     onEventEnter: function (e, el,event,d) {
15497         this.fireEvent('evententer', this, el, event);
15498     },
15499     
15500     onEventLeave: function (e, el,event,d) {
15501         this.fireEvent('eventleave', this, el, event);
15502     },
15503     
15504     onEventClick: function (e, el,event,d) {
15505         this.fireEvent('eventclick', this, el, event);
15506     },
15507     
15508     onMonthChange: function () {
15509         this.store.load();
15510     },
15511     
15512     onMoreEventClick: function(e, el, more)
15513     {
15514         var _this = this;
15515         
15516         this.calpopover.placement = 'right';
15517         this.calpopover.setTitle('More');
15518         
15519         this.calpopover.setContent('');
15520         
15521         var ctr = this.calpopover.el.select('.popover-content', true).first();
15522         
15523         Roo.each(more, function(m){
15524             var cfg = {
15525                 cls : 'fc-event-hori fc-event-draggable',
15526                 html : m.title
15527             };
15528             var cg = ctr.createChild(cfg);
15529             
15530             cg.on('click', _this.onEventClick, _this, m);
15531         });
15532         
15533         this.calpopover.show(el);
15534         
15535         
15536     },
15537     
15538     onLoad: function () 
15539     {   
15540         this.calevents = [];
15541         var cal = this;
15542         
15543         if(this.store.getCount() > 0){
15544             this.store.data.each(function(d){
15545                cal.addItem({
15546                     id : d.data.id,
15547                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15548                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15549                     time : d.data.start_time,
15550                     title : d.data.title,
15551                     description : d.data.description,
15552                     venue : d.data.venue
15553                 });
15554             });
15555         }
15556         
15557         this.renderEvents();
15558         
15559         if(this.calevents.length && this.loadMask){
15560             this.maskEl.hide();
15561         }
15562     },
15563     
15564     onBeforeLoad: function()
15565     {
15566         this.clearEvents();
15567         if(this.loadMask){
15568             this.maskEl.show();
15569         }
15570     }
15571 });
15572
15573  
15574  /*
15575  * - LGPL
15576  *
15577  * element
15578  * 
15579  */
15580
15581 /**
15582  * @class Roo.bootstrap.Popover
15583  * @extends Roo.bootstrap.Component
15584  * Bootstrap Popover class
15585  * @cfg {String} html contents of the popover   (or false to use children..)
15586  * @cfg {String} title of popover (or false to hide)
15587  * @cfg {String} placement how it is placed
15588  * @cfg {String} trigger click || hover (or false to trigger manually)
15589  * @cfg {String} over what (parent or false to trigger manually.)
15590  * @cfg {Number} delay - delay before showing
15591  
15592  * @constructor
15593  * Create a new Popover
15594  * @param {Object} config The config object
15595  */
15596
15597 Roo.bootstrap.Popover = function(config){
15598     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15599 };
15600
15601 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
15602     
15603     title: 'Fill in a title',
15604     html: false,
15605     
15606     placement : 'right',
15607     trigger : 'hover', // hover
15608     
15609     delay : 0,
15610     
15611     over: 'parent',
15612     
15613     can_build_overlaid : false,
15614     
15615     getChildContainer : function()
15616     {
15617         return this.el.select('.popover-content',true).first();
15618     },
15619     
15620     getAutoCreate : function(){
15621          Roo.log('make popover?');
15622         var cfg = {
15623            cls : 'popover roo-dynamic',
15624            style: 'display:block',
15625            cn : [
15626                 {
15627                     cls : 'arrow'
15628                 },
15629                 {
15630                     cls : 'popover-inner',
15631                     cn : [
15632                         {
15633                             tag: 'h3',
15634                             cls: 'popover-title',
15635                             html : this.title
15636                         },
15637                         {
15638                             cls : 'popover-content',
15639                             html : this.html
15640                         }
15641                     ]
15642                     
15643                 }
15644            ]
15645         };
15646         
15647         return cfg;
15648     },
15649     setTitle: function(str)
15650     {
15651         this.title = str;
15652         this.el.select('.popover-title',true).first().dom.innerHTML = str;
15653     },
15654     setContent: function(str)
15655     {
15656         this.html = str;
15657         this.el.select('.popover-content',true).first().dom.innerHTML = str;
15658     },
15659     // as it get's added to the bottom of the page.
15660     onRender : function(ct, position)
15661     {
15662         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15663         if(!this.el){
15664             var cfg = Roo.apply({},  this.getAutoCreate());
15665             cfg.id = Roo.id();
15666             
15667             if (this.cls) {
15668                 cfg.cls += ' ' + this.cls;
15669             }
15670             if (this.style) {
15671                 cfg.style = this.style;
15672             }
15673             //Roo.log("adding to ");
15674             this.el = Roo.get(document.body).createChild(cfg, position);
15675             Roo.log(this.el);
15676         }
15677         this.initEvents();
15678     },
15679     
15680     initEvents : function()
15681     {
15682         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15683         this.el.enableDisplayMode('block');
15684         this.el.hide();
15685         if (this.over === false) {
15686             return; 
15687         }
15688         if (this.triggers === false) {
15689             return;
15690         }
15691         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15692         var triggers = this.trigger ? this.trigger.split(' ') : [];
15693         Roo.each(triggers, function(trigger) {
15694         
15695             if (trigger == 'click') {
15696                 on_el.on('click', this.toggle, this);
15697             } else if (trigger != 'manual') {
15698                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
15699                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
15700       
15701                 on_el.on(eventIn  ,this.enter, this);
15702                 on_el.on(eventOut, this.leave, this);
15703             }
15704         }, this);
15705         
15706     },
15707     
15708     
15709     // private
15710     timeout : null,
15711     hoverState : null,
15712     
15713     toggle : function () {
15714         this.hoverState == 'in' ? this.leave() : this.enter();
15715     },
15716     
15717     enter : function () {
15718        
15719     
15720         clearTimeout(this.timeout);
15721     
15722         this.hoverState = 'in';
15723     
15724         if (!this.delay || !this.delay.show) {
15725             this.show();
15726             return;
15727         }
15728         var _t = this;
15729         this.timeout = setTimeout(function () {
15730             if (_t.hoverState == 'in') {
15731                 _t.show();
15732             }
15733         }, this.delay.show)
15734     },
15735     leave : function() {
15736         clearTimeout(this.timeout);
15737     
15738         this.hoverState = 'out';
15739     
15740         if (!this.delay || !this.delay.hide) {
15741             this.hide();
15742             return;
15743         }
15744         var _t = this;
15745         this.timeout = setTimeout(function () {
15746             if (_t.hoverState == 'out') {
15747                 _t.hide();
15748             }
15749         }, this.delay.hide)
15750     },
15751     
15752     show : function (on_el)
15753     {
15754         if (!on_el) {
15755             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15756         }
15757         // set content.
15758         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
15759         if (this.html !== false) {
15760             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
15761         }
15762         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
15763         if (!this.title.length) {
15764             this.el.select('.popover-title',true).hide();
15765         }
15766         
15767         var placement = typeof this.placement == 'function' ?
15768             this.placement.call(this, this.el, on_el) :
15769             this.placement;
15770             
15771         var autoToken = /\s?auto?\s?/i;
15772         var autoPlace = autoToken.test(placement);
15773         if (autoPlace) {
15774             placement = placement.replace(autoToken, '') || 'top';
15775         }
15776         
15777         //this.el.detach()
15778         //this.el.setXY([0,0]);
15779         this.el.show();
15780         this.el.dom.style.display='block';
15781         this.el.addClass(placement);
15782         
15783         //this.el.appendTo(on_el);
15784         
15785         var p = this.getPosition();
15786         var box = this.el.getBox();
15787         
15788         if (autoPlace) {
15789             // fixme..
15790         }
15791         var align = Roo.bootstrap.Popover.alignment[placement];
15792         this.el.alignTo(on_el, align[0],align[1]);
15793         //var arrow = this.el.select('.arrow',true).first();
15794         //arrow.set(align[2], 
15795         
15796         this.el.addClass('in');
15797         
15798         
15799         if (this.el.hasClass('fade')) {
15800             // fade it?
15801         }
15802         
15803     },
15804     hide : function()
15805     {
15806         this.el.setXY([0,0]);
15807         this.el.removeClass('in');
15808         this.el.hide();
15809         this.hoverState = null;
15810         
15811     }
15812     
15813 });
15814
15815 Roo.bootstrap.Popover.alignment = {
15816     'left' : ['r-l', [-10,0], 'right'],
15817     'right' : ['l-r', [10,0], 'left'],
15818     'bottom' : ['t-b', [0,10], 'top'],
15819     'top' : [ 'b-t', [0,-10], 'bottom']
15820 };
15821
15822  /*
15823  * - LGPL
15824  *
15825  * Progress
15826  * 
15827  */
15828
15829 /**
15830  * @class Roo.bootstrap.Progress
15831  * @extends Roo.bootstrap.Component
15832  * Bootstrap Progress class
15833  * @cfg {Boolean} striped striped of the progress bar
15834  * @cfg {Boolean} active animated of the progress bar
15835  * 
15836  * 
15837  * @constructor
15838  * Create a new Progress
15839  * @param {Object} config The config object
15840  */
15841
15842 Roo.bootstrap.Progress = function(config){
15843     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
15844 };
15845
15846 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
15847     
15848     striped : false,
15849     active: false,
15850     
15851     getAutoCreate : function(){
15852         var cfg = {
15853             tag: 'div',
15854             cls: 'progress'
15855         };
15856         
15857         
15858         if(this.striped){
15859             cfg.cls += ' progress-striped';
15860         }
15861       
15862         if(this.active){
15863             cfg.cls += ' active';
15864         }
15865         
15866         
15867         return cfg;
15868     }
15869    
15870 });
15871
15872  
15873
15874  /*
15875  * - LGPL
15876  *
15877  * ProgressBar
15878  * 
15879  */
15880
15881 /**
15882  * @class Roo.bootstrap.ProgressBar
15883  * @extends Roo.bootstrap.Component
15884  * Bootstrap ProgressBar class
15885  * @cfg {Number} aria_valuenow aria-value now
15886  * @cfg {Number} aria_valuemin aria-value min
15887  * @cfg {Number} aria_valuemax aria-value max
15888  * @cfg {String} label label for the progress bar
15889  * @cfg {String} panel (success | info | warning | danger )
15890  * @cfg {String} role role of the progress bar
15891  * @cfg {String} sr_only text
15892  * 
15893  * 
15894  * @constructor
15895  * Create a new ProgressBar
15896  * @param {Object} config The config object
15897  */
15898
15899 Roo.bootstrap.ProgressBar = function(config){
15900     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
15901 };
15902
15903 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
15904     
15905     aria_valuenow : 0,
15906     aria_valuemin : 0,
15907     aria_valuemax : 100,
15908     label : false,
15909     panel : false,
15910     role : false,
15911     sr_only: false,
15912     
15913     getAutoCreate : function()
15914     {
15915         
15916         var cfg = {
15917             tag: 'div',
15918             cls: 'progress-bar',
15919             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
15920         };
15921         
15922         if(this.sr_only){
15923             cfg.cn = {
15924                 tag: 'span',
15925                 cls: 'sr-only',
15926                 html: this.sr_only
15927             }
15928         }
15929         
15930         if(this.role){
15931             cfg.role = this.role;
15932         }
15933         
15934         if(this.aria_valuenow){
15935             cfg['aria-valuenow'] = this.aria_valuenow;
15936         }
15937         
15938         if(this.aria_valuemin){
15939             cfg['aria-valuemin'] = this.aria_valuemin;
15940         }
15941         
15942         if(this.aria_valuemax){
15943             cfg['aria-valuemax'] = this.aria_valuemax;
15944         }
15945         
15946         if(this.label && !this.sr_only){
15947             cfg.html = this.label;
15948         }
15949         
15950         if(this.panel){
15951             cfg.cls += ' progress-bar-' + this.panel;
15952         }
15953         
15954         return cfg;
15955     },
15956     
15957     update : function(aria_valuenow)
15958     {
15959         this.aria_valuenow = aria_valuenow;
15960         
15961         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
15962     }
15963    
15964 });
15965
15966  
15967
15968  /*
15969  * - LGPL
15970  *
15971  * column
15972  * 
15973  */
15974
15975 /**
15976  * @class Roo.bootstrap.TabGroup
15977  * @extends Roo.bootstrap.Column
15978  * Bootstrap Column class
15979  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
15980  * @cfg {Boolean} carousel true to make the group behave like a carousel
15981  * @cfg {Boolean} bullets show bullets for the panels
15982  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
15983  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
15984  * @cfg {Number} timer auto slide timer .. default 0 millisecond
15985  * 
15986  * @constructor
15987  * Create a new TabGroup
15988  * @param {Object} config The config object
15989  */
15990
15991 Roo.bootstrap.TabGroup = function(config){
15992     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
15993     if (!this.navId) {
15994         this.navId = Roo.id();
15995     }
15996     this.tabs = [];
15997     Roo.bootstrap.TabGroup.register(this);
15998     
15999 };
16000
16001 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
16002     
16003     carousel : false,
16004     transition : false,
16005     bullets : 0,
16006     timer : 0,
16007     autoslide : false,
16008     slideFn : false,
16009     slideOnTouch : false,
16010     
16011     getAutoCreate : function()
16012     {
16013         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16014         
16015         cfg.cls += ' tab-content';
16016         
16017         Roo.log('get auto create...............');
16018         
16019         if (this.carousel) {
16020             cfg.cls += ' carousel slide';
16021             
16022             cfg.cn = [{
16023                cls : 'carousel-inner'
16024             }];
16025         
16026             if(this.bullets  && !Roo.isTouch){
16027                 
16028                 var bullets = {
16029                     cls : 'carousel-bullets',
16030                     cn : []
16031                 };
16032                
16033                 if(this.bullets_cls){
16034                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16035                 }
16036                  /*
16037                 for (var i = 0; i < this.bullets; i++){
16038                     bullets.cn.push({
16039                         cls : 'bullet bullet-' + i
16040                     });
16041                 }
16042                 */
16043                 bullets.cn.push({
16044                     cls : 'clear'
16045                 });
16046                 
16047                 cfg.cn[0].cn = bullets;
16048             }
16049         }
16050         
16051         return cfg;
16052     },
16053     
16054     initEvents:  function()
16055     {
16056         Roo.log('-------- init events on tab group ---------');
16057         
16058         
16059         
16060         Roo.log(this);
16061         
16062         if(Roo.isTouch && this.slideOnTouch){
16063             this.el.on("touchstart", this.onTouchStart, this);
16064         }
16065         
16066         if(this.autoslide){
16067             var _this = this;
16068             
16069             this.slideFn = window.setInterval(function() {
16070                 _this.showPanelNext();
16071             }, this.timer);
16072         }
16073         
16074     },
16075     
16076     onTouchStart : function(e, el, o)
16077     {
16078         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
16079             return;
16080         }
16081         
16082         this.showPanelNext();
16083     },
16084     
16085     getChildContainer : function()
16086     {
16087         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
16088     },
16089     
16090     /**
16091     * register a Navigation item
16092     * @param {Roo.bootstrap.NavItem} the navitem to add
16093     */
16094     register : function(item)
16095     {
16096         this.tabs.push( item);
16097         item.navId = this.navId; // not really needed..
16098         this.addBullet();
16099     
16100     },
16101     
16102     getActivePanel : function()
16103     {
16104         var r = false;
16105         Roo.each(this.tabs, function(t) {
16106             if (t.active) {
16107                 r = t;
16108                 return false;
16109             }
16110             return null;
16111         });
16112         return r;
16113         
16114     },
16115     getPanelByName : function(n)
16116     {
16117         var r = false;
16118         Roo.each(this.tabs, function(t) {
16119             if (t.tabId == n) {
16120                 r = t;
16121                 return false;
16122             }
16123             return null;
16124         });
16125         return r;
16126     },
16127     indexOfPanel : function(p)
16128     {
16129         var r = false;
16130         Roo.each(this.tabs, function(t,i) {
16131             if (t.tabId == p.tabId) {
16132                 r = i;
16133                 return false;
16134             }
16135             return null;
16136         });
16137         return r;
16138     },
16139     /**
16140      * show a specific panel
16141      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16142      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16143      */
16144     showPanel : function (pan)
16145     {
16146         if(this.transition){
16147             Roo.log("waiting for the transitionend");
16148             return;
16149         }
16150         
16151         if (typeof(pan) == 'number') {
16152             pan = this.tabs[pan];
16153         }
16154         if (typeof(pan) == 'string') {
16155             pan = this.getPanelByName(pan);
16156         }
16157         if (pan.tabId == this.getActivePanel().tabId) {
16158             return true;
16159         }
16160         var cur = this.getActivePanel();
16161         
16162         if (false === cur.fireEvent('beforedeactivate')) {
16163             return false;
16164         }
16165         
16166         if(this.bullets > 0 && !Roo.isTouch){
16167             this.setActiveBullet(this.indexOfPanel(pan));
16168         }
16169         
16170         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16171             
16172             this.transition = true;
16173             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
16174             var lr = dir == 'next' ? 'left' : 'right';
16175             pan.el.addClass(dir); // or prev
16176             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16177             cur.el.addClass(lr); // or right
16178             pan.el.addClass(lr);
16179             
16180             var _this = this;
16181             cur.el.on('transitionend', function() {
16182                 Roo.log("trans end?");
16183                 
16184                 pan.el.removeClass([lr,dir]);
16185                 pan.setActive(true);
16186                 
16187                 cur.el.removeClass([lr]);
16188                 cur.setActive(false);
16189                 
16190                 _this.transition = false;
16191                 
16192             }, this, { single:  true } );
16193             
16194             return true;
16195         }
16196         
16197         cur.setActive(false);
16198         pan.setActive(true);
16199         
16200         return true;
16201         
16202     },
16203     showPanelNext : function()
16204     {
16205         var i = this.indexOfPanel(this.getActivePanel());
16206         
16207         if (i >= this.tabs.length - 1 && !this.autoslide) {
16208             return;
16209         }
16210         
16211         if (i >= this.tabs.length - 1 && this.autoslide) {
16212             i = -1;
16213         }
16214         
16215         this.showPanel(this.tabs[i+1]);
16216     },
16217     
16218     showPanelPrev : function()
16219     {
16220         var i = this.indexOfPanel(this.getActivePanel());
16221         
16222         if (i  < 1 && !this.autoslide) {
16223             return;
16224         }
16225         
16226         if (i < 1 && this.autoslide) {
16227             i = this.tabs.length;
16228         }
16229         
16230         this.showPanel(this.tabs[i-1]);
16231     },
16232     
16233     
16234     addBullet: function()
16235     {
16236         if(!this.bullets || Roo.isTouch){
16237             return;
16238         }
16239         var ctr = this.el.select('.carousel-bullets',true).first();
16240         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16241         var bullet = ctr.createChild({
16242             cls : 'bullet bullet-' + i
16243         },ctr.dom.lastChild);
16244         
16245         
16246         var _this = this;
16247         
16248         bullet.on('click', (function(e, el, o, ii, t){
16249
16250             e.preventDefault();
16251
16252             this.showPanel(ii);
16253
16254             if(this.autoslide && this.slideFn){
16255                 clearInterval(this.slideFn);
16256                 this.slideFn = window.setInterval(function() {
16257                     _this.showPanelNext();
16258                 }, this.timer);
16259             }
16260
16261         }).createDelegate(this, [i, bullet], true));
16262                 
16263         
16264     },
16265      
16266     setActiveBullet : function(i)
16267     {
16268         if(Roo.isTouch){
16269             return;
16270         }
16271         
16272         Roo.each(this.el.select('.bullet', true).elements, function(el){
16273             el.removeClass('selected');
16274         });
16275
16276         var bullet = this.el.select('.bullet-' + i, true).first();
16277         
16278         if(!bullet){
16279             return;
16280         }
16281         
16282         bullet.addClass('selected');
16283     }
16284     
16285     
16286   
16287 });
16288
16289  
16290
16291  
16292  
16293 Roo.apply(Roo.bootstrap.TabGroup, {
16294     
16295     groups: {},
16296      /**
16297     * register a Navigation Group
16298     * @param {Roo.bootstrap.NavGroup} the navgroup to add
16299     */
16300     register : function(navgrp)
16301     {
16302         this.groups[navgrp.navId] = navgrp;
16303         
16304     },
16305     /**
16306     * fetch a Navigation Group based on the navigation ID
16307     * if one does not exist , it will get created.
16308     * @param {string} the navgroup to add
16309     * @returns {Roo.bootstrap.NavGroup} the navgroup 
16310     */
16311     get: function(navId) {
16312         if (typeof(this.groups[navId]) == 'undefined') {
16313             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
16314         }
16315         return this.groups[navId] ;
16316     }
16317     
16318     
16319     
16320 });
16321
16322  /*
16323  * - LGPL
16324  *
16325  * TabPanel
16326  * 
16327  */
16328
16329 /**
16330  * @class Roo.bootstrap.TabPanel
16331  * @extends Roo.bootstrap.Component
16332  * Bootstrap TabPanel class
16333  * @cfg {Boolean} active panel active
16334  * @cfg {String} html panel content
16335  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16336  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16337  * 
16338  * 
16339  * @constructor
16340  * Create a new TabPanel
16341  * @param {Object} config The config object
16342  */
16343
16344 Roo.bootstrap.TabPanel = function(config){
16345     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16346     this.addEvents({
16347         /**
16348              * @event changed
16349              * Fires when the active status changes
16350              * @param {Roo.bootstrap.TabPanel} this
16351              * @param {Boolean} state the new state
16352             
16353          */
16354         'changed': true,
16355         /**
16356              * @event beforedeactivate
16357              * Fires before a tab is de-activated - can be used to do validation on a form.
16358              * @param {Roo.bootstrap.TabPanel} this
16359              * @return {Boolean} false if there is an error
16360             
16361          */
16362         'beforedeactivate': true
16363      });
16364     
16365     this.tabId = this.tabId || Roo.id();
16366   
16367 };
16368
16369 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
16370     
16371     active: false,
16372     html: false,
16373     tabId: false,
16374     navId : false,
16375     
16376     getAutoCreate : function(){
16377         var cfg = {
16378             tag: 'div',
16379             // item is needed for carousel - not sure if it has any effect otherwise
16380             cls: 'tab-pane item',
16381             html: this.html || ''
16382         };
16383         
16384         if(this.active){
16385             cfg.cls += ' active';
16386         }
16387         
16388         if(this.tabId){
16389             cfg.tabId = this.tabId;
16390         }
16391         
16392         
16393         return cfg;
16394     },
16395     
16396     initEvents:  function()
16397     {
16398         Roo.log('-------- init events on tab panel ---------');
16399         
16400         var p = this.parent();
16401         this.navId = this.navId || p.navId;
16402         
16403         if (typeof(this.navId) != 'undefined') {
16404             // not really needed.. but just in case.. parent should be a NavGroup.
16405             var tg = Roo.bootstrap.TabGroup.get(this.navId);
16406             Roo.log(['register', tg, this]);
16407             tg.register(this);
16408             
16409             var i = tg.tabs.length - 1;
16410             
16411             if(this.active && tg.bullets > 0 && i < tg.bullets){
16412                 tg.setActiveBullet(i);
16413             }
16414         }
16415         
16416     },
16417     
16418     
16419     onRender : function(ct, position)
16420     {
16421        // Roo.log("Call onRender: " + this.xtype);
16422         
16423         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16424         
16425         
16426         
16427         
16428         
16429     },
16430     
16431     setActive: function(state)
16432     {
16433         Roo.log("panel - set active " + this.tabId + "=" + state);
16434         
16435         this.active = state;
16436         if (!state) {
16437             this.el.removeClass('active');
16438             
16439         } else  if (!this.el.hasClass('active')) {
16440             this.el.addClass('active');
16441         }
16442         
16443         this.fireEvent('changed', this, state);
16444     }
16445     
16446     
16447 });
16448  
16449
16450  
16451
16452  /*
16453  * - LGPL
16454  *
16455  * DateField
16456  * 
16457  */
16458
16459 /**
16460  * @class Roo.bootstrap.DateField
16461  * @extends Roo.bootstrap.Input
16462  * Bootstrap DateField class
16463  * @cfg {Number} weekStart default 0
16464  * @cfg {String} viewMode default empty, (months|years)
16465  * @cfg {String} minViewMode default empty, (months|years)
16466  * @cfg {Number} startDate default -Infinity
16467  * @cfg {Number} endDate default Infinity
16468  * @cfg {Boolean} todayHighlight default false
16469  * @cfg {Boolean} todayBtn default false
16470  * @cfg {Boolean} calendarWeeks default false
16471  * @cfg {Object} daysOfWeekDisabled default empty
16472  * @cfg {Boolean} singleMode default false (true | false)
16473  * 
16474  * @cfg {Boolean} keyboardNavigation default true
16475  * @cfg {String} language default en
16476  * 
16477  * @constructor
16478  * Create a new DateField
16479  * @param {Object} config The config object
16480  */
16481
16482 Roo.bootstrap.DateField = function(config){
16483     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16484      this.addEvents({
16485             /**
16486              * @event show
16487              * Fires when this field show.
16488              * @param {Roo.bootstrap.DateField} this
16489              * @param {Mixed} date The date value
16490              */
16491             show : true,
16492             /**
16493              * @event show
16494              * Fires when this field hide.
16495              * @param {Roo.bootstrap.DateField} this
16496              * @param {Mixed} date The date value
16497              */
16498             hide : true,
16499             /**
16500              * @event select
16501              * Fires when select a date.
16502              * @param {Roo.bootstrap.DateField} this
16503              * @param {Mixed} date The date value
16504              */
16505             select : true,
16506             /**
16507              * @event beforeselect
16508              * Fires when before select a date.
16509              * @param {Roo.bootstrap.DateField} this
16510              * @param {Mixed} date The date value
16511              */
16512             beforeselect : true
16513         });
16514 };
16515
16516 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
16517     
16518     /**
16519      * @cfg {String} format
16520      * The default date format string which can be overriden for localization support.  The format must be
16521      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16522      */
16523     format : "m/d/y",
16524     /**
16525      * @cfg {String} altFormats
16526      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16527      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16528      */
16529     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16530     
16531     weekStart : 0,
16532     
16533     viewMode : '',
16534     
16535     minViewMode : '',
16536     
16537     todayHighlight : false,
16538     
16539     todayBtn: false,
16540     
16541     language: 'en',
16542     
16543     keyboardNavigation: true,
16544     
16545     calendarWeeks: false,
16546     
16547     startDate: -Infinity,
16548     
16549     endDate: Infinity,
16550     
16551     daysOfWeekDisabled: [],
16552     
16553     _events: [],
16554     
16555     singleMode : false,
16556     
16557     UTCDate: function()
16558     {
16559         return new Date(Date.UTC.apply(Date, arguments));
16560     },
16561     
16562     UTCToday: function()
16563     {
16564         var today = new Date();
16565         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16566     },
16567     
16568     getDate: function() {
16569             var d = this.getUTCDate();
16570             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16571     },
16572     
16573     getUTCDate: function() {
16574             return this.date;
16575     },
16576     
16577     setDate: function(d) {
16578             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16579     },
16580     
16581     setUTCDate: function(d) {
16582             this.date = d;
16583             this.setValue(this.formatDate(this.date));
16584     },
16585         
16586     onRender: function(ct, position)
16587     {
16588         
16589         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16590         
16591         this.language = this.language || 'en';
16592         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16593         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16594         
16595         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16596         this.format = this.format || 'm/d/y';
16597         this.isInline = false;
16598         this.isInput = true;
16599         this.component = this.el.select('.add-on', true).first() || false;
16600         this.component = (this.component && this.component.length === 0) ? false : this.component;
16601         this.hasInput = this.component && this.inputEL().length;
16602         
16603         if (typeof(this.minViewMode === 'string')) {
16604             switch (this.minViewMode) {
16605                 case 'months':
16606                     this.minViewMode = 1;
16607                     break;
16608                 case 'years':
16609                     this.minViewMode = 2;
16610                     break;
16611                 default:
16612                     this.minViewMode = 0;
16613                     break;
16614             }
16615         }
16616         
16617         if (typeof(this.viewMode === 'string')) {
16618             switch (this.viewMode) {
16619                 case 'months':
16620                     this.viewMode = 1;
16621                     break;
16622                 case 'years':
16623                     this.viewMode = 2;
16624                     break;
16625                 default:
16626                     this.viewMode = 0;
16627                     break;
16628             }
16629         }
16630                 
16631         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16632         
16633 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16634         
16635         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16636         
16637         this.picker().on('mousedown', this.onMousedown, this);
16638         this.picker().on('click', this.onClick, this);
16639         
16640         this.picker().addClass('datepicker-dropdown');
16641         
16642         this.startViewMode = this.viewMode;
16643         
16644         if(this.singleMode){
16645             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16646                 v.setVisibilityMode(Roo.Element.DISPLAY);
16647                 v.hide();
16648             });
16649             
16650             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16651                 v.setStyle('width', '189px');
16652             });
16653         }
16654         
16655         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16656             if(!this.calendarWeeks){
16657                 v.remove();
16658                 return;
16659             }
16660             
16661             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16662             v.attr('colspan', function(i, val){
16663                 return parseInt(val) + 1;
16664             });
16665         });
16666                         
16667         
16668         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16669         
16670         this.setStartDate(this.startDate);
16671         this.setEndDate(this.endDate);
16672         
16673         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16674         
16675         this.fillDow();
16676         this.fillMonths();
16677         this.update();
16678         this.showMode();
16679         
16680         if(this.isInline) {
16681             this.show();
16682         }
16683     },
16684     
16685     picker : function()
16686     {
16687         return this.pickerEl;
16688 //        return this.el.select('.datepicker', true).first();
16689     },
16690     
16691     fillDow: function()
16692     {
16693         var dowCnt = this.weekStart;
16694         
16695         var dow = {
16696             tag: 'tr',
16697             cn: [
16698                 
16699             ]
16700         };
16701         
16702         if(this.calendarWeeks){
16703             dow.cn.push({
16704                 tag: 'th',
16705                 cls: 'cw',
16706                 html: '&nbsp;'
16707             })
16708         }
16709         
16710         while (dowCnt < this.weekStart + 7) {
16711             dow.cn.push({
16712                 tag: 'th',
16713                 cls: 'dow',
16714                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16715             });
16716         }
16717         
16718         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16719     },
16720     
16721     fillMonths: function()
16722     {    
16723         var i = 0;
16724         var months = this.picker().select('>.datepicker-months td', true).first();
16725         
16726         months.dom.innerHTML = '';
16727         
16728         while (i < 12) {
16729             var month = {
16730                 tag: 'span',
16731                 cls: 'month',
16732                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16733             };
16734             
16735             months.createChild(month);
16736         }
16737         
16738     },
16739     
16740     update: function()
16741     {
16742         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;
16743         
16744         if (this.date < this.startDate) {
16745             this.viewDate = new Date(this.startDate);
16746         } else if (this.date > this.endDate) {
16747             this.viewDate = new Date(this.endDate);
16748         } else {
16749             this.viewDate = new Date(this.date);
16750         }
16751         
16752         this.fill();
16753     },
16754     
16755     fill: function() 
16756     {
16757         var d = new Date(this.viewDate),
16758                 year = d.getUTCFullYear(),
16759                 month = d.getUTCMonth(),
16760                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16761                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16762                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16763                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16764                 currentDate = this.date && this.date.valueOf(),
16765                 today = this.UTCToday();
16766         
16767         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16768         
16769 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16770         
16771 //        this.picker.select('>tfoot th.today').
16772 //                                              .text(dates[this.language].today)
16773 //                                              .toggle(this.todayBtn !== false);
16774     
16775         this.updateNavArrows();
16776         this.fillMonths();
16777                                                 
16778         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16779         
16780         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16781          
16782         prevMonth.setUTCDate(day);
16783         
16784         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16785         
16786         var nextMonth = new Date(prevMonth);
16787         
16788         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16789         
16790         nextMonth = nextMonth.valueOf();
16791         
16792         var fillMonths = false;
16793         
16794         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16795         
16796         while(prevMonth.valueOf() < nextMonth) {
16797             var clsName = '';
16798             
16799             if (prevMonth.getUTCDay() === this.weekStart) {
16800                 if(fillMonths){
16801                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16802                 }
16803                     
16804                 fillMonths = {
16805                     tag: 'tr',
16806                     cn: []
16807                 };
16808                 
16809                 if(this.calendarWeeks){
16810                     // ISO 8601: First week contains first thursday.
16811                     // ISO also states week starts on Monday, but we can be more abstract here.
16812                     var
16813                     // Start of current week: based on weekstart/current date
16814                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16815                     // Thursday of this week
16816                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16817                     // First Thursday of year, year from thursday
16818                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16819                     // Calendar week: ms between thursdays, div ms per day, div 7 days
16820                     calWeek =  (th - yth) / 864e5 / 7 + 1;
16821                     
16822                     fillMonths.cn.push({
16823                         tag: 'td',
16824                         cls: 'cw',
16825                         html: calWeek
16826                     });
16827                 }
16828             }
16829             
16830             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16831                 clsName += ' old';
16832             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16833                 clsName += ' new';
16834             }
16835             if (this.todayHighlight &&
16836                 prevMonth.getUTCFullYear() == today.getFullYear() &&
16837                 prevMonth.getUTCMonth() == today.getMonth() &&
16838                 prevMonth.getUTCDate() == today.getDate()) {
16839                 clsName += ' today';
16840             }
16841             
16842             if (currentDate && prevMonth.valueOf() === currentDate) {
16843                 clsName += ' active';
16844             }
16845             
16846             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
16847                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
16848                     clsName += ' disabled';
16849             }
16850             
16851             fillMonths.cn.push({
16852                 tag: 'td',
16853                 cls: 'day ' + clsName,
16854                 html: prevMonth.getDate()
16855             });
16856             
16857             prevMonth.setDate(prevMonth.getDate()+1);
16858         }
16859           
16860         var currentYear = this.date && this.date.getUTCFullYear();
16861         var currentMonth = this.date && this.date.getUTCMonth();
16862         
16863         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
16864         
16865         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
16866             v.removeClass('active');
16867             
16868             if(currentYear === year && k === currentMonth){
16869                 v.addClass('active');
16870             }
16871             
16872             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
16873                 v.addClass('disabled');
16874             }
16875             
16876         });
16877         
16878         
16879         year = parseInt(year/10, 10) * 10;
16880         
16881         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
16882         
16883         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
16884         
16885         year -= 1;
16886         for (var i = -1; i < 11; i++) {
16887             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
16888                 tag: 'span',
16889                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
16890                 html: year
16891             });
16892             
16893             year += 1;
16894         }
16895     },
16896     
16897     showMode: function(dir) 
16898     {
16899         if (dir) {
16900             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
16901         }
16902         
16903         Roo.each(this.picker().select('>div',true).elements, function(v){
16904             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16905             v.hide();
16906         });
16907         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
16908     },
16909     
16910     place: function()
16911     {
16912         if(this.isInline) {
16913             return;
16914         }
16915         
16916         this.picker().removeClass(['bottom', 'top']);
16917         
16918         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16919             /*
16920              * place to the top of element!
16921              *
16922              */
16923             
16924             this.picker().addClass('top');
16925             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16926             
16927             return;
16928         }
16929         
16930         this.picker().addClass('bottom');
16931         
16932         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16933     },
16934     
16935     parseDate : function(value)
16936     {
16937         if(!value || value instanceof Date){
16938             return value;
16939         }
16940         var v = Date.parseDate(value, this.format);
16941         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
16942             v = Date.parseDate(value, 'Y-m-d');
16943         }
16944         if(!v && this.altFormats){
16945             if(!this.altFormatsArray){
16946                 this.altFormatsArray = this.altFormats.split("|");
16947             }
16948             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
16949                 v = Date.parseDate(value, this.altFormatsArray[i]);
16950             }
16951         }
16952         return v;
16953     },
16954     
16955     formatDate : function(date, fmt)
16956     {   
16957         return (!date || !(date instanceof Date)) ?
16958         date : date.dateFormat(fmt || this.format);
16959     },
16960     
16961     onFocus : function()
16962     {
16963         Roo.bootstrap.DateField.superclass.onFocus.call(this);
16964         this.show();
16965     },
16966     
16967     onBlur : function()
16968     {
16969         Roo.bootstrap.DateField.superclass.onBlur.call(this);
16970         
16971         var d = this.inputEl().getValue();
16972         
16973         this.setValue(d);
16974                 
16975         this.hide();
16976     },
16977     
16978     show : function()
16979     {
16980         this.picker().show();
16981         this.update();
16982         this.place();
16983         
16984         this.fireEvent('show', this, this.date);
16985     },
16986     
16987     hide : function()
16988     {
16989         if(this.isInline) {
16990             return;
16991         }
16992         this.picker().hide();
16993         this.viewMode = this.startViewMode;
16994         this.showMode();
16995         
16996         this.fireEvent('hide', this, this.date);
16997         
16998     },
16999     
17000     onMousedown: function(e)
17001     {
17002         e.stopPropagation();
17003         e.preventDefault();
17004     },
17005     
17006     keyup: function(e)
17007     {
17008         Roo.bootstrap.DateField.superclass.keyup.call(this);
17009         this.update();
17010     },
17011
17012     setValue: function(v)
17013     {
17014         if(this.fireEvent('beforeselect', this, v) !== false){
17015             var d = new Date(this.parseDate(v) ).clearTime();
17016         
17017             if(isNaN(d.getTime())){
17018                 this.date = this.viewDate = '';
17019                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17020                 return;
17021             }
17022
17023             v = this.formatDate(d);
17024
17025             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17026
17027             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17028
17029             this.update();
17030
17031             this.fireEvent('select', this, this.date);
17032         }
17033     },
17034     
17035     getValue: function()
17036     {
17037         return this.formatDate(this.date);
17038     },
17039     
17040     fireKey: function(e)
17041     {
17042         if (!this.picker().isVisible()){
17043             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17044                 this.show();
17045             }
17046             return;
17047         }
17048         
17049         var dateChanged = false,
17050         dir, day, month,
17051         newDate, newViewDate;
17052         
17053         switch(e.keyCode){
17054             case 27: // escape
17055                 this.hide();
17056                 e.preventDefault();
17057                 break;
17058             case 37: // left
17059             case 39: // right
17060                 if (!this.keyboardNavigation) {
17061                     break;
17062                 }
17063                 dir = e.keyCode == 37 ? -1 : 1;
17064                 
17065                 if (e.ctrlKey){
17066                     newDate = this.moveYear(this.date, dir);
17067                     newViewDate = this.moveYear(this.viewDate, dir);
17068                 } else if (e.shiftKey){
17069                     newDate = this.moveMonth(this.date, dir);
17070                     newViewDate = this.moveMonth(this.viewDate, dir);
17071                 } else {
17072                     newDate = new Date(this.date);
17073                     newDate.setUTCDate(this.date.getUTCDate() + dir);
17074                     newViewDate = new Date(this.viewDate);
17075                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
17076                 }
17077                 if (this.dateWithinRange(newDate)){
17078                     this.date = newDate;
17079                     this.viewDate = newViewDate;
17080                     this.setValue(this.formatDate(this.date));
17081 //                    this.update();
17082                     e.preventDefault();
17083                     dateChanged = true;
17084                 }
17085                 break;
17086             case 38: // up
17087             case 40: // down
17088                 if (!this.keyboardNavigation) {
17089                     break;
17090                 }
17091                 dir = e.keyCode == 38 ? -1 : 1;
17092                 if (e.ctrlKey){
17093                     newDate = this.moveYear(this.date, dir);
17094                     newViewDate = this.moveYear(this.viewDate, dir);
17095                 } else if (e.shiftKey){
17096                     newDate = this.moveMonth(this.date, dir);
17097                     newViewDate = this.moveMonth(this.viewDate, dir);
17098                 } else {
17099                     newDate = new Date(this.date);
17100                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
17101                     newViewDate = new Date(this.viewDate);
17102                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
17103                 }
17104                 if (this.dateWithinRange(newDate)){
17105                     this.date = newDate;
17106                     this.viewDate = newViewDate;
17107                     this.setValue(this.formatDate(this.date));
17108 //                    this.update();
17109                     e.preventDefault();
17110                     dateChanged = true;
17111                 }
17112                 break;
17113             case 13: // enter
17114                 this.setValue(this.formatDate(this.date));
17115                 this.hide();
17116                 e.preventDefault();
17117                 break;
17118             case 9: // tab
17119                 this.setValue(this.formatDate(this.date));
17120                 this.hide();
17121                 break;
17122             case 16: // shift
17123             case 17: // ctrl
17124             case 18: // alt
17125                 break;
17126             default :
17127                 this.hide();
17128                 
17129         }
17130     },
17131     
17132     
17133     onClick: function(e) 
17134     {
17135         e.stopPropagation();
17136         e.preventDefault();
17137         
17138         var target = e.getTarget();
17139         
17140         if(target.nodeName.toLowerCase() === 'i'){
17141             target = Roo.get(target).dom.parentNode;
17142         }
17143         
17144         var nodeName = target.nodeName;
17145         var className = target.className;
17146         var html = target.innerHTML;
17147         //Roo.log(nodeName);
17148         
17149         switch(nodeName.toLowerCase()) {
17150             case 'th':
17151                 switch(className) {
17152                     case 'switch':
17153                         this.showMode(1);
17154                         break;
17155                     case 'prev':
17156                     case 'next':
17157                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17158                         switch(this.viewMode){
17159                                 case 0:
17160                                         this.viewDate = this.moveMonth(this.viewDate, dir);
17161                                         break;
17162                                 case 1:
17163                                 case 2:
17164                                         this.viewDate = this.moveYear(this.viewDate, dir);
17165                                         break;
17166                         }
17167                         this.fill();
17168                         break;
17169                     case 'today':
17170                         var date = new Date();
17171                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17172 //                        this.fill()
17173                         this.setValue(this.formatDate(this.date));
17174                         
17175                         this.hide();
17176                         break;
17177                 }
17178                 break;
17179             case 'span':
17180                 if (className.indexOf('disabled') < 0) {
17181                     this.viewDate.setUTCDate(1);
17182                     if (className.indexOf('month') > -1) {
17183                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17184                     } else {
17185                         var year = parseInt(html, 10) || 0;
17186                         this.viewDate.setUTCFullYear(year);
17187                         
17188                     }
17189                     
17190                     if(this.singleMode){
17191                         this.setValue(this.formatDate(this.viewDate));
17192                         this.hide();
17193                         return;
17194                     }
17195                     
17196                     this.showMode(-1);
17197                     this.fill();
17198                 }
17199                 break;
17200                 
17201             case 'td':
17202                 //Roo.log(className);
17203                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17204                     var day = parseInt(html, 10) || 1;
17205                     var year = this.viewDate.getUTCFullYear(),
17206                         month = this.viewDate.getUTCMonth();
17207
17208                     if (className.indexOf('old') > -1) {
17209                         if(month === 0 ){
17210                             month = 11;
17211                             year -= 1;
17212                         }else{
17213                             month -= 1;
17214                         }
17215                     } else if (className.indexOf('new') > -1) {
17216                         if (month == 11) {
17217                             month = 0;
17218                             year += 1;
17219                         } else {
17220                             month += 1;
17221                         }
17222                     }
17223                     //Roo.log([year,month,day]);
17224                     this.date = this.UTCDate(year, month, day,0,0,0,0);
17225                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17226 //                    this.fill();
17227                     //Roo.log(this.formatDate(this.date));
17228                     this.setValue(this.formatDate(this.date));
17229                     this.hide();
17230                 }
17231                 break;
17232         }
17233     },
17234     
17235     setStartDate: function(startDate)
17236     {
17237         this.startDate = startDate || -Infinity;
17238         if (this.startDate !== -Infinity) {
17239             this.startDate = this.parseDate(this.startDate);
17240         }
17241         this.update();
17242         this.updateNavArrows();
17243     },
17244
17245     setEndDate: function(endDate)
17246     {
17247         this.endDate = endDate || Infinity;
17248         if (this.endDate !== Infinity) {
17249             this.endDate = this.parseDate(this.endDate);
17250         }
17251         this.update();
17252         this.updateNavArrows();
17253     },
17254     
17255     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17256     {
17257         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17258         if (typeof(this.daysOfWeekDisabled) !== 'object') {
17259             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17260         }
17261         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17262             return parseInt(d, 10);
17263         });
17264         this.update();
17265         this.updateNavArrows();
17266     },
17267     
17268     updateNavArrows: function() 
17269     {
17270         if(this.singleMode){
17271             return;
17272         }
17273         
17274         var d = new Date(this.viewDate),
17275         year = d.getUTCFullYear(),
17276         month = d.getUTCMonth();
17277         
17278         Roo.each(this.picker().select('.prev', true).elements, function(v){
17279             v.show();
17280             switch (this.viewMode) {
17281                 case 0:
17282
17283                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
17284                         v.hide();
17285                     }
17286                     break;
17287                 case 1:
17288                 case 2:
17289                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
17290                         v.hide();
17291                     }
17292                     break;
17293             }
17294         });
17295         
17296         Roo.each(this.picker().select('.next', true).elements, function(v){
17297             v.show();
17298             switch (this.viewMode) {
17299                 case 0:
17300
17301                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
17302                         v.hide();
17303                     }
17304                     break;
17305                 case 1:
17306                 case 2:
17307                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
17308                         v.hide();
17309                     }
17310                     break;
17311             }
17312         })
17313     },
17314     
17315     moveMonth: function(date, dir)
17316     {
17317         if (!dir) {
17318             return date;
17319         }
17320         var new_date = new Date(date.valueOf()),
17321         day = new_date.getUTCDate(),
17322         month = new_date.getUTCMonth(),
17323         mag = Math.abs(dir),
17324         new_month, test;
17325         dir = dir > 0 ? 1 : -1;
17326         if (mag == 1){
17327             test = dir == -1
17328             // If going back one month, make sure month is not current month
17329             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
17330             ? function(){
17331                 return new_date.getUTCMonth() == month;
17332             }
17333             // If going forward one month, make sure month is as expected
17334             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
17335             : function(){
17336                 return new_date.getUTCMonth() != new_month;
17337             };
17338             new_month = month + dir;
17339             new_date.setUTCMonth(new_month);
17340             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
17341             if (new_month < 0 || new_month > 11) {
17342                 new_month = (new_month + 12) % 12;
17343             }
17344         } else {
17345             // For magnitudes >1, move one month at a time...
17346             for (var i=0; i<mag; i++) {
17347                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
17348                 new_date = this.moveMonth(new_date, dir);
17349             }
17350             // ...then reset the day, keeping it in the new month
17351             new_month = new_date.getUTCMonth();
17352             new_date.setUTCDate(day);
17353             test = function(){
17354                 return new_month != new_date.getUTCMonth();
17355             };
17356         }
17357         // Common date-resetting loop -- if date is beyond end of month, make it
17358         // end of month
17359         while (test()){
17360             new_date.setUTCDate(--day);
17361             new_date.setUTCMonth(new_month);
17362         }
17363         return new_date;
17364     },
17365
17366     moveYear: function(date, dir)
17367     {
17368         return this.moveMonth(date, dir*12);
17369     },
17370
17371     dateWithinRange: function(date)
17372     {
17373         return date >= this.startDate && date <= this.endDate;
17374     },
17375
17376     
17377     remove: function() 
17378     {
17379         this.picker().remove();
17380     }
17381    
17382 });
17383
17384 Roo.apply(Roo.bootstrap.DateField,  {
17385     
17386     head : {
17387         tag: 'thead',
17388         cn: [
17389         {
17390             tag: 'tr',
17391             cn: [
17392             {
17393                 tag: 'th',
17394                 cls: 'prev',
17395                 html: '<i class="fa fa-arrow-left"/>'
17396             },
17397             {
17398                 tag: 'th',
17399                 cls: 'switch',
17400                 colspan: '5'
17401             },
17402             {
17403                 tag: 'th',
17404                 cls: 'next',
17405                 html: '<i class="fa fa-arrow-right"/>'
17406             }
17407
17408             ]
17409         }
17410         ]
17411     },
17412     
17413     content : {
17414         tag: 'tbody',
17415         cn: [
17416         {
17417             tag: 'tr',
17418             cn: [
17419             {
17420                 tag: 'td',
17421                 colspan: '7'
17422             }
17423             ]
17424         }
17425         ]
17426     },
17427     
17428     footer : {
17429         tag: 'tfoot',
17430         cn: [
17431         {
17432             tag: 'tr',
17433             cn: [
17434             {
17435                 tag: 'th',
17436                 colspan: '7',
17437                 cls: 'today'
17438             }
17439                     
17440             ]
17441         }
17442         ]
17443     },
17444     
17445     dates:{
17446         en: {
17447             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17448             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17449             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17450             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17451             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17452             today: "Today"
17453         }
17454     },
17455     
17456     modes: [
17457     {
17458         clsName: 'days',
17459         navFnc: 'Month',
17460         navStep: 1
17461     },
17462     {
17463         clsName: 'months',
17464         navFnc: 'FullYear',
17465         navStep: 1
17466     },
17467     {
17468         clsName: 'years',
17469         navFnc: 'FullYear',
17470         navStep: 10
17471     }]
17472 });
17473
17474 Roo.apply(Roo.bootstrap.DateField,  {
17475   
17476     template : {
17477         tag: 'div',
17478         cls: 'datepicker dropdown-menu roo-dynamic',
17479         cn: [
17480         {
17481             tag: 'div',
17482             cls: 'datepicker-days',
17483             cn: [
17484             {
17485                 tag: 'table',
17486                 cls: 'table-condensed',
17487                 cn:[
17488                 Roo.bootstrap.DateField.head,
17489                 {
17490                     tag: 'tbody'
17491                 },
17492                 Roo.bootstrap.DateField.footer
17493                 ]
17494             }
17495             ]
17496         },
17497         {
17498             tag: 'div',
17499             cls: 'datepicker-months',
17500             cn: [
17501             {
17502                 tag: 'table',
17503                 cls: 'table-condensed',
17504                 cn:[
17505                 Roo.bootstrap.DateField.head,
17506                 Roo.bootstrap.DateField.content,
17507                 Roo.bootstrap.DateField.footer
17508                 ]
17509             }
17510             ]
17511         },
17512         {
17513             tag: 'div',
17514             cls: 'datepicker-years',
17515             cn: [
17516             {
17517                 tag: 'table',
17518                 cls: 'table-condensed',
17519                 cn:[
17520                 Roo.bootstrap.DateField.head,
17521                 Roo.bootstrap.DateField.content,
17522                 Roo.bootstrap.DateField.footer
17523                 ]
17524             }
17525             ]
17526         }
17527         ]
17528     }
17529 });
17530
17531  
17532
17533  /*
17534  * - LGPL
17535  *
17536  * TimeField
17537  * 
17538  */
17539
17540 /**
17541  * @class Roo.bootstrap.TimeField
17542  * @extends Roo.bootstrap.Input
17543  * Bootstrap DateField class
17544  * 
17545  * 
17546  * @constructor
17547  * Create a new TimeField
17548  * @param {Object} config The config object
17549  */
17550
17551 Roo.bootstrap.TimeField = function(config){
17552     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17553     this.addEvents({
17554             /**
17555              * @event show
17556              * Fires when this field show.
17557              * @param {Roo.bootstrap.DateField} thisthis
17558              * @param {Mixed} date The date value
17559              */
17560             show : true,
17561             /**
17562              * @event show
17563              * Fires when this field hide.
17564              * @param {Roo.bootstrap.DateField} this
17565              * @param {Mixed} date The date value
17566              */
17567             hide : true,
17568             /**
17569              * @event select
17570              * Fires when select a date.
17571              * @param {Roo.bootstrap.DateField} this
17572              * @param {Mixed} date The date value
17573              */
17574             select : true
17575         });
17576 };
17577
17578 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
17579     
17580     /**
17581      * @cfg {String} format
17582      * The default time format string which can be overriden for localization support.  The format must be
17583      * valid according to {@link Date#parseDate} (defaults to 'H:i').
17584      */
17585     format : "H:i",
17586        
17587     onRender: function(ct, position)
17588     {
17589         
17590         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17591                 
17592         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17593         
17594         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17595         
17596         this.pop = this.picker().select('>.datepicker-time',true).first();
17597         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17598         
17599         this.picker().on('mousedown', this.onMousedown, this);
17600         this.picker().on('click', this.onClick, this);
17601         
17602         this.picker().addClass('datepicker-dropdown');
17603     
17604         this.fillTime();
17605         this.update();
17606             
17607         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17608         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17609         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17610         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17611         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17612         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17613
17614     },
17615     
17616     fireKey: function(e){
17617         if (!this.picker().isVisible()){
17618             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17619                 this.show();
17620             }
17621             return;
17622         }
17623
17624         e.preventDefault();
17625         
17626         switch(e.keyCode){
17627             case 27: // escape
17628                 this.hide();
17629                 break;
17630             case 37: // left
17631             case 39: // right
17632                 this.onTogglePeriod();
17633                 break;
17634             case 38: // up
17635                 this.onIncrementMinutes();
17636                 break;
17637             case 40: // down
17638                 this.onDecrementMinutes();
17639                 break;
17640             case 13: // enter
17641             case 9: // tab
17642                 this.setTime();
17643                 break;
17644         }
17645     },
17646     
17647     onClick: function(e) {
17648         e.stopPropagation();
17649         e.preventDefault();
17650     },
17651     
17652     picker : function()
17653     {
17654         return this.el.select('.datepicker', true).first();
17655     },
17656     
17657     fillTime: function()
17658     {    
17659         var time = this.pop.select('tbody', true).first();
17660         
17661         time.dom.innerHTML = '';
17662         
17663         time.createChild({
17664             tag: 'tr',
17665             cn: [
17666                 {
17667                     tag: 'td',
17668                     cn: [
17669                         {
17670                             tag: 'a',
17671                             href: '#',
17672                             cls: 'btn',
17673                             cn: [
17674                                 {
17675                                     tag: 'span',
17676                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
17677                                 }
17678                             ]
17679                         } 
17680                     ]
17681                 },
17682                 {
17683                     tag: 'td',
17684                     cls: 'separator'
17685                 },
17686                 {
17687                     tag: 'td',
17688                     cn: [
17689                         {
17690                             tag: 'a',
17691                             href: '#',
17692                             cls: 'btn',
17693                             cn: [
17694                                 {
17695                                     tag: 'span',
17696                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
17697                                 }
17698                             ]
17699                         }
17700                     ]
17701                 },
17702                 {
17703                     tag: 'td',
17704                     cls: 'separator'
17705                 }
17706             ]
17707         });
17708         
17709         time.createChild({
17710             tag: 'tr',
17711             cn: [
17712                 {
17713                     tag: 'td',
17714                     cn: [
17715                         {
17716                             tag: 'span',
17717                             cls: 'timepicker-hour',
17718                             html: '00'
17719                         }  
17720                     ]
17721                 },
17722                 {
17723                     tag: 'td',
17724                     cls: 'separator',
17725                     html: ':'
17726                 },
17727                 {
17728                     tag: 'td',
17729                     cn: [
17730                         {
17731                             tag: 'span',
17732                             cls: 'timepicker-minute',
17733                             html: '00'
17734                         }  
17735                     ]
17736                 },
17737                 {
17738                     tag: 'td',
17739                     cls: 'separator'
17740                 },
17741                 {
17742                     tag: 'td',
17743                     cn: [
17744                         {
17745                             tag: 'button',
17746                             type: 'button',
17747                             cls: 'btn btn-primary period',
17748                             html: 'AM'
17749                             
17750                         }
17751                     ]
17752                 }
17753             ]
17754         });
17755         
17756         time.createChild({
17757             tag: 'tr',
17758             cn: [
17759                 {
17760                     tag: 'td',
17761                     cn: [
17762                         {
17763                             tag: 'a',
17764                             href: '#',
17765                             cls: 'btn',
17766                             cn: [
17767                                 {
17768                                     tag: 'span',
17769                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
17770                                 }
17771                             ]
17772                         }
17773                     ]
17774                 },
17775                 {
17776                     tag: 'td',
17777                     cls: 'separator'
17778                 },
17779                 {
17780                     tag: 'td',
17781                     cn: [
17782                         {
17783                             tag: 'a',
17784                             href: '#',
17785                             cls: 'btn',
17786                             cn: [
17787                                 {
17788                                     tag: 'span',
17789                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
17790                                 }
17791                             ]
17792                         }
17793                     ]
17794                 },
17795                 {
17796                     tag: 'td',
17797                     cls: 'separator'
17798                 }
17799             ]
17800         });
17801         
17802     },
17803     
17804     update: function()
17805     {
17806         
17807         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17808         
17809         this.fill();
17810     },
17811     
17812     fill: function() 
17813     {
17814         var hours = this.time.getHours();
17815         var minutes = this.time.getMinutes();
17816         var period = 'AM';
17817         
17818         if(hours > 11){
17819             period = 'PM';
17820         }
17821         
17822         if(hours == 0){
17823             hours = 12;
17824         }
17825         
17826         
17827         if(hours > 12){
17828             hours = hours - 12;
17829         }
17830         
17831         if(hours < 10){
17832             hours = '0' + hours;
17833         }
17834         
17835         if(minutes < 10){
17836             minutes = '0' + minutes;
17837         }
17838         
17839         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
17840         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
17841         this.pop.select('button', true).first().dom.innerHTML = period;
17842         
17843     },
17844     
17845     place: function()
17846     {   
17847         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
17848         
17849         var cls = ['bottom'];
17850         
17851         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
17852             cls.pop();
17853             cls.push('top');
17854         }
17855         
17856         cls.push('right');
17857         
17858         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
17859             cls.pop();
17860             cls.push('left');
17861         }
17862         
17863         this.picker().addClass(cls.join('-'));
17864         
17865         var _this = this;
17866         
17867         Roo.each(cls, function(c){
17868             if(c == 'bottom'){
17869                 _this.picker().setTop(_this.inputEl().getHeight());
17870                 return;
17871             }
17872             if(c == 'top'){
17873                 _this.picker().setTop(0 - _this.picker().getHeight());
17874                 return;
17875             }
17876             
17877             if(c == 'left'){
17878                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
17879                 return;
17880             }
17881             if(c == 'right'){
17882                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
17883                 return;
17884             }
17885         });
17886         
17887     },
17888   
17889     onFocus : function()
17890     {
17891         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
17892         this.show();
17893     },
17894     
17895     onBlur : function()
17896     {
17897         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
17898         this.hide();
17899     },
17900     
17901     show : function()
17902     {
17903         this.picker().show();
17904         this.pop.show();
17905         this.update();
17906         this.place();
17907         
17908         this.fireEvent('show', this, this.date);
17909     },
17910     
17911     hide : function()
17912     {
17913         this.picker().hide();
17914         this.pop.hide();
17915         
17916         this.fireEvent('hide', this, this.date);
17917     },
17918     
17919     setTime : function()
17920     {
17921         this.hide();
17922         this.setValue(this.time.format(this.format));
17923         
17924         this.fireEvent('select', this, this.date);
17925         
17926         
17927     },
17928     
17929     onMousedown: function(e){
17930         e.stopPropagation();
17931         e.preventDefault();
17932     },
17933     
17934     onIncrementHours: function()
17935     {
17936         Roo.log('onIncrementHours');
17937         this.time = this.time.add(Date.HOUR, 1);
17938         this.update();
17939         
17940     },
17941     
17942     onDecrementHours: function()
17943     {
17944         Roo.log('onDecrementHours');
17945         this.time = this.time.add(Date.HOUR, -1);
17946         this.update();
17947     },
17948     
17949     onIncrementMinutes: function()
17950     {
17951         Roo.log('onIncrementMinutes');
17952         this.time = this.time.add(Date.MINUTE, 1);
17953         this.update();
17954     },
17955     
17956     onDecrementMinutes: function()
17957     {
17958         Roo.log('onDecrementMinutes');
17959         this.time = this.time.add(Date.MINUTE, -1);
17960         this.update();
17961     },
17962     
17963     onTogglePeriod: function()
17964     {
17965         Roo.log('onTogglePeriod');
17966         this.time = this.time.add(Date.HOUR, 12);
17967         this.update();
17968     }
17969     
17970    
17971 });
17972
17973 Roo.apply(Roo.bootstrap.TimeField,  {
17974     
17975     content : {
17976         tag: 'tbody',
17977         cn: [
17978             {
17979                 tag: 'tr',
17980                 cn: [
17981                 {
17982                     tag: 'td',
17983                     colspan: '7'
17984                 }
17985                 ]
17986             }
17987         ]
17988     },
17989     
17990     footer : {
17991         tag: 'tfoot',
17992         cn: [
17993             {
17994                 tag: 'tr',
17995                 cn: [
17996                 {
17997                     tag: 'th',
17998                     colspan: '7',
17999                     cls: '',
18000                     cn: [
18001                         {
18002                             tag: 'button',
18003                             cls: 'btn btn-info ok',
18004                             html: 'OK'
18005                         }
18006                     ]
18007                 }
18008
18009                 ]
18010             }
18011         ]
18012     }
18013 });
18014
18015 Roo.apply(Roo.bootstrap.TimeField,  {
18016   
18017     template : {
18018         tag: 'div',
18019         cls: 'datepicker dropdown-menu',
18020         cn: [
18021             {
18022                 tag: 'div',
18023                 cls: 'datepicker-time',
18024                 cn: [
18025                 {
18026                     tag: 'table',
18027                     cls: 'table-condensed',
18028                     cn:[
18029                     Roo.bootstrap.TimeField.content,
18030                     Roo.bootstrap.TimeField.footer
18031                     ]
18032                 }
18033                 ]
18034             }
18035         ]
18036     }
18037 });
18038
18039  
18040
18041  /*
18042  * - LGPL
18043  *
18044  * MonthField
18045  * 
18046  */
18047
18048 /**
18049  * @class Roo.bootstrap.MonthField
18050  * @extends Roo.bootstrap.Input
18051  * Bootstrap MonthField class
18052  * 
18053  * @cfg {String} language default en
18054  * 
18055  * @constructor
18056  * Create a new MonthField
18057  * @param {Object} config The config object
18058  */
18059
18060 Roo.bootstrap.MonthField = function(config){
18061     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
18062     
18063     this.addEvents({
18064         /**
18065          * @event show
18066          * Fires when this field show.
18067          * @param {Roo.bootstrap.MonthField} this
18068          * @param {Mixed} date The date value
18069          */
18070         show : true,
18071         /**
18072          * @event show
18073          * Fires when this field hide.
18074          * @param {Roo.bootstrap.MonthField} this
18075          * @param {Mixed} date The date value
18076          */
18077         hide : true,
18078         /**
18079          * @event select
18080          * Fires when select a date.
18081          * @param {Roo.bootstrap.MonthField} this
18082          * @param {String} oldvalue The old value
18083          * @param {String} newvalue The new value
18084          */
18085         select : true
18086     });
18087 };
18088
18089 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
18090     
18091     onRender: function(ct, position)
18092     {
18093         
18094         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
18095         
18096         this.language = this.language || 'en';
18097         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
18098         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
18099         
18100         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
18101         this.isInline = false;
18102         this.isInput = true;
18103         this.component = this.el.select('.add-on', true).first() || false;
18104         this.component = (this.component && this.component.length === 0) ? false : this.component;
18105         this.hasInput = this.component && this.inputEL().length;
18106         
18107         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
18108         
18109         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18110         
18111         this.picker().on('mousedown', this.onMousedown, this);
18112         this.picker().on('click', this.onClick, this);
18113         
18114         this.picker().addClass('datepicker-dropdown');
18115         
18116         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18117             v.setStyle('width', '189px');
18118         });
18119         
18120         this.fillMonths();
18121         
18122         this.update();
18123         
18124         if(this.isInline) {
18125             this.show();
18126         }
18127         
18128     },
18129     
18130     setValue: function(v, suppressEvent)
18131     {   
18132         var o = this.getValue();
18133         
18134         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
18135         
18136         this.update();
18137
18138         if(suppressEvent !== true){
18139             this.fireEvent('select', this, o, v);
18140         }
18141         
18142     },
18143     
18144     getValue: function()
18145     {
18146         return this.value;
18147     },
18148     
18149     onClick: function(e) 
18150     {
18151         e.stopPropagation();
18152         e.preventDefault();
18153         
18154         var target = e.getTarget();
18155         
18156         if(target.nodeName.toLowerCase() === 'i'){
18157             target = Roo.get(target).dom.parentNode;
18158         }
18159         
18160         var nodeName = target.nodeName;
18161         var className = target.className;
18162         var html = target.innerHTML;
18163         
18164         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18165             return;
18166         }
18167         
18168         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18169         
18170         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18171         
18172         this.hide();
18173                         
18174     },
18175     
18176     picker : function()
18177     {
18178         return this.pickerEl;
18179     },
18180     
18181     fillMonths: function()
18182     {    
18183         var i = 0;
18184         var months = this.picker().select('>.datepicker-months td', true).first();
18185         
18186         months.dom.innerHTML = '';
18187         
18188         while (i < 12) {
18189             var month = {
18190                 tag: 'span',
18191                 cls: 'month',
18192                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18193             };
18194             
18195             months.createChild(month);
18196         }
18197         
18198     },
18199     
18200     update: function()
18201     {
18202         var _this = this;
18203         
18204         if(typeof(this.vIndex) == 'undefined' && this.value.length){
18205             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18206         }
18207         
18208         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18209             e.removeClass('active');
18210             
18211             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18212                 e.addClass('active');
18213             }
18214         })
18215     },
18216     
18217     place: function()
18218     {
18219         if(this.isInline) {
18220             return;
18221         }
18222         
18223         this.picker().removeClass(['bottom', 'top']);
18224         
18225         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18226             /*
18227              * place to the top of element!
18228              *
18229              */
18230             
18231             this.picker().addClass('top');
18232             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18233             
18234             return;
18235         }
18236         
18237         this.picker().addClass('bottom');
18238         
18239         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18240     },
18241     
18242     onFocus : function()
18243     {
18244         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18245         this.show();
18246     },
18247     
18248     onBlur : function()
18249     {
18250         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18251         
18252         var d = this.inputEl().getValue();
18253         
18254         this.setValue(d);
18255                 
18256         this.hide();
18257     },
18258     
18259     show : function()
18260     {
18261         this.picker().show();
18262         this.picker().select('>.datepicker-months', true).first().show();
18263         this.update();
18264         this.place();
18265         
18266         this.fireEvent('show', this, this.date);
18267     },
18268     
18269     hide : function()
18270     {
18271         if(this.isInline) {
18272             return;
18273         }
18274         this.picker().hide();
18275         this.fireEvent('hide', this, this.date);
18276         
18277     },
18278     
18279     onMousedown: function(e)
18280     {
18281         e.stopPropagation();
18282         e.preventDefault();
18283     },
18284     
18285     keyup: function(e)
18286     {
18287         Roo.bootstrap.MonthField.superclass.keyup.call(this);
18288         this.update();
18289     },
18290
18291     fireKey: function(e)
18292     {
18293         if (!this.picker().isVisible()){
18294             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
18295                 this.show();
18296             }
18297             return;
18298         }
18299         
18300         var dir;
18301         
18302         switch(e.keyCode){
18303             case 27: // escape
18304                 this.hide();
18305                 e.preventDefault();
18306                 break;
18307             case 37: // left
18308             case 39: // right
18309                 dir = e.keyCode == 37 ? -1 : 1;
18310                 
18311                 this.vIndex = this.vIndex + dir;
18312                 
18313                 if(this.vIndex < 0){
18314                     this.vIndex = 0;
18315                 }
18316                 
18317                 if(this.vIndex > 11){
18318                     this.vIndex = 11;
18319                 }
18320                 
18321                 if(isNaN(this.vIndex)){
18322                     this.vIndex = 0;
18323                 }
18324                 
18325                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18326                 
18327                 break;
18328             case 38: // up
18329             case 40: // down
18330                 
18331                 dir = e.keyCode == 38 ? -1 : 1;
18332                 
18333                 this.vIndex = this.vIndex + dir * 4;
18334                 
18335                 if(this.vIndex < 0){
18336                     this.vIndex = 0;
18337                 }
18338                 
18339                 if(this.vIndex > 11){
18340                     this.vIndex = 11;
18341                 }
18342                 
18343                 if(isNaN(this.vIndex)){
18344                     this.vIndex = 0;
18345                 }
18346                 
18347                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18348                 break;
18349                 
18350             case 13: // enter
18351                 
18352                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18353                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18354                 }
18355                 
18356                 this.hide();
18357                 e.preventDefault();
18358                 break;
18359             case 9: // tab
18360                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18361                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18362                 }
18363                 this.hide();
18364                 break;
18365             case 16: // shift
18366             case 17: // ctrl
18367             case 18: // alt
18368                 break;
18369             default :
18370                 this.hide();
18371                 
18372         }
18373     },
18374     
18375     remove: function() 
18376     {
18377         this.picker().remove();
18378     }
18379    
18380 });
18381
18382 Roo.apply(Roo.bootstrap.MonthField,  {
18383     
18384     content : {
18385         tag: 'tbody',
18386         cn: [
18387         {
18388             tag: 'tr',
18389             cn: [
18390             {
18391                 tag: 'td',
18392                 colspan: '7'
18393             }
18394             ]
18395         }
18396         ]
18397     },
18398     
18399     dates:{
18400         en: {
18401             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18402             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18403         }
18404     }
18405 });
18406
18407 Roo.apply(Roo.bootstrap.MonthField,  {
18408   
18409     template : {
18410         tag: 'div',
18411         cls: 'datepicker dropdown-menu roo-dynamic',
18412         cn: [
18413             {
18414                 tag: 'div',
18415                 cls: 'datepicker-months',
18416                 cn: [
18417                 {
18418                     tag: 'table',
18419                     cls: 'table-condensed',
18420                     cn:[
18421                         Roo.bootstrap.DateField.content
18422                     ]
18423                 }
18424                 ]
18425             }
18426         ]
18427     }
18428 });
18429
18430  
18431
18432  
18433  /*
18434  * - LGPL
18435  *
18436  * CheckBox
18437  * 
18438  */
18439
18440 /**
18441  * @class Roo.bootstrap.CheckBox
18442  * @extends Roo.bootstrap.Input
18443  * Bootstrap CheckBox class
18444  * 
18445  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18446  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18447  * @cfg {String} boxLabel The text that appears beside the checkbox
18448  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18449  * @cfg {Boolean} checked initnal the element
18450  * @cfg {Boolean} inline inline the element (default false)
18451  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18452  * 
18453  * @constructor
18454  * Create a new CheckBox
18455  * @param {Object} config The config object
18456  */
18457
18458 Roo.bootstrap.CheckBox = function(config){
18459     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18460    
18461     this.addEvents({
18462         /**
18463         * @event check
18464         * Fires when the element is checked or unchecked.
18465         * @param {Roo.bootstrap.CheckBox} this This input
18466         * @param {Boolean} checked The new checked value
18467         */
18468        check : true
18469     });
18470     
18471 };
18472
18473 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
18474   
18475     inputType: 'checkbox',
18476     inputValue: 1,
18477     valueOff: 0,
18478     boxLabel: false,
18479     checked: false,
18480     weight : false,
18481     inline: false,
18482     
18483     getAutoCreate : function()
18484     {
18485         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18486         
18487         var id = Roo.id();
18488         
18489         var cfg = {};
18490         
18491         cfg.cls = 'form-group ' + this.inputType; //input-group
18492         
18493         if(this.inline){
18494             cfg.cls += ' ' + this.inputType + '-inline';
18495         }
18496         
18497         var input =  {
18498             tag: 'input',
18499             id : id,
18500             type : this.inputType,
18501             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18502             cls : 'roo-' + this.inputType, //'form-box',
18503             placeholder : this.placeholder || ''
18504             
18505         };
18506         
18507         if (this.weight) { // Validity check?
18508             cfg.cls += " " + this.inputType + "-" + this.weight;
18509         }
18510         
18511         if (this.disabled) {
18512             input.disabled=true;
18513         }
18514         
18515         if(this.checked){
18516             input.checked = this.checked;
18517         }
18518         
18519         if (this.name) {
18520             input.name = this.name;
18521         }
18522         
18523         if (this.size) {
18524             input.cls += ' input-' + this.size;
18525         }
18526         
18527         var settings=this;
18528         
18529         ['xs','sm','md','lg'].map(function(size){
18530             if (settings[size]) {
18531                 cfg.cls += ' col-' + size + '-' + settings[size];
18532             }
18533         });
18534         
18535         var inputblock = input;
18536          
18537         if (this.before || this.after) {
18538             
18539             inputblock = {
18540                 cls : 'input-group',
18541                 cn :  [] 
18542             };
18543             
18544             if (this.before) {
18545                 inputblock.cn.push({
18546                     tag :'span',
18547                     cls : 'input-group-addon',
18548                     html : this.before
18549                 });
18550             }
18551             
18552             inputblock.cn.push(input);
18553             
18554             if (this.after) {
18555                 inputblock.cn.push({
18556                     tag :'span',
18557                     cls : 'input-group-addon',
18558                     html : this.after
18559                 });
18560             }
18561             
18562         }
18563         
18564         if (align ==='left' && this.fieldLabel.length) {
18565                 Roo.log("left and has label");
18566                 cfg.cn = [
18567                     
18568                     {
18569                         tag: 'label',
18570                         'for' :  id,
18571                         cls : 'control-label col-md-' + this.labelWidth,
18572                         html : this.fieldLabel
18573                         
18574                     },
18575                     {
18576                         cls : "col-md-" + (12 - this.labelWidth), 
18577                         cn: [
18578                             inputblock
18579                         ]
18580                     }
18581                     
18582                 ];
18583         } else if ( this.fieldLabel.length) {
18584                 Roo.log(" label");
18585                 cfg.cn = [
18586                    
18587                     {
18588                         tag: this.boxLabel ? 'span' : 'label',
18589                         'for': id,
18590                         cls: 'control-label box-input-label',
18591                         //cls : 'input-group-addon',
18592                         html : this.fieldLabel
18593                         
18594                     },
18595                     
18596                     inputblock
18597                     
18598                 ];
18599
18600         } else {
18601             
18602                 Roo.log(" no label && no align");
18603                 cfg.cn = [  inputblock ] ;
18604                 
18605                 
18606         }
18607         if(this.boxLabel){
18608              var boxLabelCfg = {
18609                 tag: 'label',
18610                 //'for': id, // box label is handled by onclick - so no for...
18611                 cls: 'box-label',
18612                 html: this.boxLabel
18613             };
18614             
18615             if(this.tooltip){
18616                 boxLabelCfg.tooltip = this.tooltip;
18617             }
18618              
18619             cfg.cn.push(boxLabelCfg);
18620         }
18621         
18622         
18623        
18624         return cfg;
18625         
18626     },
18627     
18628     /**
18629      * return the real input element.
18630      */
18631     inputEl: function ()
18632     {
18633         return this.el.select('input.roo-' + this.inputType,true).first();
18634     },
18635     
18636     labelEl: function()
18637     {
18638         return this.el.select('label.control-label',true).first();
18639     },
18640     /* depricated... */
18641     
18642     label: function()
18643     {
18644         return this.labelEl();
18645     },
18646     
18647     initEvents : function()
18648     {
18649 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18650         
18651         this.inputEl().on('click', this.onClick,  this);
18652         
18653         if (this.boxLabel) { 
18654             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
18655         }
18656         
18657         this.startValue = this.getValue();
18658         
18659         if(this.groupId){
18660             Roo.bootstrap.CheckBox.register(this);
18661         }
18662     },
18663     
18664     onClick : function()
18665     {   
18666         this.setChecked(!this.checked);
18667     },
18668     
18669     setChecked : function(state,suppressEvent)
18670     {
18671         this.startValue = this.getValue();
18672         
18673         if(this.inputType == 'radio'){
18674             
18675             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18676                 e.dom.checked = false;
18677             });
18678             
18679             this.inputEl().dom.checked = true;
18680             
18681             this.inputEl().dom.value = this.inputValue;
18682             
18683             if(suppressEvent !== true){
18684                 this.fireEvent('check', this, true);
18685             }
18686             
18687             this.validate();
18688             
18689             return;
18690         }
18691         
18692         this.checked = state;
18693         
18694         this.inputEl().dom.checked = state;
18695         
18696         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18697         
18698         if(suppressEvent !== true){
18699             this.fireEvent('check', this, state);
18700         }
18701         
18702         this.validate();
18703     },
18704     
18705     getValue : function()
18706     {
18707         if(this.inputType == 'radio'){
18708             return this.getGroupValue();
18709         }
18710         
18711         return this.inputEl().getValue();
18712         
18713     },
18714     
18715     getGroupValue : function()
18716     {
18717         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18718             return '';
18719         }
18720         
18721         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18722     },
18723     
18724     setValue : function(v,suppressEvent)
18725     {
18726         if(this.inputType == 'radio'){
18727             this.setGroupValue(v, suppressEvent);
18728             return;
18729         }
18730         
18731         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18732         
18733         this.validate();
18734     },
18735     
18736     setGroupValue : function(v, suppressEvent)
18737     {
18738         this.startValue = this.getValue();
18739         
18740         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18741             e.dom.checked = false;
18742             
18743             if(e.dom.value == v){
18744                 e.dom.checked = true;
18745             }
18746         });
18747         
18748         if(suppressEvent !== true){
18749             this.fireEvent('check', this, true);
18750         }
18751
18752         this.validate();
18753         
18754         return;
18755     },
18756     
18757     validate : function()
18758     {
18759         if(
18760                 this.disabled || 
18761                 (this.inputType == 'radio' && this.validateRadio()) ||
18762                 (this.inputType == 'checkbox' && this.validateCheckbox())
18763         ){
18764             this.markValid();
18765             return true;
18766         }
18767         
18768         this.markInvalid();
18769         return false;
18770     },
18771     
18772     validateRadio : function()
18773     {
18774         var valid = false;
18775         
18776         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18777             if(!e.dom.checked){
18778                 return;
18779             }
18780             
18781             valid = true;
18782             
18783             return false;
18784         });
18785         
18786         return valid;
18787     },
18788     
18789     validateCheckbox : function()
18790     {
18791         if(!this.groupId){
18792             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18793         }
18794         
18795         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18796         
18797         if(!group){
18798             return false;
18799         }
18800         
18801         var r = false;
18802         
18803         for(var i in group){
18804             if(r){
18805                 break;
18806             }
18807             
18808             r = (group[i].getValue() == group[i].inputValue) ? true : false;
18809         }
18810         
18811         return r;
18812     },
18813     
18814     /**
18815      * Mark this field as valid
18816      */
18817     markValid : function()
18818     {
18819         if(this.allowBlank){
18820             return;
18821         }
18822         
18823         var _this = this;
18824         
18825         this.fireEvent('valid', this);
18826         
18827         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18828         
18829         if(this.groupId){
18830             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18831         }
18832         
18833         if(label){
18834             label.markValid();
18835         }
18836         
18837         if(this.inputType == 'radio'){
18838             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18839                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18840                 e.findParent('.form-group', false, true).addClass(_this.validClass);
18841             });
18842             
18843             return;
18844         }
18845         
18846         if(!this.groupId){
18847             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18848             this.el.findParent('.form-group', false, true).addClass(this.validClass);
18849             return;
18850         }
18851         
18852         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18853             
18854         if(!group){
18855             return;
18856         }
18857         
18858         for(var i in group){
18859             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18860             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
18861         }
18862     },
18863     
18864      /**
18865      * Mark this field as invalid
18866      * @param {String} msg The validation message
18867      */
18868     markInvalid : function(msg)
18869     {
18870         if(this.allowBlank){
18871             return;
18872         }
18873         
18874         var _this = this;
18875         
18876         this.fireEvent('invalid', this, msg);
18877         
18878         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18879         
18880         if(this.groupId){
18881             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18882         }
18883         
18884         if(label){
18885             label.markInvalid();
18886         }
18887             
18888         if(this.inputType == 'radio'){
18889             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18890                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18891                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
18892             });
18893             
18894             return;
18895         }
18896         
18897         if(!this.groupId){
18898             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18899             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
18900             return;
18901         }
18902         
18903         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18904         
18905         if(!group){
18906             return;
18907         }
18908         
18909         for(var i in group){
18910             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18911             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
18912         }
18913         
18914     }
18915     
18916 });
18917
18918 Roo.apply(Roo.bootstrap.CheckBox, {
18919     
18920     groups: {},
18921     
18922      /**
18923     * register a CheckBox Group
18924     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
18925     */
18926     register : function(checkbox)
18927     {
18928         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
18929             this.groups[checkbox.groupId] = {};
18930         }
18931         
18932         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
18933             return;
18934         }
18935         
18936         this.groups[checkbox.groupId][checkbox.name] = checkbox;
18937         
18938     },
18939     /**
18940     * fetch a CheckBox Group based on the group ID
18941     * @param {string} the group ID
18942     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
18943     */
18944     get: function(groupId) {
18945         if (typeof(this.groups[groupId]) == 'undefined') {
18946             return false;
18947         }
18948         
18949         return this.groups[groupId] ;
18950     }
18951     
18952     
18953 });
18954 /*
18955  * - LGPL
18956  *
18957  * Radio
18958  *
18959  *
18960  * not inline
18961  *<div class="radio">
18962   <label>
18963     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
18964     Option one is this and that&mdash;be sure to include why it's great
18965   </label>
18966 </div>
18967  *
18968  *
18969  *inline
18970  *<span>
18971  *<label class="radio-inline">fieldLabel</label>
18972  *<label class="radio-inline">
18973   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
18974 </label>
18975 <span>
18976  * 
18977  * 
18978  */
18979
18980 /**
18981  * @class Roo.bootstrap.Radio
18982  * @extends Roo.bootstrap.CheckBox
18983  * Bootstrap Radio class
18984
18985  * @constructor
18986  * Create a new Radio
18987  * @param {Object} config The config object
18988  */
18989
18990 Roo.bootstrap.Radio = function(config){
18991     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
18992    
18993 };
18994
18995 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
18996     
18997     inputType: 'radio',
18998     inputValue: '',
18999     valueOff: '',
19000     
19001     getAutoCreate : function()
19002     {
19003         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19004         align = align || 'left'; // default...
19005         
19006         
19007         
19008         var id = Roo.id();
19009         
19010         var cfg = {
19011                 tag : this.inline ? 'span' : 'div',
19012                 cls : '',
19013                 cn : []
19014         };
19015         
19016         var inline = this.inline ? ' radio-inline' : '';
19017         
19018         var lbl = {
19019                 tag: 'label' ,
19020                 // does not need for, as we wrap the input with it..
19021                 'for' : id,
19022                 cls : 'control-label box-label' + inline,
19023                 cn : []
19024         };
19025         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
19026         
19027         var fieldLabel = {
19028             tag: 'label' ,
19029             //cls : 'control-label' + inline,
19030             html : this.fieldLabel,
19031             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
19032         };
19033         
19034  
19035         
19036         
19037         var input =  {
19038             tag: 'input',
19039             id : id,
19040             type : this.inputType,
19041             //value : (!this.checked) ? this.valueOff : this.inputValue,
19042             value : this.inputValue,
19043             cls : 'roo-radio',
19044             placeholder : this.placeholder || '' // ?? needed????
19045             
19046         };
19047         if (this.weight) { // Validity check?
19048             input.cls += " radio-" + this.weight;
19049         }
19050         if (this.disabled) {
19051             input.disabled=true;
19052         }
19053         
19054         if(this.checked){
19055             input.checked = this.checked;
19056         }
19057         
19058         if (this.name) {
19059             input.name = this.name;
19060         }
19061         
19062         if (this.size) {
19063             input.cls += ' input-' + this.size;
19064         }
19065         
19066         //?? can span's inline have a width??
19067         
19068         var settings=this;
19069         ['xs','sm','md','lg'].map(function(size){
19070             if (settings[size]) {
19071                 cfg.cls += ' col-' + size + '-' + settings[size];
19072             }
19073         });
19074         
19075         var inputblock = input;
19076         
19077         if (this.before || this.after) {
19078             
19079             inputblock = {
19080                 cls : 'input-group',
19081                 tag : 'span',
19082                 cn :  [] 
19083             };
19084             if (this.before) {
19085                 inputblock.cn.push({
19086                     tag :'span',
19087                     cls : 'input-group-addon',
19088                     html : this.before
19089                 });
19090             }
19091             inputblock.cn.push(input);
19092             if (this.after) {
19093                 inputblock.cn.push({
19094                     tag :'span',
19095                     cls : 'input-group-addon',
19096                     html : this.after
19097                 });
19098             }
19099             
19100         };
19101         
19102         
19103         if (this.fieldLabel && this.fieldLabel.length) {
19104             cfg.cn.push(fieldLabel);
19105         }
19106        
19107         // normal bootstrap puts the input inside the label.
19108         // however with our styled version - it has to go after the input.
19109        
19110         //lbl.cn.push(inputblock);
19111         
19112         var lblwrap =  {
19113             tag: 'span',
19114             cls: 'radio' + inline,
19115             cn: [
19116                 inputblock,
19117                 lbl
19118             ]
19119         };
19120         
19121         cfg.cn.push( lblwrap);
19122         
19123         if(this.boxLabel){
19124             lbl.cn.push({
19125                 tag: 'span',
19126                 html: this.boxLabel
19127             })
19128         }
19129          
19130         
19131         return cfg;
19132         
19133     },
19134     
19135     initEvents : function()
19136     {
19137 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19138         
19139         this.inputEl().on('click', this.onClick,  this);
19140         if (this.boxLabel) {
19141             //Roo.log('find label');
19142             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
19143         }
19144         
19145     },
19146     
19147     inputEl: function ()
19148     {
19149         return this.el.select('input.roo-radio',true).first();
19150     },
19151     onClick : function()
19152     {   
19153         Roo.log("click");
19154         this.setChecked(true);
19155     },
19156     
19157     setChecked : function(state,suppressEvent)
19158     {
19159         if(state){
19160             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19161                 v.dom.checked = false;
19162             });
19163         }
19164         Roo.log(this.inputEl().dom);
19165         this.checked = state;
19166         this.inputEl().dom.checked = state;
19167         
19168         if(suppressEvent !== true){
19169             this.fireEvent('check', this, state);
19170         }
19171         
19172         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19173         
19174     },
19175     
19176     getGroupValue : function()
19177     {
19178         var value = '';
19179         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19180             if(v.dom.checked == true){
19181                 value = v.dom.value;
19182             }
19183         });
19184         
19185         return value;
19186     },
19187     
19188     /**
19189      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
19190      * @return {Mixed} value The field value
19191      */
19192     getValue : function(){
19193         return this.getGroupValue();
19194     }
19195     
19196 });
19197
19198  
19199 //<script type="text/javascript">
19200
19201 /*
19202  * Based  Ext JS Library 1.1.1
19203  * Copyright(c) 2006-2007, Ext JS, LLC.
19204  * LGPL
19205  *
19206  */
19207  
19208 /**
19209  * @class Roo.HtmlEditorCore
19210  * @extends Roo.Component
19211  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
19212  *
19213  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
19214  */
19215
19216 Roo.HtmlEditorCore = function(config){
19217     
19218     
19219     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
19220     
19221     
19222     this.addEvents({
19223         /**
19224          * @event initialize
19225          * Fires when the editor is fully initialized (including the iframe)
19226          * @param {Roo.HtmlEditorCore} this
19227          */
19228         initialize: true,
19229         /**
19230          * @event activate
19231          * Fires when the editor is first receives the focus. Any insertion must wait
19232          * until after this event.
19233          * @param {Roo.HtmlEditorCore} this
19234          */
19235         activate: true,
19236          /**
19237          * @event beforesync
19238          * Fires before the textarea is updated with content from the editor iframe. Return false
19239          * to cancel the sync.
19240          * @param {Roo.HtmlEditorCore} this
19241          * @param {String} html
19242          */
19243         beforesync: true,
19244          /**
19245          * @event beforepush
19246          * Fires before the iframe editor is updated with content from the textarea. Return false
19247          * to cancel the push.
19248          * @param {Roo.HtmlEditorCore} this
19249          * @param {String} html
19250          */
19251         beforepush: true,
19252          /**
19253          * @event sync
19254          * Fires when the textarea is updated with content from the editor iframe.
19255          * @param {Roo.HtmlEditorCore} this
19256          * @param {String} html
19257          */
19258         sync: true,
19259          /**
19260          * @event push
19261          * Fires when the iframe editor is updated with content from the textarea.
19262          * @param {Roo.HtmlEditorCore} this
19263          * @param {String} html
19264          */
19265         push: true,
19266         
19267         /**
19268          * @event editorevent
19269          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19270          * @param {Roo.HtmlEditorCore} this
19271          */
19272         editorevent: true
19273         
19274     });
19275     
19276     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
19277     
19278     // defaults : white / black...
19279     this.applyBlacklists();
19280     
19281     
19282     
19283 };
19284
19285
19286 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
19287
19288
19289      /**
19290      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
19291      */
19292     
19293     owner : false,
19294     
19295      /**
19296      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
19297      *                        Roo.resizable.
19298      */
19299     resizable : false,
19300      /**
19301      * @cfg {Number} height (in pixels)
19302      */   
19303     height: 300,
19304    /**
19305      * @cfg {Number} width (in pixels)
19306      */   
19307     width: 500,
19308     
19309     /**
19310      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19311      * 
19312      */
19313     stylesheets: false,
19314     
19315     // id of frame..
19316     frameId: false,
19317     
19318     // private properties
19319     validationEvent : false,
19320     deferHeight: true,
19321     initialized : false,
19322     activated : false,
19323     sourceEditMode : false,
19324     onFocus : Roo.emptyFn,
19325     iframePad:3,
19326     hideMode:'offsets',
19327     
19328     clearUp: true,
19329     
19330     // blacklist + whitelisted elements..
19331     black: false,
19332     white: false,
19333      
19334     
19335
19336     /**
19337      * Protected method that will not generally be called directly. It
19338      * is called when the editor initializes the iframe with HTML contents. Override this method if you
19339      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
19340      */
19341     getDocMarkup : function(){
19342         // body styles..
19343         var st = '';
19344         
19345         // inherit styels from page...?? 
19346         if (this.stylesheets === false) {
19347             
19348             Roo.get(document.head).select('style').each(function(node) {
19349                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19350             });
19351             
19352             Roo.get(document.head).select('link').each(function(node) { 
19353                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19354             });
19355             
19356         } else if (!this.stylesheets.length) {
19357                 // simple..
19358                 st = '<style type="text/css">' +
19359                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19360                    '</style>';
19361         } else { 
19362             
19363         }
19364         
19365         st +=  '<style type="text/css">' +
19366             'IMG { cursor: pointer } ' +
19367         '</style>';
19368
19369         
19370         return '<html><head>' + st  +
19371             //<style type="text/css">' +
19372             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19373             //'</style>' +
19374             ' </head><body class="roo-htmleditor-body"></body></html>';
19375     },
19376
19377     // private
19378     onRender : function(ct, position)
19379     {
19380         var _t = this;
19381         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19382         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19383         
19384         
19385         this.el.dom.style.border = '0 none';
19386         this.el.dom.setAttribute('tabIndex', -1);
19387         this.el.addClass('x-hidden hide');
19388         
19389         
19390         
19391         if(Roo.isIE){ // fix IE 1px bogus margin
19392             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19393         }
19394        
19395         
19396         this.frameId = Roo.id();
19397         
19398          
19399         
19400         var iframe = this.owner.wrap.createChild({
19401             tag: 'iframe',
19402             cls: 'form-control', // bootstrap..
19403             id: this.frameId,
19404             name: this.frameId,
19405             frameBorder : 'no',
19406             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
19407         }, this.el
19408         );
19409         
19410         
19411         this.iframe = iframe.dom;
19412
19413          this.assignDocWin();
19414         
19415         this.doc.designMode = 'on';
19416        
19417         this.doc.open();
19418         this.doc.write(this.getDocMarkup());
19419         this.doc.close();
19420
19421         
19422         var task = { // must defer to wait for browser to be ready
19423             run : function(){
19424                 //console.log("run task?" + this.doc.readyState);
19425                 this.assignDocWin();
19426                 if(this.doc.body || this.doc.readyState == 'complete'){
19427                     try {
19428                         this.doc.designMode="on";
19429                     } catch (e) {
19430                         return;
19431                     }
19432                     Roo.TaskMgr.stop(task);
19433                     this.initEditor.defer(10, this);
19434                 }
19435             },
19436             interval : 10,
19437             duration: 10000,
19438             scope: this
19439         };
19440         Roo.TaskMgr.start(task);
19441
19442     },
19443
19444     // private
19445     onResize : function(w, h)
19446     {
19447          Roo.log('resize: ' +w + ',' + h );
19448         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19449         if(!this.iframe){
19450             return;
19451         }
19452         if(typeof w == 'number'){
19453             
19454             this.iframe.style.width = w + 'px';
19455         }
19456         if(typeof h == 'number'){
19457             
19458             this.iframe.style.height = h + 'px';
19459             if(this.doc){
19460                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19461             }
19462         }
19463         
19464     },
19465
19466     /**
19467      * Toggles the editor between standard and source edit mode.
19468      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19469      */
19470     toggleSourceEdit : function(sourceEditMode){
19471         
19472         this.sourceEditMode = sourceEditMode === true;
19473         
19474         if(this.sourceEditMode){
19475  
19476             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
19477             
19478         }else{
19479             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19480             //this.iframe.className = '';
19481             this.deferFocus();
19482         }
19483         //this.setSize(this.owner.wrap.getSize());
19484         //this.fireEvent('editmodechange', this, this.sourceEditMode);
19485     },
19486
19487     
19488   
19489
19490     /**
19491      * Protected method that will not generally be called directly. If you need/want
19492      * custom HTML cleanup, this is the method you should override.
19493      * @param {String} html The HTML to be cleaned
19494      * return {String} The cleaned HTML
19495      */
19496     cleanHtml : function(html){
19497         html = String(html);
19498         if(html.length > 5){
19499             if(Roo.isSafari){ // strip safari nonsense
19500                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19501             }
19502         }
19503         if(html == '&nbsp;'){
19504             html = '';
19505         }
19506         return html;
19507     },
19508
19509     /**
19510      * HTML Editor -> Textarea
19511      * Protected method that will not generally be called directly. Syncs the contents
19512      * of the editor iframe with the textarea.
19513      */
19514     syncValue : function(){
19515         if(this.initialized){
19516             var bd = (this.doc.body || this.doc.documentElement);
19517             //this.cleanUpPaste(); -- this is done else where and causes havoc..
19518             var html = bd.innerHTML;
19519             if(Roo.isSafari){
19520                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19521                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19522                 if(m && m[1]){
19523                     html = '<div style="'+m[0]+'">' + html + '</div>';
19524                 }
19525             }
19526             html = this.cleanHtml(html);
19527             // fix up the special chars.. normaly like back quotes in word...
19528             // however we do not want to do this with chinese..
19529             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19530                 var cc = b.charCodeAt();
19531                 if (
19532                     (cc >= 0x4E00 && cc < 0xA000 ) ||
19533                     (cc >= 0x3400 && cc < 0x4E00 ) ||
19534                     (cc >= 0xf900 && cc < 0xfb00 )
19535                 ) {
19536                         return b;
19537                 }
19538                 return "&#"+cc+";" 
19539             });
19540             if(this.owner.fireEvent('beforesync', this, html) !== false){
19541                 this.el.dom.value = html;
19542                 this.owner.fireEvent('sync', this, html);
19543             }
19544         }
19545     },
19546
19547     /**
19548      * Protected method that will not generally be called directly. Pushes the value of the textarea
19549      * into the iframe editor.
19550      */
19551     pushValue : function(){
19552         if(this.initialized){
19553             var v = this.el.dom.value.trim();
19554             
19555 //            if(v.length < 1){
19556 //                v = '&#160;';
19557 //            }
19558             
19559             if(this.owner.fireEvent('beforepush', this, v) !== false){
19560                 var d = (this.doc.body || this.doc.documentElement);
19561                 d.innerHTML = v;
19562                 this.cleanUpPaste();
19563                 this.el.dom.value = d.innerHTML;
19564                 this.owner.fireEvent('push', this, v);
19565             }
19566         }
19567     },
19568
19569     // private
19570     deferFocus : function(){
19571         this.focus.defer(10, this);
19572     },
19573
19574     // doc'ed in Field
19575     focus : function(){
19576         if(this.win && !this.sourceEditMode){
19577             this.win.focus();
19578         }else{
19579             this.el.focus();
19580         }
19581     },
19582     
19583     assignDocWin: function()
19584     {
19585         var iframe = this.iframe;
19586         
19587          if(Roo.isIE){
19588             this.doc = iframe.contentWindow.document;
19589             this.win = iframe.contentWindow;
19590         } else {
19591 //            if (!Roo.get(this.frameId)) {
19592 //                return;
19593 //            }
19594 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19595 //            this.win = Roo.get(this.frameId).dom.contentWindow;
19596             
19597             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19598                 return;
19599             }
19600             
19601             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19602             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19603         }
19604     },
19605     
19606     // private
19607     initEditor : function(){
19608         //console.log("INIT EDITOR");
19609         this.assignDocWin();
19610         
19611         
19612         
19613         this.doc.designMode="on";
19614         this.doc.open();
19615         this.doc.write(this.getDocMarkup());
19616         this.doc.close();
19617         
19618         var dbody = (this.doc.body || this.doc.documentElement);
19619         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19620         // this copies styles from the containing element into thsi one..
19621         // not sure why we need all of this..
19622         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19623         
19624         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19625         //ss['background-attachment'] = 'fixed'; // w3c
19626         dbody.bgProperties = 'fixed'; // ie
19627         //Roo.DomHelper.applyStyles(dbody, ss);
19628         Roo.EventManager.on(this.doc, {
19629             //'mousedown': this.onEditorEvent,
19630             'mouseup': this.onEditorEvent,
19631             'dblclick': this.onEditorEvent,
19632             'click': this.onEditorEvent,
19633             'keyup': this.onEditorEvent,
19634             buffer:100,
19635             scope: this
19636         });
19637         if(Roo.isGecko){
19638             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19639         }
19640         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19641             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19642         }
19643         this.initialized = true;
19644
19645         this.owner.fireEvent('initialize', this);
19646         this.pushValue();
19647     },
19648
19649     // private
19650     onDestroy : function(){
19651         
19652         
19653         
19654         if(this.rendered){
19655             
19656             //for (var i =0; i < this.toolbars.length;i++) {
19657             //    // fixme - ask toolbars for heights?
19658             //    this.toolbars[i].onDestroy();
19659            // }
19660             
19661             //this.wrap.dom.innerHTML = '';
19662             //this.wrap.remove();
19663         }
19664     },
19665
19666     // private
19667     onFirstFocus : function(){
19668         
19669         this.assignDocWin();
19670         
19671         
19672         this.activated = true;
19673          
19674     
19675         if(Roo.isGecko){ // prevent silly gecko errors
19676             this.win.focus();
19677             var s = this.win.getSelection();
19678             if(!s.focusNode || s.focusNode.nodeType != 3){
19679                 var r = s.getRangeAt(0);
19680                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19681                 r.collapse(true);
19682                 this.deferFocus();
19683             }
19684             try{
19685                 this.execCmd('useCSS', true);
19686                 this.execCmd('styleWithCSS', false);
19687             }catch(e){}
19688         }
19689         this.owner.fireEvent('activate', this);
19690     },
19691
19692     // private
19693     adjustFont: function(btn){
19694         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19695         //if(Roo.isSafari){ // safari
19696         //    adjust *= 2;
19697        // }
19698         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19699         if(Roo.isSafari){ // safari
19700             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19701             v =  (v < 10) ? 10 : v;
19702             v =  (v > 48) ? 48 : v;
19703             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19704             
19705         }
19706         
19707         
19708         v = Math.max(1, v+adjust);
19709         
19710         this.execCmd('FontSize', v  );
19711     },
19712
19713     onEditorEvent : function(e)
19714     {
19715         this.owner.fireEvent('editorevent', this, e);
19716       //  this.updateToolbar();
19717         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19718     },
19719
19720     insertTag : function(tg)
19721     {
19722         // could be a bit smarter... -> wrap the current selected tRoo..
19723         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19724             
19725             range = this.createRange(this.getSelection());
19726             var wrappingNode = this.doc.createElement(tg.toLowerCase());
19727             wrappingNode.appendChild(range.extractContents());
19728             range.insertNode(wrappingNode);
19729
19730             return;
19731             
19732             
19733             
19734         }
19735         this.execCmd("formatblock",   tg);
19736         
19737     },
19738     
19739     insertText : function(txt)
19740     {
19741         
19742         
19743         var range = this.createRange();
19744         range.deleteContents();
19745                //alert(Sender.getAttribute('label'));
19746                
19747         range.insertNode(this.doc.createTextNode(txt));
19748     } ,
19749     
19750      
19751
19752     /**
19753      * Executes a Midas editor command on the editor document and performs necessary focus and
19754      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19755      * @param {String} cmd The Midas command
19756      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19757      */
19758     relayCmd : function(cmd, value){
19759         this.win.focus();
19760         this.execCmd(cmd, value);
19761         this.owner.fireEvent('editorevent', this);
19762         //this.updateToolbar();
19763         this.owner.deferFocus();
19764     },
19765
19766     /**
19767      * Executes a Midas editor command directly on the editor document.
19768      * For visual commands, you should use {@link #relayCmd} instead.
19769      * <b>This should only be called after the editor is initialized.</b>
19770      * @param {String} cmd The Midas command
19771      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19772      */
19773     execCmd : function(cmd, value){
19774         this.doc.execCommand(cmd, false, value === undefined ? null : value);
19775         this.syncValue();
19776     },
19777  
19778  
19779    
19780     /**
19781      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19782      * to insert tRoo.
19783      * @param {String} text | dom node.. 
19784      */
19785     insertAtCursor : function(text)
19786     {
19787         
19788         
19789         
19790         if(!this.activated){
19791             return;
19792         }
19793         /*
19794         if(Roo.isIE){
19795             this.win.focus();
19796             var r = this.doc.selection.createRange();
19797             if(r){
19798                 r.collapse(true);
19799                 r.pasteHTML(text);
19800                 this.syncValue();
19801                 this.deferFocus();
19802             
19803             }
19804             return;
19805         }
19806         */
19807         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19808             this.win.focus();
19809             
19810             
19811             // from jquery ui (MIT licenced)
19812             var range, node;
19813             var win = this.win;
19814             
19815             if (win.getSelection && win.getSelection().getRangeAt) {
19816                 range = win.getSelection().getRangeAt(0);
19817                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19818                 range.insertNode(node);
19819             } else if (win.document.selection && win.document.selection.createRange) {
19820                 // no firefox support
19821                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19822                 win.document.selection.createRange().pasteHTML(txt);
19823             } else {
19824                 // no firefox support
19825                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19826                 this.execCmd('InsertHTML', txt);
19827             } 
19828             
19829             this.syncValue();
19830             
19831             this.deferFocus();
19832         }
19833     },
19834  // private
19835     mozKeyPress : function(e){
19836         if(e.ctrlKey){
19837             var c = e.getCharCode(), cmd;
19838           
19839             if(c > 0){
19840                 c = String.fromCharCode(c).toLowerCase();
19841                 switch(c){
19842                     case 'b':
19843                         cmd = 'bold';
19844                         break;
19845                     case 'i':
19846                         cmd = 'italic';
19847                         break;
19848                     
19849                     case 'u':
19850                         cmd = 'underline';
19851                         break;
19852                     
19853                     case 'v':
19854                         this.cleanUpPaste.defer(100, this);
19855                         return;
19856                         
19857                 }
19858                 if(cmd){
19859                     this.win.focus();
19860                     this.execCmd(cmd);
19861                     this.deferFocus();
19862                     e.preventDefault();
19863                 }
19864                 
19865             }
19866         }
19867     },
19868
19869     // private
19870     fixKeys : function(){ // load time branching for fastest keydown performance
19871         if(Roo.isIE){
19872             return function(e){
19873                 var k = e.getKey(), r;
19874                 if(k == e.TAB){
19875                     e.stopEvent();
19876                     r = this.doc.selection.createRange();
19877                     if(r){
19878                         r.collapse(true);
19879                         r.pasteHTML('&#160;&#160;&#160;&#160;');
19880                         this.deferFocus();
19881                     }
19882                     return;
19883                 }
19884                 
19885                 if(k == e.ENTER){
19886                     r = this.doc.selection.createRange();
19887                     if(r){
19888                         var target = r.parentElement();
19889                         if(!target || target.tagName.toLowerCase() != 'li'){
19890                             e.stopEvent();
19891                             r.pasteHTML('<br />');
19892                             r.collapse(false);
19893                             r.select();
19894                         }
19895                     }
19896                 }
19897                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19898                     this.cleanUpPaste.defer(100, this);
19899                     return;
19900                 }
19901                 
19902                 
19903             };
19904         }else if(Roo.isOpera){
19905             return function(e){
19906                 var k = e.getKey();
19907                 if(k == e.TAB){
19908                     e.stopEvent();
19909                     this.win.focus();
19910                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
19911                     this.deferFocus();
19912                 }
19913                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19914                     this.cleanUpPaste.defer(100, this);
19915                     return;
19916                 }
19917                 
19918             };
19919         }else if(Roo.isSafari){
19920             return function(e){
19921                 var k = e.getKey();
19922                 
19923                 if(k == e.TAB){
19924                     e.stopEvent();
19925                     this.execCmd('InsertText','\t');
19926                     this.deferFocus();
19927                     return;
19928                 }
19929                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19930                     this.cleanUpPaste.defer(100, this);
19931                     return;
19932                 }
19933                 
19934              };
19935         }
19936     }(),
19937     
19938     getAllAncestors: function()
19939     {
19940         var p = this.getSelectedNode();
19941         var a = [];
19942         if (!p) {
19943             a.push(p); // push blank onto stack..
19944             p = this.getParentElement();
19945         }
19946         
19947         
19948         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
19949             a.push(p);
19950             p = p.parentNode;
19951         }
19952         a.push(this.doc.body);
19953         return a;
19954     },
19955     lastSel : false,
19956     lastSelNode : false,
19957     
19958     
19959     getSelection : function() 
19960     {
19961         this.assignDocWin();
19962         return Roo.isIE ? this.doc.selection : this.win.getSelection();
19963     },
19964     
19965     getSelectedNode: function() 
19966     {
19967         // this may only work on Gecko!!!
19968         
19969         // should we cache this!!!!
19970         
19971         
19972         
19973          
19974         var range = this.createRange(this.getSelection()).cloneRange();
19975         
19976         if (Roo.isIE) {
19977             var parent = range.parentElement();
19978             while (true) {
19979                 var testRange = range.duplicate();
19980                 testRange.moveToElementText(parent);
19981                 if (testRange.inRange(range)) {
19982                     break;
19983                 }
19984                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
19985                     break;
19986                 }
19987                 parent = parent.parentElement;
19988             }
19989             return parent;
19990         }
19991         
19992         // is ancestor a text element.
19993         var ac =  range.commonAncestorContainer;
19994         if (ac.nodeType == 3) {
19995             ac = ac.parentNode;
19996         }
19997         
19998         var ar = ac.childNodes;
19999          
20000         var nodes = [];
20001         var other_nodes = [];
20002         var has_other_nodes = false;
20003         for (var i=0;i<ar.length;i++) {
20004             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
20005                 continue;
20006             }
20007             // fullly contained node.
20008             
20009             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
20010                 nodes.push(ar[i]);
20011                 continue;
20012             }
20013             
20014             // probably selected..
20015             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
20016                 other_nodes.push(ar[i]);
20017                 continue;
20018             }
20019             // outer..
20020             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
20021                 continue;
20022             }
20023             
20024             
20025             has_other_nodes = true;
20026         }
20027         if (!nodes.length && other_nodes.length) {
20028             nodes= other_nodes;
20029         }
20030         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
20031             return false;
20032         }
20033         
20034         return nodes[0];
20035     },
20036     createRange: function(sel)
20037     {
20038         // this has strange effects when using with 
20039         // top toolbar - not sure if it's a great idea.
20040         //this.editor.contentWindow.focus();
20041         if (typeof sel != "undefined") {
20042             try {
20043                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
20044             } catch(e) {
20045                 return this.doc.createRange();
20046             }
20047         } else {
20048             return this.doc.createRange();
20049         }
20050     },
20051     getParentElement: function()
20052     {
20053         
20054         this.assignDocWin();
20055         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
20056         
20057         var range = this.createRange(sel);
20058          
20059         try {
20060             var p = range.commonAncestorContainer;
20061             while (p.nodeType == 3) { // text node
20062                 p = p.parentNode;
20063             }
20064             return p;
20065         } catch (e) {
20066             return null;
20067         }
20068     
20069     },
20070     /***
20071      *
20072      * Range intersection.. the hard stuff...
20073      *  '-1' = before
20074      *  '0' = hits..
20075      *  '1' = after.
20076      *         [ -- selected range --- ]
20077      *   [fail]                        [fail]
20078      *
20079      *    basically..
20080      *      if end is before start or  hits it. fail.
20081      *      if start is after end or hits it fail.
20082      *
20083      *   if either hits (but other is outside. - then it's not 
20084      *   
20085      *    
20086      **/
20087     
20088     
20089     // @see http://www.thismuchiknow.co.uk/?p=64.
20090     rangeIntersectsNode : function(range, node)
20091     {
20092         var nodeRange = node.ownerDocument.createRange();
20093         try {
20094             nodeRange.selectNode(node);
20095         } catch (e) {
20096             nodeRange.selectNodeContents(node);
20097         }
20098     
20099         var rangeStartRange = range.cloneRange();
20100         rangeStartRange.collapse(true);
20101     
20102         var rangeEndRange = range.cloneRange();
20103         rangeEndRange.collapse(false);
20104     
20105         var nodeStartRange = nodeRange.cloneRange();
20106         nodeStartRange.collapse(true);
20107     
20108         var nodeEndRange = nodeRange.cloneRange();
20109         nodeEndRange.collapse(false);
20110     
20111         return rangeStartRange.compareBoundaryPoints(
20112                  Range.START_TO_START, nodeEndRange) == -1 &&
20113                rangeEndRange.compareBoundaryPoints(
20114                  Range.START_TO_START, nodeStartRange) == 1;
20115         
20116          
20117     },
20118     rangeCompareNode : function(range, node)
20119     {
20120         var nodeRange = node.ownerDocument.createRange();
20121         try {
20122             nodeRange.selectNode(node);
20123         } catch (e) {
20124             nodeRange.selectNodeContents(node);
20125         }
20126         
20127         
20128         range.collapse(true);
20129     
20130         nodeRange.collapse(true);
20131      
20132         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
20133         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
20134          
20135         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
20136         
20137         var nodeIsBefore   =  ss == 1;
20138         var nodeIsAfter    = ee == -1;
20139         
20140         if (nodeIsBefore && nodeIsAfter) {
20141             return 0; // outer
20142         }
20143         if (!nodeIsBefore && nodeIsAfter) {
20144             return 1; //right trailed.
20145         }
20146         
20147         if (nodeIsBefore && !nodeIsAfter) {
20148             return 2;  // left trailed.
20149         }
20150         // fully contined.
20151         return 3;
20152     },
20153
20154     // private? - in a new class?
20155     cleanUpPaste :  function()
20156     {
20157         // cleans up the whole document..
20158         Roo.log('cleanuppaste');
20159         
20160         this.cleanUpChildren(this.doc.body);
20161         var clean = this.cleanWordChars(this.doc.body.innerHTML);
20162         if (clean != this.doc.body.innerHTML) {
20163             this.doc.body.innerHTML = clean;
20164         }
20165         
20166     },
20167     
20168     cleanWordChars : function(input) {// change the chars to hex code
20169         var he = Roo.HtmlEditorCore;
20170         
20171         var output = input;
20172         Roo.each(he.swapCodes, function(sw) { 
20173             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
20174             
20175             output = output.replace(swapper, sw[1]);
20176         });
20177         
20178         return output;
20179     },
20180     
20181     
20182     cleanUpChildren : function (n)
20183     {
20184         if (!n.childNodes.length) {
20185             return;
20186         }
20187         for (var i = n.childNodes.length-1; i > -1 ; i--) {
20188            this.cleanUpChild(n.childNodes[i]);
20189         }
20190     },
20191     
20192     
20193         
20194     
20195     cleanUpChild : function (node)
20196     {
20197         var ed = this;
20198         //console.log(node);
20199         if (node.nodeName == "#text") {
20200             // clean up silly Windows -- stuff?
20201             return; 
20202         }
20203         if (node.nodeName == "#comment") {
20204             node.parentNode.removeChild(node);
20205             // clean up silly Windows -- stuff?
20206             return; 
20207         }
20208         var lcname = node.tagName.toLowerCase();
20209         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
20210         // whitelist of tags..
20211         
20212         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
20213             // remove node.
20214             node.parentNode.removeChild(node);
20215             return;
20216             
20217         }
20218         
20219         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
20220         
20221         // remove <a name=....> as rendering on yahoo mailer is borked with this.
20222         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
20223         
20224         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
20225         //    remove_keep_children = true;
20226         //}
20227         
20228         if (remove_keep_children) {
20229             this.cleanUpChildren(node);
20230             // inserts everything just before this node...
20231             while (node.childNodes.length) {
20232                 var cn = node.childNodes[0];
20233                 node.removeChild(cn);
20234                 node.parentNode.insertBefore(cn, node);
20235             }
20236             node.parentNode.removeChild(node);
20237             return;
20238         }
20239         
20240         if (!node.attributes || !node.attributes.length) {
20241             this.cleanUpChildren(node);
20242             return;
20243         }
20244         
20245         function cleanAttr(n,v)
20246         {
20247             
20248             if (v.match(/^\./) || v.match(/^\//)) {
20249                 return;
20250             }
20251             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
20252                 return;
20253             }
20254             if (v.match(/^#/)) {
20255                 return;
20256             }
20257 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
20258             node.removeAttribute(n);
20259             
20260         }
20261         
20262         var cwhite = this.cwhite;
20263         var cblack = this.cblack;
20264             
20265         function cleanStyle(n,v)
20266         {
20267             if (v.match(/expression/)) { //XSS?? should we even bother..
20268                 node.removeAttribute(n);
20269                 return;
20270             }
20271             
20272             var parts = v.split(/;/);
20273             var clean = [];
20274             
20275             Roo.each(parts, function(p) {
20276                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
20277                 if (!p.length) {
20278                     return true;
20279                 }
20280                 var l = p.split(':').shift().replace(/\s+/g,'');
20281                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
20282                 
20283                 if ( cwhite.length && cblack.indexOf(l) > -1) {
20284 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20285                     //node.removeAttribute(n);
20286                     return true;
20287                 }
20288                 //Roo.log()
20289                 // only allow 'c whitelisted system attributes'
20290                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
20291 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20292                     //node.removeAttribute(n);
20293                     return true;
20294                 }
20295                 
20296                 
20297                  
20298                 
20299                 clean.push(p);
20300                 return true;
20301             });
20302             if (clean.length) { 
20303                 node.setAttribute(n, clean.join(';'));
20304             } else {
20305                 node.removeAttribute(n);
20306             }
20307             
20308         }
20309         
20310         
20311         for (var i = node.attributes.length-1; i > -1 ; i--) {
20312             var a = node.attributes[i];
20313             //console.log(a);
20314             
20315             if (a.name.toLowerCase().substr(0,2)=='on')  {
20316                 node.removeAttribute(a.name);
20317                 continue;
20318             }
20319             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
20320                 node.removeAttribute(a.name);
20321                 continue;
20322             }
20323             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
20324                 cleanAttr(a.name,a.value); // fixme..
20325                 continue;
20326             }
20327             if (a.name == 'style') {
20328                 cleanStyle(a.name,a.value);
20329                 continue;
20330             }
20331             /// clean up MS crap..
20332             // tecnically this should be a list of valid class'es..
20333             
20334             
20335             if (a.name == 'class') {
20336                 if (a.value.match(/^Mso/)) {
20337                     node.className = '';
20338                 }
20339                 
20340                 if (a.value.match(/body/)) {
20341                     node.className = '';
20342                 }
20343                 continue;
20344             }
20345             
20346             // style cleanup!?
20347             // class cleanup?
20348             
20349         }
20350         
20351         
20352         this.cleanUpChildren(node);
20353         
20354         
20355     },
20356     
20357     /**
20358      * Clean up MS wordisms...
20359      */
20360     cleanWord : function(node)
20361     {
20362         
20363         
20364         if (!node) {
20365             this.cleanWord(this.doc.body);
20366             return;
20367         }
20368         if (node.nodeName == "#text") {
20369             // clean up silly Windows -- stuff?
20370             return; 
20371         }
20372         if (node.nodeName == "#comment") {
20373             node.parentNode.removeChild(node);
20374             // clean up silly Windows -- stuff?
20375             return; 
20376         }
20377         
20378         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
20379             node.parentNode.removeChild(node);
20380             return;
20381         }
20382         
20383         // remove - but keep children..
20384         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20385             while (node.childNodes.length) {
20386                 var cn = node.childNodes[0];
20387                 node.removeChild(cn);
20388                 node.parentNode.insertBefore(cn, node);
20389             }
20390             node.parentNode.removeChild(node);
20391             this.iterateChildren(node, this.cleanWord);
20392             return;
20393         }
20394         // clean styles
20395         if (node.className.length) {
20396             
20397             var cn = node.className.split(/\W+/);
20398             var cna = [];
20399             Roo.each(cn, function(cls) {
20400                 if (cls.match(/Mso[a-zA-Z]+/)) {
20401                     return;
20402                 }
20403                 cna.push(cls);
20404             });
20405             node.className = cna.length ? cna.join(' ') : '';
20406             if (!cna.length) {
20407                 node.removeAttribute("class");
20408             }
20409         }
20410         
20411         if (node.hasAttribute("lang")) {
20412             node.removeAttribute("lang");
20413         }
20414         
20415         if (node.hasAttribute("style")) {
20416             
20417             var styles = node.getAttribute("style").split(";");
20418             var nstyle = [];
20419             Roo.each(styles, function(s) {
20420                 if (!s.match(/:/)) {
20421                     return;
20422                 }
20423                 var kv = s.split(":");
20424                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20425                     return;
20426                 }
20427                 // what ever is left... we allow.
20428                 nstyle.push(s);
20429             });
20430             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20431             if (!nstyle.length) {
20432                 node.removeAttribute('style');
20433             }
20434         }
20435         this.iterateChildren(node, this.cleanWord);
20436         
20437         
20438         
20439     },
20440     /**
20441      * iterateChildren of a Node, calling fn each time, using this as the scole..
20442      * @param {DomNode} node node to iterate children of.
20443      * @param {Function} fn method of this class to call on each item.
20444      */
20445     iterateChildren : function(node, fn)
20446     {
20447         if (!node.childNodes.length) {
20448                 return;
20449         }
20450         for (var i = node.childNodes.length-1; i > -1 ; i--) {
20451            fn.call(this, node.childNodes[i])
20452         }
20453     },
20454     
20455     
20456     /**
20457      * cleanTableWidths.
20458      *
20459      * Quite often pasting from word etc.. results in tables with column and widths.
20460      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20461      *
20462      */
20463     cleanTableWidths : function(node)
20464     {
20465          
20466          
20467         if (!node) {
20468             this.cleanTableWidths(this.doc.body);
20469             return;
20470         }
20471         
20472         // ignore list...
20473         if (node.nodeName == "#text" || node.nodeName == "#comment") {
20474             return; 
20475         }
20476         Roo.log(node.tagName);
20477         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20478             this.iterateChildren(node, this.cleanTableWidths);
20479             return;
20480         }
20481         if (node.hasAttribute('width')) {
20482             node.removeAttribute('width');
20483         }
20484         
20485          
20486         if (node.hasAttribute("style")) {
20487             // pretty basic...
20488             
20489             var styles = node.getAttribute("style").split(";");
20490             var nstyle = [];
20491             Roo.each(styles, function(s) {
20492                 if (!s.match(/:/)) {
20493                     return;
20494                 }
20495                 var kv = s.split(":");
20496                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20497                     return;
20498                 }
20499                 // what ever is left... we allow.
20500                 nstyle.push(s);
20501             });
20502             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20503             if (!nstyle.length) {
20504                 node.removeAttribute('style');
20505             }
20506         }
20507         
20508         this.iterateChildren(node, this.cleanTableWidths);
20509         
20510         
20511     },
20512     
20513     
20514     
20515     
20516     domToHTML : function(currentElement, depth, nopadtext) {
20517         
20518         depth = depth || 0;
20519         nopadtext = nopadtext || false;
20520     
20521         if (!currentElement) {
20522             return this.domToHTML(this.doc.body);
20523         }
20524         
20525         //Roo.log(currentElement);
20526         var j;
20527         var allText = false;
20528         var nodeName = currentElement.nodeName;
20529         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20530         
20531         if  (nodeName == '#text') {
20532             
20533             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20534         }
20535         
20536         
20537         var ret = '';
20538         if (nodeName != 'BODY') {
20539              
20540             var i = 0;
20541             // Prints the node tagName, such as <A>, <IMG>, etc
20542             if (tagName) {
20543                 var attr = [];
20544                 for(i = 0; i < currentElement.attributes.length;i++) {
20545                     // quoting?
20546                     var aname = currentElement.attributes.item(i).name;
20547                     if (!currentElement.attributes.item(i).value.length) {
20548                         continue;
20549                     }
20550                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20551                 }
20552                 
20553                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20554             } 
20555             else {
20556                 
20557                 // eack
20558             }
20559         } else {
20560             tagName = false;
20561         }
20562         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20563             return ret;
20564         }
20565         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20566             nopadtext = true;
20567         }
20568         
20569         
20570         // Traverse the tree
20571         i = 0;
20572         var currentElementChild = currentElement.childNodes.item(i);
20573         var allText = true;
20574         var innerHTML  = '';
20575         lastnode = '';
20576         while (currentElementChild) {
20577             // Formatting code (indent the tree so it looks nice on the screen)
20578             var nopad = nopadtext;
20579             if (lastnode == 'SPAN') {
20580                 nopad  = true;
20581             }
20582             // text
20583             if  (currentElementChild.nodeName == '#text') {
20584                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20585                 toadd = nopadtext ? toadd : toadd.trim();
20586                 if (!nopad && toadd.length > 80) {
20587                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
20588                 }
20589                 innerHTML  += toadd;
20590                 
20591                 i++;
20592                 currentElementChild = currentElement.childNodes.item(i);
20593                 lastNode = '';
20594                 continue;
20595             }
20596             allText = false;
20597             
20598             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
20599                 
20600             // Recursively traverse the tree structure of the child node
20601             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
20602             lastnode = currentElementChild.nodeName;
20603             i++;
20604             currentElementChild=currentElement.childNodes.item(i);
20605         }
20606         
20607         ret += innerHTML;
20608         
20609         if (!allText) {
20610                 // The remaining code is mostly for formatting the tree
20611             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
20612         }
20613         
20614         
20615         if (tagName) {
20616             ret+= "</"+tagName+">";
20617         }
20618         return ret;
20619         
20620     },
20621         
20622     applyBlacklists : function()
20623     {
20624         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
20625         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
20626         
20627         this.white = [];
20628         this.black = [];
20629         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20630             if (b.indexOf(tag) > -1) {
20631                 return;
20632             }
20633             this.white.push(tag);
20634             
20635         }, this);
20636         
20637         Roo.each(w, function(tag) {
20638             if (b.indexOf(tag) > -1) {
20639                 return;
20640             }
20641             if (this.white.indexOf(tag) > -1) {
20642                 return;
20643             }
20644             this.white.push(tag);
20645             
20646         }, this);
20647         
20648         
20649         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20650             if (w.indexOf(tag) > -1) {
20651                 return;
20652             }
20653             this.black.push(tag);
20654             
20655         }, this);
20656         
20657         Roo.each(b, function(tag) {
20658             if (w.indexOf(tag) > -1) {
20659                 return;
20660             }
20661             if (this.black.indexOf(tag) > -1) {
20662                 return;
20663             }
20664             this.black.push(tag);
20665             
20666         }, this);
20667         
20668         
20669         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
20670         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
20671         
20672         this.cwhite = [];
20673         this.cblack = [];
20674         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20675             if (b.indexOf(tag) > -1) {
20676                 return;
20677             }
20678             this.cwhite.push(tag);
20679             
20680         }, this);
20681         
20682         Roo.each(w, function(tag) {
20683             if (b.indexOf(tag) > -1) {
20684                 return;
20685             }
20686             if (this.cwhite.indexOf(tag) > -1) {
20687                 return;
20688             }
20689             this.cwhite.push(tag);
20690             
20691         }, this);
20692         
20693         
20694         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20695             if (w.indexOf(tag) > -1) {
20696                 return;
20697             }
20698             this.cblack.push(tag);
20699             
20700         }, this);
20701         
20702         Roo.each(b, function(tag) {
20703             if (w.indexOf(tag) > -1) {
20704                 return;
20705             }
20706             if (this.cblack.indexOf(tag) > -1) {
20707                 return;
20708             }
20709             this.cblack.push(tag);
20710             
20711         }, this);
20712     },
20713     
20714     setStylesheets : function(stylesheets)
20715     {
20716         if(typeof(stylesheets) == 'string'){
20717             Roo.get(this.iframe.contentDocument.head).createChild({
20718                 tag : 'link',
20719                 rel : 'stylesheet',
20720                 type : 'text/css',
20721                 href : stylesheets
20722             });
20723             
20724             return;
20725         }
20726         var _this = this;
20727      
20728         Roo.each(stylesheets, function(s) {
20729             if(!s.length){
20730                 return;
20731             }
20732             
20733             Roo.get(_this.iframe.contentDocument.head).createChild({
20734                 tag : 'link',
20735                 rel : 'stylesheet',
20736                 type : 'text/css',
20737                 href : s
20738             });
20739         });
20740
20741         
20742     },
20743     
20744     removeStylesheets : function()
20745     {
20746         var _this = this;
20747         
20748         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20749             s.remove();
20750         });
20751     }
20752     
20753     // hide stuff that is not compatible
20754     /**
20755      * @event blur
20756      * @hide
20757      */
20758     /**
20759      * @event change
20760      * @hide
20761      */
20762     /**
20763      * @event focus
20764      * @hide
20765      */
20766     /**
20767      * @event specialkey
20768      * @hide
20769      */
20770     /**
20771      * @cfg {String} fieldClass @hide
20772      */
20773     /**
20774      * @cfg {String} focusClass @hide
20775      */
20776     /**
20777      * @cfg {String} autoCreate @hide
20778      */
20779     /**
20780      * @cfg {String} inputType @hide
20781      */
20782     /**
20783      * @cfg {String} invalidClass @hide
20784      */
20785     /**
20786      * @cfg {String} invalidText @hide
20787      */
20788     /**
20789      * @cfg {String} msgFx @hide
20790      */
20791     /**
20792      * @cfg {String} validateOnBlur @hide
20793      */
20794 });
20795
20796 Roo.HtmlEditorCore.white = [
20797         'area', 'br', 'img', 'input', 'hr', 'wbr',
20798         
20799        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
20800        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
20801        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
20802        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
20803        'table',   'ul',         'xmp', 
20804        
20805        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
20806       'thead',   'tr', 
20807      
20808       'dir', 'menu', 'ol', 'ul', 'dl',
20809        
20810       'embed',  'object'
20811 ];
20812
20813
20814 Roo.HtmlEditorCore.black = [
20815     //    'embed',  'object', // enable - backend responsiblity to clean thiese
20816         'applet', // 
20817         'base',   'basefont', 'bgsound', 'blink',  'body', 
20818         'frame',  'frameset', 'head',    'html',   'ilayer', 
20819         'iframe', 'layer',  'link',     'meta',    'object',   
20820         'script', 'style' ,'title',  'xml' // clean later..
20821 ];
20822 Roo.HtmlEditorCore.clean = [
20823     'script', 'style', 'title', 'xml'
20824 ];
20825 Roo.HtmlEditorCore.remove = [
20826     'font'
20827 ];
20828 // attributes..
20829
20830 Roo.HtmlEditorCore.ablack = [
20831     'on'
20832 ];
20833     
20834 Roo.HtmlEditorCore.aclean = [ 
20835     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
20836 ];
20837
20838 // protocols..
20839 Roo.HtmlEditorCore.pwhite= [
20840         'http',  'https',  'mailto'
20841 ];
20842
20843 // white listed style attributes.
20844 Roo.HtmlEditorCore.cwhite= [
20845       //  'text-align', /// default is to allow most things..
20846       
20847          
20848 //        'font-size'//??
20849 ];
20850
20851 // black listed style attributes.
20852 Roo.HtmlEditorCore.cblack= [
20853       //  'font-size' -- this can be set by the project 
20854 ];
20855
20856
20857 Roo.HtmlEditorCore.swapCodes   =[ 
20858     [    8211, "--" ], 
20859     [    8212, "--" ], 
20860     [    8216,  "'" ],  
20861     [    8217, "'" ],  
20862     [    8220, '"' ],  
20863     [    8221, '"' ],  
20864     [    8226, "*" ],  
20865     [    8230, "..." ]
20866 ]; 
20867
20868     /*
20869  * - LGPL
20870  *
20871  * HtmlEditor
20872  * 
20873  */
20874
20875 /**
20876  * @class Roo.bootstrap.HtmlEditor
20877  * @extends Roo.bootstrap.TextArea
20878  * Bootstrap HtmlEditor class
20879
20880  * @constructor
20881  * Create a new HtmlEditor
20882  * @param {Object} config The config object
20883  */
20884
20885 Roo.bootstrap.HtmlEditor = function(config){
20886     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
20887     if (!this.toolbars) {
20888         this.toolbars = [];
20889     }
20890     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
20891     this.addEvents({
20892             /**
20893              * @event initialize
20894              * Fires when the editor is fully initialized (including the iframe)
20895              * @param {HtmlEditor} this
20896              */
20897             initialize: true,
20898             /**
20899              * @event activate
20900              * Fires when the editor is first receives the focus. Any insertion must wait
20901              * until after this event.
20902              * @param {HtmlEditor} this
20903              */
20904             activate: true,
20905              /**
20906              * @event beforesync
20907              * Fires before the textarea is updated with content from the editor iframe. Return false
20908              * to cancel the sync.
20909              * @param {HtmlEditor} this
20910              * @param {String} html
20911              */
20912             beforesync: true,
20913              /**
20914              * @event beforepush
20915              * Fires before the iframe editor is updated with content from the textarea. Return false
20916              * to cancel the push.
20917              * @param {HtmlEditor} this
20918              * @param {String} html
20919              */
20920             beforepush: true,
20921              /**
20922              * @event sync
20923              * Fires when the textarea is updated with content from the editor iframe.
20924              * @param {HtmlEditor} this
20925              * @param {String} html
20926              */
20927             sync: true,
20928              /**
20929              * @event push
20930              * Fires when the iframe editor is updated with content from the textarea.
20931              * @param {HtmlEditor} this
20932              * @param {String} html
20933              */
20934             push: true,
20935              /**
20936              * @event editmodechange
20937              * Fires when the editor switches edit modes
20938              * @param {HtmlEditor} this
20939              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
20940              */
20941             editmodechange: true,
20942             /**
20943              * @event editorevent
20944              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20945              * @param {HtmlEditor} this
20946              */
20947             editorevent: true,
20948             /**
20949              * @event firstfocus
20950              * Fires when on first focus - needed by toolbars..
20951              * @param {HtmlEditor} this
20952              */
20953             firstfocus: true,
20954             /**
20955              * @event autosave
20956              * Auto save the htmlEditor value as a file into Events
20957              * @param {HtmlEditor} this
20958              */
20959             autosave: true,
20960             /**
20961              * @event savedpreview
20962              * preview the saved version of htmlEditor
20963              * @param {HtmlEditor} this
20964              */
20965             savedpreview: true
20966         });
20967 };
20968
20969
20970 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
20971     
20972     
20973       /**
20974      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
20975      */
20976     toolbars : false,
20977    
20978      /**
20979      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20980      *                        Roo.resizable.
20981      */
20982     resizable : false,
20983      /**
20984      * @cfg {Number} height (in pixels)
20985      */   
20986     height: 300,
20987    /**
20988      * @cfg {Number} width (in pixels)
20989      */   
20990     width: false,
20991     
20992     /**
20993      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20994      * 
20995      */
20996     stylesheets: false,
20997     
20998     // id of frame..
20999     frameId: false,
21000     
21001     // private properties
21002     validationEvent : false,
21003     deferHeight: true,
21004     initialized : false,
21005     activated : false,
21006     
21007     onFocus : Roo.emptyFn,
21008     iframePad:3,
21009     hideMode:'offsets',
21010     
21011     
21012     tbContainer : false,
21013     
21014     toolbarContainer :function() {
21015         return this.wrap.select('.x-html-editor-tb',true).first();
21016     },
21017
21018     /**
21019      * Protected method that will not generally be called directly. It
21020      * is called when the editor creates its toolbar. Override this method if you need to
21021      * add custom toolbar buttons.
21022      * @param {HtmlEditor} editor
21023      */
21024     createToolbar : function(){
21025         
21026         Roo.log("create toolbars");
21027         
21028         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
21029         this.toolbars[0].render(this.toolbarContainer());
21030         
21031         return;
21032         
21033 //        if (!editor.toolbars || !editor.toolbars.length) {
21034 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
21035 //        }
21036 //        
21037 //        for (var i =0 ; i < editor.toolbars.length;i++) {
21038 //            editor.toolbars[i] = Roo.factory(
21039 //                    typeof(editor.toolbars[i]) == 'string' ?
21040 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
21041 //                Roo.bootstrap.HtmlEditor);
21042 //            editor.toolbars[i].init(editor);
21043 //        }
21044     },
21045
21046      
21047     // private
21048     onRender : function(ct, position)
21049     {
21050        // Roo.log("Call onRender: " + this.xtype);
21051         var _t = this;
21052         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
21053       
21054         this.wrap = this.inputEl().wrap({
21055             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
21056         });
21057         
21058         this.editorcore.onRender(ct, position);
21059          
21060         if (this.resizable) {
21061             this.resizeEl = new Roo.Resizable(this.wrap, {
21062                 pinned : true,
21063                 wrap: true,
21064                 dynamic : true,
21065                 minHeight : this.height,
21066                 height: this.height,
21067                 handles : this.resizable,
21068                 width: this.width,
21069                 listeners : {
21070                     resize : function(r, w, h) {
21071                         _t.onResize(w,h); // -something
21072                     }
21073                 }
21074             });
21075             
21076         }
21077         this.createToolbar(this);
21078        
21079         
21080         if(!this.width && this.resizable){
21081             this.setSize(this.wrap.getSize());
21082         }
21083         if (this.resizeEl) {
21084             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
21085             // should trigger onReize..
21086         }
21087         
21088     },
21089
21090     // private
21091     onResize : function(w, h)
21092     {
21093         Roo.log('resize: ' +w + ',' + h );
21094         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
21095         var ew = false;
21096         var eh = false;
21097         
21098         if(this.inputEl() ){
21099             if(typeof w == 'number'){
21100                 var aw = w - this.wrap.getFrameWidth('lr');
21101                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
21102                 ew = aw;
21103             }
21104             if(typeof h == 'number'){
21105                  var tbh = -11;  // fixme it needs to tool bar size!
21106                 for (var i =0; i < this.toolbars.length;i++) {
21107                     // fixme - ask toolbars for heights?
21108                     tbh += this.toolbars[i].el.getHeight();
21109                     //if (this.toolbars[i].footer) {
21110                     //    tbh += this.toolbars[i].footer.el.getHeight();
21111                     //}
21112                 }
21113               
21114                 
21115                 
21116                 
21117                 
21118                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
21119                 ah -= 5; // knock a few pixes off for look..
21120                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
21121                 var eh = ah;
21122             }
21123         }
21124         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
21125         this.editorcore.onResize(ew,eh);
21126         
21127     },
21128
21129     /**
21130      * Toggles the editor between standard and source edit mode.
21131      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21132      */
21133     toggleSourceEdit : function(sourceEditMode)
21134     {
21135         this.editorcore.toggleSourceEdit(sourceEditMode);
21136         
21137         if(this.editorcore.sourceEditMode){
21138             Roo.log('editor - showing textarea');
21139             
21140 //            Roo.log('in');
21141 //            Roo.log(this.syncValue());
21142             this.syncValue();
21143             this.inputEl().removeClass(['hide', 'x-hidden']);
21144             this.inputEl().dom.removeAttribute('tabIndex');
21145             this.inputEl().focus();
21146         }else{
21147             Roo.log('editor - hiding textarea');
21148 //            Roo.log('out')
21149 //            Roo.log(this.pushValue()); 
21150             this.pushValue();
21151             
21152             this.inputEl().addClass(['hide', 'x-hidden']);
21153             this.inputEl().dom.setAttribute('tabIndex', -1);
21154             //this.deferFocus();
21155         }
21156          
21157         if(this.resizable){
21158             this.setSize(this.wrap.getSize());
21159         }
21160         
21161         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
21162     },
21163  
21164     // private (for BoxComponent)
21165     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21166
21167     // private (for BoxComponent)
21168     getResizeEl : function(){
21169         return this.wrap;
21170     },
21171
21172     // private (for BoxComponent)
21173     getPositionEl : function(){
21174         return this.wrap;
21175     },
21176
21177     // private
21178     initEvents : function(){
21179         this.originalValue = this.getValue();
21180     },
21181
21182 //    /**
21183 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21184 //     * @method
21185 //     */
21186 //    markInvalid : Roo.emptyFn,
21187 //    /**
21188 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21189 //     * @method
21190 //     */
21191 //    clearInvalid : Roo.emptyFn,
21192
21193     setValue : function(v){
21194         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
21195         this.editorcore.pushValue();
21196     },
21197
21198      
21199     // private
21200     deferFocus : function(){
21201         this.focus.defer(10, this);
21202     },
21203
21204     // doc'ed in Field
21205     focus : function(){
21206         this.editorcore.focus();
21207         
21208     },
21209       
21210
21211     // private
21212     onDestroy : function(){
21213         
21214         
21215         
21216         if(this.rendered){
21217             
21218             for (var i =0; i < this.toolbars.length;i++) {
21219                 // fixme - ask toolbars for heights?
21220                 this.toolbars[i].onDestroy();
21221             }
21222             
21223             this.wrap.dom.innerHTML = '';
21224             this.wrap.remove();
21225         }
21226     },
21227
21228     // private
21229     onFirstFocus : function(){
21230         //Roo.log("onFirstFocus");
21231         this.editorcore.onFirstFocus();
21232          for (var i =0; i < this.toolbars.length;i++) {
21233             this.toolbars[i].onFirstFocus();
21234         }
21235         
21236     },
21237     
21238     // private
21239     syncValue : function()
21240     {   
21241         this.editorcore.syncValue();
21242     },
21243     
21244     pushValue : function()
21245     {   
21246         this.editorcore.pushValue();
21247     }
21248      
21249     
21250     // hide stuff that is not compatible
21251     /**
21252      * @event blur
21253      * @hide
21254      */
21255     /**
21256      * @event change
21257      * @hide
21258      */
21259     /**
21260      * @event focus
21261      * @hide
21262      */
21263     /**
21264      * @event specialkey
21265      * @hide
21266      */
21267     /**
21268      * @cfg {String} fieldClass @hide
21269      */
21270     /**
21271      * @cfg {String} focusClass @hide
21272      */
21273     /**
21274      * @cfg {String} autoCreate @hide
21275      */
21276     /**
21277      * @cfg {String} inputType @hide
21278      */
21279     /**
21280      * @cfg {String} invalidClass @hide
21281      */
21282     /**
21283      * @cfg {String} invalidText @hide
21284      */
21285     /**
21286      * @cfg {String} msgFx @hide
21287      */
21288     /**
21289      * @cfg {String} validateOnBlur @hide
21290      */
21291 });
21292  
21293     
21294    
21295    
21296    
21297       
21298 Roo.namespace('Roo.bootstrap.htmleditor');
21299 /**
21300  * @class Roo.bootstrap.HtmlEditorToolbar1
21301  * Basic Toolbar
21302  * 
21303  * Usage:
21304  *
21305  new Roo.bootstrap.HtmlEditor({
21306     ....
21307     toolbars : [
21308         new Roo.bootstrap.HtmlEditorToolbar1({
21309             disable : { fonts: 1 , format: 1, ..., ... , ...],
21310             btns : [ .... ]
21311         })
21312     }
21313      
21314  * 
21315  * @cfg {Object} disable List of elements to disable..
21316  * @cfg {Array} btns List of additional buttons.
21317  * 
21318  * 
21319  * NEEDS Extra CSS? 
21320  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
21321  */
21322  
21323 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
21324 {
21325     
21326     Roo.apply(this, config);
21327     
21328     // default disabled, based on 'good practice'..
21329     this.disable = this.disable || {};
21330     Roo.applyIf(this.disable, {
21331         fontSize : true,
21332         colors : true,
21333         specialElements : true
21334     });
21335     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
21336     
21337     this.editor = config.editor;
21338     this.editorcore = config.editor.editorcore;
21339     
21340     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
21341     
21342     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
21343     // dont call parent... till later.
21344 }
21345 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
21346      
21347     bar : true,
21348     
21349     editor : false,
21350     editorcore : false,
21351     
21352     
21353     formats : [
21354         "p" ,  
21355         "h1","h2","h3","h4","h5","h6", 
21356         "pre", "code", 
21357         "abbr", "acronym", "address", "cite", "samp", "var",
21358         'div','span'
21359     ],
21360     
21361     onRender : function(ct, position)
21362     {
21363        // Roo.log("Call onRender: " + this.xtype);
21364         
21365        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
21366        Roo.log(this.el);
21367        this.el.dom.style.marginBottom = '0';
21368        var _this = this;
21369        var editorcore = this.editorcore;
21370        var editor= this.editor;
21371        
21372        var children = [];
21373        var btn = function(id,cmd , toggle, handler){
21374        
21375             var  event = toggle ? 'toggle' : 'click';
21376        
21377             var a = {
21378                 size : 'sm',
21379                 xtype: 'Button',
21380                 xns: Roo.bootstrap,
21381                 glyphicon : id,
21382                 cmd : id || cmd,
21383                 enableToggle:toggle !== false,
21384                 //html : 'submit'
21385                 pressed : toggle ? false : null,
21386                 listeners : {}
21387             };
21388             a.listeners[toggle ? 'toggle' : 'click'] = function() {
21389                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
21390             };
21391             children.push(a);
21392             return a;
21393        }
21394         
21395         var style = {
21396                 xtype: 'Button',
21397                 size : 'sm',
21398                 xns: Roo.bootstrap,
21399                 glyphicon : 'font',
21400                 //html : 'submit'
21401                 menu : {
21402                     xtype: 'Menu',
21403                     xns: Roo.bootstrap,
21404                     items:  []
21405                 }
21406         };
21407         Roo.each(this.formats, function(f) {
21408             style.menu.items.push({
21409                 xtype :'MenuItem',
21410                 xns: Roo.bootstrap,
21411                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21412                 tagname : f,
21413                 listeners : {
21414                     click : function()
21415                     {
21416                         editorcore.insertTag(this.tagname);
21417                         editor.focus();
21418                     }
21419                 }
21420                 
21421             });
21422         });
21423          children.push(style);   
21424             
21425             
21426         btn('bold',false,true);
21427         btn('italic',false,true);
21428         btn('align-left', 'justifyleft',true);
21429         btn('align-center', 'justifycenter',true);
21430         btn('align-right' , 'justifyright',true);
21431         btn('link', false, false, function(btn) {
21432             //Roo.log("create link?");
21433             var url = prompt(this.createLinkText, this.defaultLinkValue);
21434             if(url && url != 'http:/'+'/'){
21435                 this.editorcore.relayCmd('createlink', url);
21436             }
21437         }),
21438         btn('list','insertunorderedlist',true);
21439         btn('pencil', false,true, function(btn){
21440                 Roo.log(this);
21441                 
21442                 this.toggleSourceEdit(btn.pressed);
21443         });
21444         /*
21445         var cog = {
21446                 xtype: 'Button',
21447                 size : 'sm',
21448                 xns: Roo.bootstrap,
21449                 glyphicon : 'cog',
21450                 //html : 'submit'
21451                 menu : {
21452                     xtype: 'Menu',
21453                     xns: Roo.bootstrap,
21454                     items:  []
21455                 }
21456         };
21457         
21458         cog.menu.items.push({
21459             xtype :'MenuItem',
21460             xns: Roo.bootstrap,
21461             html : Clean styles,
21462             tagname : f,
21463             listeners : {
21464                 click : function()
21465                 {
21466                     editorcore.insertTag(this.tagname);
21467                     editor.focus();
21468                 }
21469             }
21470             
21471         });
21472        */
21473         
21474          
21475        this.xtype = 'NavSimplebar';
21476         
21477         for(var i=0;i< children.length;i++) {
21478             
21479             this.buttons.add(this.addxtypeChild(children[i]));
21480             
21481         }
21482         
21483         editor.on('editorevent', this.updateToolbar, this);
21484     },
21485     onBtnClick : function(id)
21486     {
21487        this.editorcore.relayCmd(id);
21488        this.editorcore.focus();
21489     },
21490     
21491     /**
21492      * Protected method that will not generally be called directly. It triggers
21493      * a toolbar update by reading the markup state of the current selection in the editor.
21494      */
21495     updateToolbar: function(){
21496
21497         if(!this.editorcore.activated){
21498             this.editor.onFirstFocus(); // is this neeed?
21499             return;
21500         }
21501
21502         var btns = this.buttons; 
21503         var doc = this.editorcore.doc;
21504         btns.get('bold').setActive(doc.queryCommandState('bold'));
21505         btns.get('italic').setActive(doc.queryCommandState('italic'));
21506         //btns.get('underline').setActive(doc.queryCommandState('underline'));
21507         
21508         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21509         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21510         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21511         
21512         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21513         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21514          /*
21515         
21516         var ans = this.editorcore.getAllAncestors();
21517         if (this.formatCombo) {
21518             
21519             
21520             var store = this.formatCombo.store;
21521             this.formatCombo.setValue("");
21522             for (var i =0; i < ans.length;i++) {
21523                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21524                     // select it..
21525                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21526                     break;
21527                 }
21528             }
21529         }
21530         
21531         
21532         
21533         // hides menus... - so this cant be on a menu...
21534         Roo.bootstrap.MenuMgr.hideAll();
21535         */
21536         Roo.bootstrap.MenuMgr.hideAll();
21537         //this.editorsyncValue();
21538     },
21539     onFirstFocus: function() {
21540         this.buttons.each(function(item){
21541            item.enable();
21542         });
21543     },
21544     toggleSourceEdit : function(sourceEditMode){
21545         
21546           
21547         if(sourceEditMode){
21548             Roo.log("disabling buttons");
21549            this.buttons.each( function(item){
21550                 if(item.cmd != 'pencil'){
21551                     item.disable();
21552                 }
21553             });
21554           
21555         }else{
21556             Roo.log("enabling buttons");
21557             if(this.editorcore.initialized){
21558                 this.buttons.each( function(item){
21559                     item.enable();
21560                 });
21561             }
21562             
21563         }
21564         Roo.log("calling toggole on editor");
21565         // tell the editor that it's been pressed..
21566         this.editor.toggleSourceEdit(sourceEditMode);
21567        
21568     }
21569 });
21570
21571
21572
21573
21574
21575 /**
21576  * @class Roo.bootstrap.Table.AbstractSelectionModel
21577  * @extends Roo.util.Observable
21578  * Abstract base class for grid SelectionModels.  It provides the interface that should be
21579  * implemented by descendant classes.  This class should not be directly instantiated.
21580  * @constructor
21581  */
21582 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21583     this.locked = false;
21584     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21585 };
21586
21587
21588 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
21589     /** @ignore Called by the grid automatically. Do not call directly. */
21590     init : function(grid){
21591         this.grid = grid;
21592         this.initEvents();
21593     },
21594
21595     /**
21596      * Locks the selections.
21597      */
21598     lock : function(){
21599         this.locked = true;
21600     },
21601
21602     /**
21603      * Unlocks the selections.
21604      */
21605     unlock : function(){
21606         this.locked = false;
21607     },
21608
21609     /**
21610      * Returns true if the selections are locked.
21611      * @return {Boolean}
21612      */
21613     isLocked : function(){
21614         return this.locked;
21615     }
21616 });
21617 /**
21618  * @extends Roo.bootstrap.Table.AbstractSelectionModel
21619  * @class Roo.bootstrap.Table.RowSelectionModel
21620  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21621  * It supports multiple selections and keyboard selection/navigation. 
21622  * @constructor
21623  * @param {Object} config
21624  */
21625
21626 Roo.bootstrap.Table.RowSelectionModel = function(config){
21627     Roo.apply(this, config);
21628     this.selections = new Roo.util.MixedCollection(false, function(o){
21629         return o.id;
21630     });
21631
21632     this.last = false;
21633     this.lastActive = false;
21634
21635     this.addEvents({
21636         /**
21637              * @event selectionchange
21638              * Fires when the selection changes
21639              * @param {SelectionModel} this
21640              */
21641             "selectionchange" : true,
21642         /**
21643              * @event afterselectionchange
21644              * Fires after the selection changes (eg. by key press or clicking)
21645              * @param {SelectionModel} this
21646              */
21647             "afterselectionchange" : true,
21648         /**
21649              * @event beforerowselect
21650              * Fires when a row is selected being selected, return false to cancel.
21651              * @param {SelectionModel} this
21652              * @param {Number} rowIndex The selected index
21653              * @param {Boolean} keepExisting False if other selections will be cleared
21654              */
21655             "beforerowselect" : true,
21656         /**
21657              * @event rowselect
21658              * Fires when a row is selected.
21659              * @param {SelectionModel} this
21660              * @param {Number} rowIndex The selected index
21661              * @param {Roo.data.Record} r The record
21662              */
21663             "rowselect" : true,
21664         /**
21665              * @event rowdeselect
21666              * Fires when a row is deselected.
21667              * @param {SelectionModel} this
21668              * @param {Number} rowIndex The selected index
21669              */
21670         "rowdeselect" : true
21671     });
21672     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21673     this.locked = false;
21674 };
21675
21676 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
21677     /**
21678      * @cfg {Boolean} singleSelect
21679      * True to allow selection of only one row at a time (defaults to false)
21680      */
21681     singleSelect : false,
21682
21683     // private
21684     initEvents : function(){
21685
21686         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21687             this.grid.on("mousedown", this.handleMouseDown, this);
21688         }else{ // allow click to work like normal
21689             this.grid.on("rowclick", this.handleDragableRowClick, this);
21690         }
21691
21692         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21693             "up" : function(e){
21694                 if(!e.shiftKey){
21695                     this.selectPrevious(e.shiftKey);
21696                 }else if(this.last !== false && this.lastActive !== false){
21697                     var last = this.last;
21698                     this.selectRange(this.last,  this.lastActive-1);
21699                     this.grid.getView().focusRow(this.lastActive);
21700                     if(last !== false){
21701                         this.last = last;
21702                     }
21703                 }else{
21704                     this.selectFirstRow();
21705                 }
21706                 this.fireEvent("afterselectionchange", this);
21707             },
21708             "down" : function(e){
21709                 if(!e.shiftKey){
21710                     this.selectNext(e.shiftKey);
21711                 }else if(this.last !== false && this.lastActive !== false){
21712                     var last = this.last;
21713                     this.selectRange(this.last,  this.lastActive+1);
21714                     this.grid.getView().focusRow(this.lastActive);
21715                     if(last !== false){
21716                         this.last = last;
21717                     }
21718                 }else{
21719                     this.selectFirstRow();
21720                 }
21721                 this.fireEvent("afterselectionchange", this);
21722             },
21723             scope: this
21724         });
21725
21726         var view = this.grid.view;
21727         view.on("refresh", this.onRefresh, this);
21728         view.on("rowupdated", this.onRowUpdated, this);
21729         view.on("rowremoved", this.onRemove, this);
21730     },
21731
21732     // private
21733     onRefresh : function(){
21734         var ds = this.grid.dataSource, i, v = this.grid.view;
21735         var s = this.selections;
21736         s.each(function(r){
21737             if((i = ds.indexOfId(r.id)) != -1){
21738                 v.onRowSelect(i);
21739             }else{
21740                 s.remove(r);
21741             }
21742         });
21743     },
21744
21745     // private
21746     onRemove : function(v, index, r){
21747         this.selections.remove(r);
21748     },
21749
21750     // private
21751     onRowUpdated : function(v, index, r){
21752         if(this.isSelected(r)){
21753             v.onRowSelect(index);
21754         }
21755     },
21756
21757     /**
21758      * Select records.
21759      * @param {Array} records The records to select
21760      * @param {Boolean} keepExisting (optional) True to keep existing selections
21761      */
21762     selectRecords : function(records, keepExisting){
21763         if(!keepExisting){
21764             this.clearSelections();
21765         }
21766         var ds = this.grid.dataSource;
21767         for(var i = 0, len = records.length; i < len; i++){
21768             this.selectRow(ds.indexOf(records[i]), true);
21769         }
21770     },
21771
21772     /**
21773      * Gets the number of selected rows.
21774      * @return {Number}
21775      */
21776     getCount : function(){
21777         return this.selections.length;
21778     },
21779
21780     /**
21781      * Selects the first row in the grid.
21782      */
21783     selectFirstRow : function(){
21784         this.selectRow(0);
21785     },
21786
21787     /**
21788      * Select the last row.
21789      * @param {Boolean} keepExisting (optional) True to keep existing selections
21790      */
21791     selectLastRow : function(keepExisting){
21792         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21793     },
21794
21795     /**
21796      * Selects the row immediately following the last selected row.
21797      * @param {Boolean} keepExisting (optional) True to keep existing selections
21798      */
21799     selectNext : function(keepExisting){
21800         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21801             this.selectRow(this.last+1, keepExisting);
21802             this.grid.getView().focusRow(this.last);
21803         }
21804     },
21805
21806     /**
21807      * Selects the row that precedes the last selected row.
21808      * @param {Boolean} keepExisting (optional) True to keep existing selections
21809      */
21810     selectPrevious : function(keepExisting){
21811         if(this.last){
21812             this.selectRow(this.last-1, keepExisting);
21813             this.grid.getView().focusRow(this.last);
21814         }
21815     },
21816
21817     /**
21818      * Returns the selected records
21819      * @return {Array} Array of selected records
21820      */
21821     getSelections : function(){
21822         return [].concat(this.selections.items);
21823     },
21824
21825     /**
21826      * Returns the first selected record.
21827      * @return {Record}
21828      */
21829     getSelected : function(){
21830         return this.selections.itemAt(0);
21831     },
21832
21833
21834     /**
21835      * Clears all selections.
21836      */
21837     clearSelections : function(fast){
21838         if(this.locked) {
21839             return;
21840         }
21841         if(fast !== true){
21842             var ds = this.grid.dataSource;
21843             var s = this.selections;
21844             s.each(function(r){
21845                 this.deselectRow(ds.indexOfId(r.id));
21846             }, this);
21847             s.clear();
21848         }else{
21849             this.selections.clear();
21850         }
21851         this.last = false;
21852     },
21853
21854
21855     /**
21856      * Selects all rows.
21857      */
21858     selectAll : function(){
21859         if(this.locked) {
21860             return;
21861         }
21862         this.selections.clear();
21863         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
21864             this.selectRow(i, true);
21865         }
21866     },
21867
21868     /**
21869      * Returns True if there is a selection.
21870      * @return {Boolean}
21871      */
21872     hasSelection : function(){
21873         return this.selections.length > 0;
21874     },
21875
21876     /**
21877      * Returns True if the specified row is selected.
21878      * @param {Number/Record} record The record or index of the record to check
21879      * @return {Boolean}
21880      */
21881     isSelected : function(index){
21882         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
21883         return (r && this.selections.key(r.id) ? true : false);
21884     },
21885
21886     /**
21887      * Returns True if the specified record id is selected.
21888      * @param {String} id The id of record to check
21889      * @return {Boolean}
21890      */
21891     isIdSelected : function(id){
21892         return (this.selections.key(id) ? true : false);
21893     },
21894
21895     // private
21896     handleMouseDown : function(e, t){
21897         var view = this.grid.getView(), rowIndex;
21898         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
21899             return;
21900         };
21901         if(e.shiftKey && this.last !== false){
21902             var last = this.last;
21903             this.selectRange(last, rowIndex, e.ctrlKey);
21904             this.last = last; // reset the last
21905             view.focusRow(rowIndex);
21906         }else{
21907             var isSelected = this.isSelected(rowIndex);
21908             if(e.button !== 0 && isSelected){
21909                 view.focusRow(rowIndex);
21910             }else if(e.ctrlKey && isSelected){
21911                 this.deselectRow(rowIndex);
21912             }else if(!isSelected){
21913                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
21914                 view.focusRow(rowIndex);
21915             }
21916         }
21917         this.fireEvent("afterselectionchange", this);
21918     },
21919     // private
21920     handleDragableRowClick :  function(grid, rowIndex, e) 
21921     {
21922         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
21923             this.selectRow(rowIndex, false);
21924             grid.view.focusRow(rowIndex);
21925              this.fireEvent("afterselectionchange", this);
21926         }
21927     },
21928     
21929     /**
21930      * Selects multiple rows.
21931      * @param {Array} rows Array of the indexes of the row to select
21932      * @param {Boolean} keepExisting (optional) True to keep existing selections
21933      */
21934     selectRows : function(rows, keepExisting){
21935         if(!keepExisting){
21936             this.clearSelections();
21937         }
21938         for(var i = 0, len = rows.length; i < len; i++){
21939             this.selectRow(rows[i], true);
21940         }
21941     },
21942
21943     /**
21944      * Selects a range of rows. All rows in between startRow and endRow are also selected.
21945      * @param {Number} startRow The index of the first row in the range
21946      * @param {Number} endRow The index of the last row in the range
21947      * @param {Boolean} keepExisting (optional) True to retain existing selections
21948      */
21949     selectRange : function(startRow, endRow, keepExisting){
21950         if(this.locked) {
21951             return;
21952         }
21953         if(!keepExisting){
21954             this.clearSelections();
21955         }
21956         if(startRow <= endRow){
21957             for(var i = startRow; i <= endRow; i++){
21958                 this.selectRow(i, true);
21959             }
21960         }else{
21961             for(var i = startRow; i >= endRow; i--){
21962                 this.selectRow(i, true);
21963             }
21964         }
21965     },
21966
21967     /**
21968      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
21969      * @param {Number} startRow The index of the first row in the range
21970      * @param {Number} endRow The index of the last row in the range
21971      */
21972     deselectRange : function(startRow, endRow, preventViewNotify){
21973         if(this.locked) {
21974             return;
21975         }
21976         for(var i = startRow; i <= endRow; i++){
21977             this.deselectRow(i, preventViewNotify);
21978         }
21979     },
21980
21981     /**
21982      * Selects a row.
21983      * @param {Number} row The index of the row to select
21984      * @param {Boolean} keepExisting (optional) True to keep existing selections
21985      */
21986     selectRow : function(index, keepExisting, preventViewNotify){
21987         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
21988             return;
21989         }
21990         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
21991             if(!keepExisting || this.singleSelect){
21992                 this.clearSelections();
21993             }
21994             var r = this.grid.dataSource.getAt(index);
21995             this.selections.add(r);
21996             this.last = this.lastActive = index;
21997             if(!preventViewNotify){
21998                 this.grid.getView().onRowSelect(index);
21999             }
22000             this.fireEvent("rowselect", this, index, r);
22001             this.fireEvent("selectionchange", this);
22002         }
22003     },
22004
22005     /**
22006      * Deselects a row.
22007      * @param {Number} row The index of the row to deselect
22008      */
22009     deselectRow : function(index, preventViewNotify){
22010         if(this.locked) {
22011             return;
22012         }
22013         if(this.last == index){
22014             this.last = false;
22015         }
22016         if(this.lastActive == index){
22017             this.lastActive = false;
22018         }
22019         var r = this.grid.dataSource.getAt(index);
22020         this.selections.remove(r);
22021         if(!preventViewNotify){
22022             this.grid.getView().onRowDeselect(index);
22023         }
22024         this.fireEvent("rowdeselect", this, index);
22025         this.fireEvent("selectionchange", this);
22026     },
22027
22028     // private
22029     restoreLast : function(){
22030         if(this._last){
22031             this.last = this._last;
22032         }
22033     },
22034
22035     // private
22036     acceptsNav : function(row, col, cm){
22037         return !cm.isHidden(col) && cm.isCellEditable(col, row);
22038     },
22039
22040     // private
22041     onEditorKey : function(field, e){
22042         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22043         if(k == e.TAB){
22044             e.stopEvent();
22045             ed.completeEdit();
22046             if(e.shiftKey){
22047                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22048             }else{
22049                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22050             }
22051         }else if(k == e.ENTER && !e.ctrlKey){
22052             e.stopEvent();
22053             ed.completeEdit();
22054             if(e.shiftKey){
22055                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22056             }else{
22057                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22058             }
22059         }else if(k == e.ESC){
22060             ed.cancelEdit();
22061         }
22062         if(newCell){
22063             g.startEditing(newCell[0], newCell[1]);
22064         }
22065     }
22066 });/*
22067  * Based on:
22068  * Ext JS Library 1.1.1
22069  * Copyright(c) 2006-2007, Ext JS, LLC.
22070  *
22071  * Originally Released Under LGPL - original licence link has changed is not relivant.
22072  *
22073  * Fork - LGPL
22074  * <script type="text/javascript">
22075  */
22076  
22077 /**
22078  * @class Roo.bootstrap.PagingToolbar
22079  * @extends Roo.bootstrap.NavSimplebar
22080  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
22081  * @constructor
22082  * Create a new PagingToolbar
22083  * @param {Object} config The config object
22084  * @param {Roo.data.Store} store
22085  */
22086 Roo.bootstrap.PagingToolbar = function(config)
22087 {
22088     // old args format still supported... - xtype is prefered..
22089         // created from xtype...
22090     
22091     this.ds = config.dataSource;
22092     
22093     if (config.store && !this.ds) {
22094         this.store= Roo.factory(config.store, Roo.data);
22095         this.ds = this.store;
22096         this.ds.xmodule = this.xmodule || false;
22097     }
22098     
22099     this.toolbarItems = [];
22100     if (config.items) {
22101         this.toolbarItems = config.items;
22102     }
22103     
22104     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
22105     
22106     this.cursor = 0;
22107     
22108     if (this.ds) { 
22109         this.bind(this.ds);
22110     }
22111     
22112     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
22113     
22114 };
22115
22116 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
22117     /**
22118      * @cfg {Roo.data.Store} dataSource
22119      * The underlying data store providing the paged data
22120      */
22121     /**
22122      * @cfg {String/HTMLElement/Element} container
22123      * container The id or element that will contain the toolbar
22124      */
22125     /**
22126      * @cfg {Boolean} displayInfo
22127      * True to display the displayMsg (defaults to false)
22128      */
22129     /**
22130      * @cfg {Number} pageSize
22131      * The number of records to display per page (defaults to 20)
22132      */
22133     pageSize: 20,
22134     /**
22135      * @cfg {String} displayMsg
22136      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
22137      */
22138     displayMsg : 'Displaying {0} - {1} of {2}',
22139     /**
22140      * @cfg {String} emptyMsg
22141      * The message to display when no records are found (defaults to "No data to display")
22142      */
22143     emptyMsg : 'No data to display',
22144     /**
22145      * Customizable piece of the default paging text (defaults to "Page")
22146      * @type String
22147      */
22148     beforePageText : "Page",
22149     /**
22150      * Customizable piece of the default paging text (defaults to "of %0")
22151      * @type String
22152      */
22153     afterPageText : "of {0}",
22154     /**
22155      * Customizable piece of the default paging text (defaults to "First Page")
22156      * @type String
22157      */
22158     firstText : "First Page",
22159     /**
22160      * Customizable piece of the default paging text (defaults to "Previous Page")
22161      * @type String
22162      */
22163     prevText : "Previous Page",
22164     /**
22165      * Customizable piece of the default paging text (defaults to "Next Page")
22166      * @type String
22167      */
22168     nextText : "Next Page",
22169     /**
22170      * Customizable piece of the default paging text (defaults to "Last Page")
22171      * @type String
22172      */
22173     lastText : "Last Page",
22174     /**
22175      * Customizable piece of the default paging text (defaults to "Refresh")
22176      * @type String
22177      */
22178     refreshText : "Refresh",
22179
22180     buttons : false,
22181     // private
22182     onRender : function(ct, position) 
22183     {
22184         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
22185         this.navgroup.parentId = this.id;
22186         this.navgroup.onRender(this.el, null);
22187         // add the buttons to the navgroup
22188         
22189         if(this.displayInfo){
22190             Roo.log(this.el.select('ul.navbar-nav',true).first());
22191             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
22192             this.displayEl = this.el.select('.x-paging-info', true).first();
22193 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
22194 //            this.displayEl = navel.el.select('span',true).first();
22195         }
22196         
22197         var _this = this;
22198         
22199         if(this.buttons){
22200             Roo.each(_this.buttons, function(e){ // this might need to use render????
22201                Roo.factory(e).onRender(_this.el, null);
22202             });
22203         }
22204             
22205         Roo.each(_this.toolbarItems, function(e) {
22206             _this.navgroup.addItem(e);
22207         });
22208         
22209         
22210         this.first = this.navgroup.addItem({
22211             tooltip: this.firstText,
22212             cls: "prev",
22213             icon : 'fa fa-backward',
22214             disabled: true,
22215             preventDefault: true,
22216             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
22217         });
22218         
22219         this.prev =  this.navgroup.addItem({
22220             tooltip: this.prevText,
22221             cls: "prev",
22222             icon : 'fa fa-step-backward',
22223             disabled: true,
22224             preventDefault: true,
22225             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
22226         });
22227     //this.addSeparator();
22228         
22229         
22230         var field = this.navgroup.addItem( {
22231             tagtype : 'span',
22232             cls : 'x-paging-position',
22233             
22234             html : this.beforePageText  +
22235                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22236                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
22237          } ); //?? escaped?
22238         
22239         this.field = field.el.select('input', true).first();
22240         this.field.on("keydown", this.onPagingKeydown, this);
22241         this.field.on("focus", function(){this.dom.select();});
22242     
22243     
22244         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
22245         //this.field.setHeight(18);
22246         //this.addSeparator();
22247         this.next = this.navgroup.addItem({
22248             tooltip: this.nextText,
22249             cls: "next",
22250             html : ' <i class="fa fa-step-forward">',
22251             disabled: true,
22252             preventDefault: true,
22253             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
22254         });
22255         this.last = this.navgroup.addItem({
22256             tooltip: this.lastText,
22257             icon : 'fa fa-forward',
22258             cls: "next",
22259             disabled: true,
22260             preventDefault: true,
22261             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
22262         });
22263     //this.addSeparator();
22264         this.loading = this.navgroup.addItem({
22265             tooltip: this.refreshText,
22266             icon: 'fa fa-refresh',
22267             preventDefault: true,
22268             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
22269         });
22270         
22271     },
22272
22273     // private
22274     updateInfo : function(){
22275         if(this.displayEl){
22276             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
22277             var msg = count == 0 ?
22278                 this.emptyMsg :
22279                 String.format(
22280                     this.displayMsg,
22281                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
22282                 );
22283             this.displayEl.update(msg);
22284         }
22285     },
22286
22287     // private
22288     onLoad : function(ds, r, o){
22289        this.cursor = o.params ? o.params.start : 0;
22290        var d = this.getPageData(),
22291             ap = d.activePage,
22292             ps = d.pages;
22293         
22294        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
22295        this.field.dom.value = ap;
22296        this.first.setDisabled(ap == 1);
22297        this.prev.setDisabled(ap == 1);
22298        this.next.setDisabled(ap == ps);
22299        this.last.setDisabled(ap == ps);
22300        this.loading.enable();
22301        this.updateInfo();
22302     },
22303
22304     // private
22305     getPageData : function(){
22306         var total = this.ds.getTotalCount();
22307         return {
22308             total : total,
22309             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
22310             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
22311         };
22312     },
22313
22314     // private
22315     onLoadError : function(){
22316         this.loading.enable();
22317     },
22318
22319     // private
22320     onPagingKeydown : function(e){
22321         var k = e.getKey();
22322         var d = this.getPageData();
22323         if(k == e.RETURN){
22324             var v = this.field.dom.value, pageNum;
22325             if(!v || isNaN(pageNum = parseInt(v, 10))){
22326                 this.field.dom.value = d.activePage;
22327                 return;
22328             }
22329             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
22330             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22331             e.stopEvent();
22332         }
22333         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))
22334         {
22335           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
22336           this.field.dom.value = pageNum;
22337           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
22338           e.stopEvent();
22339         }
22340         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22341         {
22342           var v = this.field.dom.value, pageNum; 
22343           var increment = (e.shiftKey) ? 10 : 1;
22344           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
22345                 increment *= -1;
22346           }
22347           if(!v || isNaN(pageNum = parseInt(v, 10))) {
22348             this.field.dom.value = d.activePage;
22349             return;
22350           }
22351           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
22352           {
22353             this.field.dom.value = parseInt(v, 10) + increment;
22354             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
22355             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22356           }
22357           e.stopEvent();
22358         }
22359     },
22360
22361     // private
22362     beforeLoad : function(){
22363         if(this.loading){
22364             this.loading.disable();
22365         }
22366     },
22367
22368     // private
22369     onClick : function(which){
22370         
22371         var ds = this.ds;
22372         if (!ds) {
22373             return;
22374         }
22375         
22376         switch(which){
22377             case "first":
22378                 ds.load({params:{start: 0, limit: this.pageSize}});
22379             break;
22380             case "prev":
22381                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
22382             break;
22383             case "next":
22384                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
22385             break;
22386             case "last":
22387                 var total = ds.getTotalCount();
22388                 var extra = total % this.pageSize;
22389                 var lastStart = extra ? (total - extra) : total-this.pageSize;
22390                 ds.load({params:{start: lastStart, limit: this.pageSize}});
22391             break;
22392             case "refresh":
22393                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22394             break;
22395         }
22396     },
22397
22398     /**
22399      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22400      * @param {Roo.data.Store} store The data store to unbind
22401      */
22402     unbind : function(ds){
22403         ds.un("beforeload", this.beforeLoad, this);
22404         ds.un("load", this.onLoad, this);
22405         ds.un("loadexception", this.onLoadError, this);
22406         ds.un("remove", this.updateInfo, this);
22407         ds.un("add", this.updateInfo, this);
22408         this.ds = undefined;
22409     },
22410
22411     /**
22412      * Binds the paging toolbar to the specified {@link Roo.data.Store}
22413      * @param {Roo.data.Store} store The data store to bind
22414      */
22415     bind : function(ds){
22416         ds.on("beforeload", this.beforeLoad, this);
22417         ds.on("load", this.onLoad, this);
22418         ds.on("loadexception", this.onLoadError, this);
22419         ds.on("remove", this.updateInfo, this);
22420         ds.on("add", this.updateInfo, this);
22421         this.ds = ds;
22422     }
22423 });/*
22424  * - LGPL
22425  *
22426  * element
22427  * 
22428  */
22429
22430 /**
22431  * @class Roo.bootstrap.MessageBar
22432  * @extends Roo.bootstrap.Component
22433  * Bootstrap MessageBar class
22434  * @cfg {String} html contents of the MessageBar
22435  * @cfg {String} weight (info | success | warning | danger) default info
22436  * @cfg {String} beforeClass insert the bar before the given class
22437  * @cfg {Boolean} closable (true | false) default false
22438  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22439  * 
22440  * @constructor
22441  * Create a new Element
22442  * @param {Object} config The config object
22443  */
22444
22445 Roo.bootstrap.MessageBar = function(config){
22446     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22447 };
22448
22449 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
22450     
22451     html: '',
22452     weight: 'info',
22453     closable: false,
22454     fixed: false,
22455     beforeClass: 'bootstrap-sticky-wrap',
22456     
22457     getAutoCreate : function(){
22458         
22459         var cfg = {
22460             tag: 'div',
22461             cls: 'alert alert-dismissable alert-' + this.weight,
22462             cn: [
22463                 {
22464                     tag: 'span',
22465                     cls: 'message',
22466                     html: this.html || ''
22467                 }
22468             ]
22469         };
22470         
22471         if(this.fixed){
22472             cfg.cls += ' alert-messages-fixed';
22473         }
22474         
22475         if(this.closable){
22476             cfg.cn.push({
22477                 tag: 'button',
22478                 cls: 'close',
22479                 html: 'x'
22480             });
22481         }
22482         
22483         return cfg;
22484     },
22485     
22486     onRender : function(ct, position)
22487     {
22488         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22489         
22490         if(!this.el){
22491             var cfg = Roo.apply({},  this.getAutoCreate());
22492             cfg.id = Roo.id();
22493             
22494             if (this.cls) {
22495                 cfg.cls += ' ' + this.cls;
22496             }
22497             if (this.style) {
22498                 cfg.style = this.style;
22499             }
22500             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22501             
22502             this.el.setVisibilityMode(Roo.Element.DISPLAY);
22503         }
22504         
22505         this.el.select('>button.close').on('click', this.hide, this);
22506         
22507     },
22508     
22509     show : function()
22510     {
22511         if (!this.rendered) {
22512             this.render();
22513         }
22514         
22515         this.el.show();
22516         
22517         this.fireEvent('show', this);
22518         
22519     },
22520     
22521     hide : function()
22522     {
22523         if (!this.rendered) {
22524             this.render();
22525         }
22526         
22527         this.el.hide();
22528         
22529         this.fireEvent('hide', this);
22530     },
22531     
22532     update : function()
22533     {
22534 //        var e = this.el.dom.firstChild;
22535 //        
22536 //        if(this.closable){
22537 //            e = e.nextSibling;
22538 //        }
22539 //        
22540 //        e.data = this.html || '';
22541
22542         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22543     }
22544    
22545 });
22546
22547  
22548
22549      /*
22550  * - LGPL
22551  *
22552  * Graph
22553  * 
22554  */
22555
22556
22557 /**
22558  * @class Roo.bootstrap.Graph
22559  * @extends Roo.bootstrap.Component
22560  * Bootstrap Graph class
22561 > Prameters
22562  -sm {number} sm 4
22563  -md {number} md 5
22564  @cfg {String} graphtype  bar | vbar | pie
22565  @cfg {number} g_x coodinator | centre x (pie)
22566  @cfg {number} g_y coodinator | centre y (pie)
22567  @cfg {number} g_r radius (pie)
22568  @cfg {number} g_height height of the chart (respected by all elements in the set)
22569  @cfg {number} g_width width of the chart (respected by all elements in the set)
22570  @cfg {Object} title The title of the chart
22571     
22572  -{Array}  values
22573  -opts (object) options for the chart 
22574      o {
22575      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22576      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22577      o vgutter (number)
22578      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.
22579      o stacked (boolean) whether or not to tread values as in a stacked bar chart
22580      o to
22581      o stretch (boolean)
22582      o }
22583  -opts (object) options for the pie
22584      o{
22585      o cut
22586      o startAngle (number)
22587      o endAngle (number)
22588      } 
22589  *
22590  * @constructor
22591  * Create a new Input
22592  * @param {Object} config The config object
22593  */
22594
22595 Roo.bootstrap.Graph = function(config){
22596     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22597     
22598     this.addEvents({
22599         // img events
22600         /**
22601          * @event click
22602          * The img click event for the img.
22603          * @param {Roo.EventObject} e
22604          */
22605         "click" : true
22606     });
22607 };
22608
22609 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
22610     
22611     sm: 4,
22612     md: 5,
22613     graphtype: 'bar',
22614     g_height: 250,
22615     g_width: 400,
22616     g_x: 50,
22617     g_y: 50,
22618     g_r: 30,
22619     opts:{
22620         //g_colors: this.colors,
22621         g_type: 'soft',
22622         g_gutter: '20%'
22623
22624     },
22625     title : false,
22626
22627     getAutoCreate : function(){
22628         
22629         var cfg = {
22630             tag: 'div',
22631             html : null
22632         };
22633         
22634         
22635         return  cfg;
22636     },
22637
22638     onRender : function(ct,position){
22639         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22640         this.raphael = Raphael(this.el.dom);
22641         
22642                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22643                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22644                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22645                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22646                 /*
22647                 r.text(160, 10, "Single Series Chart").attr(txtattr);
22648                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22649                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22650                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22651                 
22652                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22653                 r.barchart(330, 10, 300, 220, data1);
22654                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22655                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22656                 */
22657                 
22658                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22659                 // r.barchart(30, 30, 560, 250,  xdata, {
22660                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22661                 //     axis : "0 0 1 1",
22662                 //     axisxlabels :  xdata
22663                 //     //yvalues : cols,
22664                    
22665                 // });
22666 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22667 //        
22668 //        this.load(null,xdata,{
22669 //                axis : "0 0 1 1",
22670 //                axisxlabels :  xdata
22671 //                });
22672
22673     },
22674
22675     load : function(graphtype,xdata,opts){
22676         this.raphael.clear();
22677         if(!graphtype) {
22678             graphtype = this.graphtype;
22679         }
22680         if(!opts){
22681             opts = this.opts;
22682         }
22683         var r = this.raphael,
22684             fin = function () {
22685                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22686             },
22687             fout = function () {
22688                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22689             },
22690             pfin = function() {
22691                 this.sector.stop();
22692                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22693
22694                 if (this.label) {
22695                     this.label[0].stop();
22696                     this.label[0].attr({ r: 7.5 });
22697                     this.label[1].attr({ "font-weight": 800 });
22698                 }
22699             },
22700             pfout = function() {
22701                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22702
22703                 if (this.label) {
22704                     this.label[0].animate({ r: 5 }, 500, "bounce");
22705                     this.label[1].attr({ "font-weight": 400 });
22706                 }
22707             };
22708
22709         switch(graphtype){
22710             case 'bar':
22711                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22712                 break;
22713             case 'hbar':
22714                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22715                 break;
22716             case 'pie':
22717 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
22718 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22719 //            
22720                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22721                 
22722                 break;
22723
22724         }
22725         
22726         if(this.title){
22727             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22728         }
22729         
22730     },
22731     
22732     setTitle: function(o)
22733     {
22734         this.title = o;
22735     },
22736     
22737     initEvents: function() {
22738         
22739         if(!this.href){
22740             this.el.on('click', this.onClick, this);
22741         }
22742     },
22743     
22744     onClick : function(e)
22745     {
22746         Roo.log('img onclick');
22747         this.fireEvent('click', this, e);
22748     }
22749    
22750 });
22751
22752  
22753 /*
22754  * - LGPL
22755  *
22756  * numberBox
22757  * 
22758  */
22759 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22760
22761 /**
22762  * @class Roo.bootstrap.dash.NumberBox
22763  * @extends Roo.bootstrap.Component
22764  * Bootstrap NumberBox class
22765  * @cfg {String} headline Box headline
22766  * @cfg {String} content Box content
22767  * @cfg {String} icon Box icon
22768  * @cfg {String} footer Footer text
22769  * @cfg {String} fhref Footer href
22770  * 
22771  * @constructor
22772  * Create a new NumberBox
22773  * @param {Object} config The config object
22774  */
22775
22776
22777 Roo.bootstrap.dash.NumberBox = function(config){
22778     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22779     
22780 };
22781
22782 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
22783     
22784     headline : '',
22785     content : '',
22786     icon : '',
22787     footer : '',
22788     fhref : '',
22789     ficon : '',
22790     
22791     getAutoCreate : function(){
22792         
22793         var cfg = {
22794             tag : 'div',
22795             cls : 'small-box ',
22796             cn : [
22797                 {
22798                     tag : 'div',
22799                     cls : 'inner',
22800                     cn :[
22801                         {
22802                             tag : 'h3',
22803                             cls : 'roo-headline',
22804                             html : this.headline
22805                         },
22806                         {
22807                             tag : 'p',
22808                             cls : 'roo-content',
22809                             html : this.content
22810                         }
22811                     ]
22812                 }
22813             ]
22814         };
22815         
22816         if(this.icon){
22817             cfg.cn.push({
22818                 tag : 'div',
22819                 cls : 'icon',
22820                 cn :[
22821                     {
22822                         tag : 'i',
22823                         cls : 'ion ' + this.icon
22824                     }
22825                 ]
22826             });
22827         }
22828         
22829         if(this.footer){
22830             var footer = {
22831                 tag : 'a',
22832                 cls : 'small-box-footer',
22833                 href : this.fhref || '#',
22834                 html : this.footer
22835             };
22836             
22837             cfg.cn.push(footer);
22838             
22839         }
22840         
22841         return  cfg;
22842     },
22843
22844     onRender : function(ct,position){
22845         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
22846
22847
22848        
22849                 
22850     },
22851
22852     setHeadline: function (value)
22853     {
22854         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
22855     },
22856     
22857     setFooter: function (value, href)
22858     {
22859         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
22860         
22861         if(href){
22862             this.el.select('a.small-box-footer',true).first().attr('href', href);
22863         }
22864         
22865     },
22866
22867     setContent: function (value)
22868     {
22869         this.el.select('.roo-content',true).first().dom.innerHTML = value;
22870     },
22871
22872     initEvents: function() 
22873     {   
22874         
22875     }
22876     
22877 });
22878
22879  
22880 /*
22881  * - LGPL
22882  *
22883  * TabBox
22884  * 
22885  */
22886 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22887
22888 /**
22889  * @class Roo.bootstrap.dash.TabBox
22890  * @extends Roo.bootstrap.Component
22891  * Bootstrap TabBox class
22892  * @cfg {String} title Title of the TabBox
22893  * @cfg {String} icon Icon of the TabBox
22894  * @cfg {Boolean} showtabs (true|false) show the tabs default true
22895  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
22896  * 
22897  * @constructor
22898  * Create a new TabBox
22899  * @param {Object} config The config object
22900  */
22901
22902
22903 Roo.bootstrap.dash.TabBox = function(config){
22904     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
22905     this.addEvents({
22906         // raw events
22907         /**
22908          * @event addpane
22909          * When a pane is added
22910          * @param {Roo.bootstrap.dash.TabPane} pane
22911          */
22912         "addpane" : true,
22913         /**
22914          * @event activatepane
22915          * When a pane is activated
22916          * @param {Roo.bootstrap.dash.TabPane} pane
22917          */
22918         "activatepane" : true
22919         
22920          
22921     });
22922     
22923     this.panes = [];
22924 };
22925
22926 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
22927
22928     title : '',
22929     icon : false,
22930     showtabs : true,
22931     tabScrollable : false,
22932     
22933     getChildContainer : function()
22934     {
22935         return this.el.select('.tab-content', true).first();
22936     },
22937     
22938     getAutoCreate : function(){
22939         
22940         var header = {
22941             tag: 'li',
22942             cls: 'pull-left header',
22943             html: this.title,
22944             cn : []
22945         };
22946         
22947         if(this.icon){
22948             header.cn.push({
22949                 tag: 'i',
22950                 cls: 'fa ' + this.icon
22951             });
22952         }
22953         
22954         var h = {
22955             tag: 'ul',
22956             cls: 'nav nav-tabs pull-right',
22957             cn: [
22958                 header
22959             ]
22960         };
22961         
22962         if(this.tabScrollable){
22963             h = {
22964                 tag: 'div',
22965                 cls: 'tab-header',
22966                 cn: [
22967                     {
22968                         tag: 'ul',
22969                         cls: 'nav nav-tabs pull-right',
22970                         cn: [
22971                             header
22972                         ]
22973                     }
22974                 ]
22975             };
22976         }
22977         
22978         var cfg = {
22979             tag: 'div',
22980             cls: 'nav-tabs-custom',
22981             cn: [
22982                 h,
22983                 {
22984                     tag: 'div',
22985                     cls: 'tab-content no-padding',
22986                     cn: []
22987                 }
22988             ]
22989         };
22990
22991         return  cfg;
22992     },
22993     initEvents : function()
22994     {
22995         //Roo.log('add add pane handler');
22996         this.on('addpane', this.onAddPane, this);
22997     },
22998      /**
22999      * Updates the box title
23000      * @param {String} html to set the title to.
23001      */
23002     setTitle : function(value)
23003     {
23004         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23005     },
23006     onAddPane : function(pane)
23007     {
23008         this.panes.push(pane);
23009         //Roo.log('addpane');
23010         //Roo.log(pane);
23011         // tabs are rendere left to right..
23012         if(!this.showtabs){
23013             return;
23014         }
23015         
23016         var ctr = this.el.select('.nav-tabs', true).first();
23017          
23018          
23019         var existing = ctr.select('.nav-tab',true);
23020         var qty = existing.getCount();;
23021         
23022         
23023         var tab = ctr.createChild({
23024             tag : 'li',
23025             cls : 'nav-tab' + (qty ? '' : ' active'),
23026             cn : [
23027                 {
23028                     tag : 'a',
23029                     href:'#',
23030                     html : pane.title
23031                 }
23032             ]
23033         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23034         pane.tab = tab;
23035         
23036         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23037         if (!qty) {
23038             pane.el.addClass('active');
23039         }
23040         
23041                 
23042     },
23043     onTabClick : function(ev,un,ob,pane)
23044     {
23045         //Roo.log('tab - prev default');
23046         ev.preventDefault();
23047         
23048         
23049         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23050         pane.tab.addClass('active');
23051         //Roo.log(pane.title);
23052         this.getChildContainer().select('.tab-pane',true).removeClass('active');
23053         // technically we should have a deactivate event.. but maybe add later.
23054         // and it should not de-activate the selected tab...
23055         this.fireEvent('activatepane', pane);
23056         pane.el.addClass('active');
23057         pane.fireEvent('activate');
23058         
23059         
23060     },
23061     
23062     getActivePane : function()
23063     {
23064         var r = false;
23065         Roo.each(this.panes, function(p) {
23066             if(p.el.hasClass('active')){
23067                 r = p;
23068                 return false;
23069             }
23070             
23071             return;
23072         });
23073         
23074         return r;
23075     }
23076     
23077     
23078 });
23079
23080  
23081 /*
23082  * - LGPL
23083  *
23084  * Tab pane
23085  * 
23086  */
23087 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23088 /**
23089  * @class Roo.bootstrap.TabPane
23090  * @extends Roo.bootstrap.Component
23091  * Bootstrap TabPane class
23092  * @cfg {Boolean} active (false | true) Default false
23093  * @cfg {String} title title of panel
23094
23095  * 
23096  * @constructor
23097  * Create a new TabPane
23098  * @param {Object} config The config object
23099  */
23100
23101 Roo.bootstrap.dash.TabPane = function(config){
23102     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
23103     
23104     this.addEvents({
23105         // raw events
23106         /**
23107          * @event activate
23108          * When a pane is activated
23109          * @param {Roo.bootstrap.dash.TabPane} pane
23110          */
23111         "activate" : true
23112          
23113     });
23114 };
23115
23116 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
23117     
23118     active : false,
23119     title : '',
23120     
23121     // the tabBox that this is attached to.
23122     tab : false,
23123      
23124     getAutoCreate : function() 
23125     {
23126         var cfg = {
23127             tag: 'div',
23128             cls: 'tab-pane'
23129         };
23130         
23131         if(this.active){
23132             cfg.cls += ' active';
23133         }
23134         
23135         return cfg;
23136     },
23137     initEvents  : function()
23138     {
23139         //Roo.log('trigger add pane handler');
23140         this.parent().fireEvent('addpane', this)
23141     },
23142     
23143      /**
23144      * Updates the tab title 
23145      * @param {String} html to set the title to.
23146      */
23147     setTitle: function(str)
23148     {
23149         if (!this.tab) {
23150             return;
23151         }
23152         this.title = str;
23153         this.tab.select('a', true).first().dom.innerHTML = str;
23154         
23155     }
23156     
23157     
23158     
23159 });
23160
23161  
23162
23163
23164  /*
23165  * - LGPL
23166  *
23167  * menu
23168  * 
23169  */
23170 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23171
23172 /**
23173  * @class Roo.bootstrap.menu.Menu
23174  * @extends Roo.bootstrap.Component
23175  * Bootstrap Menu class - container for Menu
23176  * @cfg {String} html Text of the menu
23177  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
23178  * @cfg {String} icon Font awesome icon
23179  * @cfg {String} pos Menu align to (top | bottom) default bottom
23180  * 
23181  * 
23182  * @constructor
23183  * Create a new Menu
23184  * @param {Object} config The config object
23185  */
23186
23187
23188 Roo.bootstrap.menu.Menu = function(config){
23189     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
23190     
23191     this.addEvents({
23192         /**
23193          * @event beforeshow
23194          * Fires before this menu is displayed
23195          * @param {Roo.bootstrap.menu.Menu} this
23196          */
23197         beforeshow : true,
23198         /**
23199          * @event beforehide
23200          * Fires before this menu is hidden
23201          * @param {Roo.bootstrap.menu.Menu} this
23202          */
23203         beforehide : true,
23204         /**
23205          * @event show
23206          * Fires after this menu is displayed
23207          * @param {Roo.bootstrap.menu.Menu} this
23208          */
23209         show : true,
23210         /**
23211          * @event hide
23212          * Fires after this menu is hidden
23213          * @param {Roo.bootstrap.menu.Menu} this
23214          */
23215         hide : true,
23216         /**
23217          * @event click
23218          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
23219          * @param {Roo.bootstrap.menu.Menu} this
23220          * @param {Roo.EventObject} e
23221          */
23222         click : true
23223     });
23224     
23225 };
23226
23227 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
23228     
23229     submenu : false,
23230     html : '',
23231     weight : 'default',
23232     icon : false,
23233     pos : 'bottom',
23234     
23235     
23236     getChildContainer : function() {
23237         if(this.isSubMenu){
23238             return this.el;
23239         }
23240         
23241         return this.el.select('ul.dropdown-menu', true).first();  
23242     },
23243     
23244     getAutoCreate : function()
23245     {
23246         var text = [
23247             {
23248                 tag : 'span',
23249                 cls : 'roo-menu-text',
23250                 html : this.html
23251             }
23252         ];
23253         
23254         if(this.icon){
23255             text.unshift({
23256                 tag : 'i',
23257                 cls : 'fa ' + this.icon
23258             })
23259         }
23260         
23261         
23262         var cfg = {
23263             tag : 'div',
23264             cls : 'btn-group',
23265             cn : [
23266                 {
23267                     tag : 'button',
23268                     cls : 'dropdown-button btn btn-' + this.weight,
23269                     cn : text
23270                 },
23271                 {
23272                     tag : 'button',
23273                     cls : 'dropdown-toggle btn btn-' + this.weight,
23274                     cn : [
23275                         {
23276                             tag : 'span',
23277                             cls : 'caret'
23278                         }
23279                     ]
23280                 },
23281                 {
23282                     tag : 'ul',
23283                     cls : 'dropdown-menu'
23284                 }
23285             ]
23286             
23287         };
23288         
23289         if(this.pos == 'top'){
23290             cfg.cls += ' dropup';
23291         }
23292         
23293         if(this.isSubMenu){
23294             cfg = {
23295                 tag : 'ul',
23296                 cls : 'dropdown-menu'
23297             }
23298         }
23299         
23300         return cfg;
23301     },
23302     
23303     onRender : function(ct, position)
23304     {
23305         this.isSubMenu = ct.hasClass('dropdown-submenu');
23306         
23307         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
23308     },
23309     
23310     initEvents : function() 
23311     {
23312         if(this.isSubMenu){
23313             return;
23314         }
23315         
23316         this.hidden = true;
23317         
23318         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
23319         this.triggerEl.on('click', this.onTriggerPress, this);
23320         
23321         this.buttonEl = this.el.select('button.dropdown-button', true).first();
23322         this.buttonEl.on('click', this.onClick, this);
23323         
23324     },
23325     
23326     list : function()
23327     {
23328         if(this.isSubMenu){
23329             return this.el;
23330         }
23331         
23332         return this.el.select('ul.dropdown-menu', true).first();
23333     },
23334     
23335     onClick : function(e)
23336     {
23337         this.fireEvent("click", this, e);
23338     },
23339     
23340     onTriggerPress  : function(e)
23341     {   
23342         if (this.isVisible()) {
23343             this.hide();
23344         } else {
23345             this.show();
23346         }
23347     },
23348     
23349     isVisible : function(){
23350         return !this.hidden;
23351     },
23352     
23353     show : function()
23354     {
23355         this.fireEvent("beforeshow", this);
23356         
23357         this.hidden = false;
23358         this.el.addClass('open');
23359         
23360         Roo.get(document).on("mouseup", this.onMouseUp, this);
23361         
23362         this.fireEvent("show", this);
23363         
23364         
23365     },
23366     
23367     hide : function()
23368     {
23369         this.fireEvent("beforehide", this);
23370         
23371         this.hidden = true;
23372         this.el.removeClass('open');
23373         
23374         Roo.get(document).un("mouseup", this.onMouseUp);
23375         
23376         this.fireEvent("hide", this);
23377     },
23378     
23379     onMouseUp : function()
23380     {
23381         this.hide();
23382     }
23383     
23384 });
23385
23386  
23387  /*
23388  * - LGPL
23389  *
23390  * menu item
23391  * 
23392  */
23393 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23394
23395 /**
23396  * @class Roo.bootstrap.menu.Item
23397  * @extends Roo.bootstrap.Component
23398  * Bootstrap MenuItem class
23399  * @cfg {Boolean} submenu (true | false) default false
23400  * @cfg {String} html text of the item
23401  * @cfg {String} href the link
23402  * @cfg {Boolean} disable (true | false) default false
23403  * @cfg {Boolean} preventDefault (true | false) default true
23404  * @cfg {String} icon Font awesome icon
23405  * @cfg {String} pos Submenu align to (left | right) default right 
23406  * 
23407  * 
23408  * @constructor
23409  * Create a new Item
23410  * @param {Object} config The config object
23411  */
23412
23413
23414 Roo.bootstrap.menu.Item = function(config){
23415     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23416     this.addEvents({
23417         /**
23418          * @event mouseover
23419          * Fires when the mouse is hovering over this menu
23420          * @param {Roo.bootstrap.menu.Item} this
23421          * @param {Roo.EventObject} e
23422          */
23423         mouseover : true,
23424         /**
23425          * @event mouseout
23426          * Fires when the mouse exits this menu
23427          * @param {Roo.bootstrap.menu.Item} this
23428          * @param {Roo.EventObject} e
23429          */
23430         mouseout : true,
23431         // raw events
23432         /**
23433          * @event click
23434          * The raw click event for the entire grid.
23435          * @param {Roo.EventObject} e
23436          */
23437         click : true
23438     });
23439 };
23440
23441 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
23442     
23443     submenu : false,
23444     href : '',
23445     html : '',
23446     preventDefault: true,
23447     disable : false,
23448     icon : false,
23449     pos : 'right',
23450     
23451     getAutoCreate : function()
23452     {
23453         var text = [
23454             {
23455                 tag : 'span',
23456                 cls : 'roo-menu-item-text',
23457                 html : this.html
23458             }
23459         ];
23460         
23461         if(this.icon){
23462             text.unshift({
23463                 tag : 'i',
23464                 cls : 'fa ' + this.icon
23465             })
23466         }
23467         
23468         var cfg = {
23469             tag : 'li',
23470             cn : [
23471                 {
23472                     tag : 'a',
23473                     href : this.href || '#',
23474                     cn : text
23475                 }
23476             ]
23477         };
23478         
23479         if(this.disable){
23480             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23481         }
23482         
23483         if(this.submenu){
23484             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23485             
23486             if(this.pos == 'left'){
23487                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23488             }
23489         }
23490         
23491         return cfg;
23492     },
23493     
23494     initEvents : function() 
23495     {
23496         this.el.on('mouseover', this.onMouseOver, this);
23497         this.el.on('mouseout', this.onMouseOut, this);
23498         
23499         this.el.select('a', true).first().on('click', this.onClick, this);
23500         
23501     },
23502     
23503     onClick : function(e)
23504     {
23505         if(this.preventDefault){
23506             e.preventDefault();
23507         }
23508         
23509         this.fireEvent("click", this, e);
23510     },
23511     
23512     onMouseOver : function(e)
23513     {
23514         if(this.submenu && this.pos == 'left'){
23515             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23516         }
23517         
23518         this.fireEvent("mouseover", this, e);
23519     },
23520     
23521     onMouseOut : function(e)
23522     {
23523         this.fireEvent("mouseout", this, e);
23524     }
23525 });
23526
23527  
23528
23529  /*
23530  * - LGPL
23531  *
23532  * menu separator
23533  * 
23534  */
23535 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23536
23537 /**
23538  * @class Roo.bootstrap.menu.Separator
23539  * @extends Roo.bootstrap.Component
23540  * Bootstrap Separator class
23541  * 
23542  * @constructor
23543  * Create a new Separator
23544  * @param {Object} config The config object
23545  */
23546
23547
23548 Roo.bootstrap.menu.Separator = function(config){
23549     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23550 };
23551
23552 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
23553     
23554     getAutoCreate : function(){
23555         var cfg = {
23556             tag : 'li',
23557             cls: 'divider'
23558         };
23559         
23560         return cfg;
23561     }
23562    
23563 });
23564
23565  
23566
23567  /*
23568  * - LGPL
23569  *
23570  * Tooltip
23571  * 
23572  */
23573
23574 /**
23575  * @class Roo.bootstrap.Tooltip
23576  * Bootstrap Tooltip class
23577  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23578  * to determine which dom element triggers the tooltip.
23579  * 
23580  * It needs to add support for additional attributes like tooltip-position
23581  * 
23582  * @constructor
23583  * Create a new Toolti
23584  * @param {Object} config The config object
23585  */
23586
23587 Roo.bootstrap.Tooltip = function(config){
23588     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23589 };
23590
23591 Roo.apply(Roo.bootstrap.Tooltip, {
23592     /**
23593      * @function init initialize tooltip monitoring.
23594      * @static
23595      */
23596     currentEl : false,
23597     currentTip : false,
23598     currentRegion : false,
23599     
23600     //  init : delay?
23601     
23602     init : function()
23603     {
23604         Roo.get(document).on('mouseover', this.enter ,this);
23605         Roo.get(document).on('mouseout', this.leave, this);
23606          
23607         
23608         this.currentTip = new Roo.bootstrap.Tooltip();
23609     },
23610     
23611     enter : function(ev)
23612     {
23613         var dom = ev.getTarget();
23614         
23615         //Roo.log(['enter',dom]);
23616         var el = Roo.fly(dom);
23617         if (this.currentEl) {
23618             //Roo.log(dom);
23619             //Roo.log(this.currentEl);
23620             //Roo.log(this.currentEl.contains(dom));
23621             if (this.currentEl == el) {
23622                 return;
23623             }
23624             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23625                 return;
23626             }
23627
23628         }
23629         
23630         if (this.currentTip.el) {
23631             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
23632         }    
23633         //Roo.log(ev);
23634         var bindEl = el;
23635         
23636         // you can not look for children, as if el is the body.. then everythign is the child..
23637         if (!el.attr('tooltip')) { //
23638             if (!el.select("[tooltip]").elements.length) {
23639                 return;
23640             }
23641             // is the mouse over this child...?
23642             bindEl = el.select("[tooltip]").first();
23643             var xy = ev.getXY();
23644             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23645                 //Roo.log("not in region.");
23646                 return;
23647             }
23648             //Roo.log("child element over..");
23649             
23650         }
23651         this.currentEl = bindEl;
23652         this.currentTip.bind(bindEl);
23653         this.currentRegion = Roo.lib.Region.getRegion(dom);
23654         this.currentTip.enter();
23655         
23656     },
23657     leave : function(ev)
23658     {
23659         var dom = ev.getTarget();
23660         //Roo.log(['leave',dom]);
23661         if (!this.currentEl) {
23662             return;
23663         }
23664         
23665         
23666         if (dom != this.currentEl.dom) {
23667             return;
23668         }
23669         var xy = ev.getXY();
23670         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
23671             return;
23672         }
23673         // only activate leave if mouse cursor is outside... bounding box..
23674         
23675         
23676         
23677         
23678         if (this.currentTip) {
23679             this.currentTip.leave();
23680         }
23681         //Roo.log('clear currentEl');
23682         this.currentEl = false;
23683         
23684         
23685     },
23686     alignment : {
23687         'left' : ['r-l', [-2,0], 'right'],
23688         'right' : ['l-r', [2,0], 'left'],
23689         'bottom' : ['t-b', [0,2], 'top'],
23690         'top' : [ 'b-t', [0,-2], 'bottom']
23691     }
23692     
23693 });
23694
23695
23696 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
23697     
23698     
23699     bindEl : false,
23700     
23701     delay : null, // can be { show : 300 , hide: 500}
23702     
23703     timeout : null,
23704     
23705     hoverState : null, //???
23706     
23707     placement : 'bottom', 
23708     
23709     getAutoCreate : function(){
23710     
23711         var cfg = {
23712            cls : 'tooltip',
23713            role : 'tooltip',
23714            cn : [
23715                 {
23716                     cls : 'tooltip-arrow'
23717                 },
23718                 {
23719                     cls : 'tooltip-inner'
23720                 }
23721            ]
23722         };
23723         
23724         return cfg;
23725     },
23726     bind : function(el)
23727     {
23728         this.bindEl = el;
23729     },
23730       
23731     
23732     enter : function () {
23733        
23734         if (this.timeout != null) {
23735             clearTimeout(this.timeout);
23736         }
23737         
23738         this.hoverState = 'in';
23739          //Roo.log("enter - show");
23740         if (!this.delay || !this.delay.show) {
23741             this.show();
23742             return;
23743         }
23744         var _t = this;
23745         this.timeout = setTimeout(function () {
23746             if (_t.hoverState == 'in') {
23747                 _t.show();
23748             }
23749         }, this.delay.show);
23750     },
23751     leave : function()
23752     {
23753         clearTimeout(this.timeout);
23754     
23755         this.hoverState = 'out';
23756          if (!this.delay || !this.delay.hide) {
23757             this.hide();
23758             return;
23759         }
23760        
23761         var _t = this;
23762         this.timeout = setTimeout(function () {
23763             //Roo.log("leave - timeout");
23764             
23765             if (_t.hoverState == 'out') {
23766                 _t.hide();
23767                 Roo.bootstrap.Tooltip.currentEl = false;
23768             }
23769         }, delay);
23770     },
23771     
23772     show : function ()
23773     {
23774         if (!this.el) {
23775             this.render(document.body);
23776         }
23777         // set content.
23778         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23779         
23780         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23781         
23782         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23783         
23784         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23785         
23786         var placement = typeof this.placement == 'function' ?
23787             this.placement.call(this, this.el, on_el) :
23788             this.placement;
23789             
23790         var autoToken = /\s?auto?\s?/i;
23791         var autoPlace = autoToken.test(placement);
23792         if (autoPlace) {
23793             placement = placement.replace(autoToken, '') || 'top';
23794         }
23795         
23796         //this.el.detach()
23797         //this.el.setXY([0,0]);
23798         this.el.show();
23799         //this.el.dom.style.display='block';
23800         
23801         //this.el.appendTo(on_el);
23802         
23803         var p = this.getPosition();
23804         var box = this.el.getBox();
23805         
23806         if (autoPlace) {
23807             // fixme..
23808         }
23809         
23810         var align = Roo.bootstrap.Tooltip.alignment[placement];
23811         
23812         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
23813         
23814         if(placement == 'top' || placement == 'bottom'){
23815             if(xy[0] < 0){
23816                 placement = 'right';
23817             }
23818             
23819             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
23820                 placement = 'left';
23821             }
23822         }
23823         
23824         align = Roo.bootstrap.Tooltip.alignment[placement];
23825         
23826         this.el.alignTo(this.bindEl, align[0],align[1]);
23827         //var arrow = this.el.select('.arrow',true).first();
23828         //arrow.set(align[2], 
23829         
23830         this.el.addClass(placement);
23831         
23832         this.el.addClass('in fade');
23833         
23834         this.hoverState = null;
23835         
23836         if (this.el.hasClass('fade')) {
23837             // fade it?
23838         }
23839         
23840     },
23841     hide : function()
23842     {
23843          
23844         if (!this.el) {
23845             return;
23846         }
23847         //this.el.setXY([0,0]);
23848         this.el.removeClass('in');
23849         //this.el.hide();
23850         
23851     }
23852     
23853 });
23854  
23855
23856  /*
23857  * - LGPL
23858  *
23859  * Location Picker
23860  * 
23861  */
23862
23863 /**
23864  * @class Roo.bootstrap.LocationPicker
23865  * @extends Roo.bootstrap.Component
23866  * Bootstrap LocationPicker class
23867  * @cfg {Number} latitude Position when init default 0
23868  * @cfg {Number} longitude Position when init default 0
23869  * @cfg {Number} zoom default 15
23870  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
23871  * @cfg {Boolean} mapTypeControl default false
23872  * @cfg {Boolean} disableDoubleClickZoom default false
23873  * @cfg {Boolean} scrollwheel default true
23874  * @cfg {Boolean} streetViewControl default false
23875  * @cfg {Number} radius default 0
23876  * @cfg {String} locationName
23877  * @cfg {Boolean} draggable default true
23878  * @cfg {Boolean} enableAutocomplete default false
23879  * @cfg {Boolean} enableReverseGeocode default true
23880  * @cfg {String} markerTitle
23881  * 
23882  * @constructor
23883  * Create a new LocationPicker
23884  * @param {Object} config The config object
23885  */
23886
23887
23888 Roo.bootstrap.LocationPicker = function(config){
23889     
23890     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
23891     
23892     this.addEvents({
23893         /**
23894          * @event initial
23895          * Fires when the picker initialized.
23896          * @param {Roo.bootstrap.LocationPicker} this
23897          * @param {Google Location} location
23898          */
23899         initial : true,
23900         /**
23901          * @event positionchanged
23902          * Fires when the picker position changed.
23903          * @param {Roo.bootstrap.LocationPicker} this
23904          * @param {Google Location} location
23905          */
23906         positionchanged : true,
23907         /**
23908          * @event resize
23909          * Fires when the map resize.
23910          * @param {Roo.bootstrap.LocationPicker} this
23911          */
23912         resize : true,
23913         /**
23914          * @event show
23915          * Fires when the map show.
23916          * @param {Roo.bootstrap.LocationPicker} this
23917          */
23918         show : true,
23919         /**
23920          * @event hide
23921          * Fires when the map hide.
23922          * @param {Roo.bootstrap.LocationPicker} this
23923          */
23924         hide : true,
23925         /**
23926          * @event mapClick
23927          * Fires when click the map.
23928          * @param {Roo.bootstrap.LocationPicker} this
23929          * @param {Map event} e
23930          */
23931         mapClick : true,
23932         /**
23933          * @event mapRightClick
23934          * Fires when right click the map.
23935          * @param {Roo.bootstrap.LocationPicker} this
23936          * @param {Map event} e
23937          */
23938         mapRightClick : true,
23939         /**
23940          * @event markerClick
23941          * Fires when click the marker.
23942          * @param {Roo.bootstrap.LocationPicker} this
23943          * @param {Map event} e
23944          */
23945         markerClick : true,
23946         /**
23947          * @event markerRightClick
23948          * Fires when right click the marker.
23949          * @param {Roo.bootstrap.LocationPicker} this
23950          * @param {Map event} e
23951          */
23952         markerRightClick : true,
23953         /**
23954          * @event OverlayViewDraw
23955          * Fires when OverlayView Draw
23956          * @param {Roo.bootstrap.LocationPicker} this
23957          */
23958         OverlayViewDraw : true,
23959         /**
23960          * @event OverlayViewOnAdd
23961          * Fires when OverlayView Draw
23962          * @param {Roo.bootstrap.LocationPicker} this
23963          */
23964         OverlayViewOnAdd : true,
23965         /**
23966          * @event OverlayViewOnRemove
23967          * Fires when OverlayView Draw
23968          * @param {Roo.bootstrap.LocationPicker} this
23969          */
23970         OverlayViewOnRemove : true,
23971         /**
23972          * @event OverlayViewShow
23973          * Fires when OverlayView Draw
23974          * @param {Roo.bootstrap.LocationPicker} this
23975          * @param {Pixel} cpx
23976          */
23977         OverlayViewShow : true,
23978         /**
23979          * @event OverlayViewHide
23980          * Fires when OverlayView Draw
23981          * @param {Roo.bootstrap.LocationPicker} this
23982          */
23983         OverlayViewHide : true,
23984         /**
23985          * @event loadexception
23986          * Fires when load google lib failed.
23987          * @param {Roo.bootstrap.LocationPicker} this
23988          */
23989         loadexception : true
23990     });
23991         
23992 };
23993
23994 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
23995     
23996     gMapContext: false,
23997     
23998     latitude: 0,
23999     longitude: 0,
24000     zoom: 15,
24001     mapTypeId: false,
24002     mapTypeControl: false,
24003     disableDoubleClickZoom: false,
24004     scrollwheel: true,
24005     streetViewControl: false,
24006     radius: 0,
24007     locationName: '',
24008     draggable: true,
24009     enableAutocomplete: false,
24010     enableReverseGeocode: true,
24011     markerTitle: '',
24012     
24013     getAutoCreate: function()
24014     {
24015
24016         var cfg = {
24017             tag: 'div',
24018             cls: 'roo-location-picker'
24019         };
24020         
24021         return cfg
24022     },
24023     
24024     initEvents: function(ct, position)
24025     {       
24026         if(!this.el.getWidth() || this.isApplied()){
24027             return;
24028         }
24029         
24030         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24031         
24032         this.initial();
24033     },
24034     
24035     initial: function()
24036     {
24037         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24038             this.fireEvent('loadexception', this);
24039             return;
24040         }
24041         
24042         if(!this.mapTypeId){
24043             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24044         }
24045         
24046         this.gMapContext = this.GMapContext();
24047         
24048         this.initOverlayView();
24049         
24050         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24051         
24052         var _this = this;
24053                 
24054         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24055             _this.setPosition(_this.gMapContext.marker.position);
24056         });
24057         
24058         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
24059             _this.fireEvent('mapClick', this, event);
24060             
24061         });
24062
24063         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
24064             _this.fireEvent('mapRightClick', this, event);
24065             
24066         });
24067         
24068         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
24069             _this.fireEvent('markerClick', this, event);
24070             
24071         });
24072
24073         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
24074             _this.fireEvent('markerRightClick', this, event);
24075             
24076         });
24077         
24078         this.setPosition(this.gMapContext.location);
24079         
24080         this.fireEvent('initial', this, this.gMapContext.location);
24081     },
24082     
24083     initOverlayView: function()
24084     {
24085         var _this = this;
24086         
24087         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
24088             
24089             draw: function()
24090             {
24091                 _this.fireEvent('OverlayViewDraw', _this);
24092             },
24093             
24094             onAdd: function()
24095             {
24096                 _this.fireEvent('OverlayViewOnAdd', _this);
24097             },
24098             
24099             onRemove: function()
24100             {
24101                 _this.fireEvent('OverlayViewOnRemove', _this);
24102             },
24103             
24104             show: function(cpx)
24105             {
24106                 _this.fireEvent('OverlayViewShow', _this, cpx);
24107             },
24108             
24109             hide: function()
24110             {
24111                 _this.fireEvent('OverlayViewHide', _this);
24112             }
24113             
24114         });
24115     },
24116     
24117     fromLatLngToContainerPixel: function(event)
24118     {
24119         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
24120     },
24121     
24122     isApplied: function() 
24123     {
24124         return this.getGmapContext() == false ? false : true;
24125     },
24126     
24127     getGmapContext: function() 
24128     {
24129         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
24130     },
24131     
24132     GMapContext: function() 
24133     {
24134         var position = new google.maps.LatLng(this.latitude, this.longitude);
24135         
24136         var _map = new google.maps.Map(this.el.dom, {
24137             center: position,
24138             zoom: this.zoom,
24139             mapTypeId: this.mapTypeId,
24140             mapTypeControl: this.mapTypeControl,
24141             disableDoubleClickZoom: this.disableDoubleClickZoom,
24142             scrollwheel: this.scrollwheel,
24143             streetViewControl: this.streetViewControl,
24144             locationName: this.locationName,
24145             draggable: this.draggable,
24146             enableAutocomplete: this.enableAutocomplete,
24147             enableReverseGeocode: this.enableReverseGeocode
24148         });
24149         
24150         var _marker = new google.maps.Marker({
24151             position: position,
24152             map: _map,
24153             title: this.markerTitle,
24154             draggable: this.draggable
24155         });
24156         
24157         return {
24158             map: _map,
24159             marker: _marker,
24160             circle: null,
24161             location: position,
24162             radius: this.radius,
24163             locationName: this.locationName,
24164             addressComponents: {
24165                 formatted_address: null,
24166                 addressLine1: null,
24167                 addressLine2: null,
24168                 streetName: null,
24169                 streetNumber: null,
24170                 city: null,
24171                 district: null,
24172                 state: null,
24173                 stateOrProvince: null
24174             },
24175             settings: this,
24176             domContainer: this.el.dom,
24177             geodecoder: new google.maps.Geocoder()
24178         };
24179     },
24180     
24181     drawCircle: function(center, radius, options) 
24182     {
24183         if (this.gMapContext.circle != null) {
24184             this.gMapContext.circle.setMap(null);
24185         }
24186         if (radius > 0) {
24187             radius *= 1;
24188             options = Roo.apply({}, options, {
24189                 strokeColor: "#0000FF",
24190                 strokeOpacity: .35,
24191                 strokeWeight: 2,
24192                 fillColor: "#0000FF",
24193                 fillOpacity: .2
24194             });
24195             
24196             options.map = this.gMapContext.map;
24197             options.radius = radius;
24198             options.center = center;
24199             this.gMapContext.circle = new google.maps.Circle(options);
24200             return this.gMapContext.circle;
24201         }
24202         
24203         return null;
24204     },
24205     
24206     setPosition: function(location) 
24207     {
24208         this.gMapContext.location = location;
24209         this.gMapContext.marker.setPosition(location);
24210         this.gMapContext.map.panTo(location);
24211         this.drawCircle(location, this.gMapContext.radius, {});
24212         
24213         var _this = this;
24214         
24215         if (this.gMapContext.settings.enableReverseGeocode) {
24216             this.gMapContext.geodecoder.geocode({
24217                 latLng: this.gMapContext.location
24218             }, function(results, status) {
24219                 
24220                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
24221                     _this.gMapContext.locationName = results[0].formatted_address;
24222                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
24223                     
24224                     _this.fireEvent('positionchanged', this, location);
24225                 }
24226             });
24227             
24228             return;
24229         }
24230         
24231         this.fireEvent('positionchanged', this, location);
24232     },
24233     
24234     resize: function()
24235     {
24236         google.maps.event.trigger(this.gMapContext.map, "resize");
24237         
24238         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24239         
24240         this.fireEvent('resize', this);
24241     },
24242     
24243     setPositionByLatLng: function(latitude, longitude)
24244     {
24245         this.setPosition(new google.maps.LatLng(latitude, longitude));
24246     },
24247     
24248     getCurrentPosition: function() 
24249     {
24250         return {
24251             latitude: this.gMapContext.location.lat(),
24252             longitude: this.gMapContext.location.lng()
24253         };
24254     },
24255     
24256     getAddressName: function() 
24257     {
24258         return this.gMapContext.locationName;
24259     },
24260     
24261     getAddressComponents: function() 
24262     {
24263         return this.gMapContext.addressComponents;
24264     },
24265     
24266     address_component_from_google_geocode: function(address_components) 
24267     {
24268         var result = {};
24269         
24270         for (var i = 0; i < address_components.length; i++) {
24271             var component = address_components[i];
24272             if (component.types.indexOf("postal_code") >= 0) {
24273                 result.postalCode = component.short_name;
24274             } else if (component.types.indexOf("street_number") >= 0) {
24275                 result.streetNumber = component.short_name;
24276             } else if (component.types.indexOf("route") >= 0) {
24277                 result.streetName = component.short_name;
24278             } else if (component.types.indexOf("neighborhood") >= 0) {
24279                 result.city = component.short_name;
24280             } else if (component.types.indexOf("locality") >= 0) {
24281                 result.city = component.short_name;
24282             } else if (component.types.indexOf("sublocality") >= 0) {
24283                 result.district = component.short_name;
24284             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
24285                 result.stateOrProvince = component.short_name;
24286             } else if (component.types.indexOf("country") >= 0) {
24287                 result.country = component.short_name;
24288             }
24289         }
24290         
24291         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
24292         result.addressLine2 = "";
24293         return result;
24294     },
24295     
24296     setZoomLevel: function(zoom)
24297     {
24298         this.gMapContext.map.setZoom(zoom);
24299     },
24300     
24301     show: function()
24302     {
24303         if(!this.el){
24304             return;
24305         }
24306         
24307         this.el.show();
24308         
24309         this.resize();
24310         
24311         this.fireEvent('show', this);
24312     },
24313     
24314     hide: function()
24315     {
24316         if(!this.el){
24317             return;
24318         }
24319         
24320         this.el.hide();
24321         
24322         this.fireEvent('hide', this);
24323     }
24324     
24325 });
24326
24327 Roo.apply(Roo.bootstrap.LocationPicker, {
24328     
24329     OverlayView : function(map, options)
24330     {
24331         options = options || {};
24332         
24333         this.setMap(map);
24334     }
24335     
24336     
24337 });/*
24338  * - LGPL
24339  *
24340  * Alert
24341  * 
24342  */
24343
24344 /**
24345  * @class Roo.bootstrap.Alert
24346  * @extends Roo.bootstrap.Component
24347  * Bootstrap Alert class
24348  * @cfg {String} title The title of alert
24349  * @cfg {String} html The content of alert
24350  * @cfg {String} weight (  success | info | warning | danger )
24351  * @cfg {String} faicon font-awesomeicon
24352  * 
24353  * @constructor
24354  * Create a new alert
24355  * @param {Object} config The config object
24356  */
24357
24358
24359 Roo.bootstrap.Alert = function(config){
24360     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
24361     
24362 };
24363
24364 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
24365     
24366     title: '',
24367     html: '',
24368     weight: false,
24369     faicon: false,
24370     
24371     getAutoCreate : function()
24372     {
24373         
24374         var cfg = {
24375             tag : 'div',
24376             cls : 'alert',
24377             cn : [
24378                 {
24379                     tag : 'i',
24380                     cls : 'roo-alert-icon'
24381                     
24382                 },
24383                 {
24384                     tag : 'b',
24385                     cls : 'roo-alert-title',
24386                     html : this.title
24387                 },
24388                 {
24389                     tag : 'span',
24390                     cls : 'roo-alert-text',
24391                     html : this.html
24392                 }
24393             ]
24394         };
24395         
24396         if(this.faicon){
24397             cfg.cn[0].cls += ' fa ' + this.faicon;
24398         }
24399         
24400         if(this.weight){
24401             cfg.cls += ' alert-' + this.weight;
24402         }
24403         
24404         return cfg;
24405     },
24406     
24407     initEvents: function() 
24408     {
24409         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24410     },
24411     
24412     setTitle : function(str)
24413     {
24414         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24415     },
24416     
24417     setText : function(str)
24418     {
24419         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24420     },
24421     
24422     setWeight : function(weight)
24423     {
24424         if(this.weight){
24425             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24426         }
24427         
24428         this.weight = weight;
24429         
24430         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24431     },
24432     
24433     setIcon : function(icon)
24434     {
24435         if(this.faicon){
24436             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24437         }
24438         
24439         this.faicon = icon;
24440         
24441         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24442     },
24443     
24444     hide: function() 
24445     {
24446         this.el.hide();   
24447     },
24448     
24449     show: function() 
24450     {  
24451         this.el.show();   
24452     }
24453     
24454 });
24455
24456  
24457 /*
24458 * Licence: LGPL
24459 */
24460
24461 /**
24462  * @class Roo.bootstrap.UploadCropbox
24463  * @extends Roo.bootstrap.Component
24464  * Bootstrap UploadCropbox class
24465  * @cfg {String} emptyText show when image has been loaded
24466  * @cfg {String} rotateNotify show when image too small to rotate
24467  * @cfg {Number} errorTimeout default 3000
24468  * @cfg {Number} minWidth default 300
24469  * @cfg {Number} minHeight default 300
24470  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24471  * @cfg {Boolean} isDocument (true|false) default false
24472  * @cfg {String} url action url
24473  * @cfg {String} paramName default 'imageUpload'
24474  * @cfg {String} method default POST
24475  * @cfg {Boolean} loadMask (true|false) default true
24476  * @cfg {Boolean} loadingText default 'Loading...'
24477  * 
24478  * @constructor
24479  * Create a new UploadCropbox
24480  * @param {Object} config The config object
24481  */
24482
24483 Roo.bootstrap.UploadCropbox = function(config){
24484     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
24485     
24486     this.addEvents({
24487         /**
24488          * @event beforeselectfile
24489          * Fire before select file
24490          * @param {Roo.bootstrap.UploadCropbox} this
24491          */
24492         "beforeselectfile" : true,
24493         /**
24494          * @event initial
24495          * Fire after initEvent
24496          * @param {Roo.bootstrap.UploadCropbox} this
24497          */
24498         "initial" : true,
24499         /**
24500          * @event crop
24501          * Fire after initEvent
24502          * @param {Roo.bootstrap.UploadCropbox} this
24503          * @param {String} data
24504          */
24505         "crop" : true,
24506         /**
24507          * @event prepare
24508          * Fire when preparing the file data
24509          * @param {Roo.bootstrap.UploadCropbox} this
24510          * @param {Object} file
24511          */
24512         "prepare" : true,
24513         /**
24514          * @event exception
24515          * Fire when get exception
24516          * @param {Roo.bootstrap.UploadCropbox} this
24517          * @param {XMLHttpRequest} xhr
24518          */
24519         "exception" : true,
24520         /**
24521          * @event beforeloadcanvas
24522          * Fire before load the canvas
24523          * @param {Roo.bootstrap.UploadCropbox} this
24524          * @param {String} src
24525          */
24526         "beforeloadcanvas" : true,
24527         /**
24528          * @event trash
24529          * Fire when trash image
24530          * @param {Roo.bootstrap.UploadCropbox} this
24531          */
24532         "trash" : true,
24533         /**
24534          * @event download
24535          * Fire when download the image
24536          * @param {Roo.bootstrap.UploadCropbox} this
24537          */
24538         "download" : true,
24539         /**
24540          * @event footerbuttonclick
24541          * Fire when footerbuttonclick
24542          * @param {Roo.bootstrap.UploadCropbox} this
24543          * @param {String} type
24544          */
24545         "footerbuttonclick" : true,
24546         /**
24547          * @event resize
24548          * Fire when resize
24549          * @param {Roo.bootstrap.UploadCropbox} this
24550          */
24551         "resize" : true,
24552         /**
24553          * @event rotate
24554          * Fire when rotate the image
24555          * @param {Roo.bootstrap.UploadCropbox} this
24556          * @param {String} pos
24557          */
24558         "rotate" : true,
24559         /**
24560          * @event inspect
24561          * Fire when inspect the file
24562          * @param {Roo.bootstrap.UploadCropbox} this
24563          * @param {Object} file
24564          */
24565         "inspect" : true,
24566         /**
24567          * @event upload
24568          * Fire when xhr upload the file
24569          * @param {Roo.bootstrap.UploadCropbox} this
24570          * @param {Object} data
24571          */
24572         "upload" : true,
24573         /**
24574          * @event arrange
24575          * Fire when arrange the file data
24576          * @param {Roo.bootstrap.UploadCropbox} this
24577          * @param {Object} formData
24578          */
24579         "arrange" : true
24580     });
24581     
24582     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24583 };
24584
24585 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
24586     
24587     emptyText : 'Click to upload image',
24588     rotateNotify : 'Image is too small to rotate',
24589     errorTimeout : 3000,
24590     scale : 0,
24591     baseScale : 1,
24592     rotate : 0,
24593     dragable : false,
24594     pinching : false,
24595     mouseX : 0,
24596     mouseY : 0,
24597     cropData : false,
24598     minWidth : 300,
24599     minHeight : 300,
24600     file : false,
24601     exif : {},
24602     baseRotate : 1,
24603     cropType : 'image/jpeg',
24604     buttons : false,
24605     canvasLoaded : false,
24606     isDocument : false,
24607     method : 'POST',
24608     paramName : 'imageUpload',
24609     loadMask : true,
24610     loadingText : 'Loading...',
24611     maskEl : false,
24612     
24613     getAutoCreate : function()
24614     {
24615         var cfg = {
24616             tag : 'div',
24617             cls : 'roo-upload-cropbox',
24618             cn : [
24619                 {
24620                     tag : 'input',
24621                     cls : 'roo-upload-cropbox-selector',
24622                     type : 'file'
24623                 },
24624                 {
24625                     tag : 'div',
24626                     cls : 'roo-upload-cropbox-body',
24627                     style : 'cursor:pointer',
24628                     cn : [
24629                         {
24630                             tag : 'div',
24631                             cls : 'roo-upload-cropbox-preview'
24632                         },
24633                         {
24634                             tag : 'div',
24635                             cls : 'roo-upload-cropbox-thumb'
24636                         },
24637                         {
24638                             tag : 'div',
24639                             cls : 'roo-upload-cropbox-empty-notify',
24640                             html : this.emptyText
24641                         },
24642                         {
24643                             tag : 'div',
24644                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
24645                             html : this.rotateNotify
24646                         }
24647                     ]
24648                 },
24649                 {
24650                     tag : 'div',
24651                     cls : 'roo-upload-cropbox-footer',
24652                     cn : {
24653                         tag : 'div',
24654                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24655                         cn : []
24656                     }
24657                 }
24658             ]
24659         };
24660         
24661         return cfg;
24662     },
24663     
24664     onRender : function(ct, position)
24665     {
24666         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
24667         
24668         if (this.buttons.length) {
24669             
24670             Roo.each(this.buttons, function(bb) {
24671                 
24672                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
24673                 
24674                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
24675                 
24676             }, this);
24677         }
24678         
24679         if(this.loadMask){
24680             this.maskEl = this.el;
24681         }
24682     },
24683     
24684     initEvents : function()
24685     {
24686         this.urlAPI = (window.createObjectURL && window) || 
24687                                 (window.URL && URL.revokeObjectURL && URL) || 
24688                                 (window.webkitURL && webkitURL);
24689                         
24690         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
24691         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24692         
24693         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
24694         this.selectorEl.hide();
24695         
24696         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
24697         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24698         
24699         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
24700         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24701         this.thumbEl.hide();
24702         
24703         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
24704         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24705         
24706         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
24707         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24708         this.errorEl.hide();
24709         
24710         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
24711         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24712         this.footerEl.hide();
24713         
24714         this.setThumbBoxSize();
24715         
24716         this.bind();
24717         
24718         this.resize();
24719         
24720         this.fireEvent('initial', this);
24721     },
24722
24723     bind : function()
24724     {
24725         var _this = this;
24726         
24727         window.addEventListener("resize", function() { _this.resize(); } );
24728         
24729         this.bodyEl.on('click', this.beforeSelectFile, this);
24730         
24731         if(Roo.isTouch){
24732             this.bodyEl.on('touchstart', this.onTouchStart, this);
24733             this.bodyEl.on('touchmove', this.onTouchMove, this);
24734             this.bodyEl.on('touchend', this.onTouchEnd, this);
24735         }
24736         
24737         if(!Roo.isTouch){
24738             this.bodyEl.on('mousedown', this.onMouseDown, this);
24739             this.bodyEl.on('mousemove', this.onMouseMove, this);
24740             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
24741             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
24742             Roo.get(document).on('mouseup', this.onMouseUp, this);
24743         }
24744         
24745         this.selectorEl.on('change', this.onFileSelected, this);
24746     },
24747     
24748     reset : function()
24749     {    
24750         this.scale = 0;
24751         this.baseScale = 1;
24752         this.rotate = 0;
24753         this.baseRotate = 1;
24754         this.dragable = false;
24755         this.pinching = false;
24756         this.mouseX = 0;
24757         this.mouseY = 0;
24758         this.cropData = false;
24759         this.notifyEl.dom.innerHTML = this.emptyText;
24760         
24761         this.selectorEl.dom.value = '';
24762         
24763     },
24764     
24765     resize : function()
24766     {
24767         if(this.fireEvent('resize', this) != false){
24768             this.setThumbBoxPosition();
24769             this.setCanvasPosition();
24770         }
24771     },
24772     
24773     onFooterButtonClick : function(e, el, o, type)
24774     {
24775         switch (type) {
24776             case 'rotate-left' :
24777                 this.onRotateLeft(e);
24778                 break;
24779             case 'rotate-right' :
24780                 this.onRotateRight(e);
24781                 break;
24782             case 'picture' :
24783                 this.beforeSelectFile(e);
24784                 break;
24785             case 'trash' :
24786                 this.trash(e);
24787                 break;
24788             case 'crop' :
24789                 this.crop(e);
24790                 break;
24791             case 'download' :
24792                 this.download(e);
24793                 break;
24794             default :
24795                 break;
24796         }
24797         
24798         this.fireEvent('footerbuttonclick', this, type);
24799     },
24800     
24801     beforeSelectFile : function(e)
24802     {
24803         e.preventDefault();
24804         
24805         if(this.fireEvent('beforeselectfile', this) != false){
24806             this.selectorEl.dom.click();
24807         }
24808     },
24809     
24810     onFileSelected : function(e)
24811     {
24812         e.preventDefault();
24813         
24814         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
24815             return;
24816         }
24817         
24818         var file = this.selectorEl.dom.files[0];
24819         
24820         if(this.fireEvent('inspect', this, file) != false){
24821             this.prepare(file);
24822         }
24823         
24824     },
24825     
24826     trash : function(e)
24827     {
24828         this.fireEvent('trash', this);
24829     },
24830     
24831     download : function(e)
24832     {
24833         this.fireEvent('download', this);
24834     },
24835     
24836     loadCanvas : function(src)
24837     {   
24838         if(this.fireEvent('beforeloadcanvas', this, src) != false){
24839             
24840             this.reset();
24841             
24842             this.imageEl = document.createElement('img');
24843             
24844             var _this = this;
24845             
24846             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
24847             
24848             this.imageEl.src = src;
24849         }
24850     },
24851     
24852     onLoadCanvas : function()
24853     {   
24854         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
24855         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
24856         
24857         this.bodyEl.un('click', this.beforeSelectFile, this);
24858         
24859         this.notifyEl.hide();
24860         this.thumbEl.show();
24861         this.footerEl.show();
24862         
24863         this.baseRotateLevel();
24864         
24865         if(this.isDocument){
24866             this.setThumbBoxSize();
24867         }
24868         
24869         this.setThumbBoxPosition();
24870         
24871         this.baseScaleLevel();
24872         
24873         this.draw();
24874         
24875         this.resize();
24876         
24877         this.canvasLoaded = true;
24878         
24879         if(this.loadMask){
24880             this.maskEl.unmask();
24881         }
24882         
24883     },
24884     
24885     setCanvasPosition : function()
24886     {   
24887         if(!this.canvasEl){
24888             return;
24889         }
24890         
24891         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
24892         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
24893         
24894         this.previewEl.setLeft(pw);
24895         this.previewEl.setTop(ph);
24896         
24897     },
24898     
24899     onMouseDown : function(e)
24900     {   
24901         e.stopEvent();
24902         
24903         this.dragable = true;
24904         this.pinching = false;
24905         
24906         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
24907             this.dragable = false;
24908             return;
24909         }
24910         
24911         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24912         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24913         
24914     },
24915     
24916     onMouseMove : function(e)
24917     {   
24918         e.stopEvent();
24919         
24920         if(!this.canvasLoaded){
24921             return;
24922         }
24923         
24924         if (!this.dragable){
24925             return;
24926         }
24927         
24928         var minX = Math.ceil(this.thumbEl.getLeft(true));
24929         var minY = Math.ceil(this.thumbEl.getTop(true));
24930         
24931         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
24932         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
24933         
24934         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24935         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24936         
24937         x = x - this.mouseX;
24938         y = y - this.mouseY;
24939         
24940         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
24941         var bgY = Math.ceil(y + this.previewEl.getTop(true));
24942         
24943         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
24944         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
24945         
24946         this.previewEl.setLeft(bgX);
24947         this.previewEl.setTop(bgY);
24948         
24949         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24950         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24951     },
24952     
24953     onMouseUp : function(e)
24954     {   
24955         e.stopEvent();
24956         
24957         this.dragable = false;
24958     },
24959     
24960     onMouseWheel : function(e)
24961     {   
24962         e.stopEvent();
24963         
24964         this.startScale = this.scale;
24965         
24966         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
24967         
24968         if(!this.zoomable()){
24969             this.scale = this.startScale;
24970             return;
24971         }
24972         
24973         this.draw();
24974         
24975         return;
24976     },
24977     
24978     zoomable : function()
24979     {
24980         var minScale = this.thumbEl.getWidth() / this.minWidth;
24981         
24982         if(this.minWidth < this.minHeight){
24983             minScale = this.thumbEl.getHeight() / this.minHeight;
24984         }
24985         
24986         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
24987         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
24988         
24989         if(
24990                 this.isDocument &&
24991                 (this.rotate == 0 || this.rotate == 180) && 
24992                 (
24993                     width > this.imageEl.OriginWidth || 
24994                     height > this.imageEl.OriginHeight ||
24995                     (width < this.minWidth && height < this.minHeight)
24996                 )
24997         ){
24998             return false;
24999         }
25000         
25001         if(
25002                 this.isDocument &&
25003                 (this.rotate == 90 || this.rotate == 270) && 
25004                 (
25005                     width > this.imageEl.OriginWidth || 
25006                     height > this.imageEl.OriginHeight ||
25007                     (width < this.minHeight && height < this.minWidth)
25008                 )
25009         ){
25010             return false;
25011         }
25012         
25013         if(
25014                 !this.isDocument &&
25015                 (this.rotate == 0 || this.rotate == 180) && 
25016                 (
25017                     width < this.minWidth || 
25018                     width > this.imageEl.OriginWidth || 
25019                     height < this.minHeight || 
25020                     height > this.imageEl.OriginHeight
25021                 )
25022         ){
25023             return false;
25024         }
25025         
25026         if(
25027                 !this.isDocument &&
25028                 (this.rotate == 90 || this.rotate == 270) && 
25029                 (
25030                     width < this.minHeight || 
25031                     width > this.imageEl.OriginWidth || 
25032                     height < this.minWidth || 
25033                     height > this.imageEl.OriginHeight
25034                 )
25035         ){
25036             return false;
25037         }
25038         
25039         return true;
25040         
25041     },
25042     
25043     onRotateLeft : function(e)
25044     {   
25045         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25046             
25047             var minScale = this.thumbEl.getWidth() / this.minWidth;
25048             
25049             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25050             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25051             
25052             this.startScale = this.scale;
25053             
25054             while (this.getScaleLevel() < minScale){
25055             
25056                 this.scale = this.scale + 1;
25057                 
25058                 if(!this.zoomable()){
25059                     break;
25060                 }
25061                 
25062                 if(
25063                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25064                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25065                 ){
25066                     continue;
25067                 }
25068                 
25069                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25070
25071                 this.draw();
25072                 
25073                 return;
25074             }
25075             
25076             this.scale = this.startScale;
25077             
25078             this.onRotateFail();
25079             
25080             return false;
25081         }
25082         
25083         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25084
25085         if(this.isDocument){
25086             this.setThumbBoxSize();
25087             this.setThumbBoxPosition();
25088             this.setCanvasPosition();
25089         }
25090         
25091         this.draw();
25092         
25093         this.fireEvent('rotate', this, 'left');
25094         
25095     },
25096     
25097     onRotateRight : function(e)
25098     {
25099         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25100             
25101             var minScale = this.thumbEl.getWidth() / this.minWidth;
25102         
25103             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25104             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25105             
25106             this.startScale = this.scale;
25107             
25108             while (this.getScaleLevel() < minScale){
25109             
25110                 this.scale = this.scale + 1;
25111                 
25112                 if(!this.zoomable()){
25113                     break;
25114                 }
25115                 
25116                 if(
25117                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25118                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25119                 ){
25120                     continue;
25121                 }
25122                 
25123                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25124
25125                 this.draw();
25126                 
25127                 return;
25128             }
25129             
25130             this.scale = this.startScale;
25131             
25132             this.onRotateFail();
25133             
25134             return false;
25135         }
25136         
25137         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25138
25139         if(this.isDocument){
25140             this.setThumbBoxSize();
25141             this.setThumbBoxPosition();
25142             this.setCanvasPosition();
25143         }
25144         
25145         this.draw();
25146         
25147         this.fireEvent('rotate', this, 'right');
25148     },
25149     
25150     onRotateFail : function()
25151     {
25152         this.errorEl.show(true);
25153         
25154         var _this = this;
25155         
25156         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
25157     },
25158     
25159     draw : function()
25160     {
25161         this.previewEl.dom.innerHTML = '';
25162         
25163         var canvasEl = document.createElement("canvas");
25164         
25165         var contextEl = canvasEl.getContext("2d");
25166         
25167         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25168         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25169         var center = this.imageEl.OriginWidth / 2;
25170         
25171         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
25172             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25173             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25174             center = this.imageEl.OriginHeight / 2;
25175         }
25176         
25177         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
25178         
25179         contextEl.translate(center, center);
25180         contextEl.rotate(this.rotate * Math.PI / 180);
25181
25182         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25183         
25184         this.canvasEl = document.createElement("canvas");
25185         
25186         this.contextEl = this.canvasEl.getContext("2d");
25187         
25188         switch (this.rotate) {
25189             case 0 :
25190                 
25191                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25192                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25193                 
25194                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25195                 
25196                 break;
25197             case 90 : 
25198                 
25199                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25200                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25201                 
25202                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25203                     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);
25204                     break;
25205                 }
25206                 
25207                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25208                 
25209                 break;
25210             case 180 :
25211                 
25212                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25213                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25214                 
25215                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25216                     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);
25217                     break;
25218                 }
25219                 
25220                 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);
25221                 
25222                 break;
25223             case 270 :
25224                 
25225                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25226                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25227         
25228                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25229                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25230                     break;
25231                 }
25232                 
25233                 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);
25234                 
25235                 break;
25236             default : 
25237                 break;
25238         }
25239         
25240         this.previewEl.appendChild(this.canvasEl);
25241         
25242         this.setCanvasPosition();
25243     },
25244     
25245     crop : function()
25246     {
25247         if(!this.canvasLoaded){
25248             return;
25249         }
25250         
25251         var imageCanvas = document.createElement("canvas");
25252         
25253         var imageContext = imageCanvas.getContext("2d");
25254         
25255         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25256         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25257         
25258         var center = imageCanvas.width / 2;
25259         
25260         imageContext.translate(center, center);
25261         
25262         imageContext.rotate(this.rotate * Math.PI / 180);
25263         
25264         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25265         
25266         var canvas = document.createElement("canvas");
25267         
25268         var context = canvas.getContext("2d");
25269                 
25270         canvas.width = this.minWidth;
25271         canvas.height = this.minHeight;
25272
25273         switch (this.rotate) {
25274             case 0 :
25275                 
25276                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25277                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25278                 
25279                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25280                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25281                 
25282                 var targetWidth = this.minWidth - 2 * x;
25283                 var targetHeight = this.minHeight - 2 * y;
25284                 
25285                 var scale = 1;
25286                 
25287                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25288                     scale = targetWidth / width;
25289                 }
25290                 
25291                 if(x > 0 && y == 0){
25292                     scale = targetHeight / height;
25293                 }
25294                 
25295                 if(x > 0 && y > 0){
25296                     scale = targetWidth / width;
25297                     
25298                     if(width < height){
25299                         scale = targetHeight / height;
25300                     }
25301                 }
25302                 
25303                 context.scale(scale, scale);
25304                 
25305                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25306                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25307
25308                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25309                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25310
25311                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25312                 
25313                 break;
25314             case 90 : 
25315                 
25316                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25317                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25318                 
25319                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25320                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25321                 
25322                 var targetWidth = this.minWidth - 2 * x;
25323                 var targetHeight = this.minHeight - 2 * y;
25324                 
25325                 var scale = 1;
25326                 
25327                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25328                     scale = targetWidth / width;
25329                 }
25330                 
25331                 if(x > 0 && y == 0){
25332                     scale = targetHeight / height;
25333                 }
25334                 
25335                 if(x > 0 && y > 0){
25336                     scale = targetWidth / width;
25337                     
25338                     if(width < height){
25339                         scale = targetHeight / height;
25340                     }
25341                 }
25342                 
25343                 context.scale(scale, scale);
25344                 
25345                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25346                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25347
25348                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25349                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25350                 
25351                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25352                 
25353                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25354                 
25355                 break;
25356             case 180 :
25357                 
25358                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25359                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25360                 
25361                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25362                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25363                 
25364                 var targetWidth = this.minWidth - 2 * x;
25365                 var targetHeight = this.minHeight - 2 * y;
25366                 
25367                 var scale = 1;
25368                 
25369                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25370                     scale = targetWidth / width;
25371                 }
25372                 
25373                 if(x > 0 && y == 0){
25374                     scale = targetHeight / height;
25375                 }
25376                 
25377                 if(x > 0 && y > 0){
25378                     scale = targetWidth / width;
25379                     
25380                     if(width < height){
25381                         scale = targetHeight / height;
25382                     }
25383                 }
25384                 
25385                 context.scale(scale, scale);
25386                 
25387                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25388                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25389
25390                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25391                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25392
25393                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25394                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25395                 
25396                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25397                 
25398                 break;
25399             case 270 :
25400                 
25401                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25402                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25403                 
25404                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25405                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25406                 
25407                 var targetWidth = this.minWidth - 2 * x;
25408                 var targetHeight = this.minHeight - 2 * y;
25409                 
25410                 var scale = 1;
25411                 
25412                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25413                     scale = targetWidth / width;
25414                 }
25415                 
25416                 if(x > 0 && y == 0){
25417                     scale = targetHeight / height;
25418                 }
25419                 
25420                 if(x > 0 && y > 0){
25421                     scale = targetWidth / width;
25422                     
25423                     if(width < height){
25424                         scale = targetHeight / height;
25425                     }
25426                 }
25427                 
25428                 context.scale(scale, scale);
25429                 
25430                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25431                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25432
25433                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25434                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25435                 
25436                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25437                 
25438                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25439                 
25440                 break;
25441             default : 
25442                 break;
25443         }
25444         
25445         this.cropData = canvas.toDataURL(this.cropType);
25446         
25447         if(this.fireEvent('crop', this, this.cropData) !== false){
25448             this.process(this.file, this.cropData);
25449         }
25450         
25451         return;
25452         
25453     },
25454     
25455     setThumbBoxSize : function()
25456     {
25457         var width, height;
25458         
25459         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
25460             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
25461             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
25462             
25463             this.minWidth = width;
25464             this.minHeight = height;
25465             
25466             if(this.rotate == 90 || this.rotate == 270){
25467                 this.minWidth = height;
25468                 this.minHeight = width;
25469             }
25470         }
25471         
25472         height = 300;
25473         width = Math.ceil(this.minWidth * height / this.minHeight);
25474         
25475         if(this.minWidth > this.minHeight){
25476             width = 300;
25477             height = Math.ceil(this.minHeight * width / this.minWidth);
25478         }
25479         
25480         this.thumbEl.setStyle({
25481             width : width + 'px',
25482             height : height + 'px'
25483         });
25484
25485         return;
25486             
25487     },
25488     
25489     setThumbBoxPosition : function()
25490     {
25491         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
25492         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
25493         
25494         this.thumbEl.setLeft(x);
25495         this.thumbEl.setTop(y);
25496         
25497     },
25498     
25499     baseRotateLevel : function()
25500     {
25501         this.baseRotate = 1;
25502         
25503         if(
25504                 typeof(this.exif) != 'undefined' &&
25505                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
25506                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
25507         ){
25508             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
25509         }
25510         
25511         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
25512         
25513     },
25514     
25515     baseScaleLevel : function()
25516     {
25517         var width, height;
25518         
25519         if(this.isDocument){
25520             
25521             if(this.baseRotate == 6 || this.baseRotate == 8){
25522             
25523                 height = this.thumbEl.getHeight();
25524                 this.baseScale = height / this.imageEl.OriginWidth;
25525
25526                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
25527                     width = this.thumbEl.getWidth();
25528                     this.baseScale = width / this.imageEl.OriginHeight;
25529                 }
25530
25531                 return;
25532             }
25533
25534             height = this.thumbEl.getHeight();
25535             this.baseScale = height / this.imageEl.OriginHeight;
25536
25537             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
25538                 width = this.thumbEl.getWidth();
25539                 this.baseScale = width / this.imageEl.OriginWidth;
25540             }
25541
25542             return;
25543         }
25544         
25545         if(this.baseRotate == 6 || this.baseRotate == 8){
25546             
25547             width = this.thumbEl.getHeight();
25548             this.baseScale = width / this.imageEl.OriginHeight;
25549             
25550             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
25551                 height = this.thumbEl.getWidth();
25552                 this.baseScale = height / this.imageEl.OriginHeight;
25553             }
25554             
25555             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25556                 height = this.thumbEl.getWidth();
25557                 this.baseScale = height / this.imageEl.OriginHeight;
25558                 
25559                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
25560                     width = this.thumbEl.getHeight();
25561                     this.baseScale = width / this.imageEl.OriginWidth;
25562                 }
25563             }
25564             
25565             return;
25566         }
25567         
25568         width = this.thumbEl.getWidth();
25569         this.baseScale = width / this.imageEl.OriginWidth;
25570         
25571         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
25572             height = this.thumbEl.getHeight();
25573             this.baseScale = height / this.imageEl.OriginHeight;
25574         }
25575         
25576         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25577             
25578             height = this.thumbEl.getHeight();
25579             this.baseScale = height / this.imageEl.OriginHeight;
25580             
25581             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
25582                 width = this.thumbEl.getWidth();
25583                 this.baseScale = width / this.imageEl.OriginWidth;
25584             }
25585             
25586         }
25587         
25588         return;
25589     },
25590     
25591     getScaleLevel : function()
25592     {
25593         return this.baseScale * Math.pow(1.1, this.scale);
25594     },
25595     
25596     onTouchStart : function(e)
25597     {
25598         if(!this.canvasLoaded){
25599             this.beforeSelectFile(e);
25600             return;
25601         }
25602         
25603         var touches = e.browserEvent.touches;
25604         
25605         if(!touches){
25606             return;
25607         }
25608         
25609         if(touches.length == 1){
25610             this.onMouseDown(e);
25611             return;
25612         }
25613         
25614         if(touches.length != 2){
25615             return;
25616         }
25617         
25618         var coords = [];
25619         
25620         for(var i = 0, finger; finger = touches[i]; i++){
25621             coords.push(finger.pageX, finger.pageY);
25622         }
25623         
25624         var x = Math.pow(coords[0] - coords[2], 2);
25625         var y = Math.pow(coords[1] - coords[3], 2);
25626         
25627         this.startDistance = Math.sqrt(x + y);
25628         
25629         this.startScale = this.scale;
25630         
25631         this.pinching = true;
25632         this.dragable = false;
25633         
25634     },
25635     
25636     onTouchMove : function(e)
25637     {
25638         if(!this.pinching && !this.dragable){
25639             return;
25640         }
25641         
25642         var touches = e.browserEvent.touches;
25643         
25644         if(!touches){
25645             return;
25646         }
25647         
25648         if(this.dragable){
25649             this.onMouseMove(e);
25650             return;
25651         }
25652         
25653         var coords = [];
25654         
25655         for(var i = 0, finger; finger = touches[i]; i++){
25656             coords.push(finger.pageX, finger.pageY);
25657         }
25658         
25659         var x = Math.pow(coords[0] - coords[2], 2);
25660         var y = Math.pow(coords[1] - coords[3], 2);
25661         
25662         this.endDistance = Math.sqrt(x + y);
25663         
25664         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
25665         
25666         if(!this.zoomable()){
25667             this.scale = this.startScale;
25668             return;
25669         }
25670         
25671         this.draw();
25672         
25673     },
25674     
25675     onTouchEnd : function(e)
25676     {
25677         this.pinching = false;
25678         this.dragable = false;
25679         
25680     },
25681     
25682     process : function(file, crop)
25683     {
25684         if(this.loadMask){
25685             this.maskEl.mask(this.loadingText);
25686         }
25687         
25688         this.xhr = new XMLHttpRequest();
25689         
25690         file.xhr = this.xhr;
25691
25692         this.xhr.open(this.method, this.url, true);
25693         
25694         var headers = {
25695             "Accept": "application/json",
25696             "Cache-Control": "no-cache",
25697             "X-Requested-With": "XMLHttpRequest"
25698         };
25699         
25700         for (var headerName in headers) {
25701             var headerValue = headers[headerName];
25702             if (headerValue) {
25703                 this.xhr.setRequestHeader(headerName, headerValue);
25704             }
25705         }
25706         
25707         var _this = this;
25708         
25709         this.xhr.onload = function()
25710         {
25711             _this.xhrOnLoad(_this.xhr);
25712         }
25713         
25714         this.xhr.onerror = function()
25715         {
25716             _this.xhrOnError(_this.xhr);
25717         }
25718         
25719         var formData = new FormData();
25720
25721         formData.append('returnHTML', 'NO');
25722         
25723         if(crop){
25724             formData.append('crop', crop);
25725         }
25726         
25727         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
25728             formData.append(this.paramName, file, file.name);
25729         }
25730         
25731         if(typeof(file.filename) != 'undefined'){
25732             formData.append('filename', file.filename);
25733         }
25734         
25735         if(typeof(file.mimetype) != 'undefined'){
25736             formData.append('mimetype', file.mimetype);
25737         }
25738         
25739         if(this.fireEvent('arrange', this, formData) != false){
25740             this.xhr.send(formData);
25741         };
25742     },
25743     
25744     xhrOnLoad : function(xhr)
25745     {
25746         if(this.loadMask){
25747             this.maskEl.unmask();
25748         }
25749         
25750         if (xhr.readyState !== 4) {
25751             this.fireEvent('exception', this, xhr);
25752             return;
25753         }
25754
25755         var response = Roo.decode(xhr.responseText);
25756         
25757         if(!response.success){
25758             this.fireEvent('exception', this, xhr);
25759             return;
25760         }
25761         
25762         var response = Roo.decode(xhr.responseText);
25763         
25764         this.fireEvent('upload', this, response);
25765         
25766     },
25767     
25768     xhrOnError : function()
25769     {
25770         if(this.loadMask){
25771             this.maskEl.unmask();
25772         }
25773         
25774         Roo.log('xhr on error');
25775         
25776         var response = Roo.decode(xhr.responseText);
25777           
25778         Roo.log(response);
25779         
25780     },
25781     
25782     prepare : function(file)
25783     {   
25784         if(this.loadMask){
25785             this.maskEl.mask(this.loadingText);
25786         }
25787         
25788         this.file = false;
25789         this.exif = {};
25790         
25791         if(typeof(file) === 'string'){
25792             this.loadCanvas(file);
25793             return;
25794         }
25795         
25796         if(!file || !this.urlAPI){
25797             return;
25798         }
25799         
25800         this.file = file;
25801         this.cropType = file.type;
25802         
25803         var _this = this;
25804         
25805         if(this.fireEvent('prepare', this, this.file) != false){
25806             
25807             var reader = new FileReader();
25808             
25809             reader.onload = function (e) {
25810                 if (e.target.error) {
25811                     Roo.log(e.target.error);
25812                     return;
25813                 }
25814                 
25815                 var buffer = e.target.result,
25816                     dataView = new DataView(buffer),
25817                     offset = 2,
25818                     maxOffset = dataView.byteLength - 4,
25819                     markerBytes,
25820                     markerLength;
25821                 
25822                 if (dataView.getUint16(0) === 0xffd8) {
25823                     while (offset < maxOffset) {
25824                         markerBytes = dataView.getUint16(offset);
25825                         
25826                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
25827                             markerLength = dataView.getUint16(offset + 2) + 2;
25828                             if (offset + markerLength > dataView.byteLength) {
25829                                 Roo.log('Invalid meta data: Invalid segment size.');
25830                                 break;
25831                             }
25832                             
25833                             if(markerBytes == 0xffe1){
25834                                 _this.parseExifData(
25835                                     dataView,
25836                                     offset,
25837                                     markerLength
25838                                 );
25839                             }
25840                             
25841                             offset += markerLength;
25842                             
25843                             continue;
25844                         }
25845                         
25846                         break;
25847                     }
25848                     
25849                 }
25850                 
25851                 var url = _this.urlAPI.createObjectURL(_this.file);
25852                 
25853                 _this.loadCanvas(url);
25854                 
25855                 return;
25856             }
25857             
25858             reader.readAsArrayBuffer(this.file);
25859             
25860         }
25861         
25862     },
25863     
25864     parseExifData : function(dataView, offset, length)
25865     {
25866         var tiffOffset = offset + 10,
25867             littleEndian,
25868             dirOffset;
25869     
25870         if (dataView.getUint32(offset + 4) !== 0x45786966) {
25871             // No Exif data, might be XMP data instead
25872             return;
25873         }
25874         
25875         // Check for the ASCII code for "Exif" (0x45786966):
25876         if (dataView.getUint32(offset + 4) !== 0x45786966) {
25877             // No Exif data, might be XMP data instead
25878             return;
25879         }
25880         if (tiffOffset + 8 > dataView.byteLength) {
25881             Roo.log('Invalid Exif data: Invalid segment size.');
25882             return;
25883         }
25884         // Check for the two null bytes:
25885         if (dataView.getUint16(offset + 8) !== 0x0000) {
25886             Roo.log('Invalid Exif data: Missing byte alignment offset.');
25887             return;
25888         }
25889         // Check the byte alignment:
25890         switch (dataView.getUint16(tiffOffset)) {
25891         case 0x4949:
25892             littleEndian = true;
25893             break;
25894         case 0x4D4D:
25895             littleEndian = false;
25896             break;
25897         default:
25898             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
25899             return;
25900         }
25901         // Check for the TIFF tag marker (0x002A):
25902         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
25903             Roo.log('Invalid Exif data: Missing TIFF marker.');
25904             return;
25905         }
25906         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
25907         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
25908         
25909         this.parseExifTags(
25910             dataView,
25911             tiffOffset,
25912             tiffOffset + dirOffset,
25913             littleEndian
25914         );
25915     },
25916     
25917     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
25918     {
25919         var tagsNumber,
25920             dirEndOffset,
25921             i;
25922         if (dirOffset + 6 > dataView.byteLength) {
25923             Roo.log('Invalid Exif data: Invalid directory offset.');
25924             return;
25925         }
25926         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
25927         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
25928         if (dirEndOffset + 4 > dataView.byteLength) {
25929             Roo.log('Invalid Exif data: Invalid directory size.');
25930             return;
25931         }
25932         for (i = 0; i < tagsNumber; i += 1) {
25933             this.parseExifTag(
25934                 dataView,
25935                 tiffOffset,
25936                 dirOffset + 2 + 12 * i, // tag offset
25937                 littleEndian
25938             );
25939         }
25940         // Return the offset to the next directory:
25941         return dataView.getUint32(dirEndOffset, littleEndian);
25942     },
25943     
25944     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
25945     {
25946         var tag = dataView.getUint16(offset, littleEndian);
25947         
25948         this.exif[tag] = this.getExifValue(
25949             dataView,
25950             tiffOffset,
25951             offset,
25952             dataView.getUint16(offset + 2, littleEndian), // tag type
25953             dataView.getUint32(offset + 4, littleEndian), // tag length
25954             littleEndian
25955         );
25956     },
25957     
25958     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
25959     {
25960         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
25961             tagSize,
25962             dataOffset,
25963             values,
25964             i,
25965             str,
25966             c;
25967     
25968         if (!tagType) {
25969             Roo.log('Invalid Exif data: Invalid tag type.');
25970             return;
25971         }
25972         
25973         tagSize = tagType.size * length;
25974         // Determine if the value is contained in the dataOffset bytes,
25975         // or if the value at the dataOffset is a pointer to the actual data:
25976         dataOffset = tagSize > 4 ?
25977                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
25978         if (dataOffset + tagSize > dataView.byteLength) {
25979             Roo.log('Invalid Exif data: Invalid data offset.');
25980             return;
25981         }
25982         if (length === 1) {
25983             return tagType.getValue(dataView, dataOffset, littleEndian);
25984         }
25985         values = [];
25986         for (i = 0; i < length; i += 1) {
25987             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
25988         }
25989         
25990         if (tagType.ascii) {
25991             str = '';
25992             // Concatenate the chars:
25993             for (i = 0; i < values.length; i += 1) {
25994                 c = values[i];
25995                 // Ignore the terminating NULL byte(s):
25996                 if (c === '\u0000') {
25997                     break;
25998                 }
25999                 str += c;
26000             }
26001             return str;
26002         }
26003         return values;
26004     }
26005     
26006 });
26007
26008 Roo.apply(Roo.bootstrap.UploadCropbox, {
26009     tags : {
26010         'Orientation': 0x0112
26011     },
26012     
26013     Orientation: {
26014             1: 0, //'top-left',
26015 //            2: 'top-right',
26016             3: 180, //'bottom-right',
26017 //            4: 'bottom-left',
26018 //            5: 'left-top',
26019             6: 90, //'right-top',
26020 //            7: 'right-bottom',
26021             8: 270 //'left-bottom'
26022     },
26023     
26024     exifTagTypes : {
26025         // byte, 8-bit unsigned int:
26026         1: {
26027             getValue: function (dataView, dataOffset) {
26028                 return dataView.getUint8(dataOffset);
26029             },
26030             size: 1
26031         },
26032         // ascii, 8-bit byte:
26033         2: {
26034             getValue: function (dataView, dataOffset) {
26035                 return String.fromCharCode(dataView.getUint8(dataOffset));
26036             },
26037             size: 1,
26038             ascii: true
26039         },
26040         // short, 16 bit int:
26041         3: {
26042             getValue: function (dataView, dataOffset, littleEndian) {
26043                 return dataView.getUint16(dataOffset, littleEndian);
26044             },
26045             size: 2
26046         },
26047         // long, 32 bit int:
26048         4: {
26049             getValue: function (dataView, dataOffset, littleEndian) {
26050                 return dataView.getUint32(dataOffset, littleEndian);
26051             },
26052             size: 4
26053         },
26054         // rational = two long values, first is numerator, second is denominator:
26055         5: {
26056             getValue: function (dataView, dataOffset, littleEndian) {
26057                 return dataView.getUint32(dataOffset, littleEndian) /
26058                     dataView.getUint32(dataOffset + 4, littleEndian);
26059             },
26060             size: 8
26061         },
26062         // slong, 32 bit signed int:
26063         9: {
26064             getValue: function (dataView, dataOffset, littleEndian) {
26065                 return dataView.getInt32(dataOffset, littleEndian);
26066             },
26067             size: 4
26068         },
26069         // srational, two slongs, first is numerator, second is denominator:
26070         10: {
26071             getValue: function (dataView, dataOffset, littleEndian) {
26072                 return dataView.getInt32(dataOffset, littleEndian) /
26073                     dataView.getInt32(dataOffset + 4, littleEndian);
26074             },
26075             size: 8
26076         }
26077     },
26078     
26079     footer : {
26080         STANDARD : [
26081             {
26082                 tag : 'div',
26083                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26084                 action : 'rotate-left',
26085                 cn : [
26086                     {
26087                         tag : 'button',
26088                         cls : 'btn btn-default',
26089                         html : '<i class="fa fa-undo"></i>'
26090                     }
26091                 ]
26092             },
26093             {
26094                 tag : 'div',
26095                 cls : 'btn-group roo-upload-cropbox-picture',
26096                 action : 'picture',
26097                 cn : [
26098                     {
26099                         tag : 'button',
26100                         cls : 'btn btn-default',
26101                         html : '<i class="fa fa-picture-o"></i>'
26102                     }
26103                 ]
26104             },
26105             {
26106                 tag : 'div',
26107                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26108                 action : 'rotate-right',
26109                 cn : [
26110                     {
26111                         tag : 'button',
26112                         cls : 'btn btn-default',
26113                         html : '<i class="fa fa-repeat"></i>'
26114                     }
26115                 ]
26116             }
26117         ],
26118         DOCUMENT : [
26119             {
26120                 tag : 'div',
26121                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26122                 action : 'rotate-left',
26123                 cn : [
26124                     {
26125                         tag : 'button',
26126                         cls : 'btn btn-default',
26127                         html : '<i class="fa fa-undo"></i>'
26128                     }
26129                 ]
26130             },
26131             {
26132                 tag : 'div',
26133                 cls : 'btn-group roo-upload-cropbox-download',
26134                 action : 'download',
26135                 cn : [
26136                     {
26137                         tag : 'button',
26138                         cls : 'btn btn-default',
26139                         html : '<i class="fa fa-download"></i>'
26140                     }
26141                 ]
26142             },
26143             {
26144                 tag : 'div',
26145                 cls : 'btn-group roo-upload-cropbox-crop',
26146                 action : 'crop',
26147                 cn : [
26148                     {
26149                         tag : 'button',
26150                         cls : 'btn btn-default',
26151                         html : '<i class="fa fa-crop"></i>'
26152                     }
26153                 ]
26154             },
26155             {
26156                 tag : 'div',
26157                 cls : 'btn-group roo-upload-cropbox-trash',
26158                 action : 'trash',
26159                 cn : [
26160                     {
26161                         tag : 'button',
26162                         cls : 'btn btn-default',
26163                         html : '<i class="fa fa-trash"></i>'
26164                     }
26165                 ]
26166             },
26167             {
26168                 tag : 'div',
26169                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26170                 action : 'rotate-right',
26171                 cn : [
26172                     {
26173                         tag : 'button',
26174                         cls : 'btn btn-default',
26175                         html : '<i class="fa fa-repeat"></i>'
26176                     }
26177                 ]
26178             }
26179         ],
26180         ROTATOR : [
26181             {
26182                 tag : 'div',
26183                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26184                 action : 'rotate-left',
26185                 cn : [
26186                     {
26187                         tag : 'button',
26188                         cls : 'btn btn-default',
26189                         html : '<i class="fa fa-undo"></i>'
26190                     }
26191                 ]
26192             },
26193             {
26194                 tag : 'div',
26195                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26196                 action : 'rotate-right',
26197                 cn : [
26198                     {
26199                         tag : 'button',
26200                         cls : 'btn btn-default',
26201                         html : '<i class="fa fa-repeat"></i>'
26202                     }
26203                 ]
26204             }
26205         ]
26206     }
26207 });
26208
26209 /*
26210 * Licence: LGPL
26211 */
26212
26213 /**
26214  * @class Roo.bootstrap.DocumentManager
26215  * @extends Roo.bootstrap.Component
26216  * Bootstrap DocumentManager class
26217  * @cfg {String} paramName default 'imageUpload'
26218  * @cfg {String} method default POST
26219  * @cfg {String} url action url
26220  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
26221  * @cfg {Boolean} multiple multiple upload default true
26222  * @cfg {Number} thumbSize default 300
26223  * @cfg {String} fieldLabel
26224  * @cfg {Number} labelWidth default 4
26225  * @cfg {String} labelAlign (left|top) default left
26226  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
26227  * 
26228  * @constructor
26229  * Create a new DocumentManager
26230  * @param {Object} config The config object
26231  */
26232
26233 Roo.bootstrap.DocumentManager = function(config){
26234     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26235     
26236     this.addEvents({
26237         /**
26238          * @event initial
26239          * Fire when initial the DocumentManager
26240          * @param {Roo.bootstrap.DocumentManager} this
26241          */
26242         "initial" : true,
26243         /**
26244          * @event inspect
26245          * inspect selected file
26246          * @param {Roo.bootstrap.DocumentManager} this
26247          * @param {File} file
26248          */
26249         "inspect" : true,
26250         /**
26251          * @event exception
26252          * Fire when xhr load exception
26253          * @param {Roo.bootstrap.DocumentManager} this
26254          * @param {XMLHttpRequest} xhr
26255          */
26256         "exception" : true,
26257         /**
26258          * @event prepare
26259          * prepare the form data
26260          * @param {Roo.bootstrap.DocumentManager} this
26261          * @param {Object} formData
26262          */
26263         "prepare" : true,
26264         /**
26265          * @event remove
26266          * Fire when remove the file
26267          * @param {Roo.bootstrap.DocumentManager} this
26268          * @param {Object} file
26269          */
26270         "remove" : true,
26271         /**
26272          * @event refresh
26273          * Fire after refresh the file
26274          * @param {Roo.bootstrap.DocumentManager} this
26275          */
26276         "refresh" : true,
26277         /**
26278          * @event click
26279          * Fire after click the image
26280          * @param {Roo.bootstrap.DocumentManager} this
26281          * @param {Object} file
26282          */
26283         "click" : true,
26284         /**
26285          * @event edit
26286          * Fire when upload a image and editable set to true
26287          * @param {Roo.bootstrap.DocumentManager} this
26288          * @param {Object} file
26289          */
26290         "edit" : true,
26291         /**
26292          * @event beforeselectfile
26293          * Fire before select file
26294          * @param {Roo.bootstrap.DocumentManager} this
26295          */
26296         "beforeselectfile" : true,
26297         /**
26298          * @event process
26299          * Fire before process file
26300          * @param {Roo.bootstrap.DocumentManager} this
26301          * @param {Object} file
26302          */
26303         "process" : true
26304         
26305     });
26306 };
26307
26308 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
26309     
26310     boxes : 0,
26311     inputName : '',
26312     thumbSize : 300,
26313     multiple : true,
26314     files : [],
26315     method : 'POST',
26316     url : '',
26317     paramName : 'imageUpload',
26318     fieldLabel : '',
26319     labelWidth : 4,
26320     labelAlign : 'left',
26321     editable : true,
26322     delegates : [],
26323     
26324     
26325     xhr : false, 
26326     
26327     getAutoCreate : function()
26328     {   
26329         var managerWidget = {
26330             tag : 'div',
26331             cls : 'roo-document-manager',
26332             cn : [
26333                 {
26334                     tag : 'input',
26335                     cls : 'roo-document-manager-selector',
26336                     type : 'file'
26337                 },
26338                 {
26339                     tag : 'div',
26340                     cls : 'roo-document-manager-uploader',
26341                     cn : [
26342                         {
26343                             tag : 'div',
26344                             cls : 'roo-document-manager-upload-btn',
26345                             html : '<i class="fa fa-plus"></i>'
26346                         }
26347                     ]
26348                     
26349                 }
26350             ]
26351         };
26352         
26353         var content = [
26354             {
26355                 tag : 'div',
26356                 cls : 'column col-md-12',
26357                 cn : managerWidget
26358             }
26359         ];
26360         
26361         if(this.fieldLabel.length){
26362             
26363             content = [
26364                 {
26365                     tag : 'div',
26366                     cls : 'column col-md-12',
26367                     html : this.fieldLabel
26368                 },
26369                 {
26370                     tag : 'div',
26371                     cls : 'column col-md-12',
26372                     cn : managerWidget
26373                 }
26374             ];
26375
26376             if(this.labelAlign == 'left'){
26377                 content = [
26378                     {
26379                         tag : 'div',
26380                         cls : 'column col-md-' + this.labelWidth,
26381                         html : this.fieldLabel
26382                     },
26383                     {
26384                         tag : 'div',
26385                         cls : 'column col-md-' + (12 - this.labelWidth),
26386                         cn : managerWidget
26387                     }
26388                 ];
26389                 
26390             }
26391         }
26392         
26393         var cfg = {
26394             tag : 'div',
26395             cls : 'row clearfix',
26396             cn : content
26397         };
26398         
26399         return cfg;
26400         
26401     },
26402     
26403     initEvents : function()
26404     {
26405         this.managerEl = this.el.select('.roo-document-manager', true).first();
26406         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26407         
26408         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
26409         this.selectorEl.hide();
26410         
26411         if(this.multiple){
26412             this.selectorEl.attr('multiple', 'multiple');
26413         }
26414         
26415         this.selectorEl.on('change', this.onFileSelected, this);
26416         
26417         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
26418         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26419         
26420         this.uploader.on('click', this.onUploaderClick, this);
26421         
26422         this.renderProgressDialog();
26423         
26424         var _this = this;
26425         
26426         window.addEventListener("resize", function() { _this.refresh(); } );
26427         
26428         this.fireEvent('initial', this);
26429     },
26430     
26431     renderProgressDialog : function()
26432     {
26433         var _this = this;
26434         
26435         this.progressDialog = new Roo.bootstrap.Modal({
26436             cls : 'roo-document-manager-progress-dialog',
26437             allow_close : false,
26438             title : '',
26439             buttons : [
26440                 {
26441                     name  :'cancel',
26442                     weight : 'danger',
26443                     html : 'Cancel'
26444                 }
26445             ], 
26446             listeners : { 
26447                 btnclick : function() {
26448                     _this.uploadCancel();
26449                     this.hide();
26450                 }
26451             }
26452         });
26453          
26454         this.progressDialog.render(Roo.get(document.body));
26455          
26456         this.progress = new Roo.bootstrap.Progress({
26457             cls : 'roo-document-manager-progress',
26458             active : true,
26459             striped : true
26460         });
26461         
26462         this.progress.render(this.progressDialog.getChildContainer());
26463         
26464         this.progressBar = new Roo.bootstrap.ProgressBar({
26465             cls : 'roo-document-manager-progress-bar',
26466             aria_valuenow : 0,
26467             aria_valuemin : 0,
26468             aria_valuemax : 12,
26469             panel : 'success'
26470         });
26471         
26472         this.progressBar.render(this.progress.getChildContainer());
26473     },
26474     
26475     onUploaderClick : function(e)
26476     {
26477         e.preventDefault();
26478      
26479         if(this.fireEvent('beforeselectfile', this) != false){
26480             this.selectorEl.dom.click();
26481         }
26482         
26483     },
26484     
26485     onFileSelected : function(e)
26486     {
26487         e.preventDefault();
26488         
26489         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26490             return;
26491         }
26492         
26493         Roo.each(this.selectorEl.dom.files, function(file){
26494             if(this.fireEvent('inspect', this, file) != false){
26495                 this.files.push(file);
26496             }
26497         }, this);
26498         
26499         this.queue();
26500         
26501     },
26502     
26503     queue : function()
26504     {
26505         this.selectorEl.dom.value = '';
26506         
26507         if(!this.files.length){
26508             return;
26509         }
26510         
26511         if(this.boxes > 0 && this.files.length > this.boxes){
26512             this.files = this.files.slice(0, this.boxes);
26513         }
26514         
26515         this.uploader.show();
26516         
26517         if(this.boxes > 0 && this.files.length > this.boxes - 1){
26518             this.uploader.hide();
26519         }
26520         
26521         var _this = this;
26522         
26523         var files = [];
26524         
26525         var docs = [];
26526         
26527         Roo.each(this.files, function(file){
26528             
26529             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26530                 var f = this.renderPreview(file);
26531                 files.push(f);
26532                 return;
26533             }
26534             
26535             if(file.type.indexOf('image') != -1){
26536                 this.delegates.push(
26537                     (function(){
26538                         _this.process(file);
26539                     }).createDelegate(this)
26540                 );
26541         
26542                 return;
26543             }
26544             
26545             docs.push(
26546                 (function(){
26547                     _this.process(file);
26548                 }).createDelegate(this)
26549             );
26550             
26551         }, this);
26552         
26553         this.files = files;
26554         
26555         this.delegates = this.delegates.concat(docs);
26556         
26557         if(!this.delegates.length){
26558             this.refresh();
26559             return;
26560         }
26561         
26562         this.progressBar.aria_valuemax = this.delegates.length;
26563         
26564         this.arrange();
26565         
26566         return;
26567     },
26568     
26569     arrange : function()
26570     {
26571         if(!this.delegates.length){
26572             this.progressDialog.hide();
26573             this.refresh();
26574             return;
26575         }
26576         
26577         var delegate = this.delegates.shift();
26578         
26579         this.progressDialog.show();
26580         
26581         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
26582         
26583         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
26584         
26585         delegate();
26586     },
26587     
26588     refresh : function()
26589     {
26590         this.uploader.show();
26591         
26592         if(this.boxes > 0 && this.files.length > this.boxes - 1){
26593             this.uploader.hide();
26594         }
26595         
26596         Roo.isTouch ? this.closable(false) : this.closable(true);
26597         
26598         this.fireEvent('refresh', this);
26599     },
26600     
26601     onRemove : function(e, el, o)
26602     {
26603         e.preventDefault();
26604         
26605         this.fireEvent('remove', this, o);
26606         
26607     },
26608     
26609     remove : function(o)
26610     {
26611         var files = [];
26612         
26613         Roo.each(this.files, function(file){
26614             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
26615                 files.push(file);
26616                 return;
26617             }
26618
26619             o.target.remove();
26620
26621         }, this);
26622         
26623         this.files = files;
26624         
26625         this.refresh();
26626     },
26627     
26628     clear : function()
26629     {
26630         Roo.each(this.files, function(file){
26631             if(!file.target){
26632                 return;
26633             }
26634             
26635             file.target.remove();
26636
26637         }, this);
26638         
26639         this.files = [];
26640         
26641         this.refresh();
26642     },
26643     
26644     onClick : function(e, el, o)
26645     {
26646         e.preventDefault();
26647         
26648         this.fireEvent('click', this, o);
26649         
26650     },
26651     
26652     closable : function(closable)
26653     {
26654         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
26655             
26656             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26657             
26658             if(closable){
26659                 el.show();
26660                 return;
26661             }
26662             
26663             el.hide();
26664             
26665         }, this);
26666     },
26667     
26668     xhrOnLoad : function(xhr)
26669     {
26670         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26671             el.remove();
26672         }, this);
26673         
26674         if (xhr.readyState !== 4) {
26675             this.arrange();
26676             this.fireEvent('exception', this, xhr);
26677             return;
26678         }
26679
26680         var response = Roo.decode(xhr.responseText);
26681         
26682         if(!response.success){
26683             this.arrange();
26684             this.fireEvent('exception', this, xhr);
26685             return;
26686         }
26687         
26688         var file = this.renderPreview(response.data);
26689         
26690         this.files.push(file);
26691         
26692         this.arrange();
26693         
26694     },
26695     
26696     xhrOnError : function()
26697     {
26698         Roo.log('xhr on error');
26699         
26700         var response = Roo.decode(xhr.responseText);
26701           
26702         Roo.log(response);
26703         
26704         this.arrange();
26705     },
26706     
26707     process : function(file)
26708     {
26709         if(this.fireEvent('process', this, file) !== false){
26710             if(this.editable && file.type.indexOf('image') != -1){
26711                 this.fireEvent('edit', this, file);
26712                 return;
26713             }
26714
26715             this.uploadStart(file, false);
26716
26717             return;
26718         }
26719         
26720     },
26721     
26722     uploadStart : function(file, crop)
26723     {
26724         this.xhr = new XMLHttpRequest();
26725         
26726         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26727             this.arrange();
26728             return;
26729         }
26730         
26731         file.xhr = this.xhr;
26732             
26733         this.managerEl.createChild({
26734             tag : 'div',
26735             cls : 'roo-document-manager-loading',
26736             cn : [
26737                 {
26738                     tag : 'div',
26739                     tooltip : file.name,
26740                     cls : 'roo-document-manager-thumb',
26741                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26742                 }
26743             ]
26744
26745         });
26746
26747         this.xhr.open(this.method, this.url, true);
26748         
26749         var headers = {
26750             "Accept": "application/json",
26751             "Cache-Control": "no-cache",
26752             "X-Requested-With": "XMLHttpRequest"
26753         };
26754         
26755         for (var headerName in headers) {
26756             var headerValue = headers[headerName];
26757             if (headerValue) {
26758                 this.xhr.setRequestHeader(headerName, headerValue);
26759             }
26760         }
26761         
26762         var _this = this;
26763         
26764         this.xhr.onload = function()
26765         {
26766             _this.xhrOnLoad(_this.xhr);
26767         }
26768         
26769         this.xhr.onerror = function()
26770         {
26771             _this.xhrOnError(_this.xhr);
26772         }
26773         
26774         var formData = new FormData();
26775
26776         formData.append('returnHTML', 'NO');
26777         
26778         if(crop){
26779             formData.append('crop', crop);
26780         }
26781         
26782         formData.append(this.paramName, file, file.name);
26783         
26784         if(this.fireEvent('prepare', this, formData) != false){
26785             this.xhr.send(formData);
26786         };
26787     },
26788     
26789     uploadCancel : function()
26790     {
26791         if (this.xhr) {
26792             this.xhr.abort();
26793         }
26794         
26795         
26796         this.delegates = [];
26797         
26798         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26799             el.remove();
26800         }, this);
26801         
26802         this.arrange();
26803     },
26804     
26805     renderPreview : function(file)
26806     {
26807         if(typeof(file.target) != 'undefined' && file.target){
26808             return file;
26809         }
26810         
26811         var previewEl = this.managerEl.createChild({
26812             tag : 'div',
26813             cls : 'roo-document-manager-preview',
26814             cn : [
26815                 {
26816                     tag : 'div',
26817                     tooltip : file.filename,
26818                     cls : 'roo-document-manager-thumb',
26819                     html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
26820                 },
26821                 {
26822                     tag : 'button',
26823                     cls : 'close',
26824                     html : '<i class="fa fa-times-circle"></i>'
26825                 }
26826             ]
26827         });
26828
26829         var close = previewEl.select('button.close', true).first();
26830
26831         close.on('click', this.onRemove, this, file);
26832
26833         file.target = previewEl;
26834
26835         var image = previewEl.select('img', true).first();
26836         
26837         var _this = this;
26838         
26839         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
26840         
26841         image.on('click', this.onClick, this, file);
26842         
26843         return file;
26844         
26845     },
26846     
26847     onPreviewLoad : function(file, image)
26848     {
26849         if(typeof(file.target) == 'undefined' || !file.target){
26850             return;
26851         }
26852         
26853         var width = image.dom.naturalWidth || image.dom.width;
26854         var height = image.dom.naturalHeight || image.dom.height;
26855         
26856         if(width > height){
26857             file.target.addClass('wide');
26858             return;
26859         }
26860         
26861         file.target.addClass('tall');
26862         return;
26863         
26864     },
26865     
26866     uploadFromSource : function(file, crop)
26867     {
26868         this.xhr = new XMLHttpRequest();
26869         
26870         this.managerEl.createChild({
26871             tag : 'div',
26872             cls : 'roo-document-manager-loading',
26873             cn : [
26874                 {
26875                     tag : 'div',
26876                     tooltip : file.name,
26877                     cls : 'roo-document-manager-thumb',
26878                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26879                 }
26880             ]
26881
26882         });
26883
26884         this.xhr.open(this.method, this.url, true);
26885         
26886         var headers = {
26887             "Accept": "application/json",
26888             "Cache-Control": "no-cache",
26889             "X-Requested-With": "XMLHttpRequest"
26890         };
26891         
26892         for (var headerName in headers) {
26893             var headerValue = headers[headerName];
26894             if (headerValue) {
26895                 this.xhr.setRequestHeader(headerName, headerValue);
26896             }
26897         }
26898         
26899         var _this = this;
26900         
26901         this.xhr.onload = function()
26902         {
26903             _this.xhrOnLoad(_this.xhr);
26904         }
26905         
26906         this.xhr.onerror = function()
26907         {
26908             _this.xhrOnError(_this.xhr);
26909         }
26910         
26911         var formData = new FormData();
26912
26913         formData.append('returnHTML', 'NO');
26914         
26915         formData.append('crop', crop);
26916         
26917         if(typeof(file.filename) != 'undefined'){
26918             formData.append('filename', file.filename);
26919         }
26920         
26921         if(typeof(file.mimetype) != 'undefined'){
26922             formData.append('mimetype', file.mimetype);
26923         }
26924         
26925         if(this.fireEvent('prepare', this, formData) != false){
26926             this.xhr.send(formData);
26927         };
26928     }
26929 });
26930
26931 /*
26932 * Licence: LGPL
26933 */
26934
26935 /**
26936  * @class Roo.bootstrap.DocumentViewer
26937  * @extends Roo.bootstrap.Component
26938  * Bootstrap DocumentViewer class
26939  * 
26940  * @constructor
26941  * Create a new DocumentViewer
26942  * @param {Object} config The config object
26943  */
26944
26945 Roo.bootstrap.DocumentViewer = function(config){
26946     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
26947     
26948     this.addEvents({
26949         /**
26950          * @event initial
26951          * Fire after initEvent
26952          * @param {Roo.bootstrap.DocumentViewer} this
26953          */
26954         "initial" : true,
26955         /**
26956          * @event click
26957          * Fire after click
26958          * @param {Roo.bootstrap.DocumentViewer} this
26959          */
26960         "click" : true,
26961         /**
26962          * @event trash
26963          * Fire after trash button
26964          * @param {Roo.bootstrap.DocumentViewer} this
26965          */
26966         "trash" : true
26967         
26968     });
26969 };
26970
26971 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
26972     
26973     getAutoCreate : function()
26974     {
26975         var cfg = {
26976             tag : 'div',
26977             cls : 'roo-document-viewer',
26978             cn : [
26979                 {
26980                     tag : 'div',
26981                     cls : 'roo-document-viewer-body',
26982                     cn : [
26983                         {
26984                             tag : 'div',
26985                             cls : 'roo-document-viewer-thumb',
26986                             cn : [
26987                                 {
26988                                     tag : 'img',
26989                                     cls : 'roo-document-viewer-image'
26990                                 }
26991                             ]
26992                         }
26993                     ]
26994                 },
26995                 {
26996                     tag : 'div',
26997                     cls : 'roo-document-viewer-footer',
26998                     cn : {
26999                         tag : 'div',
27000                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
27001                         cn : [
27002                             {
27003                                 tag : 'div',
27004                                 cls : 'btn-group',
27005                                 cn : [
27006                                     {
27007                                         tag : 'button',
27008                                         cls : 'btn btn-default roo-document-viewer-trash',
27009                                         html : '<i class="fa fa-trash"></i>'
27010                                     }
27011                                 ]
27012                             }
27013                         ]
27014                     }
27015                 }
27016             ]
27017         };
27018         
27019         return cfg;
27020     },
27021     
27022     initEvents : function()
27023     {
27024         
27025         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27026         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27027         
27028         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27029         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27030         
27031         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27032         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27033         
27034         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27035         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27036         
27037         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27038         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27039         
27040         this.bodyEl.on('click', this.onClick, this);
27041         
27042         this.trashBtn.on('click', this.onTrash, this);
27043         
27044     },
27045     
27046     initial : function()
27047     {
27048 //        this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27049         
27050         
27051         this.fireEvent('initial', this);
27052         
27053     },
27054     
27055     onClick : function(e)
27056     {
27057         e.preventDefault();
27058         
27059         this.fireEvent('click', this);
27060     },
27061     
27062     onTrash : function(e)
27063     {
27064         e.preventDefault();
27065         
27066         this.fireEvent('trash', this);
27067     }
27068     
27069 });
27070 /*
27071  * - LGPL
27072  *
27073  * nav progress bar
27074  * 
27075  */
27076
27077 /**
27078  * @class Roo.bootstrap.NavProgressBar
27079  * @extends Roo.bootstrap.Component
27080  * Bootstrap NavProgressBar class
27081  * 
27082  * @constructor
27083  * Create a new nav progress bar
27084  * @param {Object} config The config object
27085  */
27086
27087 Roo.bootstrap.NavProgressBar = function(config){
27088     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
27089
27090     this.bullets = this.bullets || [];
27091    
27092 //    Roo.bootstrap.NavProgressBar.register(this);
27093      this.addEvents({
27094         /**
27095              * @event changed
27096              * Fires when the active item changes
27097              * @param {Roo.bootstrap.NavProgressBar} this
27098              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
27099              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
27100          */
27101         'changed': true
27102      });
27103     
27104 };
27105
27106 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
27107     
27108     bullets : [],
27109     barItems : [],
27110     
27111     getAutoCreate : function()
27112     {
27113         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
27114         
27115         cfg = {
27116             tag : 'div',
27117             cls : 'roo-navigation-bar-group',
27118             cn : [
27119                 {
27120                     tag : 'div',
27121                     cls : 'roo-navigation-top-bar'
27122                 },
27123                 {
27124                     tag : 'div',
27125                     cls : 'roo-navigation-bullets-bar',
27126                     cn : [
27127                         {
27128                             tag : 'ul',
27129                             cls : 'roo-navigation-bar'
27130                         }
27131                     ]
27132                 },
27133                 
27134                 {
27135                     tag : 'div',
27136                     cls : 'roo-navigation-bottom-bar'
27137                 }
27138             ]
27139             
27140         };
27141         
27142         return cfg;
27143         
27144     },
27145     
27146     initEvents: function() 
27147     {
27148         
27149     },
27150     
27151     onRender : function(ct, position) 
27152     {
27153         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27154         
27155         if(this.bullets.length){
27156             Roo.each(this.bullets, function(b){
27157                this.addItem(b);
27158             }, this);
27159         }
27160         
27161         this.format();
27162         
27163     },
27164     
27165     addItem : function(cfg)
27166     {
27167         var item = new Roo.bootstrap.NavProgressItem(cfg);
27168         
27169         item.parentId = this.id;
27170         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
27171         
27172         if(cfg.html){
27173             var top = new Roo.bootstrap.Element({
27174                 tag : 'div',
27175                 cls : 'roo-navigation-bar-text'
27176             });
27177             
27178             var bottom = new Roo.bootstrap.Element({
27179                 tag : 'div',
27180                 cls : 'roo-navigation-bar-text'
27181             });
27182             
27183             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
27184             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
27185             
27186             var topText = new Roo.bootstrap.Element({
27187                 tag : 'span',
27188                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
27189             });
27190             
27191             var bottomText = new Roo.bootstrap.Element({
27192                 tag : 'span',
27193                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
27194             });
27195             
27196             topText.onRender(top.el, null);
27197             bottomText.onRender(bottom.el, null);
27198             
27199             item.topEl = top;
27200             item.bottomEl = bottom;
27201         }
27202         
27203         this.barItems.push(item);
27204         
27205         return item;
27206     },
27207     
27208     getActive : function()
27209     {
27210         var active = false;
27211         
27212         Roo.each(this.barItems, function(v){
27213             
27214             if (!v.isActive()) {
27215                 return;
27216             }
27217             
27218             active = v;
27219             return false;
27220             
27221         });
27222         
27223         return active;
27224     },
27225     
27226     setActiveItem : function(item)
27227     {
27228         var prev = false;
27229         
27230         Roo.each(this.barItems, function(v){
27231             if (v.rid == item.rid) {
27232                 return ;
27233             }
27234             
27235             if (v.isActive()) {
27236                 v.setActive(false);
27237                 prev = v;
27238             }
27239         });
27240
27241         item.setActive(true);
27242         
27243         this.fireEvent('changed', this, item, prev);
27244     },
27245     
27246     getBarItem: function(rid)
27247     {
27248         var ret = false;
27249         
27250         Roo.each(this.barItems, function(e) {
27251             if (e.rid != rid) {
27252                 return;
27253             }
27254             
27255             ret =  e;
27256             return false;
27257         });
27258         
27259         return ret;
27260     },
27261     
27262     indexOfItem : function(item)
27263     {
27264         var index = false;
27265         
27266         Roo.each(this.barItems, function(v, i){
27267             
27268             if (v.rid != item.rid) {
27269                 return;
27270             }
27271             
27272             index = i;
27273             return false
27274         });
27275         
27276         return index;
27277     },
27278     
27279     setActiveNext : function()
27280     {
27281         var i = this.indexOfItem(this.getActive());
27282         
27283         if (i > this.barItems.length) {
27284             return;
27285         }
27286         
27287         this.setActiveItem(this.barItems[i+1]);
27288     },
27289     
27290     setActivePrev : function()
27291     {
27292         var i = this.indexOfItem(this.getActive());
27293         
27294         if (i  < 1) {
27295             return;
27296         }
27297         
27298         this.setActiveItem(this.barItems[i-1]);
27299     },
27300     
27301     format : function()
27302     {
27303         if(!this.barItems.length){
27304             return;
27305         }
27306      
27307         var width = 100 / this.barItems.length;
27308         
27309         Roo.each(this.barItems, function(i){
27310             i.el.setStyle('width', width + '%');
27311             i.topEl.el.setStyle('width', width + '%');
27312             i.bottomEl.el.setStyle('width', width + '%');
27313         }, this);
27314         
27315     }
27316     
27317 });
27318 /*
27319  * - LGPL
27320  *
27321  * Nav Progress Item
27322  * 
27323  */
27324
27325 /**
27326  * @class Roo.bootstrap.NavProgressItem
27327  * @extends Roo.bootstrap.Component
27328  * Bootstrap NavProgressItem class
27329  * @cfg {String} rid the reference id
27330  * @cfg {Boolean} active (true|false) Is item active default false
27331  * @cfg {Boolean} disabled (true|false) Is item active default false
27332  * @cfg {String} html
27333  * @cfg {String} position (top|bottom) text position default bottom
27334  * @cfg {String} icon show icon instead of number
27335  * 
27336  * @constructor
27337  * Create a new NavProgressItem
27338  * @param {Object} config The config object
27339  */
27340 Roo.bootstrap.NavProgressItem = function(config){
27341     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
27342     this.addEvents({
27343         // raw events
27344         /**
27345          * @event click
27346          * The raw click event for the entire grid.
27347          * @param {Roo.bootstrap.NavProgressItem} this
27348          * @param {Roo.EventObject} e
27349          */
27350         "click" : true
27351     });
27352    
27353 };
27354
27355 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
27356     
27357     rid : '',
27358     active : false,
27359     disabled : false,
27360     html : '',
27361     position : 'bottom',
27362     icon : false,
27363     
27364     getAutoCreate : function()
27365     {
27366         var iconCls = 'roo-navigation-bar-item-icon';
27367         
27368         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
27369         
27370         var cfg = {
27371             tag: 'li',
27372             cls: 'roo-navigation-bar-item',
27373             cn : [
27374                 {
27375                     tag : 'i',
27376                     cls : iconCls
27377                 }
27378             ]
27379         };
27380         
27381         if(this.active){
27382             cfg.cls += ' active';
27383         }
27384         if(this.disabled){
27385             cfg.cls += ' disabled';
27386         }
27387         
27388         return cfg;
27389     },
27390     
27391     disable : function()
27392     {
27393         this.setDisabled(true);
27394     },
27395     
27396     enable : function()
27397     {
27398         this.setDisabled(false);
27399     },
27400     
27401     initEvents: function() 
27402     {
27403         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
27404         
27405         this.iconEl.on('click', this.onClick, this);
27406     },
27407     
27408     onClick : function(e)
27409     {
27410         e.preventDefault();
27411         
27412         if(this.disabled){
27413             return;
27414         }
27415         
27416         if(this.fireEvent('click', this, e) === false){
27417             return;
27418         };
27419         
27420         this.parent().setActiveItem(this);
27421     },
27422     
27423     isActive: function () 
27424     {
27425         return this.active;
27426     },
27427     
27428     setActive : function(state)
27429     {
27430         if(this.active == state){
27431             return;
27432         }
27433         
27434         this.active = state;
27435         
27436         if (state) {
27437             this.el.addClass('active');
27438             return;
27439         }
27440         
27441         this.el.removeClass('active');
27442         
27443         return;
27444     },
27445     
27446     setDisabled : function(state)
27447     {
27448         if(this.disabled == state){
27449             return;
27450         }
27451         
27452         this.disabled = state;
27453         
27454         if (state) {
27455             this.el.addClass('disabled');
27456             return;
27457         }
27458         
27459         this.el.removeClass('disabled');
27460     },
27461     
27462     tooltipEl : function()
27463     {
27464         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
27465     }
27466 });
27467  
27468
27469  /*
27470  * - LGPL
27471  *
27472  * FieldLabel
27473  * 
27474  */
27475
27476 /**
27477  * @class Roo.bootstrap.FieldLabel
27478  * @extends Roo.bootstrap.Component
27479  * Bootstrap FieldLabel class
27480  * @cfg {String} html contents of the element
27481  * @cfg {String} tag tag of the element default label
27482  * @cfg {String} cls class of the element
27483  * @cfg {String} target label target 
27484  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
27485  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
27486  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
27487  * @cfg {String} iconTooltip default "This field is required"
27488  * 
27489  * @constructor
27490  * Create a new FieldLabel
27491  * @param {Object} config The config object
27492  */
27493
27494 Roo.bootstrap.FieldLabel = function(config){
27495     Roo.bootstrap.Element.superclass.constructor.call(this, config);
27496     
27497     this.addEvents({
27498             /**
27499              * @event invalid
27500              * Fires after the field has been marked as invalid.
27501              * @param {Roo.form.FieldLabel} this
27502              * @param {String} msg The validation message
27503              */
27504             invalid : true,
27505             /**
27506              * @event valid
27507              * Fires after the field has been validated with no errors.
27508              * @param {Roo.form.FieldLabel} this
27509              */
27510             valid : true
27511         });
27512 };
27513
27514 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
27515     
27516     tag: 'label',
27517     cls: '',
27518     html: '',
27519     target: '',
27520     allowBlank : true,
27521     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
27522     validClass : 'text-success fa fa-lg fa-check',
27523     iconTooltip : 'This field is required',
27524     
27525     getAutoCreate : function(){
27526         
27527         var cfg = {
27528             tag : this.tag,
27529             cls : 'roo-bootstrap-field-label ' + this.cls,
27530             for : this.target,
27531             cn : [
27532                 {
27533                     tag : 'i',
27534                     cls : '',
27535                     tooltip : this.iconTooltip
27536                 },
27537                 {
27538                     tag : 'span',
27539                     html : this.html
27540                 }
27541             ] 
27542         };
27543         
27544         return cfg;
27545     },
27546     
27547     initEvents: function() 
27548     {
27549         Roo.bootstrap.Element.superclass.initEvents.call(this);
27550         
27551         this.iconEl = this.el.select('i', true).first();
27552         
27553         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
27554         
27555         Roo.bootstrap.FieldLabel.register(this);
27556     },
27557     
27558     /**
27559      * Mark this field as valid
27560      */
27561     markValid : function()
27562     {
27563         this.iconEl.show();
27564         
27565         this.iconEl.removeClass(this.invalidClass);
27566         
27567         this.iconEl.addClass(this.validClass);
27568         
27569         this.fireEvent('valid', this);
27570     },
27571     
27572     /**
27573      * Mark this field as invalid
27574      * @param {String} msg The validation message
27575      */
27576     markInvalid : function(msg)
27577     {
27578         this.iconEl.show();
27579         
27580         this.iconEl.removeClass(this.validClass);
27581         
27582         this.iconEl.addClass(this.invalidClass);
27583         
27584         this.fireEvent('invalid', this, msg);
27585     }
27586     
27587    
27588 });
27589
27590 Roo.apply(Roo.bootstrap.FieldLabel, {
27591     
27592     groups: {},
27593     
27594      /**
27595     * register a FieldLabel Group
27596     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
27597     */
27598     register : function(label)
27599     {
27600         if(this.groups.hasOwnProperty(label.target)){
27601             return;
27602         }
27603      
27604         this.groups[label.target] = label;
27605         
27606     },
27607     /**
27608     * fetch a FieldLabel Group based on the target
27609     * @param {string} target
27610     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
27611     */
27612     get: function(target) {
27613         if (typeof(this.groups[target]) == 'undefined') {
27614             return false;
27615         }
27616         
27617         return this.groups[target] ;
27618     }
27619 });
27620
27621  
27622
27623  /*
27624  * - LGPL
27625  *
27626  * page DateSplitField.
27627  * 
27628  */
27629
27630
27631 /**
27632  * @class Roo.bootstrap.DateSplitField
27633  * @extends Roo.bootstrap.Component
27634  * Bootstrap DateSplitField class
27635  * @cfg {string} fieldLabel - the label associated
27636  * @cfg {Number} labelWidth set the width of label (0-12)
27637  * @cfg {String} labelAlign (top|left)
27638  * @cfg {Boolean} dayAllowBlank (true|false) default false
27639  * @cfg {Boolean} monthAllowBlank (true|false) default false
27640  * @cfg {Boolean} yearAllowBlank (true|false) default false
27641  * @cfg {string} dayPlaceholder 
27642  * @cfg {string} monthPlaceholder
27643  * @cfg {string} yearPlaceholder
27644  * @cfg {string} dayFormat default 'd'
27645  * @cfg {string} monthFormat default 'm'
27646  * @cfg {string} yearFormat default 'Y'
27647
27648  *     
27649  * @constructor
27650  * Create a new DateSplitField
27651  * @param {Object} config The config object
27652  */
27653
27654 Roo.bootstrap.DateSplitField = function(config){
27655     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
27656     
27657     this.addEvents({
27658         // raw events
27659          /**
27660          * @event years
27661          * getting the data of years
27662          * @param {Roo.bootstrap.DateSplitField} this
27663          * @param {Object} years
27664          */
27665         "years" : true,
27666         /**
27667          * @event days
27668          * getting the data of days
27669          * @param {Roo.bootstrap.DateSplitField} this
27670          * @param {Object} days
27671          */
27672         "days" : true,
27673         /**
27674          * @event invalid
27675          * Fires after the field has been marked as invalid.
27676          * @param {Roo.form.Field} this
27677          * @param {String} msg The validation message
27678          */
27679         invalid : true,
27680        /**
27681          * @event valid
27682          * Fires after the field has been validated with no errors.
27683          * @param {Roo.form.Field} this
27684          */
27685         valid : true
27686     });
27687 };
27688
27689 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
27690     
27691     fieldLabel : '',
27692     labelAlign : 'top',
27693     labelWidth : 3,
27694     dayAllowBlank : false,
27695     monthAllowBlank : false,
27696     yearAllowBlank : false,
27697     dayPlaceholder : '',
27698     monthPlaceholder : '',
27699     yearPlaceholder : '',
27700     dayFormat : 'd',
27701     monthFormat : 'm',
27702     yearFormat : 'Y',
27703     isFormField : true,
27704     
27705     getAutoCreate : function()
27706     {
27707         var cfg = {
27708             tag : 'div',
27709             cls : 'row roo-date-split-field-group',
27710             cn : [
27711                 {
27712                     tag : 'input',
27713                     type : 'hidden',
27714                     cls : 'form-hidden-field roo-date-split-field-group-value',
27715                     name : this.name
27716                 }
27717             ]
27718         };
27719         
27720         if(this.fieldLabel){
27721             cfg.cn.push({
27722                 tag : 'div',
27723                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
27724                 cn : [
27725                     {
27726                         tag : 'label',
27727                         html : this.fieldLabel
27728                     }
27729                 ]
27730             });
27731         }
27732         
27733         Roo.each(['day', 'month', 'year'], function(t){
27734             cfg.cn.push({
27735                 tag : 'div',
27736                 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
27737             });
27738         }, this);
27739         
27740         return cfg;
27741     },
27742     
27743     inputEl: function ()
27744     {
27745         return this.el.select('.roo-date-split-field-group-value', true).first();
27746     },
27747     
27748     onRender : function(ct, position) 
27749     {
27750         var _this = this;
27751         
27752         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27753         
27754         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
27755         
27756         this.dayField = new Roo.bootstrap.ComboBox({
27757             allowBlank : this.dayAllowBlank,
27758             alwaysQuery : true,
27759             displayField : 'value',
27760             editable : false,
27761             fieldLabel : '',
27762             forceSelection : true,
27763             mode : 'local',
27764             placeholder : this.dayPlaceholder,
27765             selectOnFocus : true,
27766             tpl : '<div class="select2-result"><b>{value}</b></div>',
27767             triggerAction : 'all',
27768             typeAhead : true,
27769             valueField : 'value',
27770             store : new Roo.data.SimpleStore({
27771                 data : (function() {    
27772                     var days = [];
27773                     _this.fireEvent('days', _this, days);
27774                     return days;
27775                 })(),
27776                 fields : [ 'value' ]
27777             }),
27778             listeners : {
27779                 select : function (_self, record, index)
27780                 {
27781                     _this.setValue(_this.getValue());
27782                 }
27783             }
27784         });
27785
27786         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
27787         
27788         this.monthField = new Roo.bootstrap.MonthField({
27789             after : '<i class=\"fa fa-calendar\"></i>',
27790             allowBlank : this.monthAllowBlank,
27791             placeholder : this.monthPlaceholder,
27792             readOnly : true,
27793             listeners : {
27794                 render : function (_self)
27795                 {
27796                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
27797                         e.preventDefault();
27798                         _self.focus();
27799                     });
27800                 },
27801                 select : function (_self, oldvalue, newvalue)
27802                 {
27803                     _this.setValue(_this.getValue());
27804                 }
27805             }
27806         });
27807         
27808         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
27809         
27810         this.yearField = new Roo.bootstrap.ComboBox({
27811             allowBlank : this.yearAllowBlank,
27812             alwaysQuery : true,
27813             displayField : 'value',
27814             editable : false,
27815             fieldLabel : '',
27816             forceSelection : true,
27817             mode : 'local',
27818             placeholder : this.yearPlaceholder,
27819             selectOnFocus : true,
27820             tpl : '<div class="select2-result"><b>{value}</b></div>',
27821             triggerAction : 'all',
27822             typeAhead : true,
27823             valueField : 'value',
27824             store : new Roo.data.SimpleStore({
27825                 data : (function() {
27826                     var years = [];
27827                     _this.fireEvent('years', _this, years);
27828                     return years;
27829                 })(),
27830                 fields : [ 'value' ]
27831             }),
27832             listeners : {
27833                 select : function (_self, record, index)
27834                 {
27835                     _this.setValue(_this.getValue());
27836                 }
27837             }
27838         });
27839
27840         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
27841     },
27842     
27843     setValue : function(v, format)
27844     {
27845         this.inputEl.dom.value = v;
27846         
27847         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
27848         
27849         var d = Date.parseDate(v, f);
27850         
27851         if(!d){
27852             this.validate();
27853             return;
27854         }
27855         
27856         this.setDay(d.format(this.dayFormat));
27857         this.setMonth(d.format(this.monthFormat));
27858         this.setYear(d.format(this.yearFormat));
27859         
27860         this.validate();
27861         
27862         return;
27863     },
27864     
27865     setDay : function(v)
27866     {
27867         this.dayField.setValue(v);
27868         this.inputEl.dom.value = this.getValue();
27869         this.validate();
27870         return;
27871     },
27872     
27873     setMonth : function(v)
27874     {
27875         this.monthField.setValue(v, true);
27876         this.inputEl.dom.value = this.getValue();
27877         this.validate();
27878         return;
27879     },
27880     
27881     setYear : function(v)
27882     {
27883         this.yearField.setValue(v);
27884         this.inputEl.dom.value = this.getValue();
27885         this.validate();
27886         return;
27887     },
27888     
27889     getDay : function()
27890     {
27891         return this.dayField.getValue();
27892     },
27893     
27894     getMonth : function()
27895     {
27896         return this.monthField.getValue();
27897     },
27898     
27899     getYear : function()
27900     {
27901         return this.yearField.getValue();
27902     },
27903     
27904     getValue : function()
27905     {
27906         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
27907         
27908         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
27909         
27910         return date;
27911     },
27912     
27913     reset : function()
27914     {
27915         this.setDay('');
27916         this.setMonth('');
27917         this.setYear('');
27918         this.inputEl.dom.value = '';
27919         this.validate();
27920         return;
27921     },
27922     
27923     validate : function()
27924     {
27925         var d = this.dayField.validate();
27926         var m = this.monthField.validate();
27927         var y = this.yearField.validate();
27928         
27929         var valid = true;
27930         
27931         if(
27932                 (!this.dayAllowBlank && !d) ||
27933                 (!this.monthAllowBlank && !m) ||
27934                 (!this.yearAllowBlank && !y)
27935         ){
27936             valid = false;
27937         }
27938         
27939         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
27940             return valid;
27941         }
27942         
27943         if(valid){
27944             this.markValid();
27945             return valid;
27946         }
27947         
27948         this.markInvalid();
27949         
27950         return valid;
27951     },
27952     
27953     markValid : function()
27954     {
27955         
27956         var label = this.el.select('label', true).first();
27957         var icon = this.el.select('i.fa-star', true).first();
27958
27959         if(label && icon){
27960             icon.remove();
27961         }
27962         
27963         this.fireEvent('valid', this);
27964     },
27965     
27966      /**
27967      * Mark this field as invalid
27968      * @param {String} msg The validation message
27969      */
27970     markInvalid : function(msg)
27971     {
27972         
27973         var label = this.el.select('label', true).first();
27974         var icon = this.el.select('i.fa-star', true).first();
27975
27976         if(label && !icon){
27977             this.el.select('.roo-date-split-field-label', true).createChild({
27978                 tag : 'i',
27979                 cls : 'text-danger fa fa-lg fa-star',
27980                 tooltip : 'This field is required',
27981                 style : 'margin-right:5px;'
27982             }, label, true);
27983         }
27984         
27985         this.fireEvent('invalid', this, msg);
27986     },
27987     
27988     clearInvalid : function()
27989     {
27990         var label = this.el.select('label', true).first();
27991         var icon = this.el.select('i.fa-star', true).first();
27992
27993         if(label && icon){
27994             icon.remove();
27995         }
27996         
27997         this.fireEvent('valid', this);
27998     },
27999     
28000     getName: function()
28001     {
28002         return this.name;
28003     }
28004     
28005 });
28006
28007