roojs-bootstrap.js
[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: '',
1364     href: false,
1365     target: false,
1366     xsUrl: '',
1367     smUrl: '',
1368     mdUrl: '',
1369     lgUrl: '',
1370
1371     getAutoCreate : function()
1372     {   
1373         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1374             return this.createSingleImg();
1375         }
1376         
1377         var cfg = {
1378             tag: 'div',
1379             cls: 'roo-image-responsive-group',
1380             cn: []
1381         };
1382         var _this = this;
1383         
1384         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1385             
1386             if(!_this[size + 'Url']){
1387                 return;
1388             }
1389             
1390             var img = {
1391                 tag: 'img',
1392                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1393                 html: _this.html || cfg.html,
1394                 src: _this[size + 'Url']
1395             };
1396             
1397             img.cls += ' roo-image-responsive-' + size;
1398             
1399             var s = ['xs', 'sm', 'md', 'lg'];
1400             
1401             s.splice(s.indexOf(size), 1);
1402             
1403             Roo.each(s, function(ss){
1404                 img.cls += ' hidden-' + ss;
1405             });
1406             
1407             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1408                 cfg.cls += ' img-' + _this.border;
1409             }
1410             
1411             if(_this.alt){
1412                 cfg.alt = _this.alt;
1413             }
1414             
1415             if(_this.href){
1416                 var a = {
1417                     tag: 'a',
1418                     href: _this.href,
1419                     cn: [
1420                         img
1421                     ]
1422                 };
1423
1424                 if(this.target){
1425                     a.target = _this.target;
1426                 }
1427             }
1428             
1429             cfg.cn.push((_this.href) ? a : img);
1430             
1431         });
1432         
1433         return cfg;
1434     },
1435     
1436     createSingleImg : function()
1437     {
1438         var cfg = {
1439             tag: 'img',
1440             cls: (this.imgResponsive) ? 'img-responsive' : '',
1441             html : null
1442         };
1443         
1444         cfg.html = this.html || cfg.html;
1445         
1446         cfg.src = this.src || cfg.src;
1447         
1448         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1449             cfg.cls += ' img-' + this.border;
1450         }
1451         
1452         if(this.alt){
1453             cfg.alt = this.alt;
1454         }
1455         
1456         if(this.href){
1457             var a = {
1458                 tag: 'a',
1459                 href: this.href,
1460                 cn: [
1461                     cfg
1462                 ]
1463             };
1464             
1465             if(this.target){
1466                 a.target = this.target;
1467             }
1468             
1469         }
1470         
1471         return (this.href) ? a : cfg;
1472     },
1473     
1474     initEvents: function() 
1475     {
1476         if(!this.href){
1477             this.el.on('click', this.onClick, this);
1478         }
1479         
1480     },
1481     
1482     onClick : function(e)
1483     {
1484         Roo.log('img onclick');
1485         this.fireEvent('click', this, e);
1486     }
1487    
1488 });
1489
1490  /*
1491  * - LGPL
1492  *
1493  * image
1494  * 
1495  */
1496
1497
1498 /**
1499  * @class Roo.bootstrap.Link
1500  * @extends Roo.bootstrap.Component
1501  * Bootstrap Link Class
1502  * @cfg {String} alt image alternative text
1503  * @cfg {String} href a tag href
1504  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1505  * @cfg {String} html the content of the link.
1506  * @cfg {String} anchor name for the anchor link
1507
1508  * @cfg {Boolean} preventDefault (true | false) default false
1509
1510  * 
1511  * @constructor
1512  * Create a new Input
1513  * @param {Object} config The config object
1514  */
1515
1516 Roo.bootstrap.Link = function(config){
1517     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1518     
1519     this.addEvents({
1520         // img events
1521         /**
1522          * @event click
1523          * The img click event for the img.
1524          * @param {Roo.EventObject} e
1525          */
1526         "click" : true
1527     });
1528 };
1529
1530 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1531     
1532     href: false,
1533     target: false,
1534     preventDefault: false,
1535     anchor : false,
1536     alt : false,
1537
1538     getAutoCreate : function()
1539     {
1540         
1541         var cfg = {
1542             tag: 'a'
1543         };
1544         // anchor's do not require html/href...
1545         if (this.anchor === false) {
1546             cfg.html = this.html || '';
1547             cfg.href = this.href || '#';
1548         } else {
1549             cfg.name = this.anchor;
1550             if (this.html !== false) {
1551                 cfg.html = this.html;
1552             }
1553             if (this.href !== false) {
1554                 cfg.href = this.href;
1555             }
1556         }
1557         
1558         if(this.alt !== false){
1559             cfg.alt = this.alt;
1560         }
1561         
1562         
1563         if(this.target !== false) {
1564             cfg.target = this.target;
1565         }
1566         
1567         return cfg;
1568     },
1569     
1570     initEvents: function() {
1571         
1572         if(!this.href || this.preventDefault){
1573             this.el.on('click', this.onClick, this);
1574         }
1575     },
1576     
1577     onClick : function(e)
1578     {
1579         if(this.preventDefault){
1580             e.preventDefault();
1581         }
1582         //Roo.log('img onclick');
1583         this.fireEvent('click', this, e);
1584     }
1585    
1586 });
1587
1588  /*
1589  * - LGPL
1590  *
1591  * header
1592  * 
1593  */
1594
1595 /**
1596  * @class Roo.bootstrap.Header
1597  * @extends Roo.bootstrap.Component
1598  * Bootstrap Header class
1599  * @cfg {String} html content of header
1600  * @cfg {Number} level (1|2|3|4|5|6) default 1
1601  * 
1602  * @constructor
1603  * Create a new Header
1604  * @param {Object} config The config object
1605  */
1606
1607
1608 Roo.bootstrap.Header  = function(config){
1609     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1610 };
1611
1612 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1613     
1614     //href : false,
1615     html : false,
1616     level : 1,
1617     
1618     
1619     
1620     getAutoCreate : function(){
1621         
1622         
1623         
1624         var cfg = {
1625             tag: 'h' + (1 *this.level),
1626             html: this.html || ''
1627         } ;
1628         
1629         return cfg;
1630     }
1631    
1632 });
1633
1634  
1635
1636  /*
1637  * Based on:
1638  * Ext JS Library 1.1.1
1639  * Copyright(c) 2006-2007, Ext JS, LLC.
1640  *
1641  * Originally Released Under LGPL - original licence link has changed is not relivant.
1642  *
1643  * Fork - LGPL
1644  * <script type="text/javascript">
1645  */
1646  
1647 /**
1648  * @class Roo.bootstrap.MenuMgr
1649  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1650  * @singleton
1651  */
1652 Roo.bootstrap.MenuMgr = function(){
1653    var menus, active, groups = {}, attached = false, lastShow = new Date();
1654
1655    // private - called when first menu is created
1656    function init(){
1657        menus = {};
1658        active = new Roo.util.MixedCollection();
1659        Roo.get(document).addKeyListener(27, function(){
1660            if(active.length > 0){
1661                hideAll();
1662            }
1663        });
1664    }
1665
1666    // private
1667    function hideAll(){
1668        if(active && active.length > 0){
1669            var c = active.clone();
1670            c.each(function(m){
1671                m.hide();
1672            });
1673        }
1674    }
1675
1676    // private
1677    function onHide(m){
1678        active.remove(m);
1679        if(active.length < 1){
1680            Roo.get(document).un("mouseup", onMouseDown);
1681             
1682            attached = false;
1683        }
1684    }
1685
1686    // private
1687    function onShow(m){
1688        var last = active.last();
1689        lastShow = new Date();
1690        active.add(m);
1691        if(!attached){
1692           Roo.get(document).on("mouseup", onMouseDown);
1693            
1694            attached = true;
1695        }
1696        if(m.parentMenu){
1697           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1698           m.parentMenu.activeChild = m;
1699        }else if(last && last.isVisible()){
1700           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1701        }
1702    }
1703
1704    // private
1705    function onBeforeHide(m){
1706        if(m.activeChild){
1707            m.activeChild.hide();
1708        }
1709        if(m.autoHideTimer){
1710            clearTimeout(m.autoHideTimer);
1711            delete m.autoHideTimer;
1712        }
1713    }
1714
1715    // private
1716    function onBeforeShow(m){
1717        var pm = m.parentMenu;
1718        if(!pm && !m.allowOtherMenus){
1719            hideAll();
1720        }else if(pm && pm.activeChild && active != m){
1721            pm.activeChild.hide();
1722        }
1723    }
1724
1725    // private this should really trigger on mouseup..
1726    function onMouseDown(e){
1727         Roo.log("on Mouse Up");
1728         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu") && !e.getTarget('.user-menu')){
1729             Roo.log("hideAll");
1730             hideAll();
1731             e.stopEvent();
1732         }
1733         
1734         
1735    }
1736
1737    // private
1738    function onBeforeCheck(mi, state){
1739        if(state){
1740            var g = groups[mi.group];
1741            for(var i = 0, l = g.length; i < l; i++){
1742                if(g[i] != mi){
1743                    g[i].setChecked(false);
1744                }
1745            }
1746        }
1747    }
1748
1749    return {
1750
1751        /**
1752         * Hides all menus that are currently visible
1753         */
1754        hideAll : function(){
1755             hideAll();  
1756        },
1757
1758        // private
1759        register : function(menu){
1760            if(!menus){
1761                init();
1762            }
1763            menus[menu.id] = menu;
1764            menu.on("beforehide", onBeforeHide);
1765            menu.on("hide", onHide);
1766            menu.on("beforeshow", onBeforeShow);
1767            menu.on("show", onShow);
1768            var g = menu.group;
1769            if(g && menu.events["checkchange"]){
1770                if(!groups[g]){
1771                    groups[g] = [];
1772                }
1773                groups[g].push(menu);
1774                menu.on("checkchange", onCheck);
1775            }
1776        },
1777
1778         /**
1779          * Returns a {@link Roo.menu.Menu} object
1780          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1781          * be used to generate and return a new Menu instance.
1782          */
1783        get : function(menu){
1784            if(typeof menu == "string"){ // menu id
1785                return menus[menu];
1786            }else if(menu.events){  // menu instance
1787                return menu;
1788            }
1789            /*else if(typeof menu.length == 'number'){ // array of menu items?
1790                return new Roo.bootstrap.Menu({items:menu});
1791            }else{ // otherwise, must be a config
1792                return new Roo.bootstrap.Menu(menu);
1793            }
1794            */
1795            return false;
1796        },
1797
1798        // private
1799        unregister : function(menu){
1800            delete menus[menu.id];
1801            menu.un("beforehide", onBeforeHide);
1802            menu.un("hide", onHide);
1803            menu.un("beforeshow", onBeforeShow);
1804            menu.un("show", onShow);
1805            var g = menu.group;
1806            if(g && menu.events["checkchange"]){
1807                groups[g].remove(menu);
1808                menu.un("checkchange", onCheck);
1809            }
1810        },
1811
1812        // private
1813        registerCheckable : function(menuItem){
1814            var g = menuItem.group;
1815            if(g){
1816                if(!groups[g]){
1817                    groups[g] = [];
1818                }
1819                groups[g].push(menuItem);
1820                menuItem.on("beforecheckchange", onBeforeCheck);
1821            }
1822        },
1823
1824        // private
1825        unregisterCheckable : function(menuItem){
1826            var g = menuItem.group;
1827            if(g){
1828                groups[g].remove(menuItem);
1829                menuItem.un("beforecheckchange", onBeforeCheck);
1830            }
1831        }
1832    };
1833 }();/*
1834  * - LGPL
1835  *
1836  * menu
1837  * 
1838  */
1839
1840 /**
1841  * @class Roo.bootstrap.Menu
1842  * @extends Roo.bootstrap.Component
1843  * Bootstrap Menu class - container for MenuItems
1844  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1845  * 
1846  * @constructor
1847  * Create a new Menu
1848  * @param {Object} config The config object
1849  */
1850
1851
1852 Roo.bootstrap.Menu = function(config){
1853     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1854     if (this.registerMenu) {
1855         Roo.bootstrap.MenuMgr.register(this);
1856     }
1857     this.addEvents({
1858         /**
1859          * @event beforeshow
1860          * Fires before this menu is displayed
1861          * @param {Roo.menu.Menu} this
1862          */
1863         beforeshow : true,
1864         /**
1865          * @event beforehide
1866          * Fires before this menu is hidden
1867          * @param {Roo.menu.Menu} this
1868          */
1869         beforehide : true,
1870         /**
1871          * @event show
1872          * Fires after this menu is displayed
1873          * @param {Roo.menu.Menu} this
1874          */
1875         show : true,
1876         /**
1877          * @event hide
1878          * Fires after this menu is hidden
1879          * @param {Roo.menu.Menu} this
1880          */
1881         hide : true,
1882         /**
1883          * @event click
1884          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1885          * @param {Roo.menu.Menu} this
1886          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1887          * @param {Roo.EventObject} e
1888          */
1889         click : true,
1890         /**
1891          * @event mouseover
1892          * Fires when the mouse is hovering over this menu
1893          * @param {Roo.menu.Menu} this
1894          * @param {Roo.EventObject} e
1895          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1896          */
1897         mouseover : true,
1898         /**
1899          * @event mouseout
1900          * Fires when the mouse exits this menu
1901          * @param {Roo.menu.Menu} this
1902          * @param {Roo.EventObject} e
1903          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1904          */
1905         mouseout : true,
1906         /**
1907          * @event itemclick
1908          * Fires when a menu item contained in this menu is clicked
1909          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1910          * @param {Roo.EventObject} e
1911          */
1912         itemclick: true
1913     });
1914     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1915 };
1916
1917 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1918     
1919    /// html : false,
1920     //align : '',
1921     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1922     type: false,
1923     /**
1924      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1925      */
1926     registerMenu : true,
1927     
1928     menuItems :false, // stores the menu items..
1929     
1930     hidden:true,
1931     
1932     parentMenu : false,
1933     
1934     getChildContainer : function() {
1935         return this.el;  
1936     },
1937     
1938     getAutoCreate : function(){
1939          
1940         //if (['right'].indexOf(this.align)!==-1) {
1941         //    cfg.cn[1].cls += ' pull-right'
1942         //}
1943         
1944         
1945         var cfg = {
1946             tag : 'ul',
1947             cls : 'dropdown-menu' ,
1948             style : 'z-index:1000'
1949             
1950         };
1951         
1952         if (this.type === 'submenu') {
1953             cfg.cls = 'submenu active';
1954         }
1955         if (this.type === 'treeview') {
1956             cfg.cls = 'treeview-menu';
1957         }
1958         
1959         return cfg;
1960     },
1961     initEvents : function() {
1962         
1963        // Roo.log("ADD event");
1964        // Roo.log(this.triggerEl.dom);
1965         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
1966         
1967         this.triggerEl.addClass('dropdown-toggle');
1968         
1969         
1970         
1971         
1972         if (Roo.isTouch) {
1973             this.el.on('touchstart'  , this.onTouch, this);
1974         }
1975         this.el.on('click' , this.onClick, this);
1976
1977         this.el.on("mouseover", this.onMouseOver, this);
1978         this.el.on("mouseout", this.onMouseOut, this);
1979         
1980         
1981     },
1982     findTargetItem : function(e){
1983         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1984         if(!t){
1985             return false;
1986         }
1987         //Roo.log(t);         Roo.log(t.id);
1988         if(t && t.id){
1989             //Roo.log(this.menuitems);
1990             return this.menuitems.get(t.id);
1991             
1992             //return this.items.get(t.menuItemId);
1993         }
1994         
1995         return false;
1996     },
1997     
1998     onTouch : function(e) {
1999         e.stopEvent();
2000         this.onClick(e);
2001     },
2002     
2003     onClick : function(e){
2004         Roo.log("menu.onClick");
2005         var t = this.findTargetItem(e);
2006         if(!t || t.isContainer){
2007             return;
2008         }
2009         Roo.log(e);
2010         /*
2011         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2012             if(t == this.activeItem && t.shouldDeactivate(e)){
2013                 this.activeItem.deactivate();
2014                 delete this.activeItem;
2015                 return;
2016             }
2017             if(t.canActivate){
2018                 this.setActiveItem(t, true);
2019             }
2020             return;
2021             
2022             
2023         }
2024         */
2025        
2026         Roo.log('pass click event');
2027         
2028         t.onClick(e);
2029         
2030         this.fireEvent("click", this, t, e);
2031         
2032         this.hide();
2033     },
2034      onMouseOver : function(e){
2035         var t  = this.findTargetItem(e);
2036         //Roo.log(t);
2037         //if(t){
2038         //    if(t.canActivate && !t.disabled){
2039         //        this.setActiveItem(t, true);
2040         //    }
2041         //}
2042         
2043         this.fireEvent("mouseover", this, e, t);
2044     },
2045     isVisible : function(){
2046         return !this.hidden;
2047     },
2048      onMouseOut : function(e){
2049         var t  = this.findTargetItem(e);
2050         
2051         //if(t ){
2052         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2053         //        this.activeItem.deactivate();
2054         //        delete this.activeItem;
2055         //    }
2056         //}
2057         this.fireEvent("mouseout", this, e, t);
2058     },
2059     
2060     
2061     /**
2062      * Displays this menu relative to another element
2063      * @param {String/HTMLElement/Roo.Element} element The element to align to
2064      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2065      * the element (defaults to this.defaultAlign)
2066      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2067      */
2068     show : function(el, pos, parentMenu){
2069         this.parentMenu = parentMenu;
2070         if(!this.el){
2071             this.render();
2072         }
2073         this.fireEvent("beforeshow", this);
2074         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2075     },
2076      /**
2077      * Displays this menu at a specific xy position
2078      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2079      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2080      */
2081     showAt : function(xy, parentMenu, /* private: */_e){
2082         this.parentMenu = parentMenu;
2083         if(!this.el){
2084             this.render();
2085         }
2086         if(_e !== false){
2087             this.fireEvent("beforeshow", this);
2088             //xy = this.el.adjustForConstraints(xy);
2089         }
2090         
2091         //this.el.show();
2092         this.hideMenuItems();
2093         this.hidden = false;
2094         this.triggerEl.addClass('open');
2095         
2096         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2097             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2098         }
2099         
2100         this.el.setXY(xy);
2101         this.focus();
2102         this.fireEvent("show", this);
2103     },
2104     
2105     focus : function(){
2106         return;
2107         if(!this.hidden){
2108             this.doFocus.defer(50, this);
2109         }
2110     },
2111
2112     doFocus : function(){
2113         if(!this.hidden){
2114             this.focusEl.focus();
2115         }
2116     },
2117
2118     /**
2119      * Hides this menu and optionally all parent menus
2120      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2121      */
2122     hide : function(deep){
2123         
2124         this.hideMenuItems();
2125         if(this.el && this.isVisible()){
2126             this.fireEvent("beforehide", this);
2127             if(this.activeItem){
2128                 this.activeItem.deactivate();
2129                 this.activeItem = null;
2130             }
2131             this.triggerEl.removeClass('open');;
2132             this.hidden = true;
2133             this.fireEvent("hide", this);
2134         }
2135         if(deep === true && this.parentMenu){
2136             this.parentMenu.hide(true);
2137         }
2138     },
2139     
2140     onTriggerPress  : function(e)
2141     {
2142         
2143         Roo.log('trigger press');
2144         //Roo.log(e.getTarget());
2145        // Roo.log(this.triggerEl.dom);
2146         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
2147             return;
2148         }
2149         
2150         if (this.isVisible()) {
2151             Roo.log('hide');
2152             this.hide();
2153         } else {
2154             Roo.log('show');
2155             this.show(this.triggerEl, false, false);
2156         }
2157         
2158         e.stopEvent();
2159     },
2160     
2161          
2162        
2163     
2164     hideMenuItems : function()
2165     {
2166         //$(backdrop).remove()
2167         Roo.select('.open',true).each(function(aa) {
2168             
2169             aa.removeClass('open');
2170           //var parent = getParent($(this))
2171           //var relatedTarget = { relatedTarget: this }
2172           
2173            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2174           //if (e.isDefaultPrevented()) return
2175            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2176         })
2177     },
2178     addxtypeChild : function (tree, cntr) {
2179         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2180           
2181         this.menuitems.add(comp);
2182         return comp;
2183
2184     },
2185     getEl : function()
2186     {
2187         Roo.log(this.el);
2188         return this.el;
2189     }
2190 });
2191
2192  
2193  /*
2194  * - LGPL
2195  *
2196  * menu item
2197  * 
2198  */
2199
2200
2201 /**
2202  * @class Roo.bootstrap.MenuItem
2203  * @extends Roo.bootstrap.Component
2204  * Bootstrap MenuItem class
2205  * @cfg {String} html the menu label
2206  * @cfg {String} href the link
2207  * @cfg {Boolean} preventDefault (true | false) default true
2208  * @cfg {Boolean} isContainer (true | false) default false
2209  * 
2210  * 
2211  * @constructor
2212  * Create a new MenuItem
2213  * @param {Object} config The config object
2214  */
2215
2216
2217 Roo.bootstrap.MenuItem = function(config){
2218     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2219     this.addEvents({
2220         // raw events
2221         /**
2222          * @event click
2223          * The raw click event for the entire grid.
2224          * @param {Roo.bootstrap.MenuItem} this
2225          * @param {Roo.EventObject} e
2226          */
2227         "click" : true
2228     });
2229 };
2230
2231 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2232     
2233     href : false,
2234     html : false,
2235     preventDefault: true,
2236     isContainer : false,
2237     
2238     getAutoCreate : function(){
2239         
2240         if(this.isContainer){
2241             return {
2242                 tag: 'li',
2243                 cls: 'dropdown-menu-item'
2244             };
2245         }
2246         
2247         var cfg= {
2248             tag: 'li',
2249             cls: 'dropdown-menu-item',
2250             cn: [
2251                     {
2252                         tag : 'a',
2253                         href : '#',
2254                         html : 'Link'
2255                     }
2256                 ]
2257         };
2258         if (this.parent().type == 'treeview') {
2259             cfg.cls = 'treeview-menu';
2260         }
2261         
2262         cfg.cn[0].href = this.href || cfg.cn[0].href ;
2263         cfg.cn[0].html = this.html || cfg.cn[0].html ;
2264         return cfg;
2265     },
2266     
2267     initEvents: function() {
2268         
2269         //this.el.select('a').on('click', this.onClick, this);
2270         
2271     },
2272     onClick : function(e)
2273     {
2274         Roo.log('item on click ');
2275         //if(this.preventDefault){
2276         //    e.preventDefault();
2277         //}
2278         //this.parent().hideMenuItems();
2279         
2280         this.fireEvent('click', this, e);
2281     },
2282     getEl : function()
2283     {
2284         return this.el;
2285     }
2286 });
2287
2288  
2289
2290  /*
2291  * - LGPL
2292  *
2293  * menu separator
2294  * 
2295  */
2296
2297
2298 /**
2299  * @class Roo.bootstrap.MenuSeparator
2300  * @extends Roo.bootstrap.Component
2301  * Bootstrap MenuSeparator class
2302  * 
2303  * @constructor
2304  * Create a new MenuItem
2305  * @param {Object} config The config object
2306  */
2307
2308
2309 Roo.bootstrap.MenuSeparator = function(config){
2310     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2311 };
2312
2313 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2314     
2315     getAutoCreate : function(){
2316         var cfg = {
2317             cls: 'divider',
2318             tag : 'li'
2319         };
2320         
2321         return cfg;
2322     }
2323    
2324 });
2325
2326  
2327
2328  
2329 /*
2330 * Licence: LGPL
2331 */
2332
2333 /**
2334  * @class Roo.bootstrap.Modal
2335  * @extends Roo.bootstrap.Component
2336  * Bootstrap Modal class
2337  * @cfg {String} title Title of dialog
2338  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2339  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn 
2340  * @cfg {Boolean} specificTitle default false
2341  * @cfg {Array} buttons Array of buttons or standard button set..
2342  * @cfg {String} buttonPosition (left|right|center) default right
2343  * @cfg {Boolean} animate default true
2344  * @cfg {Boolean} allow_close default true
2345  * 
2346  * @constructor
2347  * Create a new Modal Dialog
2348  * @param {Object} config The config object
2349  */
2350
2351 Roo.bootstrap.Modal = function(config){
2352     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2353     this.addEvents({
2354         // raw events
2355         /**
2356          * @event btnclick
2357          * The raw btnclick event for the button
2358          * @param {Roo.EventObject} e
2359          */
2360         "btnclick" : true
2361     });
2362     this.buttons = this.buttons || [];
2363      
2364     if (this.tmpl) {
2365         this.tmpl = Roo.factory(this.tmpl);
2366     }
2367     
2368 };
2369
2370 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2371     
2372     title : 'test dialog',
2373    
2374     buttons : false,
2375     
2376     // set on load...
2377      
2378     html: false,
2379     
2380     tmp: false,
2381     
2382     specificTitle: false,
2383     
2384     buttonPosition: 'right',
2385     
2386     allow_close : true,
2387     
2388     animate : true,
2389     
2390     
2391      // private
2392     bodyEl:  false,
2393     footerEl:  false,
2394     titleEl:  false,
2395     closeEl:  false,
2396     
2397     
2398     onRender : function(ct, position)
2399     {
2400         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2401      
2402         if(!this.el){
2403             var cfg = Roo.apply({},  this.getAutoCreate());
2404             cfg.id = Roo.id();
2405             //if(!cfg.name){
2406             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2407             //}
2408             //if (!cfg.name.length) {
2409             //    delete cfg.name;
2410            // }
2411             if (this.cls) {
2412                 cfg.cls += ' ' + this.cls;
2413             }
2414             if (this.style) {
2415                 cfg.style = this.style;
2416             }
2417             this.el = Roo.get(document.body).createChild(cfg, position);
2418         }
2419         //var type = this.el.dom.type;
2420         
2421         
2422         
2423         
2424         if(this.tabIndex !== undefined){
2425             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2426         }
2427         
2428         
2429         this.bodyEl = this.el.select('.modal-body',true).first();
2430         this.closeEl = this.el.select('.modal-header .close', true).first();
2431         this.footerEl = this.el.select('.modal-footer',true).first();
2432         this.titleEl = this.el.select('.modal-title',true).first();
2433         
2434         
2435          
2436         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2437         this.maskEl.enableDisplayMode("block");
2438         this.maskEl.hide();
2439         //this.el.addClass("x-dlg-modal");
2440     
2441         if (this.buttons.length) {
2442             Roo.each(this.buttons, function(bb) {
2443                 var b = Roo.apply({}, bb);
2444                 b.xns = b.xns || Roo.bootstrap;
2445                 b.xtype = b.xtype || 'Button';
2446                 if (typeof(b.listeners) == 'undefined') {
2447                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2448                 }
2449                 
2450                 var btn = Roo.factory(b);
2451                 
2452                 btn.onRender(this.el.select('.modal-footer div').first());
2453                 
2454             },this);
2455         }
2456         // render the children.
2457         var nitems = [];
2458         
2459         if(typeof(this.items) != 'undefined'){
2460             var items = this.items;
2461             delete this.items;
2462
2463             for(var i =0;i < items.length;i++) {
2464                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2465             }
2466         }
2467         
2468         this.items = nitems;
2469         
2470         // where are these used - they used to be body/close/footer
2471         
2472        
2473         this.initEvents();
2474         //this.el.addClass([this.fieldClass, this.cls]);
2475         
2476     },
2477     
2478     getAutoCreate : function(){
2479         
2480         
2481         var bdy = {
2482                 cls : 'modal-body',
2483                 html : this.html || ''
2484         };
2485         
2486         var title = {
2487             tag: 'h4',
2488             cls : 'modal-title',
2489             html : this.title
2490         };
2491         
2492         if(this.specificTitle){
2493             title = this.title;
2494             
2495         };
2496         
2497         var header = [];
2498         if (this.allow_close) {
2499             header.push({
2500                 tag: 'button',
2501                 cls : 'close',
2502                 html : '&times'
2503             });
2504         }
2505         header.push(title);
2506         
2507         var modal = {
2508             cls: "modal",
2509             style : 'display: none',
2510             cn : [
2511                 {
2512                     cls: "modal-dialog",
2513                     cn : [
2514                         {
2515                             cls : "modal-content",
2516                             cn : [
2517                                 {
2518                                     cls : 'modal-header',
2519                                     cn : header
2520                                 },
2521                                 bdy,
2522                                 {
2523                                     cls : 'modal-footer',
2524                                     cn : [
2525                                         {
2526                                             tag: 'div',
2527                                             cls: 'btn-' + this.buttonPosition
2528                                         }
2529                                     ]
2530                                     
2531                                 }
2532                                 
2533                                 
2534                             ]
2535                             
2536                         }
2537                     ]
2538                         
2539                 }
2540             ]
2541         };
2542         
2543         if(this.animate){
2544             modal.cls += ' fade';
2545         }
2546         
2547         return modal;
2548           
2549     },
2550     getChildContainer : function() {
2551          
2552          return this.bodyEl;
2553         
2554     },
2555     getButtonContainer : function() {
2556          return this.el.select('.modal-footer div',true).first();
2557         
2558     },
2559     initEvents : function()
2560     {
2561         if (this.allow_close) {
2562             this.closeEl.on('click', this.hide, this);
2563         }
2564         
2565         var _this = this;
2566         
2567         window.addEventListener("resize", function() { _this.resize(); } );
2568
2569     },
2570     
2571     resize : function()
2572     {
2573         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2574     },
2575     
2576     show : function() {
2577         
2578         if (!this.rendered) {
2579             this.render();
2580         }
2581         
2582         this.el.setStyle('display', 'block');
2583         
2584         if(this.animate){
2585             var _this = this;
2586             (function(){ _this.el.addClass('in'); }).defer(50);
2587         }else{
2588             this.el.addClass('in');
2589         }
2590         
2591         // not sure how we can show data in here.. 
2592         //if (this.tmpl) {
2593         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2594         //}
2595         
2596         Roo.get(document.body).addClass("x-body-masked");
2597         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2598         this.maskEl.show();
2599         this.el.setStyle('zIndex', '10001');
2600        
2601         this.fireEvent('show', this);
2602         
2603         
2604     },
2605     hide : function()
2606     {
2607         this.maskEl.hide();
2608         Roo.get(document.body).removeClass("x-body-masked");
2609         this.el.removeClass('in');
2610         
2611         if(this.animate){
2612             var _this = this;
2613             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2614         }else{
2615             this.el.setStyle('display', 'none');
2616         }
2617         
2618         this.fireEvent('hide', this);
2619     },
2620     
2621     addButton : function(str, cb)
2622     {
2623          
2624         
2625         var b = Roo.apply({}, { html : str } );
2626         b.xns = b.xns || Roo.bootstrap;
2627         b.xtype = b.xtype || 'Button';
2628         if (typeof(b.listeners) == 'undefined') {
2629             b.listeners = { click : cb.createDelegate(this)  };
2630         }
2631         
2632         var btn = Roo.factory(b);
2633            
2634         btn.onRender(this.el.select('.modal-footer div').first());
2635         
2636         return btn;   
2637        
2638     },
2639     
2640     setDefaultButton : function(btn)
2641     {
2642         //this.el.select('.modal-footer').()
2643     },
2644     resizeTo: function(w,h)
2645     {
2646         // skip..
2647     },
2648     setContentSize  : function(w, h)
2649     {
2650         
2651     },
2652     onButtonClick: function(btn,e)
2653     {
2654         //Roo.log([a,b,c]);
2655         this.fireEvent('btnclick', btn.name, e);
2656     },
2657      /**
2658      * Set the title of the Dialog
2659      * @param {String} str new Title
2660      */
2661     setTitle: function(str) {
2662         this.titleEl.dom.innerHTML = str;    
2663     },
2664     /**
2665      * Set the body of the Dialog
2666      * @param {String} str new Title
2667      */
2668     setBody: function(str) {
2669         this.bodyEl.dom.innerHTML = str;    
2670     },
2671     /**
2672      * Set the body of the Dialog using the template
2673      * @param {Obj} data - apply this data to the template and replace the body contents.
2674      */
2675     applyBody: function(obj)
2676     {
2677         if (!this.tmpl) {
2678             Roo.log("Error - using apply Body without a template");
2679             //code
2680         }
2681         this.tmpl.overwrite(this.bodyEl, obj);
2682     }
2683     
2684 });
2685
2686
2687 Roo.apply(Roo.bootstrap.Modal,  {
2688     /**
2689          * Button config that displays a single OK button
2690          * @type Object
2691          */
2692         OK :  [{
2693             name : 'ok',
2694             weight : 'primary',
2695             html : 'OK'
2696         }], 
2697         /**
2698          * Button config that displays Yes and No buttons
2699          * @type Object
2700          */
2701         YESNO : [
2702             {
2703                 name  : 'no',
2704                 html : 'No'
2705             },
2706             {
2707                 name  :'yes',
2708                 weight : 'primary',
2709                 html : 'Yes'
2710             }
2711         ],
2712         
2713         /**
2714          * Button config that displays OK and Cancel buttons
2715          * @type Object
2716          */
2717         OKCANCEL : [
2718             {
2719                name : 'cancel',
2720                 html : 'Cancel'
2721             },
2722             {
2723                 name : 'ok',
2724                 weight : 'primary',
2725                 html : 'OK'
2726             }
2727         ],
2728         /**
2729          * Button config that displays Yes, No and Cancel buttons
2730          * @type Object
2731          */
2732         YESNOCANCEL : [
2733             {
2734                 name : 'yes',
2735                 weight : 'primary',
2736                 html : 'Yes'
2737             },
2738             {
2739                 name : 'no',
2740                 html : 'No'
2741             },
2742             {
2743                 name : 'cancel',
2744                 html : 'Cancel'
2745             }
2746         ]
2747 });
2748  
2749  /*
2750  * - LGPL
2751  *
2752  * messagebox - can be used as a replace
2753  * 
2754  */
2755 /**
2756  * @class Roo.MessageBox
2757  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2758  * Example usage:
2759  *<pre><code>
2760 // Basic alert:
2761 Roo.Msg.alert('Status', 'Changes saved successfully.');
2762
2763 // Prompt for user data:
2764 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2765     if (btn == 'ok'){
2766         // process text value...
2767     }
2768 });
2769
2770 // Show a dialog using config options:
2771 Roo.Msg.show({
2772    title:'Save Changes?',
2773    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2774    buttons: Roo.Msg.YESNOCANCEL,
2775    fn: processResult,
2776    animEl: 'elId'
2777 });
2778 </code></pre>
2779  * @singleton
2780  */
2781 Roo.bootstrap.MessageBox = function(){
2782     var dlg, opt, mask, waitTimer;
2783     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2784     var buttons, activeTextEl, bwidth;
2785
2786     
2787     // private
2788     var handleButton = function(button){
2789         dlg.hide();
2790         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2791     };
2792
2793     // private
2794     var handleHide = function(){
2795         if(opt && opt.cls){
2796             dlg.el.removeClass(opt.cls);
2797         }
2798         //if(waitTimer){
2799         //    Roo.TaskMgr.stop(waitTimer);
2800         //    waitTimer = null;
2801         //}
2802     };
2803
2804     // private
2805     var updateButtons = function(b){
2806         var width = 0;
2807         if(!b){
2808             buttons["ok"].hide();
2809             buttons["cancel"].hide();
2810             buttons["yes"].hide();
2811             buttons["no"].hide();
2812             //dlg.footer.dom.style.display = 'none';
2813             return width;
2814         }
2815         dlg.footerEl.dom.style.display = '';
2816         for(var k in buttons){
2817             if(typeof buttons[k] != "function"){
2818                 if(b[k]){
2819                     buttons[k].show();
2820                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2821                     width += buttons[k].el.getWidth()+15;
2822                 }else{
2823                     buttons[k].hide();
2824                 }
2825             }
2826         }
2827         return width;
2828     };
2829
2830     // private
2831     var handleEsc = function(d, k, e){
2832         if(opt && opt.closable !== false){
2833             dlg.hide();
2834         }
2835         if(e){
2836             e.stopEvent();
2837         }
2838     };
2839
2840     return {
2841         /**
2842          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2843          * @return {Roo.BasicDialog} The BasicDialog element
2844          */
2845         getDialog : function(){
2846            if(!dlg){
2847                 dlg = new Roo.bootstrap.Modal( {
2848                     //draggable: true,
2849                     //resizable:false,
2850                     //constraintoviewport:false,
2851                     //fixedcenter:true,
2852                     //collapsible : false,
2853                     //shim:true,
2854                     //modal: true,
2855                   //  width:400,
2856                   //  height:100,
2857                     //buttonAlign:"center",
2858                     closeClick : function(){
2859                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2860                             handleButton("no");
2861                         }else{
2862                             handleButton("cancel");
2863                         }
2864                     }
2865                 });
2866                 dlg.render();
2867                 dlg.on("hide", handleHide);
2868                 mask = dlg.mask;
2869                 //dlg.addKeyListener(27, handleEsc);
2870                 buttons = {};
2871                 this.buttons = buttons;
2872                 var bt = this.buttonText;
2873                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2874                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2875                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2876                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2877                 //Roo.log(buttons);
2878                 bodyEl = dlg.bodyEl.createChild({
2879
2880                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2881                         '<textarea class="roo-mb-textarea"></textarea>' +
2882                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2883                 });
2884                 msgEl = bodyEl.dom.firstChild;
2885                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2886                 textboxEl.enableDisplayMode();
2887                 textboxEl.addKeyListener([10,13], function(){
2888                     if(dlg.isVisible() && opt && opt.buttons){
2889                         if(opt.buttons.ok){
2890                             handleButton("ok");
2891                         }else if(opt.buttons.yes){
2892                             handleButton("yes");
2893                         }
2894                     }
2895                 });
2896                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2897                 textareaEl.enableDisplayMode();
2898                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2899                 progressEl.enableDisplayMode();
2900                 var pf = progressEl.dom.firstChild;
2901                 if (pf) {
2902                     pp = Roo.get(pf.firstChild);
2903                     pp.setHeight(pf.offsetHeight);
2904                 }
2905                 
2906             }
2907             return dlg;
2908         },
2909
2910         /**
2911          * Updates the message box body text
2912          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2913          * the XHTML-compliant non-breaking space character '&amp;#160;')
2914          * @return {Roo.MessageBox} This message box
2915          */
2916         updateText : function(text){
2917             if(!dlg.isVisible() && !opt.width){
2918                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2919             }
2920             msgEl.innerHTML = text || '&#160;';
2921       
2922             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2923             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2924             var w = Math.max(
2925                     Math.min(opt.width || cw , this.maxWidth), 
2926                     Math.max(opt.minWidth || this.minWidth, bwidth)
2927             );
2928             if(opt.prompt){
2929                 activeTextEl.setWidth(w);
2930             }
2931             if(dlg.isVisible()){
2932                 dlg.fixedcenter = false;
2933             }
2934             // to big, make it scroll. = But as usual stupid IE does not support
2935             // !important..
2936             
2937             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2938                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2939                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2940             } else {
2941                 bodyEl.dom.style.height = '';
2942                 bodyEl.dom.style.overflowY = '';
2943             }
2944             if (cw > w) {
2945                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2946             } else {
2947                 bodyEl.dom.style.overflowX = '';
2948             }
2949             
2950             dlg.setContentSize(w, bodyEl.getHeight());
2951             if(dlg.isVisible()){
2952                 dlg.fixedcenter = true;
2953             }
2954             return this;
2955         },
2956
2957         /**
2958          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2959          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2960          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2961          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2962          * @return {Roo.MessageBox} This message box
2963          */
2964         updateProgress : function(value, text){
2965             if(text){
2966                 this.updateText(text);
2967             }
2968             if (pp) { // weird bug on my firefox - for some reason this is not defined
2969                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2970             }
2971             return this;
2972         },        
2973
2974         /**
2975          * Returns true if the message box is currently displayed
2976          * @return {Boolean} True if the message box is visible, else false
2977          */
2978         isVisible : function(){
2979             return dlg && dlg.isVisible();  
2980         },
2981
2982         /**
2983          * Hides the message box if it is displayed
2984          */
2985         hide : function(){
2986             if(this.isVisible()){
2987                 dlg.hide();
2988             }  
2989         },
2990
2991         /**
2992          * Displays a new message box, or reinitializes an existing message box, based on the config options
2993          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2994          * The following config object properties are supported:
2995          * <pre>
2996 Property    Type             Description
2997 ----------  ---------------  ------------------------------------------------------------------------------------
2998 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2999                                    closes (defaults to undefined)
3000 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3001                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3002 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3003                                    progress and wait dialogs will ignore this property and always hide the
3004                                    close button as they can only be closed programmatically.
3005 cls               String           A custom CSS class to apply to the message box element
3006 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3007                                    displayed (defaults to 75)
3008 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3009                                    function will be btn (the name of the button that was clicked, if applicable,
3010                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3011                                    Progress and wait dialogs will ignore this option since they do not respond to
3012                                    user actions and can only be closed programmatically, so any required function
3013                                    should be called by the same code after it closes the dialog.
3014 icon              String           A CSS class that provides a background image to be used as an icon for
3015                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3016 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3017 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3018 modal             Boolean          False to allow user interaction with the page while the message box is
3019                                    displayed (defaults to true)
3020 msg               String           A string that will replace the existing message box body text (defaults
3021                                    to the XHTML-compliant non-breaking space character '&#160;')
3022 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3023 progress          Boolean          True to display a progress bar (defaults to false)
3024 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3025 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3026 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3027 title             String           The title text
3028 value             String           The string value to set into the active textbox element if displayed
3029 wait              Boolean          True to display a progress bar (defaults to false)
3030 width             Number           The width of the dialog in pixels
3031 </pre>
3032          *
3033          * Example usage:
3034          * <pre><code>
3035 Roo.Msg.show({
3036    title: 'Address',
3037    msg: 'Please enter your address:',
3038    width: 300,
3039    buttons: Roo.MessageBox.OKCANCEL,
3040    multiline: true,
3041    fn: saveAddress,
3042    animEl: 'addAddressBtn'
3043 });
3044 </code></pre>
3045          * @param {Object} config Configuration options
3046          * @return {Roo.MessageBox} This message box
3047          */
3048         show : function(options)
3049         {
3050             
3051             // this causes nightmares if you show one dialog after another
3052             // especially on callbacks..
3053              
3054             if(this.isVisible()){
3055                 
3056                 this.hide();
3057                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3058                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3059                 Roo.log("New Dialog Message:" +  options.msg )
3060                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3061                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3062                 
3063             }
3064             var d = this.getDialog();
3065             opt = options;
3066             d.setTitle(opt.title || "&#160;");
3067             d.closeEl.setDisplayed(opt.closable !== false);
3068             activeTextEl = textboxEl;
3069             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3070             if(opt.prompt){
3071                 if(opt.multiline){
3072                     textboxEl.hide();
3073                     textareaEl.show();
3074                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3075                         opt.multiline : this.defaultTextHeight);
3076                     activeTextEl = textareaEl;
3077                 }else{
3078                     textboxEl.show();
3079                     textareaEl.hide();
3080                 }
3081             }else{
3082                 textboxEl.hide();
3083                 textareaEl.hide();
3084             }
3085             progressEl.setDisplayed(opt.progress === true);
3086             this.updateProgress(0);
3087             activeTextEl.dom.value = opt.value || "";
3088             if(opt.prompt){
3089                 dlg.setDefaultButton(activeTextEl);
3090             }else{
3091                 var bs = opt.buttons;
3092                 var db = null;
3093                 if(bs && bs.ok){
3094                     db = buttons["ok"];
3095                 }else if(bs && bs.yes){
3096                     db = buttons["yes"];
3097                 }
3098                 dlg.setDefaultButton(db);
3099             }
3100             bwidth = updateButtons(opt.buttons);
3101             this.updateText(opt.msg);
3102             if(opt.cls){
3103                 d.el.addClass(opt.cls);
3104             }
3105             d.proxyDrag = opt.proxyDrag === true;
3106             d.modal = opt.modal !== false;
3107             d.mask = opt.modal !== false ? mask : false;
3108             if(!d.isVisible()){
3109                 // force it to the end of the z-index stack so it gets a cursor in FF
3110                 document.body.appendChild(dlg.el.dom);
3111                 d.animateTarget = null;
3112                 d.show(options.animEl);
3113             }
3114             return this;
3115         },
3116
3117         /**
3118          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3119          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3120          * and closing the message box when the process is complete.
3121          * @param {String} title The title bar text
3122          * @param {String} msg The message box body text
3123          * @return {Roo.MessageBox} This message box
3124          */
3125         progress : function(title, msg){
3126             this.show({
3127                 title : title,
3128                 msg : msg,
3129                 buttons: false,
3130                 progress:true,
3131                 closable:false,
3132                 minWidth: this.minProgressWidth,
3133                 modal : true
3134             });
3135             return this;
3136         },
3137
3138         /**
3139          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3140          * If a callback function is passed it will be called after the user clicks the button, and the
3141          * id of the button that was clicked will be passed as the only parameter to the callback
3142          * (could also be the top-right close button).
3143          * @param {String} title The title bar text
3144          * @param {String} msg The message box body text
3145          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3146          * @param {Object} scope (optional) The scope of the callback function
3147          * @return {Roo.MessageBox} This message box
3148          */
3149         alert : function(title, msg, fn, scope){
3150             this.show({
3151                 title : title,
3152                 msg : msg,
3153                 buttons: this.OK,
3154                 fn: fn,
3155                 scope : scope,
3156                 modal : true
3157             });
3158             return this;
3159         },
3160
3161         /**
3162          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3163          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3164          * You are responsible for closing the message box when the process is complete.
3165          * @param {String} msg The message box body text
3166          * @param {String} title (optional) The title bar text
3167          * @return {Roo.MessageBox} This message box
3168          */
3169         wait : function(msg, title){
3170             this.show({
3171                 title : title,
3172                 msg : msg,
3173                 buttons: false,
3174                 closable:false,
3175                 progress:true,
3176                 modal:true,
3177                 width:300,
3178                 wait:true
3179             });
3180             waitTimer = Roo.TaskMgr.start({
3181                 run: function(i){
3182                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3183                 },
3184                 interval: 1000
3185             });
3186             return this;
3187         },
3188
3189         /**
3190          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3191          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3192          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3193          * @param {String} title The title bar text
3194          * @param {String} msg The message box body text
3195          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3196          * @param {Object} scope (optional) The scope of the callback function
3197          * @return {Roo.MessageBox} This message box
3198          */
3199         confirm : function(title, msg, fn, scope){
3200             this.show({
3201                 title : title,
3202                 msg : msg,
3203                 buttons: this.YESNO,
3204                 fn: fn,
3205                 scope : scope,
3206                 modal : true
3207             });
3208             return this;
3209         },
3210
3211         /**
3212          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3213          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3214          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3215          * (could also be the top-right close button) and the text that was entered will be passed as the two
3216          * parameters to the callback.
3217          * @param {String} title The title bar text
3218          * @param {String} msg The message box body text
3219          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3220          * @param {Object} scope (optional) The scope of the callback function
3221          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3222          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3223          * @return {Roo.MessageBox} This message box
3224          */
3225         prompt : function(title, msg, fn, scope, multiline){
3226             this.show({
3227                 title : title,
3228                 msg : msg,
3229                 buttons: this.OKCANCEL,
3230                 fn: fn,
3231                 minWidth:250,
3232                 scope : scope,
3233                 prompt:true,
3234                 multiline: multiline,
3235                 modal : true
3236             });
3237             return this;
3238         },
3239
3240         /**
3241          * Button config that displays a single OK button
3242          * @type Object
3243          */
3244         OK : {ok:true},
3245         /**
3246          * Button config that displays Yes and No buttons
3247          * @type Object
3248          */
3249         YESNO : {yes:true, no:true},
3250         /**
3251          * Button config that displays OK and Cancel buttons
3252          * @type Object
3253          */
3254         OKCANCEL : {ok:true, cancel:true},
3255         /**
3256          * Button config that displays Yes, No and Cancel buttons
3257          * @type Object
3258          */
3259         YESNOCANCEL : {yes:true, no:true, cancel:true},
3260
3261         /**
3262          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3263          * @type Number
3264          */
3265         defaultTextHeight : 75,
3266         /**
3267          * The maximum width in pixels of the message box (defaults to 600)
3268          * @type Number
3269          */
3270         maxWidth : 600,
3271         /**
3272          * The minimum width in pixels of the message box (defaults to 100)
3273          * @type Number
3274          */
3275         minWidth : 100,
3276         /**
3277          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3278          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3279          * @type Number
3280          */
3281         minProgressWidth : 250,
3282         /**
3283          * An object containing the default button text strings that can be overriden for localized language support.
3284          * Supported properties are: ok, cancel, yes and no.
3285          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3286          * @type Object
3287          */
3288         buttonText : {
3289             ok : "OK",
3290             cancel : "Cancel",
3291             yes : "Yes",
3292             no : "No"
3293         }
3294     };
3295 }();
3296
3297 /**
3298  * Shorthand for {@link Roo.MessageBox}
3299  */
3300 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3301 Roo.Msg = Roo.Msg || Roo.MessageBox;
3302 /*
3303  * - LGPL
3304  *
3305  * navbar
3306  * 
3307  */
3308
3309 /**
3310  * @class Roo.bootstrap.Navbar
3311  * @extends Roo.bootstrap.Component
3312  * Bootstrap Navbar class
3313
3314  * @constructor
3315  * Create a new Navbar
3316  * @param {Object} config The config object
3317  */
3318
3319
3320 Roo.bootstrap.Navbar = function(config){
3321     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3322     
3323 };
3324
3325 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3326     
3327     
3328    
3329     // private
3330     navItems : false,
3331     loadMask : false,
3332     
3333     
3334     getAutoCreate : function(){
3335         
3336         
3337         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3338         
3339     },
3340     
3341     initEvents :function ()
3342     {
3343         //Roo.log(this.el.select('.navbar-toggle',true));
3344         this.el.select('.navbar-toggle',true).on('click', function() {
3345            // Roo.log('click');
3346             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3347         }, this);
3348         
3349         var mark = {
3350             tag: "div",
3351             cls:"x-dlg-mask"
3352         };
3353         
3354         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3355         
3356         var size = this.el.getSize();
3357         this.maskEl.setSize(size.width, size.height);
3358         this.maskEl.enableDisplayMode("block");
3359         this.maskEl.hide();
3360         
3361         if(this.loadMask){
3362             this.maskEl.show();
3363         }
3364     },
3365     
3366     
3367     getChildContainer : function()
3368     {
3369         if (this.el.select('.collapse').getCount()) {
3370             return this.el.select('.collapse',true).first();
3371         }
3372         
3373         return this.el;
3374     },
3375     
3376     mask : function()
3377     {
3378         this.maskEl.show();
3379     },
3380     
3381     unmask : function()
3382     {
3383         this.maskEl.hide();
3384     } 
3385     
3386     
3387     
3388     
3389 });
3390
3391
3392
3393  
3394
3395  /*
3396  * - LGPL
3397  *
3398  * navbar
3399  * 
3400  */
3401
3402 /**
3403  * @class Roo.bootstrap.NavSimplebar
3404  * @extends Roo.bootstrap.Navbar
3405  * Bootstrap Sidebar class
3406  *
3407  * @cfg {Boolean} inverse is inverted color
3408  * 
3409  * @cfg {String} type (nav | pills | tabs)
3410  * @cfg {Boolean} arrangement stacked | justified
3411  * @cfg {String} align (left | right) alignment
3412  * 
3413  * @cfg {Boolean} main (true|false) main nav bar? default false
3414  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3415  * 
3416  * @cfg {String} tag (header|footer|nav|div) default is nav 
3417
3418  * 
3419  * 
3420  * 
3421  * @constructor
3422  * Create a new Sidebar
3423  * @param {Object} config The config object
3424  */
3425
3426
3427 Roo.bootstrap.NavSimplebar = function(config){
3428     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3429 };
3430
3431 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3432     
3433     inverse: false,
3434     
3435     type: false,
3436     arrangement: '',
3437     align : false,
3438     
3439     
3440     
3441     main : false,
3442     
3443     
3444     tag : false,
3445     
3446     
3447     getAutoCreate : function(){
3448         
3449         
3450         var cfg = {
3451             tag : this.tag || 'div',
3452             cls : 'navbar'
3453         };
3454           
3455         
3456         cfg.cn = [
3457             {
3458                 cls: 'nav',
3459                 tag : 'ul'
3460             }
3461         ];
3462         
3463          
3464         this.type = this.type || 'nav';
3465         if (['tabs','pills'].indexOf(this.type)!==-1) {
3466             cfg.cn[0].cls += ' nav-' + this.type
3467         
3468         
3469         } else {
3470             if (this.type!=='nav') {
3471                 Roo.log('nav type must be nav/tabs/pills')
3472             }
3473             cfg.cn[0].cls += ' navbar-nav'
3474         }
3475         
3476         
3477         
3478         
3479         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3480             cfg.cn[0].cls += ' nav-' + this.arrangement;
3481         }
3482         
3483         
3484         if (this.align === 'right') {
3485             cfg.cn[0].cls += ' navbar-right';
3486         }
3487         
3488         if (this.inverse) {
3489             cfg.cls += ' navbar-inverse';
3490             
3491         }
3492         
3493         
3494         return cfg;
3495     
3496         
3497     }
3498     
3499     
3500     
3501 });
3502
3503
3504
3505  
3506
3507  
3508        /*
3509  * - LGPL
3510  *
3511  * navbar
3512  * 
3513  */
3514
3515 /**
3516  * @class Roo.bootstrap.NavHeaderbar
3517  * @extends Roo.bootstrap.NavSimplebar
3518  * Bootstrap Sidebar class
3519  *
3520  * @cfg {String} brand what is brand
3521  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3522  * @cfg {String} brand_href href of the brand
3523  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3524  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3525  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3526  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3527  * 
3528  * @constructor
3529  * Create a new Sidebar
3530  * @param {Object} config The config object
3531  */
3532
3533
3534 Roo.bootstrap.NavHeaderbar = function(config){
3535     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3536       
3537 };
3538
3539 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3540     
3541     position: '',
3542     brand: '',
3543     brand_href: false,
3544     srButton : true,
3545     autohide : false,
3546     desktopCenter : false,
3547    
3548     
3549     getAutoCreate : function(){
3550         
3551         var   cfg = {
3552             tag: this.nav || 'nav',
3553             cls: 'navbar',
3554             role: 'navigation',
3555             cn: []
3556         };
3557         
3558         var cn = cfg.cn;
3559         if (this.desktopCenter) {
3560             cn.push({cls : 'container', cn : []});
3561             cn = cn[0].cn;
3562         }
3563         
3564         if(this.srButton){
3565             cn.push({
3566                 tag: 'div',
3567                 cls: 'navbar-header',
3568                 cn: [
3569                     {
3570                         tag: 'button',
3571                         type: 'button',
3572                         cls: 'navbar-toggle',
3573                         'data-toggle': 'collapse',
3574                         cn: [
3575                             {
3576                                 tag: 'span',
3577                                 cls: 'sr-only',
3578                                 html: 'Toggle navigation'
3579                             },
3580                             {
3581                                 tag: 'span',
3582                                 cls: 'icon-bar'
3583                             },
3584                             {
3585                                 tag: 'span',
3586                                 cls: 'icon-bar'
3587                             },
3588                             {
3589                                 tag: 'span',
3590                                 cls: 'icon-bar'
3591                             }
3592                         ]
3593                     }
3594                 ]
3595             });
3596         }
3597         
3598         cn.push({
3599             tag: 'div',
3600             cls: 'collapse navbar-collapse',
3601             cn : []
3602         });
3603         
3604         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3605         
3606         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3607             cfg.cls += ' navbar-' + this.position;
3608             
3609             // tag can override this..
3610             
3611             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3612         }
3613         
3614         if (this.brand !== '') {
3615             cn[0].cn.push({
3616                 tag: 'a',
3617                 href: this.brand_href ? this.brand_href : '#',
3618                 cls: 'navbar-brand',
3619                 cn: [
3620                 this.brand
3621                 ]
3622             });
3623         }
3624         
3625         if(this.main){
3626             cfg.cls += ' main-nav';
3627         }
3628         
3629         
3630         return cfg;
3631
3632         
3633     },
3634     getHeaderChildContainer : function()
3635     {
3636         if (this.el.select('.navbar-header').getCount()) {
3637             return this.el.select('.navbar-header',true).first();
3638         }
3639         
3640         return this.getChildContainer();
3641     },
3642     
3643     
3644     initEvents : function()
3645     {
3646         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3647         
3648         if (this.autohide) {
3649             
3650             var prevScroll = 0;
3651             var ft = this.el;
3652             
3653             Roo.get(document).on('scroll',function(e) {
3654                 var ns = Roo.get(document).getScroll().top;
3655                 var os = prevScroll;
3656                 prevScroll = ns;
3657                 
3658                 if(ns > os){
3659                     ft.removeClass('slideDown');
3660                     ft.addClass('slideUp');
3661                     return;
3662                 }
3663                 ft.removeClass('slideUp');
3664                 ft.addClass('slideDown');
3665                  
3666               
3667           },this);
3668         }
3669     }    
3670     
3671 });
3672
3673
3674
3675  
3676
3677  /*
3678  * - LGPL
3679  *
3680  * navbar
3681  * 
3682  */
3683
3684 /**
3685  * @class Roo.bootstrap.NavSidebar
3686  * @extends Roo.bootstrap.Navbar
3687  * Bootstrap Sidebar class
3688  * 
3689  * @constructor
3690  * Create a new Sidebar
3691  * @param {Object} config The config object
3692  */
3693
3694
3695 Roo.bootstrap.NavSidebar = function(config){
3696     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3697 };
3698
3699 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3700     
3701     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3702     
3703     getAutoCreate : function(){
3704         
3705         
3706         return  {
3707             tag: 'div',
3708             cls: 'sidebar sidebar-nav'
3709         };
3710     
3711         
3712     }
3713     
3714     
3715     
3716 });
3717
3718
3719
3720  
3721
3722  /*
3723  * - LGPL
3724  *
3725  * nav group
3726  * 
3727  */
3728
3729 /**
3730  * @class Roo.bootstrap.NavGroup
3731  * @extends Roo.bootstrap.Component
3732  * Bootstrap NavGroup class
3733  * @cfg {String} align (left|right)
3734  * @cfg {Boolean} inverse
3735  * @cfg {String} type (nav|pills|tab) default nav
3736  * @cfg {String} navId - reference Id for navbar.
3737
3738  * 
3739  * @constructor
3740  * Create a new nav group
3741  * @param {Object} config The config object
3742  */
3743
3744 Roo.bootstrap.NavGroup = function(config){
3745     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3746     this.navItems = [];
3747    
3748     Roo.bootstrap.NavGroup.register(this);
3749      this.addEvents({
3750         /**
3751              * @event changed
3752              * Fires when the active item changes
3753              * @param {Roo.bootstrap.NavGroup} this
3754              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3755              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3756          */
3757         'changed': true
3758      });
3759     
3760 };
3761
3762 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3763     
3764     align: '',
3765     inverse: false,
3766     form: false,
3767     type: 'nav',
3768     navId : '',
3769     // private
3770     
3771     navItems : false, 
3772     
3773     getAutoCreate : function()
3774     {
3775         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3776         
3777         cfg = {
3778             tag : 'ul',
3779             cls: 'nav' 
3780         };
3781         
3782         if (['tabs','pills'].indexOf(this.type)!==-1) {
3783             cfg.cls += ' nav-' + this.type
3784         } else {
3785             if (this.type!=='nav') {
3786                 Roo.log('nav type must be nav/tabs/pills')
3787             }
3788             cfg.cls += ' navbar-nav'
3789         }
3790         
3791         if (this.parent().sidebar) {
3792             cfg = {
3793                 tag: 'ul',
3794                 cls: 'dashboard-menu sidebar-menu'
3795             };
3796             
3797             return cfg;
3798         }
3799         
3800         if (this.form === true) {
3801             cfg = {
3802                 tag: 'form',
3803                 cls: 'navbar-form'
3804             };
3805             
3806             if (this.align === 'right') {
3807                 cfg.cls += ' navbar-right';
3808             } else {
3809                 cfg.cls += ' navbar-left';
3810             }
3811         }
3812         
3813         if (this.align === 'right') {
3814             cfg.cls += ' navbar-right';
3815         }
3816         
3817         if (this.inverse) {
3818             cfg.cls += ' navbar-inverse';
3819             
3820         }
3821         
3822         
3823         return cfg;
3824     },
3825     /**
3826     * sets the active Navigation item
3827     * @param {Roo.bootstrap.NavItem} the new current navitem
3828     */
3829     setActiveItem : function(item)
3830     {
3831         var prev = false;
3832         Roo.each(this.navItems, function(v){
3833             if (v == item) {
3834                 return ;
3835             }
3836             if (v.isActive()) {
3837                 v.setActive(false, true);
3838                 prev = v;
3839                 
3840             }
3841             
3842         });
3843
3844         item.setActive(true, true);
3845         this.fireEvent('changed', this, item, prev);
3846         
3847         
3848     },
3849     /**
3850     * gets the active Navigation item
3851     * @return {Roo.bootstrap.NavItem} the current navitem
3852     */
3853     getActive : function()
3854     {
3855         
3856         var prev = false;
3857         Roo.each(this.navItems, function(v){
3858             
3859             if (v.isActive()) {
3860                 prev = v;
3861                 
3862             }
3863             
3864         });
3865         return prev;
3866     },
3867     
3868     indexOfNav : function()
3869     {
3870         
3871         var prev = false;
3872         Roo.each(this.navItems, function(v,i){
3873             
3874             if (v.isActive()) {
3875                 prev = i;
3876                 
3877             }
3878             
3879         });
3880         return prev;
3881     },
3882     /**
3883     * adds a Navigation item
3884     * @param {Roo.bootstrap.NavItem} the navitem to add
3885     */
3886     addItem : function(cfg)
3887     {
3888         var cn = new Roo.bootstrap.NavItem(cfg);
3889         this.register(cn);
3890         cn.parentId = this.id;
3891         cn.onRender(this.el, null);
3892         return cn;
3893     },
3894     /**
3895     * register a Navigation item
3896     * @param {Roo.bootstrap.NavItem} the navitem to add
3897     */
3898     register : function(item)
3899     {
3900         this.navItems.push( item);
3901         item.navId = this.navId;
3902     
3903     },
3904     
3905     /**
3906     * clear all the Navigation item
3907     */
3908    
3909     clearAll : function()
3910     {
3911         this.navItems = [];
3912         this.el.dom.innerHTML = '';
3913     },
3914     
3915     getNavItem: function(tabId)
3916     {
3917         var ret = false;
3918         Roo.each(this.navItems, function(e) {
3919             if (e.tabId == tabId) {
3920                ret =  e;
3921                return false;
3922             }
3923             return true;
3924             
3925         });
3926         return ret;
3927     },
3928     
3929     setActiveNext : function()
3930     {
3931         var i = this.indexOfNav(this.getActive());
3932         if (i > this.navItems.length) {
3933             return;
3934         }
3935         this.setActiveItem(this.navItems[i+1]);
3936     },
3937     setActivePrev : function()
3938     {
3939         var i = this.indexOfNav(this.getActive());
3940         if (i  < 1) {
3941             return;
3942         }
3943         this.setActiveItem(this.navItems[i-1]);
3944     },
3945     clearWasActive : function(except) {
3946         Roo.each(this.navItems, function(e) {
3947             if (e.tabId != except.tabId && e.was_active) {
3948                e.was_active = false;
3949                return false;
3950             }
3951             return true;
3952             
3953         });
3954     },
3955     getWasActive : function ()
3956     {
3957         var r = false;
3958         Roo.each(this.navItems, function(e) {
3959             if (e.was_active) {
3960                r = e;
3961                return false;
3962             }
3963             return true;
3964             
3965         });
3966         return r;
3967     }
3968     
3969     
3970 });
3971
3972  
3973 Roo.apply(Roo.bootstrap.NavGroup, {
3974     
3975     groups: {},
3976      /**
3977     * register a Navigation Group
3978     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3979     */
3980     register : function(navgrp)
3981     {
3982         this.groups[navgrp.navId] = navgrp;
3983         
3984     },
3985     /**
3986     * fetch a Navigation Group based on the navigation ID
3987     * @param {string} the navgroup to add
3988     * @returns {Roo.bootstrap.NavGroup} the navgroup 
3989     */
3990     get: function(navId) {
3991         if (typeof(this.groups[navId]) == 'undefined') {
3992             return false;
3993             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3994         }
3995         return this.groups[navId] ;
3996     }
3997     
3998     
3999     
4000 });
4001
4002  /*
4003  * - LGPL
4004  *
4005  * row
4006  * 
4007  */
4008
4009 /**
4010  * @class Roo.bootstrap.NavItem
4011  * @extends Roo.bootstrap.Component
4012  * Bootstrap Navbar.NavItem class
4013  * @cfg {String} href  link to
4014  * @cfg {String} html content of button
4015  * @cfg {String} badge text inside badge
4016  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4017  * @cfg {String} glyphicon name of glyphicon
4018  * @cfg {String} icon name of font awesome icon
4019  * @cfg {Boolean} active Is item active
4020  * @cfg {Boolean} disabled Is item disabled
4021  
4022  * @cfg {Boolean} preventDefault (true | false) default false
4023  * @cfg {String} tabId the tab that this item activates.
4024  * @cfg {String} tagtype (a|span) render as a href or span?
4025  * @cfg {Boolean} animateRef (true|false) link to element default false  
4026   
4027  * @constructor
4028  * Create a new Navbar Item
4029  * @param {Object} config The config object
4030  */
4031 Roo.bootstrap.NavItem = function(config){
4032     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4033     this.addEvents({
4034         // raw events
4035         /**
4036          * @event click
4037          * The raw click event for the entire grid.
4038          * @param {Roo.EventObject} e
4039          */
4040         "click" : true,
4041          /**
4042             * @event changed
4043             * Fires when the active item active state changes
4044             * @param {Roo.bootstrap.NavItem} this
4045             * @param {boolean} state the new state
4046              
4047          */
4048         'changed': true,
4049         /**
4050             * @event scrollto
4051             * Fires when scroll to element
4052             * @param {Roo.bootstrap.NavItem} this
4053             * @param {Object} options
4054             * @param {Roo.EventObject} e
4055              
4056          */
4057         'scrollto': true
4058     });
4059    
4060 };
4061
4062 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4063     
4064     href: false,
4065     html: '',
4066     badge: '',
4067     icon: false,
4068     glyphicon: false,
4069     active: false,
4070     preventDefault : false,
4071     tabId : false,
4072     tagtype : 'a',
4073     disabled : false,
4074     animateRef : false,
4075     was_active : false,
4076     
4077     getAutoCreate : function(){
4078          
4079         var cfg = {
4080             tag: 'li',
4081             cls: 'nav-item'
4082             
4083         };
4084         
4085         if (this.active) {
4086             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4087         }
4088         if (this.disabled) {
4089             cfg.cls += ' disabled';
4090         }
4091         
4092         if (this.href || this.html || this.glyphicon || this.icon) {
4093             cfg.cn = [
4094                 {
4095                     tag: this.tagtype,
4096                     href : this.href || "#",
4097                     html: this.html || ''
4098                 }
4099             ];
4100             
4101             if (this.icon) {
4102                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4103             }
4104
4105             if(this.glyphicon) {
4106                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4107             }
4108             
4109             if (this.menu) {
4110                 
4111                 cfg.cn[0].html += " <span class='caret'></span>";
4112              
4113             }
4114             
4115             if (this.badge !== '') {
4116                  
4117                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4118             }
4119         }
4120         
4121         
4122         
4123         return cfg;
4124     },
4125     initEvents: function() 
4126     {
4127         if (typeof (this.menu) != 'undefined') {
4128             this.menu.parentType = this.xtype;
4129             this.menu.triggerEl = this.el;
4130             this.menu = this.addxtype(Roo.apply({}, this.menu));
4131         }
4132         
4133         this.el.select('a',true).on('click', this.onClick, this);
4134         
4135         if(this.tagtype == 'span'){
4136             this.el.select('span',true).on('click', this.onClick, this);
4137         }
4138        
4139         // at this point parent should be available..
4140         this.parent().register(this);
4141     },
4142     
4143     onClick : function(e)
4144     {
4145         if(
4146                 this.preventDefault || 
4147                 this.href == '#' 
4148         ){
4149             
4150             e.preventDefault();
4151         }
4152         
4153         if (this.disabled) {
4154             return;
4155         }
4156         
4157         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4158         if (tg && tg.transition) {
4159             Roo.log("waiting for the transitionend");
4160             return;
4161         }
4162         
4163         
4164         
4165         //Roo.log("fire event clicked");
4166         if(this.fireEvent('click', this, e) === false){
4167             return;
4168         };
4169         
4170         if(this.tagtype == 'span'){
4171             return;
4172         }
4173         
4174         //Roo.log(this.href);
4175         var ael = this.el.select('a',true).first();
4176         //Roo.log(ael);
4177         
4178         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4179             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4180             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4181                 return; // ignore... - it's a 'hash' to another page.
4182             }
4183             
4184             e.preventDefault();
4185             this.scrollToElement(e);
4186         }
4187         
4188         
4189         var p =  this.parent();
4190    
4191         if (['tabs','pills'].indexOf(p.type)!==-1) {
4192             if (typeof(p.setActiveItem) !== 'undefined') {
4193                 p.setActiveItem(this);
4194             }
4195         }
4196         
4197         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4198         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4199             // remove the collapsed menu expand...
4200             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4201         }
4202     },
4203     
4204     isActive: function () {
4205         return this.active
4206     },
4207     setActive : function(state, fire, is_was_active)
4208     {
4209         if (this.active && !state && this.navId) {
4210             this.was_active = true;
4211             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4212             if (nv) {
4213                 nv.clearWasActive(this);
4214             }
4215             
4216         }
4217         this.active = state;
4218         
4219         if (!state ) {
4220             this.el.removeClass('active');
4221         } else if (!this.el.hasClass('active')) {
4222             this.el.addClass('active');
4223         }
4224         if (fire) {
4225             this.fireEvent('changed', this, state);
4226         }
4227         
4228         // show a panel if it's registered and related..
4229         
4230         if (!this.navId || !this.tabId || !state || is_was_active) {
4231             return;
4232         }
4233         
4234         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4235         if (!tg) {
4236             return;
4237         }
4238         var pan = tg.getPanelByName(this.tabId);
4239         if (!pan) {
4240             return;
4241         }
4242         // if we can not flip to new panel - go back to old nav highlight..
4243         if (false == tg.showPanel(pan)) {
4244             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4245             if (nv) {
4246                 var onav = nv.getWasActive();
4247                 if (onav) {
4248                     onav.setActive(true, false, true);
4249                 }
4250             }
4251             
4252         }
4253         
4254         
4255         
4256     },
4257      // this should not be here...
4258     setDisabled : function(state)
4259     {
4260         this.disabled = state;
4261         if (!state ) {
4262             this.el.removeClass('disabled');
4263         } else if (!this.el.hasClass('disabled')) {
4264             this.el.addClass('disabled');
4265         }
4266         
4267     },
4268     
4269     /**
4270      * Fetch the element to display the tooltip on.
4271      * @return {Roo.Element} defaults to this.el
4272      */
4273     tooltipEl : function()
4274     {
4275         return this.el.select('' + this.tagtype + '', true).first();
4276     },
4277     
4278     scrollToElement : function(e)
4279     {
4280         var c = document.body;
4281         
4282         /*
4283          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4284          */
4285         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4286             c = document.documentElement;
4287         }
4288         
4289         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4290         
4291         if(!target){
4292             return;
4293         }
4294
4295         var o = target.calcOffsetsTo(c);
4296         
4297         var options = {
4298             target : target,
4299             value : o[1]
4300         };
4301         
4302         this.fireEvent('scrollto', this, options, e);
4303         
4304         Roo.get(c).scrollTo('top', options.value, true);
4305         
4306         return;
4307     }
4308 });
4309  
4310
4311  /*
4312  * - LGPL
4313  *
4314  * sidebar item
4315  *
4316  *  li
4317  *    <span> icon </span>
4318  *    <span> text </span>
4319  *    <span>badge </span>
4320  */
4321
4322 /**
4323  * @class Roo.bootstrap.NavSidebarItem
4324  * @extends Roo.bootstrap.NavItem
4325  * Bootstrap Navbar.NavSidebarItem class
4326  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4327  * @constructor
4328  * Create a new Navbar Button
4329  * @param {Object} config The config object
4330  */
4331 Roo.bootstrap.NavSidebarItem = function(config){
4332     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4333     this.addEvents({
4334         // raw events
4335         /**
4336          * @event click
4337          * The raw click event for the entire grid.
4338          * @param {Roo.EventObject} e
4339          */
4340         "click" : true,
4341          /**
4342             * @event changed
4343             * Fires when the active item active state changes
4344             * @param {Roo.bootstrap.NavSidebarItem} this
4345             * @param {boolean} state the new state
4346              
4347          */
4348         'changed': true
4349     });
4350    
4351 };
4352
4353 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4354     
4355     badgeWeight : 'default',
4356     
4357     getAutoCreate : function(){
4358         
4359         
4360         var a = {
4361                 tag: 'a',
4362                 href : this.href || '#',
4363                 cls: '',
4364                 html : '',
4365                 cn : []
4366         };
4367         var cfg = {
4368             tag: 'li',
4369             cls: '',
4370             cn: [ a ]
4371         };
4372         var span = {
4373             tag: 'span',
4374             html : this.html || ''
4375         };
4376         
4377         
4378         if (this.active) {
4379             cfg.cls += ' active';
4380         }
4381         
4382         if (this.disabled) {
4383             cfg.cls += ' disabled';
4384         }
4385         
4386         // left icon..
4387         if (this.glyphicon || this.icon) {
4388             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4389             a.cn.push({ tag : 'i', cls : c }) ;
4390         }
4391         // html..
4392         a.cn.push(span);
4393         // then badge..
4394         if (this.badge !== '') {
4395             
4396             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4397         }
4398         // fi
4399         if (this.menu) {
4400             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4401             a.cls += 'dropdown-toggle treeview' ;
4402             
4403         }
4404         
4405         
4406         
4407         return cfg;
4408          
4409            
4410     },
4411     
4412     initEvents : function()
4413     { 
4414         this.el.on('click', this.onClick, this);
4415        
4416     
4417         if(this.badge !== ''){
4418  
4419             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4420         }
4421         
4422     },
4423     
4424     onClick : function(e)
4425     {
4426         if(this.disabled){
4427             e.preventDefault();
4428             return;
4429         }
4430         
4431         if(this.preventDefault){
4432             e.preventDefault();
4433         }
4434         
4435         this.fireEvent('click', this);
4436     },
4437     
4438     disable : function()
4439     {
4440         this.setDisabled(true);
4441     },
4442     
4443     enable : function()
4444     {
4445         this.setDisabled(false);
4446     },
4447     
4448     setDisabled : function(state)
4449     {
4450         if(this.disabled == state){
4451             return;
4452         }
4453         
4454         this.disabled = state;
4455         
4456         if (state) {
4457             this.el.addClass('disabled');
4458             return;
4459         }
4460         
4461         this.el.removeClass('disabled');
4462         
4463         return;
4464     },
4465     
4466     setActive : function(state)
4467     {
4468         if(this.active == state){
4469             return;
4470         }
4471         
4472         this.active = state;
4473         
4474         if (state) {
4475             this.el.addClass('active');
4476             return;
4477         }
4478         
4479         this.el.removeClass('active');
4480         
4481         return;
4482     },
4483     
4484     isActive: function () 
4485     {
4486         return this.active;
4487     },
4488     
4489     setBadge : function(str)
4490     {
4491         if(!this.badgeEl){
4492             return;
4493         }
4494         
4495         this.badgeEl.dom.innerHTML = str;
4496     }
4497     
4498    
4499      
4500  
4501 });
4502  
4503
4504  /*
4505  * - LGPL
4506  *
4507  * row
4508  * 
4509  */
4510
4511 /**
4512  * @class Roo.bootstrap.Row
4513  * @extends Roo.bootstrap.Component
4514  * Bootstrap Row class (contains columns...)
4515  * 
4516  * @constructor
4517  * Create a new Row
4518  * @param {Object} config The config object
4519  */
4520
4521 Roo.bootstrap.Row = function(config){
4522     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4523 };
4524
4525 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4526     
4527     getAutoCreate : function(){
4528        return {
4529             cls: 'row clearfix'
4530        };
4531     }
4532     
4533     
4534 });
4535
4536  
4537
4538  /*
4539  * - LGPL
4540  *
4541  * element
4542  * 
4543  */
4544
4545 /**
4546  * @class Roo.bootstrap.Element
4547  * @extends Roo.bootstrap.Component
4548  * Bootstrap Element class
4549  * @cfg {String} html contents of the element
4550  * @cfg {String} tag tag of the element
4551  * @cfg {String} cls class of the element
4552  * @cfg {Boolean} preventDefault (true|false) default false
4553  * @cfg {Boolean} clickable (true|false) default false
4554  * 
4555  * @constructor
4556  * Create a new Element
4557  * @param {Object} config The config object
4558  */
4559
4560 Roo.bootstrap.Element = function(config){
4561     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4562     
4563     this.addEvents({
4564         // raw events
4565         /**
4566          * @event click
4567          * When a element is chick
4568          * @param {Roo.bootstrap.Element} this
4569          * @param {Roo.EventObject} e
4570          */
4571         "click" : true
4572     });
4573 };
4574
4575 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4576     
4577     tag: 'div',
4578     cls: '',
4579     html: '',
4580     preventDefault: false, 
4581     clickable: false,
4582     
4583     getAutoCreate : function(){
4584         
4585         var cfg = {
4586             tag: this.tag,
4587             cls: this.cls,
4588             html: this.html
4589         };
4590         
4591         return cfg;
4592     },
4593     
4594     initEvents: function() 
4595     {
4596         Roo.bootstrap.Element.superclass.initEvents.call(this);
4597         
4598         if(this.clickable){
4599             this.el.on('click', this.onClick, this);
4600         }
4601         
4602     },
4603     
4604     onClick : function(e)
4605     {
4606         if(this.preventDefault){
4607             e.preventDefault();
4608         }
4609         
4610         this.fireEvent('click', this, e);
4611     },
4612     
4613     getValue : function()
4614     {
4615         return this.el.dom.innerHTML;
4616     },
4617     
4618     setValue : function(value)
4619     {
4620         this.el.dom.innerHTML = value;
4621     }
4622    
4623 });
4624
4625  
4626
4627  /*
4628  * - LGPL
4629  *
4630  * pagination
4631  * 
4632  */
4633
4634 /**
4635  * @class Roo.bootstrap.Pagination
4636  * @extends Roo.bootstrap.Component
4637  * Bootstrap Pagination class
4638  * @cfg {String} size xs | sm | md | lg
4639  * @cfg {Boolean} inverse false | true
4640  * 
4641  * @constructor
4642  * Create a new Pagination
4643  * @param {Object} config The config object
4644  */
4645
4646 Roo.bootstrap.Pagination = function(config){
4647     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4648 };
4649
4650 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4651     
4652     cls: false,
4653     size: false,
4654     inverse: false,
4655     
4656     getAutoCreate : function(){
4657         var cfg = {
4658             tag: 'ul',
4659                 cls: 'pagination'
4660         };
4661         if (this.inverse) {
4662             cfg.cls += ' inverse';
4663         }
4664         if (this.html) {
4665             cfg.html=this.html;
4666         }
4667         if (this.cls) {
4668             cfg.cls += " " + this.cls;
4669         }
4670         return cfg;
4671     }
4672    
4673 });
4674
4675  
4676
4677  /*
4678  * - LGPL
4679  *
4680  * Pagination item
4681  * 
4682  */
4683
4684
4685 /**
4686  * @class Roo.bootstrap.PaginationItem
4687  * @extends Roo.bootstrap.Component
4688  * Bootstrap PaginationItem class
4689  * @cfg {String} html text
4690  * @cfg {String} href the link
4691  * @cfg {Boolean} preventDefault (true | false) default true
4692  * @cfg {Boolean} active (true | false) default false
4693  * @cfg {Boolean} disabled default false
4694  * 
4695  * 
4696  * @constructor
4697  * Create a new PaginationItem
4698  * @param {Object} config The config object
4699  */
4700
4701
4702 Roo.bootstrap.PaginationItem = function(config){
4703     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4704     this.addEvents({
4705         // raw events
4706         /**
4707          * @event click
4708          * The raw click event for the entire grid.
4709          * @param {Roo.EventObject} e
4710          */
4711         "click" : true
4712     });
4713 };
4714
4715 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4716     
4717     href : false,
4718     html : false,
4719     preventDefault: true,
4720     active : false,
4721     cls : false,
4722     disabled: false,
4723     
4724     getAutoCreate : function(){
4725         var cfg= {
4726             tag: 'li',
4727             cn: [
4728                 {
4729                     tag : 'a',
4730                     href : this.href ? this.href : '#',
4731                     html : this.html ? this.html : ''
4732                 }
4733             ]
4734         };
4735         
4736         if(this.cls){
4737             cfg.cls = this.cls;
4738         }
4739         
4740         if(this.disabled){
4741             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4742         }
4743         
4744         if(this.active){
4745             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4746         }
4747         
4748         return cfg;
4749     },
4750     
4751     initEvents: function() {
4752         
4753         this.el.on('click', this.onClick, this);
4754         
4755     },
4756     onClick : function(e)
4757     {
4758         Roo.log('PaginationItem on click ');
4759         if(this.preventDefault){
4760             e.preventDefault();
4761         }
4762         
4763         if(this.disabled){
4764             return;
4765         }
4766         
4767         this.fireEvent('click', this, e);
4768     }
4769    
4770 });
4771
4772  
4773
4774  /*
4775  * - LGPL
4776  *
4777  * slider
4778  * 
4779  */
4780
4781
4782 /**
4783  * @class Roo.bootstrap.Slider
4784  * @extends Roo.bootstrap.Component
4785  * Bootstrap Slider class
4786  *    
4787  * @constructor
4788  * Create a new Slider
4789  * @param {Object} config The config object
4790  */
4791
4792 Roo.bootstrap.Slider = function(config){
4793     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4794 };
4795
4796 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4797     
4798     getAutoCreate : function(){
4799         
4800         var cfg = {
4801             tag: 'div',
4802             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4803             cn: [
4804                 {
4805                     tag: 'a',
4806                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4807                 }
4808             ]
4809         };
4810         
4811         return cfg;
4812     }
4813    
4814 });
4815
4816  /*
4817  * Based on:
4818  * Ext JS Library 1.1.1
4819  * Copyright(c) 2006-2007, Ext JS, LLC.
4820  *
4821  * Originally Released Under LGPL - original licence link has changed is not relivant.
4822  *
4823  * Fork - LGPL
4824  * <script type="text/javascript">
4825  */
4826  
4827
4828 /**
4829  * @class Roo.grid.ColumnModel
4830  * @extends Roo.util.Observable
4831  * This is the default implementation of a ColumnModel used by the Grid. It defines
4832  * the columns in the grid.
4833  * <br>Usage:<br>
4834  <pre><code>
4835  var colModel = new Roo.grid.ColumnModel([
4836         {header: "Ticker", width: 60, sortable: true, locked: true},
4837         {header: "Company Name", width: 150, sortable: true},
4838         {header: "Market Cap.", width: 100, sortable: true},
4839         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4840         {header: "Employees", width: 100, sortable: true, resizable: false}
4841  ]);
4842  </code></pre>
4843  * <p>
4844  
4845  * The config options listed for this class are options which may appear in each
4846  * individual column definition.
4847  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4848  * @constructor
4849  * @param {Object} config An Array of column config objects. See this class's
4850  * config objects for details.
4851 */
4852 Roo.grid.ColumnModel = function(config){
4853         /**
4854      * The config passed into the constructor
4855      */
4856     this.config = config;
4857     this.lookup = {};
4858
4859     // if no id, create one
4860     // if the column does not have a dataIndex mapping,
4861     // map it to the order it is in the config
4862     for(var i = 0, len = config.length; i < len; i++){
4863         var c = config[i];
4864         if(typeof c.dataIndex == "undefined"){
4865             c.dataIndex = i;
4866         }
4867         if(typeof c.renderer == "string"){
4868             c.renderer = Roo.util.Format[c.renderer];
4869         }
4870         if(typeof c.id == "undefined"){
4871             c.id = Roo.id();
4872         }
4873         if(c.editor && c.editor.xtype){
4874             c.editor  = Roo.factory(c.editor, Roo.grid);
4875         }
4876         if(c.editor && c.editor.isFormField){
4877             c.editor = new Roo.grid.GridEditor(c.editor);
4878         }
4879         this.lookup[c.id] = c;
4880     }
4881
4882     /**
4883      * The width of columns which have no width specified (defaults to 100)
4884      * @type Number
4885      */
4886     this.defaultWidth = 100;
4887
4888     /**
4889      * Default sortable of columns which have no sortable specified (defaults to false)
4890      * @type Boolean
4891      */
4892     this.defaultSortable = false;
4893
4894     this.addEvents({
4895         /**
4896              * @event widthchange
4897              * Fires when the width of a column changes.
4898              * @param {ColumnModel} this
4899              * @param {Number} columnIndex The column index
4900              * @param {Number} newWidth The new width
4901              */
4902             "widthchange": true,
4903         /**
4904              * @event headerchange
4905              * Fires when the text of a header changes.
4906              * @param {ColumnModel} this
4907              * @param {Number} columnIndex The column index
4908              * @param {Number} newText The new header text
4909              */
4910             "headerchange": true,
4911         /**
4912              * @event hiddenchange
4913              * Fires when a column is hidden or "unhidden".
4914              * @param {ColumnModel} this
4915              * @param {Number} columnIndex The column index
4916              * @param {Boolean} hidden true if hidden, false otherwise
4917              */
4918             "hiddenchange": true,
4919             /**
4920          * @event columnmoved
4921          * Fires when a column is moved.
4922          * @param {ColumnModel} this
4923          * @param {Number} oldIndex
4924          * @param {Number} newIndex
4925          */
4926         "columnmoved" : true,
4927         /**
4928          * @event columlockchange
4929          * Fires when a column's locked state is changed
4930          * @param {ColumnModel} this
4931          * @param {Number} colIndex
4932          * @param {Boolean} locked true if locked
4933          */
4934         "columnlockchange" : true
4935     });
4936     Roo.grid.ColumnModel.superclass.constructor.call(this);
4937 };
4938 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4939     /**
4940      * @cfg {String} header The header text to display in the Grid view.
4941      */
4942     /**
4943      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4944      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4945      * specified, the column's index is used as an index into the Record's data Array.
4946      */
4947     /**
4948      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4949      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4950      */
4951     /**
4952      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4953      * Defaults to the value of the {@link #defaultSortable} property.
4954      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4955      */
4956     /**
4957      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4958      */
4959     /**
4960      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4961      */
4962     /**
4963      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4964      */
4965     /**
4966      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4967      */
4968     /**
4969      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4970      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4971      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4972      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4973      */
4974        /**
4975      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4976      */
4977     /**
4978      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4979      */
4980     /**
4981      * @cfg {String} cursor (Optional)
4982      */
4983     /**
4984      * @cfg {String} tooltip (Optional)
4985      */
4986     /**
4987      * @cfg {Number} xs (Optional)
4988      */
4989     /**
4990      * @cfg {Number} sm (Optional)
4991      */
4992     /**
4993      * @cfg {Number} md (Optional)
4994      */
4995     /**
4996      * @cfg {Number} lg (Optional)
4997      */
4998     /**
4999      * Returns the id of the column at the specified index.
5000      * @param {Number} index The column index
5001      * @return {String} the id
5002      */
5003     getColumnId : function(index){
5004         return this.config[index].id;
5005     },
5006
5007     /**
5008      * Returns the column for a specified id.
5009      * @param {String} id The column id
5010      * @return {Object} the column
5011      */
5012     getColumnById : function(id){
5013         return this.lookup[id];
5014     },
5015
5016     
5017     /**
5018      * Returns the column for a specified dataIndex.
5019      * @param {String} dataIndex The column dataIndex
5020      * @return {Object|Boolean} the column or false if not found
5021      */
5022     getColumnByDataIndex: function(dataIndex){
5023         var index = this.findColumnIndex(dataIndex);
5024         return index > -1 ? this.config[index] : false;
5025     },
5026     
5027     /**
5028      * Returns the index for a specified column id.
5029      * @param {String} id The column id
5030      * @return {Number} the index, or -1 if not found
5031      */
5032     getIndexById : function(id){
5033         for(var i = 0, len = this.config.length; i < len; i++){
5034             if(this.config[i].id == id){
5035                 return i;
5036             }
5037         }
5038         return -1;
5039     },
5040     
5041     /**
5042      * Returns the index for a specified column dataIndex.
5043      * @param {String} dataIndex The column dataIndex
5044      * @return {Number} the index, or -1 if not found
5045      */
5046     
5047     findColumnIndex : function(dataIndex){
5048         for(var i = 0, len = this.config.length; i < len; i++){
5049             if(this.config[i].dataIndex == dataIndex){
5050                 return i;
5051             }
5052         }
5053         return -1;
5054     },
5055     
5056     
5057     moveColumn : function(oldIndex, newIndex){
5058         var c = this.config[oldIndex];
5059         this.config.splice(oldIndex, 1);
5060         this.config.splice(newIndex, 0, c);
5061         this.dataMap = null;
5062         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5063     },
5064
5065     isLocked : function(colIndex){
5066         return this.config[colIndex].locked === true;
5067     },
5068
5069     setLocked : function(colIndex, value, suppressEvent){
5070         if(this.isLocked(colIndex) == value){
5071             return;
5072         }
5073         this.config[colIndex].locked = value;
5074         if(!suppressEvent){
5075             this.fireEvent("columnlockchange", this, colIndex, value);
5076         }
5077     },
5078
5079     getTotalLockedWidth : function(){
5080         var totalWidth = 0;
5081         for(var i = 0; i < this.config.length; i++){
5082             if(this.isLocked(i) && !this.isHidden(i)){
5083                 this.totalWidth += this.getColumnWidth(i);
5084             }
5085         }
5086         return totalWidth;
5087     },
5088
5089     getLockedCount : function(){
5090         for(var i = 0, len = this.config.length; i < len; i++){
5091             if(!this.isLocked(i)){
5092                 return i;
5093             }
5094         }
5095     },
5096
5097     /**
5098      * Returns the number of columns.
5099      * @return {Number}
5100      */
5101     getColumnCount : function(visibleOnly){
5102         if(visibleOnly === true){
5103             var c = 0;
5104             for(var i = 0, len = this.config.length; i < len; i++){
5105                 if(!this.isHidden(i)){
5106                     c++;
5107                 }
5108             }
5109             return c;
5110         }
5111         return this.config.length;
5112     },
5113
5114     /**
5115      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5116      * @param {Function} fn
5117      * @param {Object} scope (optional)
5118      * @return {Array} result
5119      */
5120     getColumnsBy : function(fn, scope){
5121         var r = [];
5122         for(var i = 0, len = this.config.length; i < len; i++){
5123             var c = this.config[i];
5124             if(fn.call(scope||this, c, i) === true){
5125                 r[r.length] = c;
5126             }
5127         }
5128         return r;
5129     },
5130
5131     /**
5132      * Returns true if the specified column is sortable.
5133      * @param {Number} col The column index
5134      * @return {Boolean}
5135      */
5136     isSortable : function(col){
5137         if(typeof this.config[col].sortable == "undefined"){
5138             return this.defaultSortable;
5139         }
5140         return this.config[col].sortable;
5141     },
5142
5143     /**
5144      * Returns the rendering (formatting) function defined for the column.
5145      * @param {Number} col The column index.
5146      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5147      */
5148     getRenderer : function(col){
5149         if(!this.config[col].renderer){
5150             return Roo.grid.ColumnModel.defaultRenderer;
5151         }
5152         return this.config[col].renderer;
5153     },
5154
5155     /**
5156      * Sets the rendering (formatting) function for a column.
5157      * @param {Number} col The column index
5158      * @param {Function} fn The function to use to process the cell's raw data
5159      * to return HTML markup for the grid view. The render function is called with
5160      * the following parameters:<ul>
5161      * <li>Data value.</li>
5162      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5163      * <li>css A CSS style string to apply to the table cell.</li>
5164      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5165      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5166      * <li>Row index</li>
5167      * <li>Column index</li>
5168      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5169      */
5170     setRenderer : function(col, fn){
5171         this.config[col].renderer = fn;
5172     },
5173
5174     /**
5175      * Returns the width for the specified column.
5176      * @param {Number} col The column index
5177      * @return {Number}
5178      */
5179     getColumnWidth : function(col){
5180         return this.config[col].width * 1 || this.defaultWidth;
5181     },
5182
5183     /**
5184      * Sets the width for a column.
5185      * @param {Number} col The column index
5186      * @param {Number} width The new width
5187      */
5188     setColumnWidth : function(col, width, suppressEvent){
5189         this.config[col].width = width;
5190         this.totalWidth = null;
5191         if(!suppressEvent){
5192              this.fireEvent("widthchange", this, col, width);
5193         }
5194     },
5195
5196     /**
5197      * Returns the total width of all columns.
5198      * @param {Boolean} includeHidden True to include hidden column widths
5199      * @return {Number}
5200      */
5201     getTotalWidth : function(includeHidden){
5202         if(!this.totalWidth){
5203             this.totalWidth = 0;
5204             for(var i = 0, len = this.config.length; i < len; i++){
5205                 if(includeHidden || !this.isHidden(i)){
5206                     this.totalWidth += this.getColumnWidth(i);
5207                 }
5208             }
5209         }
5210         return this.totalWidth;
5211     },
5212
5213     /**
5214      * Returns the header for the specified column.
5215      * @param {Number} col The column index
5216      * @return {String}
5217      */
5218     getColumnHeader : function(col){
5219         return this.config[col].header;
5220     },
5221
5222     /**
5223      * Sets the header for a column.
5224      * @param {Number} col The column index
5225      * @param {String} header The new header
5226      */
5227     setColumnHeader : function(col, header){
5228         this.config[col].header = header;
5229         this.fireEvent("headerchange", this, col, header);
5230     },
5231
5232     /**
5233      * Returns the tooltip for the specified column.
5234      * @param {Number} col The column index
5235      * @return {String}
5236      */
5237     getColumnTooltip : function(col){
5238             return this.config[col].tooltip;
5239     },
5240     /**
5241      * Sets the tooltip for a column.
5242      * @param {Number} col The column index
5243      * @param {String} tooltip The new tooltip
5244      */
5245     setColumnTooltip : function(col, tooltip){
5246             this.config[col].tooltip = tooltip;
5247     },
5248
5249     /**
5250      * Returns the dataIndex for the specified column.
5251      * @param {Number} col The column index
5252      * @return {Number}
5253      */
5254     getDataIndex : function(col){
5255         return this.config[col].dataIndex;
5256     },
5257
5258     /**
5259      * Sets the dataIndex for a column.
5260      * @param {Number} col The column index
5261      * @param {Number} dataIndex The new dataIndex
5262      */
5263     setDataIndex : function(col, dataIndex){
5264         this.config[col].dataIndex = dataIndex;
5265     },
5266
5267     
5268     
5269     /**
5270      * Returns true if the cell is editable.
5271      * @param {Number} colIndex The column index
5272      * @param {Number} rowIndex The row index
5273      * @return {Boolean}
5274      */
5275     isCellEditable : function(colIndex, rowIndex){
5276         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5277     },
5278
5279     /**
5280      * Returns the editor defined for the cell/column.
5281      * return false or null to disable editing.
5282      * @param {Number} colIndex The column index
5283      * @param {Number} rowIndex The row index
5284      * @return {Object}
5285      */
5286     getCellEditor : function(colIndex, rowIndex){
5287         return this.config[colIndex].editor;
5288     },
5289
5290     /**
5291      * Sets if a column is editable.
5292      * @param {Number} col The column index
5293      * @param {Boolean} editable True if the column is editable
5294      */
5295     setEditable : function(col, editable){
5296         this.config[col].editable = editable;
5297     },
5298
5299
5300     /**
5301      * Returns true if the column is hidden.
5302      * @param {Number} colIndex The column index
5303      * @return {Boolean}
5304      */
5305     isHidden : function(colIndex){
5306         return this.config[colIndex].hidden;
5307     },
5308
5309
5310     /**
5311      * Returns true if the column width cannot be changed
5312      */
5313     isFixed : function(colIndex){
5314         return this.config[colIndex].fixed;
5315     },
5316
5317     /**
5318      * Returns true if the column can be resized
5319      * @return {Boolean}
5320      */
5321     isResizable : function(colIndex){
5322         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5323     },
5324     /**
5325      * Sets if a column is hidden.
5326      * @param {Number} colIndex The column index
5327      * @param {Boolean} hidden True if the column is hidden
5328      */
5329     setHidden : function(colIndex, hidden){
5330         this.config[colIndex].hidden = hidden;
5331         this.totalWidth = null;
5332         this.fireEvent("hiddenchange", this, colIndex, hidden);
5333     },
5334
5335     /**
5336      * Sets the editor for a column.
5337      * @param {Number} col The column index
5338      * @param {Object} editor The editor object
5339      */
5340     setEditor : function(col, editor){
5341         this.config[col].editor = editor;
5342     }
5343 });
5344
5345 Roo.grid.ColumnModel.defaultRenderer = function(value){
5346         if(typeof value == "string" && value.length < 1){
5347             return "&#160;";
5348         }
5349         return value;
5350 };
5351
5352 // Alias for backwards compatibility
5353 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5354 /*
5355  * Based on:
5356  * Ext JS Library 1.1.1
5357  * Copyright(c) 2006-2007, Ext JS, LLC.
5358  *
5359  * Originally Released Under LGPL - original licence link has changed is not relivant.
5360  *
5361  * Fork - LGPL
5362  * <script type="text/javascript">
5363  */
5364  
5365 /**
5366  * @class Roo.LoadMask
5367  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5368  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5369  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5370  * element's UpdateManager load indicator and will be destroyed after the initial load.
5371  * @constructor
5372  * Create a new LoadMask
5373  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5374  * @param {Object} config The config object
5375  */
5376 Roo.LoadMask = function(el, config){
5377     this.el = Roo.get(el);
5378     Roo.apply(this, config);
5379     if(this.store){
5380         this.store.on('beforeload', this.onBeforeLoad, this);
5381         this.store.on('load', this.onLoad, this);
5382         this.store.on('loadexception', this.onLoadException, this);
5383         this.removeMask = false;
5384     }else{
5385         var um = this.el.getUpdateManager();
5386         um.showLoadIndicator = false; // disable the default indicator
5387         um.on('beforeupdate', this.onBeforeLoad, this);
5388         um.on('update', this.onLoad, this);
5389         um.on('failure', this.onLoad, this);
5390         this.removeMask = true;
5391     }
5392 };
5393
5394 Roo.LoadMask.prototype = {
5395     /**
5396      * @cfg {Boolean} removeMask
5397      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5398      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5399      */
5400     /**
5401      * @cfg {String} msg
5402      * The text to display in a centered loading message box (defaults to 'Loading...')
5403      */
5404     msg : 'Loading...',
5405     /**
5406      * @cfg {String} msgCls
5407      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5408      */
5409     msgCls : 'x-mask-loading',
5410
5411     /**
5412      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5413      * @type Boolean
5414      */
5415     disabled: false,
5416
5417     /**
5418      * Disables the mask to prevent it from being displayed
5419      */
5420     disable : function(){
5421        this.disabled = true;
5422     },
5423
5424     /**
5425      * Enables the mask so that it can be displayed
5426      */
5427     enable : function(){
5428         this.disabled = false;
5429     },
5430     
5431     onLoadException : function()
5432     {
5433         Roo.log(arguments);
5434         
5435         if (typeof(arguments[3]) != 'undefined') {
5436             Roo.MessageBox.alert("Error loading",arguments[3]);
5437         } 
5438         /*
5439         try {
5440             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5441                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5442             }   
5443         } catch(e) {
5444             
5445         }
5446         */
5447     
5448         
5449         
5450         this.el.unmask(this.removeMask);
5451     },
5452     // private
5453     onLoad : function()
5454     {
5455         this.el.unmask(this.removeMask);
5456     },
5457
5458     // private
5459     onBeforeLoad : function(){
5460         if(!this.disabled){
5461             this.el.mask(this.msg, this.msgCls);
5462         }
5463     },
5464
5465     // private
5466     destroy : function(){
5467         if(this.store){
5468             this.store.un('beforeload', this.onBeforeLoad, this);
5469             this.store.un('load', this.onLoad, this);
5470             this.store.un('loadexception', this.onLoadException, this);
5471         }else{
5472             var um = this.el.getUpdateManager();
5473             um.un('beforeupdate', this.onBeforeLoad, this);
5474             um.un('update', this.onLoad, this);
5475             um.un('failure', this.onLoad, this);
5476         }
5477     }
5478 };/*
5479  * - LGPL
5480  *
5481  * table
5482  * 
5483  */
5484
5485 /**
5486  * @class Roo.bootstrap.Table
5487  * @extends Roo.bootstrap.Component
5488  * Bootstrap Table class
5489  * @cfg {String} cls table class
5490  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5491  * @cfg {String} bgcolor Specifies the background color for a table
5492  * @cfg {Number} border Specifies whether the table cells should have borders or not
5493  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5494  * @cfg {Number} cellspacing Specifies the space between cells
5495  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5496  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5497  * @cfg {String} sortable Specifies that the table should be sortable
5498  * @cfg {String} summary Specifies a summary of the content of a table
5499  * @cfg {Number} width Specifies the width of a table
5500  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5501  * 
5502  * @cfg {boolean} striped Should the rows be alternative striped
5503  * @cfg {boolean} bordered Add borders to the table
5504  * @cfg {boolean} hover Add hover highlighting
5505  * @cfg {boolean} condensed Format condensed
5506  * @cfg {boolean} responsive Format condensed
5507  * @cfg {Boolean} loadMask (true|false) default false
5508  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5509  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5510  * @cfg {Boolean} rowSelection (true|false) default false
5511  * @cfg {Boolean} cellSelection (true|false) default false
5512  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5513  
5514  * 
5515  * @constructor
5516  * Create a new Table
5517  * @param {Object} config The config object
5518  */
5519
5520 Roo.bootstrap.Table = function(config){
5521     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5522     
5523     // BC...
5524     this.rowSelection = (typeof(config.RowSelection) != 'undefined') ? config.RowSelection : this.rowSelection;
5525     this.cellSelection = (typeof(config.CellSelection) != 'undefined') ? config.CellSelection : this.cellSelection;
5526     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5527     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5528     
5529     
5530     if (this.sm) {
5531         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5532         this.sm = this.selModel;
5533         this.sm.xmodule = this.xmodule || false;
5534     }
5535     if (this.cm && typeof(this.cm.config) == 'undefined') {
5536         this.colModel = new Roo.grid.ColumnModel(this.cm);
5537         this.cm = this.colModel;
5538         this.cm.xmodule = this.xmodule || false;
5539     }
5540     if (this.store) {
5541         this.store= Roo.factory(this.store, Roo.data);
5542         this.ds = this.store;
5543         this.ds.xmodule = this.xmodule || false;
5544          
5545     }
5546     if (this.footer && this.store) {
5547         this.footer.dataSource = this.ds;
5548         this.footer = Roo.factory(this.footer);
5549     }
5550     
5551     /** @private */
5552     this.addEvents({
5553         /**
5554          * @event cellclick
5555          * Fires when a cell is clicked
5556          * @param {Roo.bootstrap.Table} this
5557          * @param {Roo.Element} el
5558          * @param {Number} rowIndex
5559          * @param {Number} columnIndex
5560          * @param {Roo.EventObject} e
5561          */
5562         "cellclick" : true,
5563         /**
5564          * @event celldblclick
5565          * Fires when a cell is double clicked
5566          * @param {Roo.bootstrap.Table} this
5567          * @param {Roo.Element} el
5568          * @param {Number} rowIndex
5569          * @param {Number} columnIndex
5570          * @param {Roo.EventObject} e
5571          */
5572         "celldblclick" : true,
5573         /**
5574          * @event rowclick
5575          * Fires when a row is clicked
5576          * @param {Roo.bootstrap.Table} this
5577          * @param {Roo.Element} el
5578          * @param {Number} rowIndex
5579          * @param {Roo.EventObject} e
5580          */
5581         "rowclick" : true,
5582         /**
5583          * @event rowdblclick
5584          * Fires when a row is double clicked
5585          * @param {Roo.bootstrap.Table} this
5586          * @param {Roo.Element} el
5587          * @param {Number} rowIndex
5588          * @param {Roo.EventObject} e
5589          */
5590         "rowdblclick" : true,
5591         /**
5592          * @event mouseover
5593          * Fires when a mouseover occur
5594          * @param {Roo.bootstrap.Table} this
5595          * @param {Roo.Element} el
5596          * @param {Number} rowIndex
5597          * @param {Number} columnIndex
5598          * @param {Roo.EventObject} e
5599          */
5600         "mouseover" : true,
5601         /**
5602          * @event mouseout
5603          * Fires when a mouseout occur
5604          * @param {Roo.bootstrap.Table} this
5605          * @param {Roo.Element} el
5606          * @param {Number} rowIndex
5607          * @param {Number} columnIndex
5608          * @param {Roo.EventObject} e
5609          */
5610         "mouseout" : true,
5611         /**
5612          * @event rowclass
5613          * Fires when a row is rendered, so you can change add a style to it.
5614          * @param {Roo.bootstrap.Table} this
5615          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5616          */
5617         'rowclass' : true,
5618           /**
5619          * @event rowsrendered
5620          * Fires when all the  rows have been rendered
5621          * @param {Roo.bootstrap.Table} this
5622          */
5623         'rowsrendered' : true
5624         
5625     });
5626 };
5627
5628 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5629     
5630     cls: false,
5631     align: false,
5632     bgcolor: false,
5633     border: false,
5634     cellpadding: false,
5635     cellspacing: false,
5636     frame: false,
5637     rules: false,
5638     sortable: false,
5639     summary: false,
5640     width: false,
5641     striped : false,
5642     bordered: false,
5643     hover:  false,
5644     condensed : false,
5645     responsive : false,
5646     sm : false,
5647     cm : false,
5648     store : false,
5649     loadMask : false,
5650     footerShow : true,
5651     headerShow : true,
5652   
5653     rowSelection : false,
5654     cellSelection : false,
5655     layout : false,
5656     
5657     // Roo.Element - the tbody
5658     mainBody: false, 
5659     
5660     getAutoCreate : function(){
5661         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5662         
5663         cfg = {
5664             tag: 'table',
5665             cls : 'table',
5666             cn : []
5667         };
5668             
5669         if (this.striped) {
5670             cfg.cls += ' table-striped';
5671         }
5672         
5673         if (this.hover) {
5674             cfg.cls += ' table-hover';
5675         }
5676         if (this.bordered) {
5677             cfg.cls += ' table-bordered';
5678         }
5679         if (this.condensed) {
5680             cfg.cls += ' table-condensed';
5681         }
5682         if (this.responsive) {
5683             cfg.cls += ' table-responsive';
5684         }
5685         
5686         if (this.cls) {
5687             cfg.cls+=  ' ' +this.cls;
5688         }
5689         
5690         // this lot should be simplifed...
5691         
5692         if (this.align) {
5693             cfg.align=this.align;
5694         }
5695         if (this.bgcolor) {
5696             cfg.bgcolor=this.bgcolor;
5697         }
5698         if (this.border) {
5699             cfg.border=this.border;
5700         }
5701         if (this.cellpadding) {
5702             cfg.cellpadding=this.cellpadding;
5703         }
5704         if (this.cellspacing) {
5705             cfg.cellspacing=this.cellspacing;
5706         }
5707         if (this.frame) {
5708             cfg.frame=this.frame;
5709         }
5710         if (this.rules) {
5711             cfg.rules=this.rules;
5712         }
5713         if (this.sortable) {
5714             cfg.sortable=this.sortable;
5715         }
5716         if (this.summary) {
5717             cfg.summary=this.summary;
5718         }
5719         if (this.width) {
5720             cfg.width=this.width;
5721         }
5722         if (this.layout) {
5723             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5724         }
5725         
5726         if(this.store || this.cm){
5727             if(this.headerShow){
5728                 cfg.cn.push(this.renderHeader());
5729             }
5730             
5731             cfg.cn.push(this.renderBody());
5732             
5733             if(this.footerShow){
5734                 cfg.cn.push(this.renderFooter());
5735             }
5736             
5737             cfg.cls+=  ' TableGrid';
5738         }
5739         
5740         return { cn : [ cfg ] };
5741     },
5742     
5743     initEvents : function()
5744     {   
5745         if(!this.store || !this.cm){
5746             return;
5747         }
5748         
5749         //Roo.log('initEvents with ds!!!!');
5750         
5751         this.mainBody = this.el.select('tbody', true).first();
5752         
5753         
5754         var _this = this;
5755         
5756         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5757             e.on('click', _this.sort, _this);
5758         });
5759         
5760         this.el.on("click", this.onClick, this);
5761         this.el.on("dblclick", this.onDblClick, this);
5762         
5763         // why is this done????? = it breaks dialogs??
5764         //this.parent().el.setStyle('position', 'relative');
5765         
5766         
5767         if (this.footer) {
5768             this.footer.parentId = this.id;
5769             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5770         }
5771         
5772         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5773         
5774         this.store.on('load', this.onLoad, this);
5775         this.store.on('beforeload', this.onBeforeLoad, this);
5776         this.store.on('update', this.onUpdate, this);
5777         this.store.on('add', this.onAdd, this);
5778         
5779     },
5780     
5781     onMouseover : function(e, el)
5782     {
5783         var cell = Roo.get(el);
5784         
5785         if(!cell){
5786             return;
5787         }
5788         
5789         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5790             cell = cell.findParent('td', false, true);
5791         }
5792         
5793         var row = cell.findParent('tr', false, true);
5794         var cellIndex = cell.dom.cellIndex;
5795         var rowIndex = row.dom.rowIndex - 1; // start from 0
5796         
5797         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5798         
5799     },
5800     
5801     onMouseout : function(e, el)
5802     {
5803         var cell = Roo.get(el);
5804         
5805         if(!cell){
5806             return;
5807         }
5808         
5809         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5810             cell = cell.findParent('td', false, true);
5811         }
5812         
5813         var row = cell.findParent('tr', false, true);
5814         var cellIndex = cell.dom.cellIndex;
5815         var rowIndex = row.dom.rowIndex - 1; // start from 0
5816         
5817         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5818         
5819     },
5820     
5821     onClick : function(e, el)
5822     {
5823         var cell = Roo.get(el);
5824         
5825         if(!cell || (!this.cellSelection && !this.rowSelection)){
5826             return;
5827         }
5828         
5829         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5830             cell = cell.findParent('td', false, true);
5831         }
5832         
5833         if(!cell || typeof(cell) == 'undefined'){
5834             return;
5835         }
5836         
5837         var row = cell.findParent('tr', false, true);
5838         
5839         if(!row || typeof(row) == 'undefined'){
5840             return;
5841         }
5842         
5843         var cellIndex = cell.dom.cellIndex;
5844         var rowIndex = this.getRowIndex(row);
5845         
5846         // why??? - should these not be based on SelectionModel?
5847         if(this.cellSelection){
5848             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5849         }
5850         
5851         if(this.rowSelection){
5852             this.fireEvent('rowclick', this, row, rowIndex, e);
5853         }
5854         
5855         
5856     },
5857     
5858     onDblClick : function(e,el)
5859     {
5860         var cell = Roo.get(el);
5861         
5862         if(!cell || (!this.CellSelection && !this.RowSelection)){
5863             return;
5864         }
5865         
5866         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5867             cell = cell.findParent('td', false, true);
5868         }
5869         
5870         if(!cell || typeof(cell) == 'undefined'){
5871             return;
5872         }
5873         
5874         var row = cell.findParent('tr', false, true);
5875         
5876         if(!row || typeof(row) == 'undefined'){
5877             return;
5878         }
5879         
5880         var cellIndex = cell.dom.cellIndex;
5881         var rowIndex = this.getRowIndex(row);
5882         
5883         if(this.CellSelection){
5884             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5885         }
5886         
5887         if(this.RowSelection){
5888             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5889         }
5890     },
5891     
5892     sort : function(e,el)
5893     {
5894         var col = Roo.get(el);
5895         
5896         if(!col.hasClass('sortable')){
5897             return;
5898         }
5899         
5900         var sort = col.attr('sort');
5901         var dir = 'ASC';
5902         
5903         if(col.hasClass('glyphicon-arrow-up')){
5904             dir = 'DESC';
5905         }
5906         
5907         this.store.sortInfo = {field : sort, direction : dir};
5908         
5909         if (this.footer) {
5910             Roo.log("calling footer first");
5911             this.footer.onClick('first');
5912         } else {
5913         
5914             this.store.load({ params : { start : 0 } });
5915         }
5916     },
5917     
5918     renderHeader : function()
5919     {
5920         var header = {
5921             tag: 'thead',
5922             cn : []
5923         };
5924         
5925         var cm = this.cm;
5926         
5927         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5928             
5929             var config = cm.config[i];
5930             
5931             var c = {
5932                 tag: 'th',
5933                 style : '',
5934                 html: cm.getColumnHeader(i)
5935             };
5936             
5937             var hh = '';
5938             
5939             if(typeof(config.lgHeader) != 'undefined'){
5940                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
5941             }
5942             
5943             if(typeof(config.mdHeader) != 'undefined'){
5944                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
5945             }
5946             
5947             if(typeof(config.smHeader) != 'undefined'){
5948                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
5949             }
5950             
5951             if(typeof(config.xsHeader) != 'undefined'){
5952                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
5953             }
5954             
5955             if(hh.length){
5956                 c.html = hh;
5957             }
5958             
5959             if(typeof(config.tooltip) != 'undefined'){
5960                 c.tooltip = config.tooltip;
5961             }
5962             
5963             if(typeof(config.colspan) != 'undefined'){
5964                 c.colspan = config.colspan;
5965             }
5966             
5967             if(typeof(config.hidden) != 'undefined' && config.hidden){
5968                 c.style += ' display:none;';
5969             }
5970             
5971             if(typeof(config.dataIndex) != 'undefined'){
5972                 c.sort = config.dataIndex;
5973             }
5974             
5975             if(typeof(config.sortable) != 'undefined' && config.sortable){
5976                 c.cls = 'sortable';
5977             }
5978             
5979             if(typeof(config.align) != 'undefined' && config.align.length){
5980                 c.style += ' text-align:' + config.align + ';';
5981             }
5982             
5983             if(typeof(config.width) != 'undefined'){
5984                 c.style += ' width:' + config.width + 'px;';
5985             }
5986             
5987             if(typeof(config.cls) != 'undefined'){
5988                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
5989             }
5990             
5991             ['xs','sm','md','lg'].map(function(size){
5992                 
5993                 if(typeof(config[size]) == 'undefined'){
5994                     return;
5995                 }
5996                 
5997                 if (!config[size]) { // 0 = hidden
5998                     cfg.cls += ' hidden-' + size;
5999                     return;
6000                 }
6001                 
6002                 cfg.cls += ' col-' + size + '-' + config[size];
6003
6004             });
6005             
6006             header.cn.push(c)
6007         }
6008         
6009         return header;
6010     },
6011     
6012     renderBody : function()
6013     {
6014         var body = {
6015             tag: 'tbody',
6016             cn : [
6017                 {
6018                     tag: 'tr',
6019                     cn : [
6020                         {
6021                             tag : 'td',
6022                             colspan :  this.cm.getColumnCount()
6023                         }
6024                     ]
6025                 }
6026             ]
6027         };
6028         
6029         return body;
6030     },
6031     
6032     renderFooter : function()
6033     {
6034         var footer = {
6035             tag: 'tfoot',
6036             cn : [
6037                 {
6038                     tag: 'tr',
6039                     cn : [
6040                         {
6041                             tag : 'td',
6042                             colspan :  this.cm.getColumnCount()
6043                         }
6044                     ]
6045                 }
6046             ]
6047         };
6048         
6049         return footer;
6050     },
6051     
6052     
6053     
6054     onLoad : function()
6055     {
6056         Roo.log('ds onload');
6057         this.clear();
6058         
6059         var _this = this;
6060         var cm = this.cm;
6061         var ds = this.store;
6062         
6063         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6064             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
6065             
6066             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6067                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
6068             }
6069             
6070             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6071                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
6072             }
6073         });
6074         
6075         var tbody =  this.mainBody;
6076               
6077         if(ds.getCount() > 0){
6078             ds.data.each(function(d,rowIndex){
6079                 var row =  this.renderRow(cm, ds, rowIndex);
6080                 
6081                 tbody.createChild(row);
6082                 
6083                 var _this = this;
6084                 
6085                 if(row.cellObjects.length){
6086                     Roo.each(row.cellObjects, function(r){
6087                         _this.renderCellObject(r);
6088                     })
6089                 }
6090                 
6091             }, this);
6092         }
6093         
6094         Roo.each(this.el.select('tbody td', true).elements, function(e){
6095             e.on('mouseover', _this.onMouseover, _this);
6096         });
6097         
6098         Roo.each(this.el.select('tbody td', true).elements, function(e){
6099             e.on('mouseout', _this.onMouseout, _this);
6100         });
6101         this.fireEvent('rowsrendered', this);
6102         //if(this.loadMask){
6103         //    this.maskEl.hide();
6104         //}
6105     },
6106     
6107     
6108     onUpdate : function(ds,record)
6109     {
6110         this.refreshRow(record);
6111     },
6112     
6113     onRemove : function(ds, record, index, isUpdate){
6114         if(isUpdate !== true){
6115             this.fireEvent("beforerowremoved", this, index, record);
6116         }
6117         var bt = this.mainBody.dom;
6118         
6119         var rows = this.el.select('tbody > tr', true).elements;
6120         
6121         if(typeof(rows[index]) != 'undefined'){
6122             bt.removeChild(rows[index].dom);
6123         }
6124         
6125 //        if(bt.rows[index]){
6126 //            bt.removeChild(bt.rows[index]);
6127 //        }
6128         
6129         if(isUpdate !== true){
6130             //this.stripeRows(index);
6131             //this.syncRowHeights(index, index);
6132             //this.layout();
6133             this.fireEvent("rowremoved", this, index, record);
6134         }
6135     },
6136     
6137     onAdd : function(ds, records, rowIndex)
6138     {
6139         //Roo.log('on Add called');
6140         // - note this does not handle multiple adding very well..
6141         var bt = this.mainBody.dom;
6142         for (var i =0 ; i < records.length;i++) {
6143             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6144             //Roo.log(records[i]);
6145             //Roo.log(this.store.getAt(rowIndex+i));
6146             this.insertRow(this.store, rowIndex + i, false);
6147             return;
6148         }
6149         
6150     },
6151     
6152     
6153     refreshRow : function(record){
6154         var ds = this.store, index;
6155         if(typeof record == 'number'){
6156             index = record;
6157             record = ds.getAt(index);
6158         }else{
6159             index = ds.indexOf(record);
6160         }
6161         this.insertRow(ds, index, true);
6162         this.onRemove(ds, record, index+1, true);
6163         //this.syncRowHeights(index, index);
6164         //this.layout();
6165         this.fireEvent("rowupdated", this, index, record);
6166     },
6167     
6168     insertRow : function(dm, rowIndex, isUpdate){
6169         
6170         if(!isUpdate){
6171             this.fireEvent("beforerowsinserted", this, rowIndex);
6172         }
6173             //var s = this.getScrollState();
6174         var row = this.renderRow(this.cm, this.store, rowIndex);
6175         // insert before rowIndex..
6176         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6177         
6178         var _this = this;
6179                 
6180         if(row.cellObjects.length){
6181             Roo.each(row.cellObjects, function(r){
6182                 _this.renderCellObject(r);
6183             })
6184         }
6185             
6186         if(!isUpdate){
6187             this.fireEvent("rowsinserted", this, rowIndex);
6188             //this.syncRowHeights(firstRow, lastRow);
6189             //this.stripeRows(firstRow);
6190             //this.layout();
6191         }
6192         
6193     },
6194     
6195     
6196     getRowDom : function(rowIndex)
6197     {
6198         var rows = this.el.select('tbody > tr', true).elements;
6199         
6200         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6201         
6202     },
6203     // returns the object tree for a tr..
6204   
6205     
6206     renderRow : function(cm, ds, rowIndex) 
6207     {
6208         
6209         var d = ds.getAt(rowIndex);
6210         
6211         var row = {
6212             tag : 'tr',
6213             cn : []
6214         };
6215             
6216         var cellObjects = [];
6217         
6218         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6219             var config = cm.config[i];
6220             
6221             var renderer = cm.getRenderer(i);
6222             var value = '';
6223             var id = false;
6224             
6225             if(typeof(renderer) !== 'undefined'){
6226                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6227             }
6228             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6229             // and are rendered into the cells after the row is rendered - using the id for the element.
6230             
6231             if(typeof(value) === 'object'){
6232                 id = Roo.id();
6233                 cellObjects.push({
6234                     container : id,
6235                     cfg : value 
6236                 })
6237             }
6238             
6239             var rowcfg = {
6240                 record: d,
6241                 rowIndex : rowIndex,
6242                 colIndex : i,
6243                 rowClass : ''
6244             };
6245
6246             this.fireEvent('rowclass', this, rowcfg);
6247             
6248             var td = {
6249                 tag: 'td',
6250                 cls : rowcfg.rowClass,
6251                 style: '',
6252                 html: (typeof(value) === 'object') ? '' : value
6253             };
6254             
6255             if (id) {
6256                 td.id = id;
6257             }
6258             
6259             if(typeof(config.colspan) != 'undefined'){
6260                 td.colspan = config.colspan;
6261             }
6262             
6263             if(typeof(config.hidden) != 'undefined' && config.hidden){
6264                 td.style += ' display:none;';
6265             }
6266             
6267             if(typeof(config.align) != 'undefined' && config.align.length){
6268                 td.style += ' text-align:' + config.align + ';';
6269             }
6270             
6271             if(typeof(config.width) != 'undefined'){
6272                 td.style += ' width:' +  config.width + 'px;';
6273             }
6274             
6275             if(typeof(config.cursor) != 'undefined'){
6276                 td.style += ' cursor:' +  config.cursor + ';';
6277             }
6278             
6279             if(typeof(config.cls) != 'undefined'){
6280                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6281             }
6282             
6283             ['xs','sm','md','lg'].map(function(size){
6284                 
6285                 if(typeof(config[size]) == 'undefined'){
6286                     return;
6287                 }
6288                 
6289                 if (!config[size]) { // 0 = hidden
6290                     td.cls += ' hidden-' + size;
6291                     return;
6292                 }
6293                 
6294                 td.cls += ' col-' + size + '-' + config[size];
6295
6296             });
6297              
6298             row.cn.push(td);
6299            
6300         }
6301         
6302         row.cellObjects = cellObjects;
6303         
6304         return row;
6305           
6306     },
6307     
6308     
6309     
6310     onBeforeLoad : function()
6311     {
6312         //Roo.log('ds onBeforeLoad');
6313         
6314         //this.clear();
6315         
6316         //if(this.loadMask){
6317         //    this.maskEl.show();
6318         //}
6319     },
6320      /**
6321      * Remove all rows
6322      */
6323     clear : function()
6324     {
6325         this.el.select('tbody', true).first().dom.innerHTML = '';
6326     },
6327     /**
6328      * Show or hide a row.
6329      * @param {Number} rowIndex to show or hide
6330      * @param {Boolean} state hide
6331      */
6332     setRowVisibility : function(rowIndex, state)
6333     {
6334         var bt = this.mainBody.dom;
6335         
6336         var rows = this.el.select('tbody > tr', true).elements;
6337         
6338         if(typeof(rows[rowIndex]) == 'undefined'){
6339             return;
6340         }
6341         rows[rowIndex].dom.style.display = state ? '' : 'none';
6342     },
6343     
6344     
6345     getSelectionModel : function(){
6346         if(!this.selModel){
6347             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6348         }
6349         return this.selModel;
6350     },
6351     /*
6352      * Render the Roo.bootstrap object from renderder
6353      */
6354     renderCellObject : function(r)
6355     {
6356         var _this = this;
6357         
6358         var t = r.cfg.render(r.container);
6359         
6360         if(r.cfg.cn){
6361             Roo.each(r.cfg.cn, function(c){
6362                 var child = {
6363                     container: t.getChildContainer(),
6364                     cfg: c
6365                 };
6366                 _this.renderCellObject(child);
6367             })
6368         }
6369     },
6370     
6371     getRowIndex : function(row)
6372     {
6373         var rowIndex = -1;
6374         
6375         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6376             if(el != row){
6377                 return;
6378             }
6379             
6380             rowIndex = index;
6381         });
6382         
6383         return rowIndex;
6384     }
6385    
6386 });
6387
6388  
6389
6390  /*
6391  * - LGPL
6392  *
6393  * table cell
6394  * 
6395  */
6396
6397 /**
6398  * @class Roo.bootstrap.TableCell
6399  * @extends Roo.bootstrap.Component
6400  * Bootstrap TableCell class
6401  * @cfg {String} html cell contain text
6402  * @cfg {String} cls cell class
6403  * @cfg {String} tag cell tag (td|th) default td
6404  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6405  * @cfg {String} align Aligns the content in a cell
6406  * @cfg {String} axis Categorizes cells
6407  * @cfg {String} bgcolor Specifies the background color of a cell
6408  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6409  * @cfg {Number} colspan Specifies the number of columns a cell should span
6410  * @cfg {String} headers Specifies one or more header cells a cell is related to
6411  * @cfg {Number} height Sets the height of a cell
6412  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6413  * @cfg {Number} rowspan Sets the number of rows a cell should span
6414  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6415  * @cfg {String} valign Vertical aligns the content in a cell
6416  * @cfg {Number} width Specifies the width of a cell
6417  * 
6418  * @constructor
6419  * Create a new TableCell
6420  * @param {Object} config The config object
6421  */
6422
6423 Roo.bootstrap.TableCell = function(config){
6424     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6425 };
6426
6427 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6428     
6429     html: false,
6430     cls: false,
6431     tag: false,
6432     abbr: false,
6433     align: false,
6434     axis: false,
6435     bgcolor: false,
6436     charoff: false,
6437     colspan: false,
6438     headers: false,
6439     height: false,
6440     nowrap: false,
6441     rowspan: false,
6442     scope: false,
6443     valign: false,
6444     width: false,
6445     
6446     
6447     getAutoCreate : function(){
6448         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6449         
6450         cfg = {
6451             tag: 'td'
6452         };
6453         
6454         if(this.tag){
6455             cfg.tag = this.tag;
6456         }
6457         
6458         if (this.html) {
6459             cfg.html=this.html
6460         }
6461         if (this.cls) {
6462             cfg.cls=this.cls
6463         }
6464         if (this.abbr) {
6465             cfg.abbr=this.abbr
6466         }
6467         if (this.align) {
6468             cfg.align=this.align
6469         }
6470         if (this.axis) {
6471             cfg.axis=this.axis
6472         }
6473         if (this.bgcolor) {
6474             cfg.bgcolor=this.bgcolor
6475         }
6476         if (this.charoff) {
6477             cfg.charoff=this.charoff
6478         }
6479         if (this.colspan) {
6480             cfg.colspan=this.colspan
6481         }
6482         if (this.headers) {
6483             cfg.headers=this.headers
6484         }
6485         if (this.height) {
6486             cfg.height=this.height
6487         }
6488         if (this.nowrap) {
6489             cfg.nowrap=this.nowrap
6490         }
6491         if (this.rowspan) {
6492             cfg.rowspan=this.rowspan
6493         }
6494         if (this.scope) {
6495             cfg.scope=this.scope
6496         }
6497         if (this.valign) {
6498             cfg.valign=this.valign
6499         }
6500         if (this.width) {
6501             cfg.width=this.width
6502         }
6503         
6504         
6505         return cfg;
6506     }
6507    
6508 });
6509
6510  
6511
6512  /*
6513  * - LGPL
6514  *
6515  * table row
6516  * 
6517  */
6518
6519 /**
6520  * @class Roo.bootstrap.TableRow
6521  * @extends Roo.bootstrap.Component
6522  * Bootstrap TableRow class
6523  * @cfg {String} cls row class
6524  * @cfg {String} align Aligns the content in a table row
6525  * @cfg {String} bgcolor Specifies a background color for a table row
6526  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6527  * @cfg {String} valign Vertical aligns the content in a table row
6528  * 
6529  * @constructor
6530  * Create a new TableRow
6531  * @param {Object} config The config object
6532  */
6533
6534 Roo.bootstrap.TableRow = function(config){
6535     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6536 };
6537
6538 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6539     
6540     cls: false,
6541     align: false,
6542     bgcolor: false,
6543     charoff: false,
6544     valign: false,
6545     
6546     getAutoCreate : function(){
6547         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6548         
6549         cfg = {
6550             tag: 'tr'
6551         };
6552             
6553         if(this.cls){
6554             cfg.cls = this.cls;
6555         }
6556         if(this.align){
6557             cfg.align = this.align;
6558         }
6559         if(this.bgcolor){
6560             cfg.bgcolor = this.bgcolor;
6561         }
6562         if(this.charoff){
6563             cfg.charoff = this.charoff;
6564         }
6565         if(this.valign){
6566             cfg.valign = this.valign;
6567         }
6568         
6569         return cfg;
6570     }
6571    
6572 });
6573
6574  
6575
6576  /*
6577  * - LGPL
6578  *
6579  * table body
6580  * 
6581  */
6582
6583 /**
6584  * @class Roo.bootstrap.TableBody
6585  * @extends Roo.bootstrap.Component
6586  * Bootstrap TableBody class
6587  * @cfg {String} cls element class
6588  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6589  * @cfg {String} align Aligns the content inside the element
6590  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6591  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6592  * 
6593  * @constructor
6594  * Create a new TableBody
6595  * @param {Object} config The config object
6596  */
6597
6598 Roo.bootstrap.TableBody = function(config){
6599     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6600 };
6601
6602 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6603     
6604     cls: false,
6605     tag: false,
6606     align: false,
6607     charoff: false,
6608     valign: false,
6609     
6610     getAutoCreate : function(){
6611         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6612         
6613         cfg = {
6614             tag: 'tbody'
6615         };
6616             
6617         if (this.cls) {
6618             cfg.cls=this.cls
6619         }
6620         if(this.tag){
6621             cfg.tag = this.tag;
6622         }
6623         
6624         if(this.align){
6625             cfg.align = this.align;
6626         }
6627         if(this.charoff){
6628             cfg.charoff = this.charoff;
6629         }
6630         if(this.valign){
6631             cfg.valign = this.valign;
6632         }
6633         
6634         return cfg;
6635     }
6636     
6637     
6638 //    initEvents : function()
6639 //    {
6640 //        
6641 //        if(!this.store){
6642 //            return;
6643 //        }
6644 //        
6645 //        this.store = Roo.factory(this.store, Roo.data);
6646 //        this.store.on('load', this.onLoad, this);
6647 //        
6648 //        this.store.load();
6649 //        
6650 //    },
6651 //    
6652 //    onLoad: function () 
6653 //    {   
6654 //        this.fireEvent('load', this);
6655 //    }
6656 //    
6657 //   
6658 });
6659
6660  
6661
6662  /*
6663  * Based on:
6664  * Ext JS Library 1.1.1
6665  * Copyright(c) 2006-2007, Ext JS, LLC.
6666  *
6667  * Originally Released Under LGPL - original licence link has changed is not relivant.
6668  *
6669  * Fork - LGPL
6670  * <script type="text/javascript">
6671  */
6672
6673 // as we use this in bootstrap.
6674 Roo.namespace('Roo.form');
6675  /**
6676  * @class Roo.form.Action
6677  * Internal Class used to handle form actions
6678  * @constructor
6679  * @param {Roo.form.BasicForm} el The form element or its id
6680  * @param {Object} config Configuration options
6681  */
6682
6683  
6684  
6685 // define the action interface
6686 Roo.form.Action = function(form, options){
6687     this.form = form;
6688     this.options = options || {};
6689 };
6690 /**
6691  * Client Validation Failed
6692  * @const 
6693  */
6694 Roo.form.Action.CLIENT_INVALID = 'client';
6695 /**
6696  * Server Validation Failed
6697  * @const 
6698  */
6699 Roo.form.Action.SERVER_INVALID = 'server';
6700  /**
6701  * Connect to Server Failed
6702  * @const 
6703  */
6704 Roo.form.Action.CONNECT_FAILURE = 'connect';
6705 /**
6706  * Reading Data from Server Failed
6707  * @const 
6708  */
6709 Roo.form.Action.LOAD_FAILURE = 'load';
6710
6711 Roo.form.Action.prototype = {
6712     type : 'default',
6713     failureType : undefined,
6714     response : undefined,
6715     result : undefined,
6716
6717     // interface method
6718     run : function(options){
6719
6720     },
6721
6722     // interface method
6723     success : function(response){
6724
6725     },
6726
6727     // interface method
6728     handleResponse : function(response){
6729
6730     },
6731
6732     // default connection failure
6733     failure : function(response){
6734         
6735         this.response = response;
6736         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6737         this.form.afterAction(this, false);
6738     },
6739
6740     processResponse : function(response){
6741         this.response = response;
6742         if(!response.responseText){
6743             return true;
6744         }
6745         this.result = this.handleResponse(response);
6746         return this.result;
6747     },
6748
6749     // utility functions used internally
6750     getUrl : function(appendParams){
6751         var url = this.options.url || this.form.url || this.form.el.dom.action;
6752         if(appendParams){
6753             var p = this.getParams();
6754             if(p){
6755                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6756             }
6757         }
6758         return url;
6759     },
6760
6761     getMethod : function(){
6762         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6763     },
6764
6765     getParams : function(){
6766         var bp = this.form.baseParams;
6767         var p = this.options.params;
6768         if(p){
6769             if(typeof p == "object"){
6770                 p = Roo.urlEncode(Roo.applyIf(p, bp));
6771             }else if(typeof p == 'string' && bp){
6772                 p += '&' + Roo.urlEncode(bp);
6773             }
6774         }else if(bp){
6775             p = Roo.urlEncode(bp);
6776         }
6777         return p;
6778     },
6779
6780     createCallback : function(){
6781         return {
6782             success: this.success,
6783             failure: this.failure,
6784             scope: this,
6785             timeout: (this.form.timeout*1000),
6786             upload: this.form.fileUpload ? this.success : undefined
6787         };
6788     }
6789 };
6790
6791 Roo.form.Action.Submit = function(form, options){
6792     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6793 };
6794
6795 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6796     type : 'submit',
6797
6798     haveProgress : false,
6799     uploadComplete : false,
6800     
6801     // uploadProgress indicator.
6802     uploadProgress : function()
6803     {
6804         if (!this.form.progressUrl) {
6805             return;
6806         }
6807         
6808         if (!this.haveProgress) {
6809             Roo.MessageBox.progress("Uploading", "Uploading");
6810         }
6811         if (this.uploadComplete) {
6812            Roo.MessageBox.hide();
6813            return;
6814         }
6815         
6816         this.haveProgress = true;
6817    
6818         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6819         
6820         var c = new Roo.data.Connection();
6821         c.request({
6822             url : this.form.progressUrl,
6823             params: {
6824                 id : uid
6825             },
6826             method: 'GET',
6827             success : function(req){
6828                //console.log(data);
6829                 var rdata = false;
6830                 var edata;
6831                 try  {
6832                    rdata = Roo.decode(req.responseText)
6833                 } catch (e) {
6834                     Roo.log("Invalid data from server..");
6835                     Roo.log(edata);
6836                     return;
6837                 }
6838                 if (!rdata || !rdata.success) {
6839                     Roo.log(rdata);
6840                     Roo.MessageBox.alert(Roo.encode(rdata));
6841                     return;
6842                 }
6843                 var data = rdata.data;
6844                 
6845                 if (this.uploadComplete) {
6846                    Roo.MessageBox.hide();
6847                    return;
6848                 }
6849                    
6850                 if (data){
6851                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6852                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6853                     );
6854                 }
6855                 this.uploadProgress.defer(2000,this);
6856             },
6857        
6858             failure: function(data) {
6859                 Roo.log('progress url failed ');
6860                 Roo.log(data);
6861             },
6862             scope : this
6863         });
6864            
6865     },
6866     
6867     
6868     run : function()
6869     {
6870         // run get Values on the form, so it syncs any secondary forms.
6871         this.form.getValues();
6872         
6873         var o = this.options;
6874         var method = this.getMethod();
6875         var isPost = method == 'POST';
6876         if(o.clientValidation === false || this.form.isValid()){
6877             
6878             if (this.form.progressUrl) {
6879                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6880                     (new Date() * 1) + '' + Math.random());
6881                     
6882             } 
6883             
6884             
6885             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6886                 form:this.form.el.dom,
6887                 url:this.getUrl(!isPost),
6888                 method: method,
6889                 params:isPost ? this.getParams() : null,
6890                 isUpload: this.form.fileUpload
6891             }));
6892             
6893             this.uploadProgress();
6894
6895         }else if (o.clientValidation !== false){ // client validation failed
6896             this.failureType = Roo.form.Action.CLIENT_INVALID;
6897             this.form.afterAction(this, false);
6898         }
6899     },
6900
6901     success : function(response)
6902     {
6903         this.uploadComplete= true;
6904         if (this.haveProgress) {
6905             Roo.MessageBox.hide();
6906         }
6907         
6908         
6909         var result = this.processResponse(response);
6910         if(result === true || result.success){
6911             this.form.afterAction(this, true);
6912             return;
6913         }
6914         if(result.errors){
6915             this.form.markInvalid(result.errors);
6916             this.failureType = Roo.form.Action.SERVER_INVALID;
6917         }
6918         this.form.afterAction(this, false);
6919     },
6920     failure : function(response)
6921     {
6922         this.uploadComplete= true;
6923         if (this.haveProgress) {
6924             Roo.MessageBox.hide();
6925         }
6926         
6927         this.response = response;
6928         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6929         this.form.afterAction(this, false);
6930     },
6931     
6932     handleResponse : function(response){
6933         if(this.form.errorReader){
6934             var rs = this.form.errorReader.read(response);
6935             var errors = [];
6936             if(rs.records){
6937                 for(var i = 0, len = rs.records.length; i < len; i++) {
6938                     var r = rs.records[i];
6939                     errors[i] = r.data;
6940                 }
6941             }
6942             if(errors.length < 1){
6943                 errors = null;
6944             }
6945             return {
6946                 success : rs.success,
6947                 errors : errors
6948             };
6949         }
6950         var ret = false;
6951         try {
6952             ret = Roo.decode(response.responseText);
6953         } catch (e) {
6954             ret = {
6955                 success: false,
6956                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6957                 errors : []
6958             };
6959         }
6960         return ret;
6961         
6962     }
6963 });
6964
6965
6966 Roo.form.Action.Load = function(form, options){
6967     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6968     this.reader = this.form.reader;
6969 };
6970
6971 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6972     type : 'load',
6973
6974     run : function(){
6975         
6976         Roo.Ajax.request(Roo.apply(
6977                 this.createCallback(), {
6978                     method:this.getMethod(),
6979                     url:this.getUrl(false),
6980                     params:this.getParams()
6981         }));
6982     },
6983
6984     success : function(response){
6985         
6986         var result = this.processResponse(response);
6987         if(result === true || !result.success || !result.data){
6988             this.failureType = Roo.form.Action.LOAD_FAILURE;
6989             this.form.afterAction(this, false);
6990             return;
6991         }
6992         this.form.clearInvalid();
6993         this.form.setValues(result.data);
6994         this.form.afterAction(this, true);
6995     },
6996
6997     handleResponse : function(response){
6998         if(this.form.reader){
6999             var rs = this.form.reader.read(response);
7000             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7001             return {
7002                 success : rs.success,
7003                 data : data
7004             };
7005         }
7006         return Roo.decode(response.responseText);
7007     }
7008 });
7009
7010 Roo.form.Action.ACTION_TYPES = {
7011     'load' : Roo.form.Action.Load,
7012     'submit' : Roo.form.Action.Submit
7013 };/*
7014  * - LGPL
7015  *
7016  * form
7017  * 
7018  */
7019
7020 /**
7021  * @class Roo.bootstrap.Form
7022  * @extends Roo.bootstrap.Component
7023  * Bootstrap Form class
7024  * @cfg {String} method  GET | POST (default POST)
7025  * @cfg {String} labelAlign top | left (default top)
7026  * @cfg {String} align left  | right - for navbars
7027  * @cfg {Boolean} loadMask load mask when submit (default true)
7028
7029  * 
7030  * @constructor
7031  * Create a new Form
7032  * @param {Object} config The config object
7033  */
7034
7035
7036 Roo.bootstrap.Form = function(config){
7037     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7038     this.addEvents({
7039         /**
7040          * @event clientvalidation
7041          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7042          * @param {Form} this
7043          * @param {Boolean} valid true if the form has passed client-side validation
7044          */
7045         clientvalidation: true,
7046         /**
7047          * @event beforeaction
7048          * Fires before any action is performed. Return false to cancel the action.
7049          * @param {Form} this
7050          * @param {Action} action The action to be performed
7051          */
7052         beforeaction: true,
7053         /**
7054          * @event actionfailed
7055          * Fires when an action fails.
7056          * @param {Form} this
7057          * @param {Action} action The action that failed
7058          */
7059         actionfailed : true,
7060         /**
7061          * @event actioncomplete
7062          * Fires when an action is completed.
7063          * @param {Form} this
7064          * @param {Action} action The action that completed
7065          */
7066         actioncomplete : true
7067     });
7068     
7069 };
7070
7071 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7072       
7073      /**
7074      * @cfg {String} method
7075      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7076      */
7077     method : 'POST',
7078     /**
7079      * @cfg {String} url
7080      * The URL to use for form actions if one isn't supplied in the action options.
7081      */
7082     /**
7083      * @cfg {Boolean} fileUpload
7084      * Set to true if this form is a file upload.
7085      */
7086      
7087     /**
7088      * @cfg {Object} baseParams
7089      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7090      */
7091       
7092     /**
7093      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7094      */
7095     timeout: 30,
7096     /**
7097      * @cfg {Sting} align (left|right) for navbar forms
7098      */
7099     align : 'left',
7100
7101     // private
7102     activeAction : null,
7103  
7104     /**
7105      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7106      * element by passing it or its id or mask the form itself by passing in true.
7107      * @type Mixed
7108      */
7109     waitMsgTarget : false,
7110     
7111     loadMask : true,
7112     
7113     getAutoCreate : function(){
7114         
7115         var cfg = {
7116             tag: 'form',
7117             method : this.method || 'POST',
7118             id : this.id || Roo.id(),
7119             cls : ''
7120         };
7121         if (this.parent().xtype.match(/^Nav/)) {
7122             cfg.cls = 'navbar-form navbar-' + this.align;
7123             
7124         }
7125         
7126         if (this.labelAlign == 'left' ) {
7127             cfg.cls += ' form-horizontal';
7128         }
7129         
7130         
7131         return cfg;
7132     },
7133     initEvents : function()
7134     {
7135         this.el.on('submit', this.onSubmit, this);
7136         // this was added as random key presses on the form where triggering form submit.
7137         this.el.on('keypress', function(e) {
7138             if (e.getCharCode() != 13) {
7139                 return true;
7140             }
7141             // we might need to allow it for textareas.. and some other items.
7142             // check e.getTarget().
7143             
7144             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7145                 return true;
7146             }
7147         
7148             Roo.log("keypress blocked");
7149             
7150             e.preventDefault();
7151             return false;
7152         });
7153         
7154     },
7155     // private
7156     onSubmit : function(e){
7157         e.stopEvent();
7158     },
7159     
7160      /**
7161      * Returns true if client-side validation on the form is successful.
7162      * @return Boolean
7163      */
7164     isValid : function(){
7165         var items = this.getItems();
7166         var valid = true;
7167         items.each(function(f){
7168            if(!f.validate()){
7169                valid = false;
7170                
7171            }
7172         });
7173         return valid;
7174     },
7175     /**
7176      * Returns true if any fields in this form have changed since their original load.
7177      * @return Boolean
7178      */
7179     isDirty : function(){
7180         var dirty = false;
7181         var items = this.getItems();
7182         items.each(function(f){
7183            if(f.isDirty()){
7184                dirty = true;
7185                return false;
7186            }
7187            return true;
7188         });
7189         return dirty;
7190     },
7191      /**
7192      * Performs a predefined action (submit or load) or custom actions you define on this form.
7193      * @param {String} actionName The name of the action type
7194      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7195      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7196      * accept other config options):
7197      * <pre>
7198 Property          Type             Description
7199 ----------------  ---------------  ----------------------------------------------------------------------------------
7200 url               String           The url for the action (defaults to the form's url)
7201 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7202 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7203 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7204                                    validate the form on the client (defaults to false)
7205      * </pre>
7206      * @return {BasicForm} this
7207      */
7208     doAction : function(action, options){
7209         if(typeof action == 'string'){
7210             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7211         }
7212         if(this.fireEvent('beforeaction', this, action) !== false){
7213             this.beforeAction(action);
7214             action.run.defer(100, action);
7215         }
7216         return this;
7217     },
7218     
7219     // private
7220     beforeAction : function(action){
7221         var o = action.options;
7222         
7223         if(this.loadMask){
7224             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7225         }
7226         // not really supported yet.. ??
7227         
7228         //if(this.waitMsgTarget === true){
7229         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7230         //}else if(this.waitMsgTarget){
7231         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7232         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7233         //}else {
7234         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7235        // }
7236          
7237     },
7238
7239     // private
7240     afterAction : function(action, success){
7241         this.activeAction = null;
7242         var o = action.options;
7243         
7244         //if(this.waitMsgTarget === true){
7245             this.el.unmask();
7246         //}else if(this.waitMsgTarget){
7247         //    this.waitMsgTarget.unmask();
7248         //}else{
7249         //    Roo.MessageBox.updateProgress(1);
7250         //    Roo.MessageBox.hide();
7251        // }
7252         // 
7253         if(success){
7254             if(o.reset){
7255                 this.reset();
7256             }
7257             Roo.callback(o.success, o.scope, [this, action]);
7258             this.fireEvent('actioncomplete', this, action);
7259             
7260         }else{
7261             
7262             // failure condition..
7263             // we have a scenario where updates need confirming.
7264             // eg. if a locking scenario exists..
7265             // we look for { errors : { needs_confirm : true }} in the response.
7266             if (
7267                 (typeof(action.result) != 'undefined')  &&
7268                 (typeof(action.result.errors) != 'undefined')  &&
7269                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7270            ){
7271                 var _t = this;
7272                 Roo.log("not supported yet");
7273                  /*
7274                 
7275                 Roo.MessageBox.confirm(
7276                     "Change requires confirmation",
7277                     action.result.errorMsg,
7278                     function(r) {
7279                         if (r != 'yes') {
7280                             return;
7281                         }
7282                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7283                     }
7284                     
7285                 );
7286                 */
7287                 
7288                 
7289                 return;
7290             }
7291             
7292             Roo.callback(o.failure, o.scope, [this, action]);
7293             // show an error message if no failed handler is set..
7294             if (!this.hasListener('actionfailed')) {
7295                 Roo.log("need to add dialog support");
7296                 /*
7297                 Roo.MessageBox.alert("Error",
7298                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7299                         action.result.errorMsg :
7300                         "Saving Failed, please check your entries or try again"
7301                 );
7302                 */
7303             }
7304             
7305             this.fireEvent('actionfailed', this, action);
7306         }
7307         
7308     },
7309     /**
7310      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7311      * @param {String} id The value to search for
7312      * @return Field
7313      */
7314     findField : function(id){
7315         var items = this.getItems();
7316         var field = items.get(id);
7317         if(!field){
7318              items.each(function(f){
7319                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7320                     field = f;
7321                     return false;
7322                 }
7323                 return true;
7324             });
7325         }
7326         return field || null;
7327     },
7328      /**
7329      * Mark fields in this form invalid in bulk.
7330      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7331      * @return {BasicForm} this
7332      */
7333     markInvalid : function(errors){
7334         if(errors instanceof Array){
7335             for(var i = 0, len = errors.length; i < len; i++){
7336                 var fieldError = errors[i];
7337                 var f = this.findField(fieldError.id);
7338                 if(f){
7339                     f.markInvalid(fieldError.msg);
7340                 }
7341             }
7342         }else{
7343             var field, id;
7344             for(id in errors){
7345                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7346                     field.markInvalid(errors[id]);
7347                 }
7348             }
7349         }
7350         //Roo.each(this.childForms || [], function (f) {
7351         //    f.markInvalid(errors);
7352         //});
7353         
7354         return this;
7355     },
7356
7357     /**
7358      * Set values for fields in this form in bulk.
7359      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7360      * @return {BasicForm} this
7361      */
7362     setValues : function(values){
7363         if(values instanceof Array){ // array of objects
7364             for(var i = 0, len = values.length; i < len; i++){
7365                 var v = values[i];
7366                 var f = this.findField(v.id);
7367                 if(f){
7368                     f.setValue(v.value);
7369                     if(this.trackResetOnLoad){
7370                         f.originalValue = f.getValue();
7371                     }
7372                 }
7373             }
7374         }else{ // object hash
7375             var field, id;
7376             for(id in values){
7377                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7378                     
7379                     if (field.setFromData && 
7380                         field.valueField && 
7381                         field.displayField &&
7382                         // combos' with local stores can 
7383                         // be queried via setValue()
7384                         // to set their value..
7385                         (field.store && !field.store.isLocal)
7386                         ) {
7387                         // it's a combo
7388                         var sd = { };
7389                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7390                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7391                         field.setFromData(sd);
7392                         
7393                     } else {
7394                         field.setValue(values[id]);
7395                     }
7396                     
7397                     
7398                     if(this.trackResetOnLoad){
7399                         field.originalValue = field.getValue();
7400                     }
7401                 }
7402             }
7403         }
7404          
7405         //Roo.each(this.childForms || [], function (f) {
7406         //    f.setValues(values);
7407         //});
7408                 
7409         return this;
7410     },
7411
7412     /**
7413      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7414      * they are returned as an array.
7415      * @param {Boolean} asString
7416      * @return {Object}
7417      */
7418     getValues : function(asString){
7419         //if (this.childForms) {
7420             // copy values from the child forms
7421         //    Roo.each(this.childForms, function (f) {
7422         //        this.setValues(f.getValues());
7423         //    }, this);
7424         //}
7425         
7426         
7427         
7428         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7429         if(asString === true){
7430             return fs;
7431         }
7432         return Roo.urlDecode(fs);
7433     },
7434     
7435     /**
7436      * Returns the fields in this form as an object with key/value pairs. 
7437      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7438      * @return {Object}
7439      */
7440     getFieldValues : function(with_hidden)
7441     {
7442         var items = this.getItems();
7443         var ret = {};
7444         items.each(function(f){
7445             if (!f.getName()) {
7446                 return;
7447             }
7448             var v = f.getValue();
7449             if (f.inputType =='radio') {
7450                 if (typeof(ret[f.getName()]) == 'undefined') {
7451                     ret[f.getName()] = ''; // empty..
7452                 }
7453                 
7454                 if (!f.el.dom.checked) {
7455                     return;
7456                     
7457                 }
7458                 v = f.el.dom.value;
7459                 
7460             }
7461             
7462             // not sure if this supported any more..
7463             if ((typeof(v) == 'object') && f.getRawValue) {
7464                 v = f.getRawValue() ; // dates..
7465             }
7466             // combo boxes where name != hiddenName...
7467             if (f.name != f.getName()) {
7468                 ret[f.name] = f.getRawValue();
7469             }
7470             ret[f.getName()] = v;
7471         });
7472         
7473         return ret;
7474     },
7475
7476     /**
7477      * Clears all invalid messages in this form.
7478      * @return {BasicForm} this
7479      */
7480     clearInvalid : function(){
7481         var items = this.getItems();
7482         
7483         items.each(function(f){
7484            f.clearInvalid();
7485         });
7486         
7487         
7488         
7489         return this;
7490     },
7491
7492     /**
7493      * Resets this form.
7494      * @return {BasicForm} this
7495      */
7496     reset : function(){
7497         var items = this.getItems();
7498         items.each(function(f){
7499             f.reset();
7500         });
7501         
7502         Roo.each(this.childForms || [], function (f) {
7503             f.reset();
7504         });
7505        
7506         
7507         return this;
7508     },
7509     getItems : function()
7510     {
7511         var r=new Roo.util.MixedCollection(false, function(o){
7512             return o.id || (o.id = Roo.id());
7513         });
7514         var iter = function(el) {
7515             if (el.inputEl) {
7516                 r.add(el);
7517             }
7518             if (!el.items) {
7519                 return;
7520             }
7521             Roo.each(el.items,function(e) {
7522                 iter(e);
7523             });
7524             
7525             
7526         };
7527         
7528         iter(this);
7529         return r;
7530         
7531         
7532         
7533         
7534     }
7535     
7536 });
7537
7538  
7539 /*
7540  * Based on:
7541  * Ext JS Library 1.1.1
7542  * Copyright(c) 2006-2007, Ext JS, LLC.
7543  *
7544  * Originally Released Under LGPL - original licence link has changed is not relivant.
7545  *
7546  * Fork - LGPL
7547  * <script type="text/javascript">
7548  */
7549 /**
7550  * @class Roo.form.VTypes
7551  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7552  * @singleton
7553  */
7554 Roo.form.VTypes = function(){
7555     // closure these in so they are only created once.
7556     var alpha = /^[a-zA-Z_]+$/;
7557     var alphanum = /^[a-zA-Z0-9_]+$/;
7558     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
7559     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7560
7561     // All these messages and functions are configurable
7562     return {
7563         /**
7564          * The function used to validate email addresses
7565          * @param {String} value The email address
7566          */
7567         'email' : function(v){
7568             return email.test(v);
7569         },
7570         /**
7571          * The error text to display when the email validation function returns false
7572          * @type String
7573          */
7574         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7575         /**
7576          * The keystroke filter mask to be applied on email input
7577          * @type RegExp
7578          */
7579         'emailMask' : /[a-z0-9_\.\-@]/i,
7580
7581         /**
7582          * The function used to validate URLs
7583          * @param {String} value The URL
7584          */
7585         'url' : function(v){
7586             return url.test(v);
7587         },
7588         /**
7589          * The error text to display when the url validation function returns false
7590          * @type String
7591          */
7592         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7593         
7594         /**
7595          * The function used to validate alpha values
7596          * @param {String} value The value
7597          */
7598         'alpha' : function(v){
7599             return alpha.test(v);
7600         },
7601         /**
7602          * The error text to display when the alpha validation function returns false
7603          * @type String
7604          */
7605         'alphaText' : 'This field should only contain letters and _',
7606         /**
7607          * The keystroke filter mask to be applied on alpha input
7608          * @type RegExp
7609          */
7610         'alphaMask' : /[a-z_]/i,
7611
7612         /**
7613          * The function used to validate alphanumeric values
7614          * @param {String} value The value
7615          */
7616         'alphanum' : function(v){
7617             return alphanum.test(v);
7618         },
7619         /**
7620          * The error text to display when the alphanumeric validation function returns false
7621          * @type String
7622          */
7623         'alphanumText' : 'This field should only contain letters, numbers and _',
7624         /**
7625          * The keystroke filter mask to be applied on alphanumeric input
7626          * @type RegExp
7627          */
7628         'alphanumMask' : /[a-z0-9_]/i
7629     };
7630 }();/*
7631  * - LGPL
7632  *
7633  * Input
7634  * 
7635  */
7636
7637 /**
7638  * @class Roo.bootstrap.Input
7639  * @extends Roo.bootstrap.Component
7640  * Bootstrap Input class
7641  * @cfg {Boolean} disabled is it disabled
7642  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7643  * @cfg {String} name name of the input
7644  * @cfg {string} fieldLabel - the label associated
7645  * @cfg {string} placeholder - placeholder to put in text.
7646  * @cfg {string}  before - input group add on before
7647  * @cfg {string} after - input group add on after
7648  * @cfg {string} size - (lg|sm) or leave empty..
7649  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7650  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7651  * @cfg {Number} md colspan out of 12 for computer-sized screens
7652  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7653  * @cfg {string} value default value of the input
7654  * @cfg {Number} labelWidth set the width of label (0-12)
7655  * @cfg {String} labelAlign (top|left)
7656  * @cfg {Boolean} readOnly Specifies that the field should be read-only
7657  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7658
7659  * @cfg {String} align (left|center|right) Default left
7660  * @cfg {Boolean} forceFeedback (true|false) Default false
7661  * 
7662  * 
7663  * 
7664  * 
7665  * @constructor
7666  * Create a new Input
7667  * @param {Object} config The config object
7668  */
7669
7670 Roo.bootstrap.Input = function(config){
7671     Roo.bootstrap.Input.superclass.constructor.call(this, config);
7672    
7673         this.addEvents({
7674             /**
7675              * @event focus
7676              * Fires when this field receives input focus.
7677              * @param {Roo.form.Field} this
7678              */
7679             focus : true,
7680             /**
7681              * @event blur
7682              * Fires when this field loses input focus.
7683              * @param {Roo.form.Field} this
7684              */
7685             blur : true,
7686             /**
7687              * @event specialkey
7688              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
7689              * {@link Roo.EventObject#getKey} to determine which key was pressed.
7690              * @param {Roo.form.Field} this
7691              * @param {Roo.EventObject} e The event object
7692              */
7693             specialkey : true,
7694             /**
7695              * @event change
7696              * Fires just before the field blurs if the field value has changed.
7697              * @param {Roo.form.Field} this
7698              * @param {Mixed} newValue The new value
7699              * @param {Mixed} oldValue The original value
7700              */
7701             change : true,
7702             /**
7703              * @event invalid
7704              * Fires after the field has been marked as invalid.
7705              * @param {Roo.form.Field} this
7706              * @param {String} msg The validation message
7707              */
7708             invalid : true,
7709             /**
7710              * @event valid
7711              * Fires after the field has been validated with no errors.
7712              * @param {Roo.form.Field} this
7713              */
7714             valid : true,
7715              /**
7716              * @event keyup
7717              * Fires after the key up
7718              * @param {Roo.form.Field} this
7719              * @param {Roo.EventObject}  e The event Object
7720              */
7721             keyup : true
7722         });
7723 };
7724
7725 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
7726      /**
7727      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7728       automatic validation (defaults to "keyup").
7729      */
7730     validationEvent : "keyup",
7731      /**
7732      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7733      */
7734     validateOnBlur : true,
7735     /**
7736      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7737      */
7738     validationDelay : 250,
7739      /**
7740      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7741      */
7742     focusClass : "x-form-focus",  // not needed???
7743     
7744        
7745     /**
7746      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7747      */
7748     invalidClass : "has-warning",
7749     
7750     /**
7751      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7752      */
7753     validClass : "has-success",
7754     
7755     /**
7756      * @cfg {Boolean} hasFeedback (true|false) default true
7757      */
7758     hasFeedback : true,
7759     
7760     /**
7761      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7762      */
7763     invalidFeedbackClass : "glyphicon-warning-sign",
7764     
7765     /**
7766      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7767      */
7768     validFeedbackClass : "glyphicon-ok",
7769     
7770     /**
7771      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7772      */
7773     selectOnFocus : false,
7774     
7775      /**
7776      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7777      */
7778     maskRe : null,
7779        /**
7780      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7781      */
7782     vtype : null,
7783     
7784       /**
7785      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7786      */
7787     disableKeyFilter : false,
7788     
7789        /**
7790      * @cfg {Boolean} disabled True to disable the field (defaults to false).
7791      */
7792     disabled : false,
7793      /**
7794      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7795      */
7796     allowBlank : true,
7797     /**
7798      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7799      */
7800     blankText : "This field is required",
7801     
7802      /**
7803      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7804      */
7805     minLength : 0,
7806     /**
7807      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7808      */
7809     maxLength : Number.MAX_VALUE,
7810     /**
7811      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7812      */
7813     minLengthText : "The minimum length for this field is {0}",
7814     /**
7815      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7816      */
7817     maxLengthText : "The maximum length for this field is {0}",
7818   
7819     
7820     /**
7821      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7822      * If available, this function will be called only after the basic validators all return true, and will be passed the
7823      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7824      */
7825     validator : null,
7826     /**
7827      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7828      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7829      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7830      */
7831     regex : null,
7832     /**
7833      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7834      */
7835     regexText : "",
7836     
7837     autocomplete: false,
7838     
7839     
7840     fieldLabel : '',
7841     inputType : 'text',
7842     
7843     name : false,
7844     placeholder: false,
7845     before : false,
7846     after : false,
7847     size : false,
7848     hasFocus : false,
7849     preventMark: false,
7850     isFormField : true,
7851     value : '',
7852     labelWidth : 2,
7853     labelAlign : false,
7854     readOnly : false,
7855     align : false,
7856     formatedValue : false,
7857     forceFeedback : false,
7858     
7859     parentLabelAlign : function()
7860     {
7861         var parent = this;
7862         while (parent.parent()) {
7863             parent = parent.parent();
7864             if (typeof(parent.labelAlign) !='undefined') {
7865                 return parent.labelAlign;
7866             }
7867         }
7868         return 'left';
7869         
7870     },
7871     
7872     getAutoCreate : function(){
7873         
7874         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7875         
7876         var id = Roo.id();
7877         
7878         var cfg = {};
7879         
7880         if(this.inputType != 'hidden'){
7881             cfg.cls = 'form-group' //input-group
7882         }
7883         
7884         var input =  {
7885             tag: 'input',
7886             id : id,
7887             type : this.inputType,
7888             value : this.value,
7889             cls : 'form-control',
7890             placeholder : this.placeholder || '',
7891             autocomplete : this.autocomplete || 'new-password'
7892         };
7893         
7894         
7895         if(this.align){
7896             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7897         }
7898         
7899         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7900             input.maxLength = this.maxLength;
7901         }
7902         
7903         if (this.disabled) {
7904             input.disabled=true;
7905         }
7906         
7907         if (this.readOnly) {
7908             input.readonly=true;
7909         }
7910         
7911         if (this.name) {
7912             input.name = this.name;
7913         }
7914         if (this.size) {
7915             input.cls += ' input-' + this.size;
7916         }
7917         var settings=this;
7918         ['xs','sm','md','lg'].map(function(size){
7919             if (settings[size]) {
7920                 cfg.cls += ' col-' + size + '-' + settings[size];
7921             }
7922         });
7923         
7924         var inputblock = input;
7925         
7926         var feedback = {
7927             tag: 'span',
7928             cls: 'glyphicon form-control-feedback'
7929         };
7930             
7931         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7932             
7933             inputblock = {
7934                 cls : 'has-feedback',
7935                 cn :  [
7936                     input,
7937                     feedback
7938                 ] 
7939             };  
7940         }
7941         
7942         if (this.before || this.after) {
7943             
7944             inputblock = {
7945                 cls : 'input-group',
7946                 cn :  [] 
7947             };
7948             
7949             if (this.before && typeof(this.before) == 'string') {
7950                 
7951                 inputblock.cn.push({
7952                     tag :'span',
7953                     cls : 'roo-input-before input-group-addon',
7954                     html : this.before
7955                 });
7956             }
7957             if (this.before && typeof(this.before) == 'object') {
7958                 this.before = Roo.factory(this.before);
7959                 Roo.log(this.before);
7960                 inputblock.cn.push({
7961                     tag :'span',
7962                     cls : 'roo-input-before input-group-' +
7963                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7964                 });
7965             }
7966             
7967             inputblock.cn.push(input);
7968             
7969             if (this.after && typeof(this.after) == 'string') {
7970                 inputblock.cn.push({
7971                     tag :'span',
7972                     cls : 'roo-input-after input-group-addon',
7973                     html : this.after
7974                 });
7975             }
7976             if (this.after && typeof(this.after) == 'object') {
7977                 this.after = Roo.factory(this.after);
7978                 Roo.log(this.after);
7979                 inputblock.cn.push({
7980                     tag :'span',
7981                     cls : 'roo-input-after input-group-' +
7982                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7983                 });
7984             }
7985             
7986             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7987                 inputblock.cls += ' has-feedback';
7988                 inputblock.cn.push(feedback);
7989             }
7990         };
7991         
7992         if (align ==='left' && this.fieldLabel.length) {
7993                 Roo.log("left and has label");
7994                 cfg.cn = [
7995                     
7996                     {
7997                         tag: 'label',
7998                         'for' :  id,
7999                         cls : 'control-label col-sm-' + this.labelWidth,
8000                         html : this.fieldLabel
8001                         
8002                     },
8003                     {
8004                         cls : "col-sm-" + (12 - this.labelWidth), 
8005                         cn: [
8006                             inputblock
8007                         ]
8008                     }
8009                     
8010                 ];
8011         } else if ( this.fieldLabel.length) {
8012                 Roo.log(" label");
8013                  cfg.cn = [
8014                    
8015                     {
8016                         tag: 'label',
8017                         //cls : 'input-group-addon',
8018                         html : this.fieldLabel
8019                         
8020                     },
8021                     
8022                     inputblock
8023                     
8024                 ];
8025
8026         } else {
8027             
8028                 Roo.log(" no label && no align");
8029                 cfg.cn = [
8030                     
8031                         inputblock
8032                     
8033                 ];
8034                 
8035                 
8036         };
8037         Roo.log('input-parentType: ' + this.parentType);
8038         
8039         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8040            cfg.cls += ' navbar-form';
8041            Roo.log(cfg);
8042         }
8043         
8044         return cfg;
8045         
8046     },
8047     /**
8048      * return the real input element.
8049      */
8050     inputEl: function ()
8051     {
8052         return this.el.select('input.form-control',true).first();
8053     },
8054     
8055     tooltipEl : function()
8056     {
8057         return this.inputEl();
8058     },
8059     
8060     setDisabled : function(v)
8061     {
8062         var i  = this.inputEl().dom;
8063         if (!v) {
8064             i.removeAttribute('disabled');
8065             return;
8066             
8067         }
8068         i.setAttribute('disabled','true');
8069     },
8070     initEvents : function()
8071     {
8072           
8073         this.inputEl().on("keydown" , this.fireKey,  this);
8074         this.inputEl().on("focus", this.onFocus,  this);
8075         this.inputEl().on("blur", this.onBlur,  this);
8076         
8077         this.inputEl().relayEvent('keyup', this);
8078  
8079         // reference to original value for reset
8080         this.originalValue = this.getValue();
8081         //Roo.form.TextField.superclass.initEvents.call(this);
8082         if(this.validationEvent == 'keyup'){
8083             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8084             this.inputEl().on('keyup', this.filterValidation, this);
8085         }
8086         else if(this.validationEvent !== false){
8087             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8088         }
8089         
8090         if(this.selectOnFocus){
8091             this.on("focus", this.preFocus, this);
8092             
8093         }
8094         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8095             this.inputEl().on("keypress", this.filterKeys, this);
8096         }
8097        /* if(this.grow){
8098             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8099             this.el.on("click", this.autoSize,  this);
8100         }
8101         */
8102         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8103             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8104         }
8105         
8106         if (typeof(this.before) == 'object') {
8107             this.before.render(this.el.select('.roo-input-before',true).first());
8108         }
8109         if (typeof(this.after) == 'object') {
8110             this.after.render(this.el.select('.roo-input-after',true).first());
8111         }
8112         
8113         
8114     },
8115     filterValidation : function(e){
8116         if(!e.isNavKeyPress()){
8117             this.validationTask.delay(this.validationDelay);
8118         }
8119     },
8120      /**
8121      * Validates the field value
8122      * @return {Boolean} True if the value is valid, else false
8123      */
8124     validate : function(){
8125         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8126         if(this.disabled || this.validateValue(this.getRawValue())){
8127             this.markValid();
8128             return true;
8129         }
8130         
8131         this.markInvalid();
8132         return false;
8133     },
8134     
8135     
8136     /**
8137      * Validates a value according to the field's validation rules and marks the field as invalid
8138      * if the validation fails
8139      * @param {Mixed} value The value to validate
8140      * @return {Boolean} True if the value is valid, else false
8141      */
8142     validateValue : function(value){
8143         if(value.length < 1)  { // if it's blank
8144             if(this.allowBlank){
8145                 return true;
8146             }
8147             return false;
8148         }
8149         
8150         if(value.length < this.minLength){
8151             return false;
8152         }
8153         if(value.length > this.maxLength){
8154             return false;
8155         }
8156         if(this.vtype){
8157             var vt = Roo.form.VTypes;
8158             if(!vt[this.vtype](value, this)){
8159                 return false;
8160             }
8161         }
8162         if(typeof this.validator == "function"){
8163             var msg = this.validator(value);
8164             if(msg !== true){
8165                 return false;
8166             }
8167         }
8168         
8169         if(this.regex && !this.regex.test(value)){
8170             return false;
8171         }
8172         
8173         return true;
8174     },
8175
8176     
8177     
8178      // private
8179     fireKey : function(e){
8180         //Roo.log('field ' + e.getKey());
8181         if(e.isNavKeyPress()){
8182             this.fireEvent("specialkey", this, e);
8183         }
8184     },
8185     focus : function (selectText){
8186         if(this.rendered){
8187             this.inputEl().focus();
8188             if(selectText === true){
8189                 this.inputEl().dom.select();
8190             }
8191         }
8192         return this;
8193     } ,
8194     
8195     onFocus : function(){
8196         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8197            // this.el.addClass(this.focusClass);
8198         }
8199         if(!this.hasFocus){
8200             this.hasFocus = true;
8201             this.startValue = this.getValue();
8202             this.fireEvent("focus", this);
8203         }
8204     },
8205     
8206     beforeBlur : Roo.emptyFn,
8207
8208     
8209     // private
8210     onBlur : function(){
8211         this.beforeBlur();
8212         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8213             //this.el.removeClass(this.focusClass);
8214         }
8215         this.hasFocus = false;
8216         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8217             this.validate();
8218         }
8219         var v = this.getValue();
8220         if(String(v) !== String(this.startValue)){
8221             this.fireEvent('change', this, v, this.startValue);
8222         }
8223         this.fireEvent("blur", this);
8224     },
8225     
8226     /**
8227      * Resets the current field value to the originally loaded value and clears any validation messages
8228      */
8229     reset : function(){
8230         this.setValue(this.originalValue);
8231         this.validate();
8232     },
8233      /**
8234      * Returns the name of the field
8235      * @return {Mixed} name The name field
8236      */
8237     getName: function(){
8238         return this.name;
8239     },
8240      /**
8241      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8242      * @return {Mixed} value The field value
8243      */
8244     getValue : function(){
8245         
8246         var v = this.inputEl().getValue();
8247         
8248         return v;
8249     },
8250     /**
8251      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8252      * @return {Mixed} value The field value
8253      */
8254     getRawValue : function(){
8255         var v = this.inputEl().getValue();
8256         
8257         return v;
8258     },
8259     
8260     /**
8261      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8262      * @param {Mixed} value The value to set
8263      */
8264     setRawValue : function(v){
8265         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8266     },
8267     
8268     selectText : function(start, end){
8269         var v = this.getRawValue();
8270         if(v.length > 0){
8271             start = start === undefined ? 0 : start;
8272             end = end === undefined ? v.length : end;
8273             var d = this.inputEl().dom;
8274             if(d.setSelectionRange){
8275                 d.setSelectionRange(start, end);
8276             }else if(d.createTextRange){
8277                 var range = d.createTextRange();
8278                 range.moveStart("character", start);
8279                 range.moveEnd("character", v.length-end);
8280                 range.select();
8281             }
8282         }
8283     },
8284     
8285     /**
8286      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8287      * @param {Mixed} value The value to set
8288      */
8289     setValue : function(v){
8290         this.value = v;
8291         if(this.rendered){
8292             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8293             this.validate();
8294         }
8295     },
8296     
8297     /*
8298     processValue : function(value){
8299         if(this.stripCharsRe){
8300             var newValue = value.replace(this.stripCharsRe, '');
8301             if(newValue !== value){
8302                 this.setRawValue(newValue);
8303                 return newValue;
8304             }
8305         }
8306         return value;
8307     },
8308   */
8309     preFocus : function(){
8310         
8311         if(this.selectOnFocus){
8312             this.inputEl().dom.select();
8313         }
8314     },
8315     filterKeys : function(e){
8316         var k = e.getKey();
8317         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8318             return;
8319         }
8320         var c = e.getCharCode(), cc = String.fromCharCode(c);
8321         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8322             return;
8323         }
8324         if(!this.maskRe.test(cc)){
8325             e.stopEvent();
8326         }
8327     },
8328      /**
8329      * Clear any invalid styles/messages for this field
8330      */
8331     clearInvalid : function(){
8332         
8333         if(!this.el || this.preventMark){ // not rendered
8334             return;
8335         }
8336         this.el.removeClass(this.invalidClass);
8337         
8338         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8339             
8340             var feedback = this.el.select('.form-control-feedback', true).first();
8341             
8342             if(feedback){
8343                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8344             }
8345             
8346         }
8347         
8348         this.fireEvent('valid', this);
8349     },
8350     
8351      /**
8352      * Mark this field as valid
8353      */
8354     markValid : function()
8355     {
8356         if(!this.el  || this.preventMark){ // not rendered
8357             return;
8358         }
8359         
8360         this.el.removeClass([this.invalidClass, this.validClass]);
8361         
8362         var feedback = this.el.select('.form-control-feedback', true).first();
8363             
8364         if(feedback){
8365             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8366         }
8367
8368         if(this.disabled || this.allowBlank){
8369             return;
8370         }
8371         
8372         var formGroup = this.el.findParent('.form-group', false, true);
8373         
8374         if(formGroup){
8375             
8376             var label = formGroup.select('label', true).first();
8377             var icon = formGroup.select('i.fa-star', true).first();
8378             
8379             if(label && icon){
8380                 icon.remove();
8381             }
8382         }
8383         
8384         this.el.addClass(this.validClass);
8385         
8386         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8387             
8388             var feedback = this.el.select('.form-control-feedback', true).first();
8389             
8390             if(feedback){
8391                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8392                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8393             }
8394             
8395         }
8396         
8397         this.fireEvent('valid', this);
8398     },
8399     
8400      /**
8401      * Mark this field as invalid
8402      * @param {String} msg The validation message
8403      */
8404     markInvalid : function(msg)
8405     {
8406         if(!this.el  || this.preventMark){ // not rendered
8407             return;
8408         }
8409         
8410         this.el.removeClass([this.invalidClass, this.validClass]);
8411         
8412         var feedback = this.el.select('.form-control-feedback', true).first();
8413             
8414         if(feedback){
8415             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8416         }
8417
8418         if(this.disabled || this.allowBlank){
8419             return;
8420         }
8421         
8422         var formGroup = this.el.findParent('.form-group', false, true);
8423         
8424         if(formGroup){
8425             var label = formGroup.select('label', true).first();
8426             var icon = formGroup.select('i.fa-star', true).first();
8427
8428             if(!this.getValue().length && label && !icon){
8429                 this.el.findParent('.form-group', false, true).createChild({
8430                     tag : 'i',
8431                     cls : 'text-danger fa fa-lg fa-star',
8432                     tooltip : 'This field is required',
8433                     style : 'margin-right:5px;'
8434                 }, label, true);
8435             }
8436         }
8437         
8438         
8439         this.el.addClass(this.invalidClass);
8440         
8441         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8442             
8443             var feedback = this.el.select('.form-control-feedback', true).first();
8444             
8445             if(feedback){
8446                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8447                 
8448                 if(this.getValue().length || this.forceFeedback){
8449                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8450                 }
8451                 
8452             }
8453             
8454         }
8455         
8456         this.fireEvent('invalid', this, msg);
8457     },
8458     // private
8459     SafariOnKeyDown : function(event)
8460     {
8461         // this is a workaround for a password hang bug on chrome/ webkit.
8462         
8463         var isSelectAll = false;
8464         
8465         if(this.inputEl().dom.selectionEnd > 0){
8466             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8467         }
8468         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8469             event.preventDefault();
8470             this.setValue('');
8471             return;
8472         }
8473         
8474         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
8475             
8476             event.preventDefault();
8477             // this is very hacky as keydown always get's upper case.
8478             //
8479             var cc = String.fromCharCode(event.getCharCode());
8480             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8481             
8482         }
8483     },
8484     adjustWidth : function(tag, w){
8485         tag = tag.toLowerCase();
8486         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8487             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8488                 if(tag == 'input'){
8489                     return w + 2;
8490                 }
8491                 if(tag == 'textarea'){
8492                     return w-2;
8493                 }
8494             }else if(Roo.isOpera){
8495                 if(tag == 'input'){
8496                     return w + 2;
8497                 }
8498                 if(tag == 'textarea'){
8499                     return w-2;
8500                 }
8501             }
8502         }
8503         return w;
8504     }
8505     
8506 });
8507
8508  
8509 /*
8510  * - LGPL
8511  *
8512  * Input
8513  * 
8514  */
8515
8516 /**
8517  * @class Roo.bootstrap.TextArea
8518  * @extends Roo.bootstrap.Input
8519  * Bootstrap TextArea class
8520  * @cfg {Number} cols Specifies the visible width of a text area
8521  * @cfg {Number} rows Specifies the visible number of lines in a text area
8522  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8523  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8524  * @cfg {string} html text
8525  * 
8526  * @constructor
8527  * Create a new TextArea
8528  * @param {Object} config The config object
8529  */
8530
8531 Roo.bootstrap.TextArea = function(config){
8532     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8533    
8534 };
8535
8536 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8537      
8538     cols : false,
8539     rows : 5,
8540     readOnly : false,
8541     warp : 'soft',
8542     resize : false,
8543     value: false,
8544     html: false,
8545     
8546     getAutoCreate : function(){
8547         
8548         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8549         
8550         var id = Roo.id();
8551         
8552         var cfg = {};
8553         
8554         var input =  {
8555             tag: 'textarea',
8556             id : id,
8557             warp : this.warp,
8558             rows : this.rows,
8559             value : this.value || '',
8560             html: this.html || '',
8561             cls : 'form-control',
8562             placeholder : this.placeholder || '' 
8563             
8564         };
8565         
8566         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8567             input.maxLength = this.maxLength;
8568         }
8569         
8570         if(this.resize){
8571             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8572         }
8573         
8574         if(this.cols){
8575             input.cols = this.cols;
8576         }
8577         
8578         if (this.readOnly) {
8579             input.readonly = true;
8580         }
8581         
8582         if (this.name) {
8583             input.name = this.name;
8584         }
8585         
8586         if (this.size) {
8587             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8588         }
8589         
8590         var settings=this;
8591         ['xs','sm','md','lg'].map(function(size){
8592             if (settings[size]) {
8593                 cfg.cls += ' col-' + size + '-' + settings[size];
8594             }
8595         });
8596         
8597         var inputblock = input;
8598         
8599         if(this.hasFeedback && !this.allowBlank){
8600             
8601             var feedback = {
8602                 tag: 'span',
8603                 cls: 'glyphicon form-control-feedback'
8604             };
8605
8606             inputblock = {
8607                 cls : 'has-feedback',
8608                 cn :  [
8609                     input,
8610                     feedback
8611                 ] 
8612             };  
8613         }
8614         
8615         
8616         if (this.before || this.after) {
8617             
8618             inputblock = {
8619                 cls : 'input-group',
8620                 cn :  [] 
8621             };
8622             if (this.before) {
8623                 inputblock.cn.push({
8624                     tag :'span',
8625                     cls : 'input-group-addon',
8626                     html : this.before
8627                 });
8628             }
8629             
8630             inputblock.cn.push(input);
8631             
8632             if(this.hasFeedback && !this.allowBlank){
8633                 inputblock.cls += ' has-feedback';
8634                 inputblock.cn.push(feedback);
8635             }
8636             
8637             if (this.after) {
8638                 inputblock.cn.push({
8639                     tag :'span',
8640                     cls : 'input-group-addon',
8641                     html : this.after
8642                 });
8643             }
8644             
8645         }
8646         
8647         if (align ==='left' && this.fieldLabel.length) {
8648                 Roo.log("left and has label");
8649                 cfg.cn = [
8650                     
8651                     {
8652                         tag: 'label',
8653                         'for' :  id,
8654                         cls : 'control-label col-sm-' + this.labelWidth,
8655                         html : this.fieldLabel
8656                         
8657                     },
8658                     {
8659                         cls : "col-sm-" + (12 - this.labelWidth), 
8660                         cn: [
8661                             inputblock
8662                         ]
8663                     }
8664                     
8665                 ];
8666         } else if ( this.fieldLabel.length) {
8667                 Roo.log(" label");
8668                  cfg.cn = [
8669                    
8670                     {
8671                         tag: 'label',
8672                         //cls : 'input-group-addon',
8673                         html : this.fieldLabel
8674                         
8675                     },
8676                     
8677                     inputblock
8678                     
8679                 ];
8680
8681         } else {
8682             
8683                    Roo.log(" no label && no align");
8684                 cfg.cn = [
8685                     
8686                         inputblock
8687                     
8688                 ];
8689                 
8690                 
8691         }
8692         
8693         if (this.disabled) {
8694             input.disabled=true;
8695         }
8696         
8697         return cfg;
8698         
8699     },
8700     /**
8701      * return the real textarea element.
8702      */
8703     inputEl: function ()
8704     {
8705         return this.el.select('textarea.form-control',true).first();
8706     },
8707     
8708     /**
8709      * Clear any invalid styles/messages for this field
8710      */
8711     clearInvalid : function()
8712     {
8713         
8714         if(!this.el || this.preventMark){ // not rendered
8715             return;
8716         }
8717         
8718         var label = this.el.select('label', true).first();
8719         var icon = thie.el.select('i.fa-star', true).first();
8720         
8721         if(label && icon){
8722             icon.remove();
8723         }
8724         
8725         this.el.removeClass(this.invalidClass);
8726         
8727         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8728             
8729             var feedback = this.el.select('.form-control-feedback', true).first();
8730             
8731             if(feedback){
8732                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8733             }
8734             
8735         }
8736         
8737         this.fireEvent('valid', this);
8738     },
8739     
8740      /**
8741      * Mark this field as valid
8742      */
8743     markValid : function()
8744     {
8745         if(!this.el  || this.preventMark){ // not rendered
8746             return;
8747         }
8748         
8749         this.el.removeClass([this.invalidClass, this.validClass]);
8750         
8751         var feedback = this.el.select('.form-control-feedback', true).first();
8752             
8753         if(feedback){
8754             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8755         }
8756
8757         if(this.disabled || this.allowBlank){
8758             return;
8759         }
8760         
8761         var label = this.el.select('label', true).first();
8762         var icon = thie.el.select('i.fa-star', true).first();
8763         
8764         if(label && icon){
8765             icon.remove();
8766         }
8767         
8768         this.el.addClass(this.validClass);
8769         
8770         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8771             
8772             var feedback = this.el.select('.form-control-feedback', true).first();
8773             
8774             if(feedback){
8775                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8776                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8777             }
8778             
8779         }
8780         
8781         this.fireEvent('valid', this);
8782     },
8783     
8784      /**
8785      * Mark this field as invalid
8786      * @param {String} msg The validation message
8787      */
8788     markInvalid : function(msg)
8789     {
8790         if(!this.el  || this.preventMark){ // not rendered
8791             return;
8792         }
8793         
8794         this.el.removeClass([this.invalidClass, this.validClass]);
8795         
8796         var feedback = this.el.select('.form-control-feedback', true).first();
8797             
8798         if(feedback){
8799             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8800         }
8801
8802         if(this.disabled || this.allowBlank){
8803             return;
8804         }
8805         
8806         var label = this.el.select('label', true).first();
8807         var icon = thie.el.select('i.fa-star', true).first();
8808         
8809         if(!this.getValue().length && label && !icon){
8810             this.el.createChild({
8811                 tag : 'i',
8812                 cls : 'text-danger fa fa-lg fa-star',
8813                 tooltip : 'This field is required',
8814                 style : 'margin-right:5px;'
8815             }, label, true);
8816         }
8817
8818         this.el.addClass(this.invalidClass);
8819         
8820         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8821             
8822             var feedback = this.el.select('.form-control-feedback', true).first();
8823             
8824             if(feedback){
8825                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8826                 
8827                 if(this.getValue().length || this.forceFeedback){
8828                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8829                 }
8830                 
8831             }
8832             
8833         }
8834         
8835         this.fireEvent('invalid', this, msg);
8836     }
8837 });
8838
8839  
8840 /*
8841  * - LGPL
8842  *
8843  * trigger field - base class for combo..
8844  * 
8845  */
8846  
8847 /**
8848  * @class Roo.bootstrap.TriggerField
8849  * @extends Roo.bootstrap.Input
8850  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8851  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8852  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8853  * for which you can provide a custom implementation.  For example:
8854  * <pre><code>
8855 var trigger = new Roo.bootstrap.TriggerField();
8856 trigger.onTriggerClick = myTriggerFn;
8857 trigger.applyTo('my-field');
8858 </code></pre>
8859  *
8860  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8861  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8862  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
8863  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8864  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8865
8866  * @constructor
8867  * Create a new TriggerField.
8868  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8869  * to the base TextField)
8870  */
8871 Roo.bootstrap.TriggerField = function(config){
8872     this.mimicing = false;
8873     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8874 };
8875
8876 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
8877     /**
8878      * @cfg {String} triggerClass A CSS class to apply to the trigger
8879      */
8880      /**
8881      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8882      */
8883     hideTrigger:false,
8884
8885     /**
8886      * @cfg {Boolean} removable (true|false) special filter default false
8887      */
8888     removable : false,
8889     
8890     /** @cfg {Boolean} grow @hide */
8891     /** @cfg {Number} growMin @hide */
8892     /** @cfg {Number} growMax @hide */
8893
8894     /**
8895      * @hide 
8896      * @method
8897      */
8898     autoSize: Roo.emptyFn,
8899     // private
8900     monitorTab : true,
8901     // private
8902     deferHeight : true,
8903
8904     
8905     actionMode : 'wrap',
8906     
8907     caret : false,
8908     
8909     
8910     getAutoCreate : function(){
8911        
8912         var align = this.labelAlign || this.parentLabelAlign();
8913         
8914         var id = Roo.id();
8915         
8916         var cfg = {
8917             cls: 'form-group' //input-group
8918         };
8919         
8920         
8921         var input =  {
8922             tag: 'input',
8923             id : id,
8924             type : this.inputType,
8925             cls : 'form-control',
8926             autocomplete: 'new-password',
8927             placeholder : this.placeholder || '' 
8928             
8929         };
8930         if (this.name) {
8931             input.name = this.name;
8932         }
8933         if (this.size) {
8934             input.cls += ' input-' + this.size;
8935         }
8936         
8937         if (this.disabled) {
8938             input.disabled=true;
8939         }
8940         
8941         var inputblock = input;
8942         
8943         if(this.hasFeedback && !this.allowBlank){
8944             
8945             var feedback = {
8946                 tag: 'span',
8947                 cls: 'glyphicon form-control-feedback'
8948             };
8949             
8950             if(this.removable && !this.editable && !this.tickable){
8951                 inputblock = {
8952                     cls : 'has-feedback',
8953                     cn :  [
8954                         inputblock,
8955                         {
8956                             tag: 'button',
8957                             html : 'x',
8958                             cls : 'roo-combo-removable-btn close'
8959                         },
8960                         feedback
8961                     ] 
8962                 };
8963             } else {
8964                 inputblock = {
8965                     cls : 'has-feedback',
8966                     cn :  [
8967                         inputblock,
8968                         feedback
8969                     ] 
8970                 };
8971             }
8972
8973         } else {
8974             if(this.removable && !this.editable && !this.tickable){
8975                 inputblock = {
8976                     cls : 'roo-removable',
8977                     cn :  [
8978                         inputblock,
8979                         {
8980                             tag: 'button',
8981                             html : 'x',
8982                             cls : 'roo-combo-removable-btn close'
8983                         }
8984                     ] 
8985                 };
8986             }
8987         }
8988         
8989         if (this.before || this.after) {
8990             
8991             inputblock = {
8992                 cls : 'input-group',
8993                 cn :  [] 
8994             };
8995             if (this.before) {
8996                 inputblock.cn.push({
8997                     tag :'span',
8998                     cls : 'input-group-addon',
8999                     html : this.before
9000                 });
9001             }
9002             
9003             inputblock.cn.push(input);
9004             
9005             if(this.hasFeedback && !this.allowBlank){
9006                 inputblock.cls += ' has-feedback';
9007                 inputblock.cn.push(feedback);
9008             }
9009             
9010             if (this.after) {
9011                 inputblock.cn.push({
9012                     tag :'span',
9013                     cls : 'input-group-addon',
9014                     html : this.after
9015                 });
9016             }
9017             
9018         };
9019         
9020         var box = {
9021             tag: 'div',
9022             cn: [
9023                 {
9024                     tag: 'input',
9025                     type : 'hidden',
9026                     cls: 'form-hidden-field'
9027                 },
9028                 inputblock
9029             ]
9030             
9031         };
9032         
9033         if(this.multiple){
9034             Roo.log('multiple');
9035             
9036             box = {
9037                 tag: 'div',
9038                 cn: [
9039                     {
9040                         tag: 'input',
9041                         type : 'hidden',
9042                         cls: 'form-hidden-field'
9043                     },
9044                     {
9045                         tag: 'ul',
9046                         cls: 'select2-choices',
9047                         cn:[
9048                             {
9049                                 tag: 'li',
9050                                 cls: 'select2-search-field',
9051                                 cn: [
9052
9053                                     inputblock
9054                                 ]
9055                             }
9056                         ]
9057                     }
9058                 ]
9059             }
9060         };
9061         
9062         var combobox = {
9063             cls: 'select2-container input-group',
9064             cn: [
9065                 box
9066 //                {
9067 //                    tag: 'ul',
9068 //                    cls: 'typeahead typeahead-long dropdown-menu',
9069 //                    style: 'display:none'
9070 //                }
9071             ]
9072         };
9073         
9074         if(!this.multiple && this.showToggleBtn){
9075             
9076             var caret = {
9077                         tag: 'span',
9078                         cls: 'caret'
9079              };
9080             if (this.caret != false) {
9081                 caret = {
9082                      tag: 'i',
9083                      cls: 'fa fa-' + this.caret
9084                 };
9085                 
9086             }
9087             
9088             combobox.cn.push({
9089                 tag :'span',
9090                 cls : 'input-group-addon btn dropdown-toggle',
9091                 cn : [
9092                     caret,
9093                     {
9094                         tag: 'span',
9095                         cls: 'combobox-clear',
9096                         cn  : [
9097                             {
9098                                 tag : 'i',
9099                                 cls: 'icon-remove'
9100                             }
9101                         ]
9102                     }
9103                 ]
9104
9105             })
9106         }
9107         
9108         if(this.multiple){
9109             combobox.cls += ' select2-container-multi';
9110         }
9111         
9112         if (align ==='left' && this.fieldLabel.length) {
9113             
9114                 Roo.log("left and has label");
9115                 cfg.cn = [
9116                     
9117                     {
9118                         tag: 'label',
9119                         'for' :  id,
9120                         cls : 'control-label col-sm-' + this.labelWidth,
9121                         html : this.fieldLabel
9122                         
9123                     },
9124                     {
9125                         cls : "col-sm-" + (12 - this.labelWidth), 
9126                         cn: [
9127                             combobox
9128                         ]
9129                     }
9130                     
9131                 ];
9132         } else if ( this.fieldLabel.length) {
9133                 Roo.log(" label");
9134                  cfg.cn = [
9135                    
9136                     {
9137                         tag: 'label',
9138                         //cls : 'input-group-addon',
9139                         html : this.fieldLabel
9140                         
9141                     },
9142                     
9143                     combobox
9144                     
9145                 ];
9146
9147         } else {
9148             
9149                 Roo.log(" no label && no align");
9150                 cfg = combobox
9151                      
9152                 
9153         }
9154          
9155         var settings=this;
9156         ['xs','sm','md','lg'].map(function(size){
9157             if (settings[size]) {
9158                 cfg.cls += ' col-' + size + '-' + settings[size];
9159             }
9160         });
9161         Roo.log(cfg);
9162         return cfg;
9163         
9164     },
9165     
9166     
9167     
9168     // private
9169     onResize : function(w, h){
9170 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9171 //        if(typeof w == 'number'){
9172 //            var x = w - this.trigger.getWidth();
9173 //            this.inputEl().setWidth(this.adjustWidth('input', x));
9174 //            this.trigger.setStyle('left', x+'px');
9175 //        }
9176     },
9177
9178     // private
9179     adjustSize : Roo.BoxComponent.prototype.adjustSize,
9180
9181     // private
9182     getResizeEl : function(){
9183         return this.inputEl();
9184     },
9185
9186     // private
9187     getPositionEl : function(){
9188         return this.inputEl();
9189     },
9190
9191     // private
9192     alignErrorIcon : function(){
9193         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9194     },
9195
9196     // private
9197     initEvents : function(){
9198         
9199         this.createList();
9200         
9201         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9202         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9203         if(!this.multiple && this.showToggleBtn){
9204             this.trigger = this.el.select('span.dropdown-toggle',true).first();
9205             if(this.hideTrigger){
9206                 this.trigger.setDisplayed(false);
9207             }
9208             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9209         }
9210         
9211         if(this.multiple){
9212             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9213         }
9214         
9215         if(this.removable && !this.editable && !this.tickable){
9216             var close = this.closeTriggerEl();
9217             
9218             if(close){
9219                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9220                 close.on('click', this.removeBtnClick, this, close);
9221             }
9222         }
9223         
9224         //this.trigger.addClassOnOver('x-form-trigger-over');
9225         //this.trigger.addClassOnClick('x-form-trigger-click');
9226         
9227         //if(!this.width){
9228         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9229         //}
9230     },
9231     
9232     closeTriggerEl : function()
9233     {
9234         var close = this.el.select('.roo-combo-removable-btn', true).first();
9235         return close ? close : false;
9236     },
9237     
9238     removeBtnClick : function(e, h, el)
9239     {
9240         e.preventDefault();
9241         
9242         if(this.fireEvent("remove", this) !== false){
9243             this.reset();
9244         }
9245     },
9246     
9247     createList : function()
9248     {
9249         this.list = Roo.get(document.body).createChild({
9250             tag: 'ul',
9251             cls: 'typeahead typeahead-long dropdown-menu',
9252             style: 'display:none'
9253         });
9254         
9255         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9256         
9257     },
9258
9259     // private
9260     initTrigger : function(){
9261        
9262     },
9263
9264     // private
9265     onDestroy : function(){
9266         if(this.trigger){
9267             this.trigger.removeAllListeners();
9268           //  this.trigger.remove();
9269         }
9270         //if(this.wrap){
9271         //    this.wrap.remove();
9272         //}
9273         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9274     },
9275
9276     // private
9277     onFocus : function(){
9278         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9279         /*
9280         if(!this.mimicing){
9281             this.wrap.addClass('x-trigger-wrap-focus');
9282             this.mimicing = true;
9283             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9284             if(this.monitorTab){
9285                 this.el.on("keydown", this.checkTab, this);
9286             }
9287         }
9288         */
9289     },
9290
9291     // private
9292     checkTab : function(e){
9293         if(e.getKey() == e.TAB){
9294             this.triggerBlur();
9295         }
9296     },
9297
9298     // private
9299     onBlur : function(){
9300         // do nothing
9301     },
9302
9303     // private
9304     mimicBlur : function(e, t){
9305         /*
9306         if(!this.wrap.contains(t) && this.validateBlur()){
9307             this.triggerBlur();
9308         }
9309         */
9310     },
9311
9312     // private
9313     triggerBlur : function(){
9314         this.mimicing = false;
9315         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9316         if(this.monitorTab){
9317             this.el.un("keydown", this.checkTab, this);
9318         }
9319         //this.wrap.removeClass('x-trigger-wrap-focus');
9320         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9321     },
9322
9323     // private
9324     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9325     validateBlur : function(e, t){
9326         return true;
9327     },
9328
9329     // private
9330     onDisable : function(){
9331         this.inputEl().dom.disabled = true;
9332         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9333         //if(this.wrap){
9334         //    this.wrap.addClass('x-item-disabled');
9335         //}
9336     },
9337
9338     // private
9339     onEnable : function(){
9340         this.inputEl().dom.disabled = false;
9341         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9342         //if(this.wrap){
9343         //    this.el.removeClass('x-item-disabled');
9344         //}
9345     },
9346
9347     // private
9348     onShow : function(){
9349         var ae = this.getActionEl();
9350         
9351         if(ae){
9352             ae.dom.style.display = '';
9353             ae.dom.style.visibility = 'visible';
9354         }
9355     },
9356
9357     // private
9358     
9359     onHide : function(){
9360         var ae = this.getActionEl();
9361         ae.dom.style.display = 'none';
9362     },
9363
9364     /**
9365      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
9366      * by an implementing function.
9367      * @method
9368      * @param {EventObject} e
9369      */
9370     onTriggerClick : Roo.emptyFn
9371 });
9372  /*
9373  * Based on:
9374  * Ext JS Library 1.1.1
9375  * Copyright(c) 2006-2007, Ext JS, LLC.
9376  *
9377  * Originally Released Under LGPL - original licence link has changed is not relivant.
9378  *
9379  * Fork - LGPL
9380  * <script type="text/javascript">
9381  */
9382
9383
9384 /**
9385  * @class Roo.data.SortTypes
9386  * @singleton
9387  * Defines the default sorting (casting?) comparison functions used when sorting data.
9388  */
9389 Roo.data.SortTypes = {
9390     /**
9391      * Default sort that does nothing
9392      * @param {Mixed} s The value being converted
9393      * @return {Mixed} The comparison value
9394      */
9395     none : function(s){
9396         return s;
9397     },
9398     
9399     /**
9400      * The regular expression used to strip tags
9401      * @type {RegExp}
9402      * @property
9403      */
9404     stripTagsRE : /<\/?[^>]+>/gi,
9405     
9406     /**
9407      * Strips all HTML tags to sort on text only
9408      * @param {Mixed} s The value being converted
9409      * @return {String} The comparison value
9410      */
9411     asText : function(s){
9412         return String(s).replace(this.stripTagsRE, "");
9413     },
9414     
9415     /**
9416      * Strips all HTML tags to sort on text only - Case insensitive
9417      * @param {Mixed} s The value being converted
9418      * @return {String} The comparison value
9419      */
9420     asUCText : function(s){
9421         return String(s).toUpperCase().replace(this.stripTagsRE, "");
9422     },
9423     
9424     /**
9425      * Case insensitive string
9426      * @param {Mixed} s The value being converted
9427      * @return {String} The comparison value
9428      */
9429     asUCString : function(s) {
9430         return String(s).toUpperCase();
9431     },
9432     
9433     /**
9434      * Date sorting
9435      * @param {Mixed} s The value being converted
9436      * @return {Number} The comparison value
9437      */
9438     asDate : function(s) {
9439         if(!s){
9440             return 0;
9441         }
9442         if(s instanceof Date){
9443             return s.getTime();
9444         }
9445         return Date.parse(String(s));
9446     },
9447     
9448     /**
9449      * Float sorting
9450      * @param {Mixed} s The value being converted
9451      * @return {Float} The comparison value
9452      */
9453     asFloat : function(s) {
9454         var val = parseFloat(String(s).replace(/,/g, ""));
9455         if(isNaN(val)) {
9456             val = 0;
9457         }
9458         return val;
9459     },
9460     
9461     /**
9462      * Integer sorting
9463      * @param {Mixed} s The value being converted
9464      * @return {Number} The comparison value
9465      */
9466     asInt : function(s) {
9467         var val = parseInt(String(s).replace(/,/g, ""));
9468         if(isNaN(val)) {
9469             val = 0;
9470         }
9471         return val;
9472     }
9473 };/*
9474  * Based on:
9475  * Ext JS Library 1.1.1
9476  * Copyright(c) 2006-2007, Ext JS, LLC.
9477  *
9478  * Originally Released Under LGPL - original licence link has changed is not relivant.
9479  *
9480  * Fork - LGPL
9481  * <script type="text/javascript">
9482  */
9483
9484 /**
9485 * @class Roo.data.Record
9486  * Instances of this class encapsulate both record <em>definition</em> information, and record
9487  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9488  * to access Records cached in an {@link Roo.data.Store} object.<br>
9489  * <p>
9490  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9491  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9492  * objects.<br>
9493  * <p>
9494  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9495  * @constructor
9496  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9497  * {@link #create}. The parameters are the same.
9498  * @param {Array} data An associative Array of data values keyed by the field name.
9499  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9500  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9501  * not specified an integer id is generated.
9502  */
9503 Roo.data.Record = function(data, id){
9504     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9505     this.data = data;
9506 };
9507
9508 /**
9509  * Generate a constructor for a specific record layout.
9510  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9511  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9512  * Each field definition object may contain the following properties: <ul>
9513  * <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,
9514  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9515  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9516  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9517  * is being used, then this is a string containing the javascript expression to reference the data relative to 
9518  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9519  * to the data item relative to the record element. If the mapping expression is the same as the field name,
9520  * this may be omitted.</p></li>
9521  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9522  * <ul><li>auto (Default, implies no conversion)</li>
9523  * <li>string</li>
9524  * <li>int</li>
9525  * <li>float</li>
9526  * <li>boolean</li>
9527  * <li>date</li></ul></p></li>
9528  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9529  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9530  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9531  * by the Reader into an object that will be stored in the Record. It is passed the
9532  * following parameters:<ul>
9533  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9534  * </ul></p></li>
9535  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9536  * </ul>
9537  * <br>usage:<br><pre><code>
9538 var TopicRecord = Roo.data.Record.create(
9539     {name: 'title', mapping: 'topic_title'},
9540     {name: 'author', mapping: 'username'},
9541     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9542     {name: 'lastPost', mapping: 'post_time', type: 'date'},
9543     {name: 'lastPoster', mapping: 'user2'},
9544     {name: 'excerpt', mapping: 'post_text'}
9545 );
9546
9547 var myNewRecord = new TopicRecord({
9548     title: 'Do my job please',
9549     author: 'noobie',
9550     totalPosts: 1,
9551     lastPost: new Date(),
9552     lastPoster: 'Animal',
9553     excerpt: 'No way dude!'
9554 });
9555 myStore.add(myNewRecord);
9556 </code></pre>
9557  * @method create
9558  * @static
9559  */
9560 Roo.data.Record.create = function(o){
9561     var f = function(){
9562         f.superclass.constructor.apply(this, arguments);
9563     };
9564     Roo.extend(f, Roo.data.Record);
9565     var p = f.prototype;
9566     p.fields = new Roo.util.MixedCollection(false, function(field){
9567         return field.name;
9568     });
9569     for(var i = 0, len = o.length; i < len; i++){
9570         p.fields.add(new Roo.data.Field(o[i]));
9571     }
9572     f.getField = function(name){
9573         return p.fields.get(name);  
9574     };
9575     return f;
9576 };
9577
9578 Roo.data.Record.AUTO_ID = 1000;
9579 Roo.data.Record.EDIT = 'edit';
9580 Roo.data.Record.REJECT = 'reject';
9581 Roo.data.Record.COMMIT = 'commit';
9582
9583 Roo.data.Record.prototype = {
9584     /**
9585      * Readonly flag - true if this record has been modified.
9586      * @type Boolean
9587      */
9588     dirty : false,
9589     editing : false,
9590     error: null,
9591     modified: null,
9592
9593     // private
9594     join : function(store){
9595         this.store = store;
9596     },
9597
9598     /**
9599      * Set the named field to the specified value.
9600      * @param {String} name The name of the field to set.
9601      * @param {Object} value The value to set the field to.
9602      */
9603     set : function(name, value){
9604         if(this.data[name] == value){
9605             return;
9606         }
9607         this.dirty = true;
9608         if(!this.modified){
9609             this.modified = {};
9610         }
9611         if(typeof this.modified[name] == 'undefined'){
9612             this.modified[name] = this.data[name];
9613         }
9614         this.data[name] = value;
9615         if(!this.editing && this.store){
9616             this.store.afterEdit(this);
9617         }       
9618     },
9619
9620     /**
9621      * Get the value of the named field.
9622      * @param {String} name The name of the field to get the value of.
9623      * @return {Object} The value of the field.
9624      */
9625     get : function(name){
9626         return this.data[name]; 
9627     },
9628
9629     // private
9630     beginEdit : function(){
9631         this.editing = true;
9632         this.modified = {}; 
9633     },
9634
9635     // private
9636     cancelEdit : function(){
9637         this.editing = false;
9638         delete this.modified;
9639     },
9640
9641     // private
9642     endEdit : function(){
9643         this.editing = false;
9644         if(this.dirty && this.store){
9645             this.store.afterEdit(this);
9646         }
9647     },
9648
9649     /**
9650      * Usually called by the {@link Roo.data.Store} which owns the Record.
9651      * Rejects all changes made to the Record since either creation, or the last commit operation.
9652      * Modified fields are reverted to their original values.
9653      * <p>
9654      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9655      * of reject operations.
9656      */
9657     reject : function(){
9658         var m = this.modified;
9659         for(var n in m){
9660             if(typeof m[n] != "function"){
9661                 this.data[n] = m[n];
9662             }
9663         }
9664         this.dirty = false;
9665         delete this.modified;
9666         this.editing = false;
9667         if(this.store){
9668             this.store.afterReject(this);
9669         }
9670     },
9671
9672     /**
9673      * Usually called by the {@link Roo.data.Store} which owns the Record.
9674      * Commits all changes made to the Record since either creation, or the last commit operation.
9675      * <p>
9676      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9677      * of commit operations.
9678      */
9679     commit : function(){
9680         this.dirty = false;
9681         delete this.modified;
9682         this.editing = false;
9683         if(this.store){
9684             this.store.afterCommit(this);
9685         }
9686     },
9687
9688     // private
9689     hasError : function(){
9690         return this.error != null;
9691     },
9692
9693     // private
9694     clearError : function(){
9695         this.error = null;
9696     },
9697
9698     /**
9699      * Creates a copy of this record.
9700      * @param {String} id (optional) A new record id if you don't want to use this record's id
9701      * @return {Record}
9702      */
9703     copy : function(newId) {
9704         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9705     }
9706 };/*
9707  * Based on:
9708  * Ext JS Library 1.1.1
9709  * Copyright(c) 2006-2007, Ext JS, LLC.
9710  *
9711  * Originally Released Under LGPL - original licence link has changed is not relivant.
9712  *
9713  * Fork - LGPL
9714  * <script type="text/javascript">
9715  */
9716
9717
9718
9719 /**
9720  * @class Roo.data.Store
9721  * @extends Roo.util.Observable
9722  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9723  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9724  * <p>
9725  * 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
9726  * has no knowledge of the format of the data returned by the Proxy.<br>
9727  * <p>
9728  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9729  * instances from the data object. These records are cached and made available through accessor functions.
9730  * @constructor
9731  * Creates a new Store.
9732  * @param {Object} config A config object containing the objects needed for the Store to access data,
9733  * and read the data into Records.
9734  */
9735 Roo.data.Store = function(config){
9736     this.data = new Roo.util.MixedCollection(false);
9737     this.data.getKey = function(o){
9738         return o.id;
9739     };
9740     this.baseParams = {};
9741     // private
9742     this.paramNames = {
9743         "start" : "start",
9744         "limit" : "limit",
9745         "sort" : "sort",
9746         "dir" : "dir",
9747         "multisort" : "_multisort"
9748     };
9749
9750     if(config && config.data){
9751         this.inlineData = config.data;
9752         delete config.data;
9753     }
9754
9755     Roo.apply(this, config);
9756     
9757     if(this.reader){ // reader passed
9758         this.reader = Roo.factory(this.reader, Roo.data);
9759         this.reader.xmodule = this.xmodule || false;
9760         if(!this.recordType){
9761             this.recordType = this.reader.recordType;
9762         }
9763         if(this.reader.onMetaChange){
9764             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9765         }
9766     }
9767
9768     if(this.recordType){
9769         this.fields = this.recordType.prototype.fields;
9770     }
9771     this.modified = [];
9772
9773     this.addEvents({
9774         /**
9775          * @event datachanged
9776          * Fires when the data cache has changed, and a widget which is using this Store
9777          * as a Record cache should refresh its view.
9778          * @param {Store} this
9779          */
9780         datachanged : true,
9781         /**
9782          * @event metachange
9783          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9784          * @param {Store} this
9785          * @param {Object} meta The JSON metadata
9786          */
9787         metachange : true,
9788         /**
9789          * @event add
9790          * Fires when Records have been added to the Store
9791          * @param {Store} this
9792          * @param {Roo.data.Record[]} records The array of Records added
9793          * @param {Number} index The index at which the record(s) were added
9794          */
9795         add : true,
9796         /**
9797          * @event remove
9798          * Fires when a Record has been removed from the Store
9799          * @param {Store} this
9800          * @param {Roo.data.Record} record The Record that was removed
9801          * @param {Number} index The index at which the record was removed
9802          */
9803         remove : true,
9804         /**
9805          * @event update
9806          * Fires when a Record has been updated
9807          * @param {Store} this
9808          * @param {Roo.data.Record} record The Record that was updated
9809          * @param {String} operation The update operation being performed.  Value may be one of:
9810          * <pre><code>
9811  Roo.data.Record.EDIT
9812  Roo.data.Record.REJECT
9813  Roo.data.Record.COMMIT
9814          * </code></pre>
9815          */
9816         update : true,
9817         /**
9818          * @event clear
9819          * Fires when the data cache has been cleared.
9820          * @param {Store} this
9821          */
9822         clear : true,
9823         /**
9824          * @event beforeload
9825          * Fires before a request is made for a new data object.  If the beforeload handler returns false
9826          * the load action will be canceled.
9827          * @param {Store} this
9828          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9829          */
9830         beforeload : true,
9831         /**
9832          * @event beforeloadadd
9833          * Fires after a new set of Records has been loaded.
9834          * @param {Store} this
9835          * @param {Roo.data.Record[]} records The Records that were loaded
9836          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9837          */
9838         beforeloadadd : true,
9839         /**
9840          * @event load
9841          * Fires after a new set of Records has been loaded, before they are added to the store.
9842          * @param {Store} this
9843          * @param {Roo.data.Record[]} records The Records that were loaded
9844          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9845          * @params {Object} return from reader
9846          */
9847         load : true,
9848         /**
9849          * @event loadexception
9850          * Fires if an exception occurs in the Proxy during loading.
9851          * Called with the signature of the Proxy's "loadexception" event.
9852          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9853          * 
9854          * @param {Proxy} 
9855          * @param {Object} return from JsonData.reader() - success, totalRecords, records
9856          * @param {Object} load options 
9857          * @param {Object} jsonData from your request (normally this contains the Exception)
9858          */
9859         loadexception : true
9860     });
9861     
9862     if(this.proxy){
9863         this.proxy = Roo.factory(this.proxy, Roo.data);
9864         this.proxy.xmodule = this.xmodule || false;
9865         this.relayEvents(this.proxy,  ["loadexception"]);
9866     }
9867     this.sortToggle = {};
9868     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9869
9870     Roo.data.Store.superclass.constructor.call(this);
9871
9872     if(this.inlineData){
9873         this.loadData(this.inlineData);
9874         delete this.inlineData;
9875     }
9876 };
9877
9878 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9879      /**
9880     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
9881     * without a remote query - used by combo/forms at present.
9882     */
9883     
9884     /**
9885     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9886     */
9887     /**
9888     * @cfg {Array} data Inline data to be loaded when the store is initialized.
9889     */
9890     /**
9891     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9892     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9893     */
9894     /**
9895     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9896     * on any HTTP request
9897     */
9898     /**
9899     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9900     */
9901     /**
9902     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9903     */
9904     multiSort: false,
9905     /**
9906     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9907     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9908     */
9909     remoteSort : false,
9910
9911     /**
9912     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9913      * loaded or when a record is removed. (defaults to false).
9914     */
9915     pruneModifiedRecords : false,
9916
9917     // private
9918     lastOptions : null,
9919
9920     /**
9921      * Add Records to the Store and fires the add event.
9922      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9923      */
9924     add : function(records){
9925         records = [].concat(records);
9926         for(var i = 0, len = records.length; i < len; i++){
9927             records[i].join(this);
9928         }
9929         var index = this.data.length;
9930         this.data.addAll(records);
9931         this.fireEvent("add", this, records, index);
9932     },
9933
9934     /**
9935      * Remove a Record from the Store and fires the remove event.
9936      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9937      */
9938     remove : function(record){
9939         var index = this.data.indexOf(record);
9940         this.data.removeAt(index);
9941         if(this.pruneModifiedRecords){
9942             this.modified.remove(record);
9943         }
9944         this.fireEvent("remove", this, record, index);
9945     },
9946
9947     /**
9948      * Remove all Records from the Store and fires the clear event.
9949      */
9950     removeAll : function(){
9951         this.data.clear();
9952         if(this.pruneModifiedRecords){
9953             this.modified = [];
9954         }
9955         this.fireEvent("clear", this);
9956     },
9957
9958     /**
9959      * Inserts Records to the Store at the given index and fires the add event.
9960      * @param {Number} index The start index at which to insert the passed Records.
9961      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9962      */
9963     insert : function(index, records){
9964         records = [].concat(records);
9965         for(var i = 0, len = records.length; i < len; i++){
9966             this.data.insert(index, records[i]);
9967             records[i].join(this);
9968         }
9969         this.fireEvent("add", this, records, index);
9970     },
9971
9972     /**
9973      * Get the index within the cache of the passed Record.
9974      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9975      * @return {Number} The index of the passed Record. Returns -1 if not found.
9976      */
9977     indexOf : function(record){
9978         return this.data.indexOf(record);
9979     },
9980
9981     /**
9982      * Get the index within the cache of the Record with the passed id.
9983      * @param {String} id The id of the Record to find.
9984      * @return {Number} The index of the Record. Returns -1 if not found.
9985      */
9986     indexOfId : function(id){
9987         return this.data.indexOfKey(id);
9988     },
9989
9990     /**
9991      * Get the Record with the specified id.
9992      * @param {String} id The id of the Record to find.
9993      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
9994      */
9995     getById : function(id){
9996         return this.data.key(id);
9997     },
9998
9999     /**
10000      * Get the Record at the specified index.
10001      * @param {Number} index The index of the Record to find.
10002      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10003      */
10004     getAt : function(index){
10005         return this.data.itemAt(index);
10006     },
10007
10008     /**
10009      * Returns a range of Records between specified indices.
10010      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10011      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10012      * @return {Roo.data.Record[]} An array of Records
10013      */
10014     getRange : function(start, end){
10015         return this.data.getRange(start, end);
10016     },
10017
10018     // private
10019     storeOptions : function(o){
10020         o = Roo.apply({}, o);
10021         delete o.callback;
10022         delete o.scope;
10023         this.lastOptions = o;
10024     },
10025
10026     /**
10027      * Loads the Record cache from the configured Proxy using the configured Reader.
10028      * <p>
10029      * If using remote paging, then the first load call must specify the <em>start</em>
10030      * and <em>limit</em> properties in the options.params property to establish the initial
10031      * position within the dataset, and the number of Records to cache on each read from the Proxy.
10032      * <p>
10033      * <strong>It is important to note that for remote data sources, loading is asynchronous,
10034      * and this call will return before the new data has been loaded. Perform any post-processing
10035      * in a callback function, or in a "load" event handler.</strong>
10036      * <p>
10037      * @param {Object} options An object containing properties which control loading options:<ul>
10038      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10039      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10040      * passed the following arguments:<ul>
10041      * <li>r : Roo.data.Record[]</li>
10042      * <li>options: Options object from the load call</li>
10043      * <li>success: Boolean success indicator</li></ul></li>
10044      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10045      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10046      * </ul>
10047      */
10048     load : function(options){
10049         options = options || {};
10050         if(this.fireEvent("beforeload", this, options) !== false){
10051             this.storeOptions(options);
10052             var p = Roo.apply(options.params || {}, this.baseParams);
10053             // if meta was not loaded from remote source.. try requesting it.
10054             if (!this.reader.metaFromRemote) {
10055                 p._requestMeta = 1;
10056             }
10057             if(this.sortInfo && this.remoteSort){
10058                 var pn = this.paramNames;
10059                 p[pn["sort"]] = this.sortInfo.field;
10060                 p[pn["dir"]] = this.sortInfo.direction;
10061             }
10062             if (this.multiSort) {
10063                 var pn = this.paramNames;
10064                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10065             }
10066             
10067             this.proxy.load(p, this.reader, this.loadRecords, this, options);
10068         }
10069     },
10070
10071     /**
10072      * Reloads the Record cache from the configured Proxy using the configured Reader and
10073      * the options from the last load operation performed.
10074      * @param {Object} options (optional) An object containing properties which may override the options
10075      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10076      * the most recently used options are reused).
10077      */
10078     reload : function(options){
10079         this.load(Roo.applyIf(options||{}, this.lastOptions));
10080     },
10081
10082     // private
10083     // Called as a callback by the Reader during a load operation.
10084     loadRecords : function(o, options, success){
10085         if(!o || success === false){
10086             if(success !== false){
10087                 this.fireEvent("load", this, [], options, o);
10088             }
10089             if(options.callback){
10090                 options.callback.call(options.scope || this, [], options, false);
10091             }
10092             return;
10093         }
10094         // if data returned failure - throw an exception.
10095         if (o.success === false) {
10096             // show a message if no listener is registered.
10097             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10098                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10099             }
10100             // loadmask wil be hooked into this..
10101             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10102             return;
10103         }
10104         var r = o.records, t = o.totalRecords || r.length;
10105         
10106         this.fireEvent("beforeloadadd", this, r, options, o);
10107         
10108         if(!options || options.add !== true){
10109             if(this.pruneModifiedRecords){
10110                 this.modified = [];
10111             }
10112             for(var i = 0, len = r.length; i < len; i++){
10113                 r[i].join(this);
10114             }
10115             if(this.snapshot){
10116                 this.data = this.snapshot;
10117                 delete this.snapshot;
10118             }
10119             this.data.clear();
10120             this.data.addAll(r);
10121             this.totalLength = t;
10122             this.applySort();
10123             this.fireEvent("datachanged", this);
10124         }else{
10125             this.totalLength = Math.max(t, this.data.length+r.length);
10126             this.add(r);
10127         }
10128         this.fireEvent("load", this, r, options, o);
10129         if(options.callback){
10130             options.callback.call(options.scope || this, r, options, true);
10131         }
10132     },
10133
10134
10135     /**
10136      * Loads data from a passed data block. A Reader which understands the format of the data
10137      * must have been configured in the constructor.
10138      * @param {Object} data The data block from which to read the Records.  The format of the data expected
10139      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10140      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10141      */
10142     loadData : function(o, append){
10143         var r = this.reader.readRecords(o);
10144         this.loadRecords(r, {add: append}, true);
10145     },
10146
10147     /**
10148      * Gets the number of cached records.
10149      * <p>
10150      * <em>If using paging, this may not be the total size of the dataset. If the data object
10151      * used by the Reader contains the dataset size, then the getTotalCount() function returns
10152      * the data set size</em>
10153      */
10154     getCount : function(){
10155         return this.data.length || 0;
10156     },
10157
10158     /**
10159      * Gets the total number of records in the dataset as returned by the server.
10160      * <p>
10161      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10162      * the dataset size</em>
10163      */
10164     getTotalCount : function(){
10165         return this.totalLength || 0;
10166     },
10167
10168     /**
10169      * Returns the sort state of the Store as an object with two properties:
10170      * <pre><code>
10171  field {String} The name of the field by which the Records are sorted
10172  direction {String} The sort order, "ASC" or "DESC"
10173      * </code></pre>
10174      */
10175     getSortState : function(){
10176         return this.sortInfo;
10177     },
10178
10179     // private
10180     applySort : function(){
10181         if(this.sortInfo && !this.remoteSort){
10182             var s = this.sortInfo, f = s.field;
10183             var st = this.fields.get(f).sortType;
10184             var fn = function(r1, r2){
10185                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10186                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10187             };
10188             this.data.sort(s.direction, fn);
10189             if(this.snapshot && this.snapshot != this.data){
10190                 this.snapshot.sort(s.direction, fn);
10191             }
10192         }
10193     },
10194
10195     /**
10196      * Sets the default sort column and order to be used by the next load operation.
10197      * @param {String} fieldName The name of the field to sort by.
10198      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10199      */
10200     setDefaultSort : function(field, dir){
10201         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10202     },
10203
10204     /**
10205      * Sort the Records.
10206      * If remote sorting is used, the sort is performed on the server, and the cache is
10207      * reloaded. If local sorting is used, the cache is sorted internally.
10208      * @param {String} fieldName The name of the field to sort by.
10209      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10210      */
10211     sort : function(fieldName, dir){
10212         var f = this.fields.get(fieldName);
10213         if(!dir){
10214             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10215             
10216             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10217                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10218             }else{
10219                 dir = f.sortDir;
10220             }
10221         }
10222         this.sortToggle[f.name] = dir;
10223         this.sortInfo = {field: f.name, direction: dir};
10224         if(!this.remoteSort){
10225             this.applySort();
10226             this.fireEvent("datachanged", this);
10227         }else{
10228             this.load(this.lastOptions);
10229         }
10230     },
10231
10232     /**
10233      * Calls the specified function for each of the Records in the cache.
10234      * @param {Function} fn The function to call. The Record is passed as the first parameter.
10235      * Returning <em>false</em> aborts and exits the iteration.
10236      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10237      */
10238     each : function(fn, scope){
10239         this.data.each(fn, scope);
10240     },
10241
10242     /**
10243      * Gets all records modified since the last commit.  Modified records are persisted across load operations
10244      * (e.g., during paging).
10245      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10246      */
10247     getModifiedRecords : function(){
10248         return this.modified;
10249     },
10250
10251     // private
10252     createFilterFn : function(property, value, anyMatch){
10253         if(!value.exec){ // not a regex
10254             value = String(value);
10255             if(value.length == 0){
10256                 return false;
10257             }
10258             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10259         }
10260         return function(r){
10261             return value.test(r.data[property]);
10262         };
10263     },
10264
10265     /**
10266      * Sums the value of <i>property</i> for each record between start and end and returns the result.
10267      * @param {String} property A field on your records
10268      * @param {Number} start The record index to start at (defaults to 0)
10269      * @param {Number} end The last record index to include (defaults to length - 1)
10270      * @return {Number} The sum
10271      */
10272     sum : function(property, start, end){
10273         var rs = this.data.items, v = 0;
10274         start = start || 0;
10275         end = (end || end === 0) ? end : rs.length-1;
10276
10277         for(var i = start; i <= end; i++){
10278             v += (rs[i].data[property] || 0);
10279         }
10280         return v;
10281     },
10282
10283     /**
10284      * Filter the records by a specified property.
10285      * @param {String} field A field on your records
10286      * @param {String/RegExp} value Either a string that the field
10287      * should start with or a RegExp to test against the field
10288      * @param {Boolean} anyMatch True to match any part not just the beginning
10289      */
10290     filter : function(property, value, anyMatch){
10291         var fn = this.createFilterFn(property, value, anyMatch);
10292         return fn ? this.filterBy(fn) : this.clearFilter();
10293     },
10294
10295     /**
10296      * Filter by a function. The specified function will be called with each
10297      * record in this data source. If the function returns true the record is included,
10298      * otherwise it is filtered.
10299      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10300      * @param {Object} scope (optional) The scope of the function (defaults to this)
10301      */
10302     filterBy : function(fn, scope){
10303         this.snapshot = this.snapshot || this.data;
10304         this.data = this.queryBy(fn, scope||this);
10305         this.fireEvent("datachanged", this);
10306     },
10307
10308     /**
10309      * Query the records by a specified property.
10310      * @param {String} field A field on your records
10311      * @param {String/RegExp} value Either a string that the field
10312      * should start with or a RegExp to test against the field
10313      * @param {Boolean} anyMatch True to match any part not just the beginning
10314      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10315      */
10316     query : function(property, value, anyMatch){
10317         var fn = this.createFilterFn(property, value, anyMatch);
10318         return fn ? this.queryBy(fn) : this.data.clone();
10319     },
10320
10321     /**
10322      * Query by a function. The specified function will be called with each
10323      * record in this data source. If the function returns true the record is included
10324      * in the results.
10325      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10326      * @param {Object} scope (optional) The scope of the function (defaults to this)
10327       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10328      **/
10329     queryBy : function(fn, scope){
10330         var data = this.snapshot || this.data;
10331         return data.filterBy(fn, scope||this);
10332     },
10333
10334     /**
10335      * Collects unique values for a particular dataIndex from this store.
10336      * @param {String} dataIndex The property to collect
10337      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10338      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10339      * @return {Array} An array of the unique values
10340      **/
10341     collect : function(dataIndex, allowNull, bypassFilter){
10342         var d = (bypassFilter === true && this.snapshot) ?
10343                 this.snapshot.items : this.data.items;
10344         var v, sv, r = [], l = {};
10345         for(var i = 0, len = d.length; i < len; i++){
10346             v = d[i].data[dataIndex];
10347             sv = String(v);
10348             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10349                 l[sv] = true;
10350                 r[r.length] = v;
10351             }
10352         }
10353         return r;
10354     },
10355
10356     /**
10357      * Revert to a view of the Record cache with no filtering applied.
10358      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10359      */
10360     clearFilter : function(suppressEvent){
10361         if(this.snapshot && this.snapshot != this.data){
10362             this.data = this.snapshot;
10363             delete this.snapshot;
10364             if(suppressEvent !== true){
10365                 this.fireEvent("datachanged", this);
10366             }
10367         }
10368     },
10369
10370     // private
10371     afterEdit : function(record){
10372         if(this.modified.indexOf(record) == -1){
10373             this.modified.push(record);
10374         }
10375         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10376     },
10377     
10378     // private
10379     afterReject : function(record){
10380         this.modified.remove(record);
10381         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10382     },
10383
10384     // private
10385     afterCommit : function(record){
10386         this.modified.remove(record);
10387         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10388     },
10389
10390     /**
10391      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10392      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10393      */
10394     commitChanges : function(){
10395         var m = this.modified.slice(0);
10396         this.modified = [];
10397         for(var i = 0, len = m.length; i < len; i++){
10398             m[i].commit();
10399         }
10400     },
10401
10402     /**
10403      * Cancel outstanding changes on all changed records.
10404      */
10405     rejectChanges : function(){
10406         var m = this.modified.slice(0);
10407         this.modified = [];
10408         for(var i = 0, len = m.length; i < len; i++){
10409             m[i].reject();
10410         }
10411     },
10412
10413     onMetaChange : function(meta, rtype, o){
10414         this.recordType = rtype;
10415         this.fields = rtype.prototype.fields;
10416         delete this.snapshot;
10417         this.sortInfo = meta.sortInfo || this.sortInfo;
10418         this.modified = [];
10419         this.fireEvent('metachange', this, this.reader.meta);
10420     },
10421     
10422     moveIndex : function(data, type)
10423     {
10424         var index = this.indexOf(data);
10425         
10426         var newIndex = index + type;
10427         
10428         this.remove(data);
10429         
10430         this.insert(newIndex, data);
10431         
10432     }
10433 });/*
10434  * Based on:
10435  * Ext JS Library 1.1.1
10436  * Copyright(c) 2006-2007, Ext JS, LLC.
10437  *
10438  * Originally Released Under LGPL - original licence link has changed is not relivant.
10439  *
10440  * Fork - LGPL
10441  * <script type="text/javascript">
10442  */
10443
10444 /**
10445  * @class Roo.data.SimpleStore
10446  * @extends Roo.data.Store
10447  * Small helper class to make creating Stores from Array data easier.
10448  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10449  * @cfg {Array} fields An array of field definition objects, or field name strings.
10450  * @cfg {Array} data The multi-dimensional array of data
10451  * @constructor
10452  * @param {Object} config
10453  */
10454 Roo.data.SimpleStore = function(config){
10455     Roo.data.SimpleStore.superclass.constructor.call(this, {
10456         isLocal : true,
10457         reader: new Roo.data.ArrayReader({
10458                 id: config.id
10459             },
10460             Roo.data.Record.create(config.fields)
10461         ),
10462         proxy : new Roo.data.MemoryProxy(config.data)
10463     });
10464     this.load();
10465 };
10466 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10467  * Based on:
10468  * Ext JS Library 1.1.1
10469  * Copyright(c) 2006-2007, Ext JS, LLC.
10470  *
10471  * Originally Released Under LGPL - original licence link has changed is not relivant.
10472  *
10473  * Fork - LGPL
10474  * <script type="text/javascript">
10475  */
10476
10477 /**
10478 /**
10479  * @extends Roo.data.Store
10480  * @class Roo.data.JsonStore
10481  * Small helper class to make creating Stores for JSON data easier. <br/>
10482 <pre><code>
10483 var store = new Roo.data.JsonStore({
10484     url: 'get-images.php',
10485     root: 'images',
10486     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10487 });
10488 </code></pre>
10489  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10490  * JsonReader and HttpProxy (unless inline data is provided).</b>
10491  * @cfg {Array} fields An array of field definition objects, or field name strings.
10492  * @constructor
10493  * @param {Object} config
10494  */
10495 Roo.data.JsonStore = function(c){
10496     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10497         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10498         reader: new Roo.data.JsonReader(c, c.fields)
10499     }));
10500 };
10501 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10502  * Based on:
10503  * Ext JS Library 1.1.1
10504  * Copyright(c) 2006-2007, Ext JS, LLC.
10505  *
10506  * Originally Released Under LGPL - original licence link has changed is not relivant.
10507  *
10508  * Fork - LGPL
10509  * <script type="text/javascript">
10510  */
10511
10512  
10513 Roo.data.Field = function(config){
10514     if(typeof config == "string"){
10515         config = {name: config};
10516     }
10517     Roo.apply(this, config);
10518     
10519     if(!this.type){
10520         this.type = "auto";
10521     }
10522     
10523     var st = Roo.data.SortTypes;
10524     // named sortTypes are supported, here we look them up
10525     if(typeof this.sortType == "string"){
10526         this.sortType = st[this.sortType];
10527     }
10528     
10529     // set default sortType for strings and dates
10530     if(!this.sortType){
10531         switch(this.type){
10532             case "string":
10533                 this.sortType = st.asUCString;
10534                 break;
10535             case "date":
10536                 this.sortType = st.asDate;
10537                 break;
10538             default:
10539                 this.sortType = st.none;
10540         }
10541     }
10542
10543     // define once
10544     var stripRe = /[\$,%]/g;
10545
10546     // prebuilt conversion function for this field, instead of
10547     // switching every time we're reading a value
10548     if(!this.convert){
10549         var cv, dateFormat = this.dateFormat;
10550         switch(this.type){
10551             case "":
10552             case "auto":
10553             case undefined:
10554                 cv = function(v){ return v; };
10555                 break;
10556             case "string":
10557                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10558                 break;
10559             case "int":
10560                 cv = function(v){
10561                     return v !== undefined && v !== null && v !== '' ?
10562                            parseInt(String(v).replace(stripRe, ""), 10) : '';
10563                     };
10564                 break;
10565             case "float":
10566                 cv = function(v){
10567                     return v !== undefined && v !== null && v !== '' ?
10568                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
10569                     };
10570                 break;
10571             case "bool":
10572             case "boolean":
10573                 cv = function(v){ return v === true || v === "true" || v == 1; };
10574                 break;
10575             case "date":
10576                 cv = function(v){
10577                     if(!v){
10578                         return '';
10579                     }
10580                     if(v instanceof Date){
10581                         return v;
10582                     }
10583                     if(dateFormat){
10584                         if(dateFormat == "timestamp"){
10585                             return new Date(v*1000);
10586                         }
10587                         return Date.parseDate(v, dateFormat);
10588                     }
10589                     var parsed = Date.parse(v);
10590                     return parsed ? new Date(parsed) : null;
10591                 };
10592              break;
10593             
10594         }
10595         this.convert = cv;
10596     }
10597 };
10598
10599 Roo.data.Field.prototype = {
10600     dateFormat: null,
10601     defaultValue: "",
10602     mapping: null,
10603     sortType : null,
10604     sortDir : "ASC"
10605 };/*
10606  * Based on:
10607  * Ext JS Library 1.1.1
10608  * Copyright(c) 2006-2007, Ext JS, LLC.
10609  *
10610  * Originally Released Under LGPL - original licence link has changed is not relivant.
10611  *
10612  * Fork - LGPL
10613  * <script type="text/javascript">
10614  */
10615  
10616 // Base class for reading structured data from a data source.  This class is intended to be
10617 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10618
10619 /**
10620  * @class Roo.data.DataReader
10621  * Base class for reading structured data from a data source.  This class is intended to be
10622  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10623  */
10624
10625 Roo.data.DataReader = function(meta, recordType){
10626     
10627     this.meta = meta;
10628     
10629     this.recordType = recordType instanceof Array ? 
10630         Roo.data.Record.create(recordType) : recordType;
10631 };
10632
10633 Roo.data.DataReader.prototype = {
10634      /**
10635      * Create an empty record
10636      * @param {Object} data (optional) - overlay some values
10637      * @return {Roo.data.Record} record created.
10638      */
10639     newRow :  function(d) {
10640         var da =  {};
10641         this.recordType.prototype.fields.each(function(c) {
10642             switch( c.type) {
10643                 case 'int' : da[c.name] = 0; break;
10644                 case 'date' : da[c.name] = new Date(); break;
10645                 case 'float' : da[c.name] = 0.0; break;
10646                 case 'boolean' : da[c.name] = false; break;
10647                 default : da[c.name] = ""; break;
10648             }
10649             
10650         });
10651         return new this.recordType(Roo.apply(da, d));
10652     }
10653     
10654 };/*
10655  * Based on:
10656  * Ext JS Library 1.1.1
10657  * Copyright(c) 2006-2007, Ext JS, LLC.
10658  *
10659  * Originally Released Under LGPL - original licence link has changed is not relivant.
10660  *
10661  * Fork - LGPL
10662  * <script type="text/javascript">
10663  */
10664
10665 /**
10666  * @class Roo.data.DataProxy
10667  * @extends Roo.data.Observable
10668  * This class is an abstract base class for implementations which provide retrieval of
10669  * unformatted data objects.<br>
10670  * <p>
10671  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10672  * (of the appropriate type which knows how to parse the data object) to provide a block of
10673  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10674  * <p>
10675  * Custom implementations must implement the load method as described in
10676  * {@link Roo.data.HttpProxy#load}.
10677  */
10678 Roo.data.DataProxy = function(){
10679     this.addEvents({
10680         /**
10681          * @event beforeload
10682          * Fires before a network request is made to retrieve a data object.
10683          * @param {Object} This DataProxy object.
10684          * @param {Object} params The params parameter to the load function.
10685          */
10686         beforeload : true,
10687         /**
10688          * @event load
10689          * Fires before the load method's callback is called.
10690          * @param {Object} This DataProxy object.
10691          * @param {Object} o The data object.
10692          * @param {Object} arg The callback argument object passed to the load function.
10693          */
10694         load : true,
10695         /**
10696          * @event loadexception
10697          * Fires if an Exception occurs during data retrieval.
10698          * @param {Object} This DataProxy object.
10699          * @param {Object} o The data object.
10700          * @param {Object} arg The callback argument object passed to the load function.
10701          * @param {Object} e The Exception.
10702          */
10703         loadexception : true
10704     });
10705     Roo.data.DataProxy.superclass.constructor.call(this);
10706 };
10707
10708 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10709
10710     /**
10711      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10712      */
10713 /*
10714  * Based on:
10715  * Ext JS Library 1.1.1
10716  * Copyright(c) 2006-2007, Ext JS, LLC.
10717  *
10718  * Originally Released Under LGPL - original licence link has changed is not relivant.
10719  *
10720  * Fork - LGPL
10721  * <script type="text/javascript">
10722  */
10723 /**
10724  * @class Roo.data.MemoryProxy
10725  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10726  * to the Reader when its load method is called.
10727  * @constructor
10728  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10729  */
10730 Roo.data.MemoryProxy = function(data){
10731     if (data.data) {
10732         data = data.data;
10733     }
10734     Roo.data.MemoryProxy.superclass.constructor.call(this);
10735     this.data = data;
10736 };
10737
10738 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10739     /**
10740      * Load data from the requested source (in this case an in-memory
10741      * data object passed to the constructor), read the data object into
10742      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10743      * process that block using the passed callback.
10744      * @param {Object} params This parameter is not used by the MemoryProxy class.
10745      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10746      * object into a block of Roo.data.Records.
10747      * @param {Function} callback The function into which to pass the block of Roo.data.records.
10748      * The function must be passed <ul>
10749      * <li>The Record block object</li>
10750      * <li>The "arg" argument from the load function</li>
10751      * <li>A boolean success indicator</li>
10752      * </ul>
10753      * @param {Object} scope The scope in which to call the callback
10754      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10755      */
10756     load : function(params, reader, callback, scope, arg){
10757         params = params || {};
10758         var result;
10759         try {
10760             result = reader.readRecords(this.data);
10761         }catch(e){
10762             this.fireEvent("loadexception", this, arg, null, e);
10763             callback.call(scope, null, arg, false);
10764             return;
10765         }
10766         callback.call(scope, result, arg, true);
10767     },
10768     
10769     // private
10770     update : function(params, records){
10771         
10772     }
10773 });/*
10774  * Based on:
10775  * Ext JS Library 1.1.1
10776  * Copyright(c) 2006-2007, Ext JS, LLC.
10777  *
10778  * Originally Released Under LGPL - original licence link has changed is not relivant.
10779  *
10780  * Fork - LGPL
10781  * <script type="text/javascript">
10782  */
10783 /**
10784  * @class Roo.data.HttpProxy
10785  * @extends Roo.data.DataProxy
10786  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10787  * configured to reference a certain URL.<br><br>
10788  * <p>
10789  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10790  * from which the running page was served.<br><br>
10791  * <p>
10792  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10793  * <p>
10794  * Be aware that to enable the browser to parse an XML document, the server must set
10795  * the Content-Type header in the HTTP response to "text/xml".
10796  * @constructor
10797  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10798  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
10799  * will be used to make the request.
10800  */
10801 Roo.data.HttpProxy = function(conn){
10802     Roo.data.HttpProxy.superclass.constructor.call(this);
10803     // is conn a conn config or a real conn?
10804     this.conn = conn;
10805     this.useAjax = !conn || !conn.events;
10806   
10807 };
10808
10809 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10810     // thse are take from connection...
10811     
10812     /**
10813      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10814      */
10815     /**
10816      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10817      * extra parameters to each request made by this object. (defaults to undefined)
10818      */
10819     /**
10820      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10821      *  to each request made by this object. (defaults to undefined)
10822      */
10823     /**
10824      * @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)
10825      */
10826     /**
10827      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10828      */
10829      /**
10830      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10831      * @type Boolean
10832      */
10833   
10834
10835     /**
10836      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10837      * @type Boolean
10838      */
10839     /**
10840      * Return the {@link Roo.data.Connection} object being used by this Proxy.
10841      * @return {Connection} The Connection object. This object may be used to subscribe to events on
10842      * a finer-grained basis than the DataProxy events.
10843      */
10844     getConnection : function(){
10845         return this.useAjax ? Roo.Ajax : this.conn;
10846     },
10847
10848     /**
10849      * Load data from the configured {@link Roo.data.Connection}, read the data object into
10850      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10851      * process that block using the passed callback.
10852      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10853      * for the request to the remote server.
10854      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10855      * object into a block of Roo.data.Records.
10856      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10857      * The function must be passed <ul>
10858      * <li>The Record block object</li>
10859      * <li>The "arg" argument from the load function</li>
10860      * <li>A boolean success indicator</li>
10861      * </ul>
10862      * @param {Object} scope The scope in which to call the callback
10863      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10864      */
10865     load : function(params, reader, callback, scope, arg){
10866         if(this.fireEvent("beforeload", this, params) !== false){
10867             var  o = {
10868                 params : params || {},
10869                 request: {
10870                     callback : callback,
10871                     scope : scope,
10872                     arg : arg
10873                 },
10874                 reader: reader,
10875                 callback : this.loadResponse,
10876                 scope: this
10877             };
10878             if(this.useAjax){
10879                 Roo.applyIf(o, this.conn);
10880                 if(this.activeRequest){
10881                     Roo.Ajax.abort(this.activeRequest);
10882                 }
10883                 this.activeRequest = Roo.Ajax.request(o);
10884             }else{
10885                 this.conn.request(o);
10886             }
10887         }else{
10888             callback.call(scope||this, null, arg, false);
10889         }
10890     },
10891
10892     // private
10893     loadResponse : function(o, success, response){
10894         delete this.activeRequest;
10895         if(!success){
10896             this.fireEvent("loadexception", this, o, response);
10897             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10898             return;
10899         }
10900         var result;
10901         try {
10902             result = o.reader.read(response);
10903         }catch(e){
10904             this.fireEvent("loadexception", this, o, response, e);
10905             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10906             return;
10907         }
10908         
10909         this.fireEvent("load", this, o, o.request.arg);
10910         o.request.callback.call(o.request.scope, result, o.request.arg, true);
10911     },
10912
10913     // private
10914     update : function(dataSet){
10915
10916     },
10917
10918     // private
10919     updateResponse : function(dataSet){
10920
10921     }
10922 });/*
10923  * Based on:
10924  * Ext JS Library 1.1.1
10925  * Copyright(c) 2006-2007, Ext JS, LLC.
10926  *
10927  * Originally Released Under LGPL - original licence link has changed is not relivant.
10928  *
10929  * Fork - LGPL
10930  * <script type="text/javascript">
10931  */
10932
10933 /**
10934  * @class Roo.data.ScriptTagProxy
10935  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10936  * other than the originating domain of the running page.<br><br>
10937  * <p>
10938  * <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
10939  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10940  * <p>
10941  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10942  * source code that is used as the source inside a &lt;script> tag.<br><br>
10943  * <p>
10944  * In order for the browser to process the returned data, the server must wrap the data object
10945  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10946  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10947  * depending on whether the callback name was passed:
10948  * <p>
10949  * <pre><code>
10950 boolean scriptTag = false;
10951 String cb = request.getParameter("callback");
10952 if (cb != null) {
10953     scriptTag = true;
10954     response.setContentType("text/javascript");
10955 } else {
10956     response.setContentType("application/x-json");
10957 }
10958 Writer out = response.getWriter();
10959 if (scriptTag) {
10960     out.write(cb + "(");
10961 }
10962 out.print(dataBlock.toJsonString());
10963 if (scriptTag) {
10964     out.write(");");
10965 }
10966 </pre></code>
10967  *
10968  * @constructor
10969  * @param {Object} config A configuration object.
10970  */
10971 Roo.data.ScriptTagProxy = function(config){
10972     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10973     Roo.apply(this, config);
10974     this.head = document.getElementsByTagName("head")[0];
10975 };
10976
10977 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10978
10979 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10980     /**
10981      * @cfg {String} url The URL from which to request the data object.
10982      */
10983     /**
10984      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
10985      */
10986     timeout : 30000,
10987     /**
10988      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
10989      * the server the name of the callback function set up by the load call to process the returned data object.
10990      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
10991      * javascript output which calls this named function passing the data object as its only parameter.
10992      */
10993     callbackParam : "callback",
10994     /**
10995      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
10996      * name to the request.
10997      */
10998     nocache : true,
10999
11000     /**
11001      * Load data from the configured URL, read the data object into
11002      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11003      * process that block using the passed callback.
11004      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11005      * for the request to the remote server.
11006      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11007      * object into a block of Roo.data.Records.
11008      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11009      * The function must be passed <ul>
11010      * <li>The Record block object</li>
11011      * <li>The "arg" argument from the load function</li>
11012      * <li>A boolean success indicator</li>
11013      * </ul>
11014      * @param {Object} scope The scope in which to call the callback
11015      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11016      */
11017     load : function(params, reader, callback, scope, arg){
11018         if(this.fireEvent("beforeload", this, params) !== false){
11019
11020             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11021
11022             var url = this.url;
11023             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11024             if(this.nocache){
11025                 url += "&_dc=" + (new Date().getTime());
11026             }
11027             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11028             var trans = {
11029                 id : transId,
11030                 cb : "stcCallback"+transId,
11031                 scriptId : "stcScript"+transId,
11032                 params : params,
11033                 arg : arg,
11034                 url : url,
11035                 callback : callback,
11036                 scope : scope,
11037                 reader : reader
11038             };
11039             var conn = this;
11040
11041             window[trans.cb] = function(o){
11042                 conn.handleResponse(o, trans);
11043             };
11044
11045             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11046
11047             if(this.autoAbort !== false){
11048                 this.abort();
11049             }
11050
11051             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11052
11053             var script = document.createElement("script");
11054             script.setAttribute("src", url);
11055             script.setAttribute("type", "text/javascript");
11056             script.setAttribute("id", trans.scriptId);
11057             this.head.appendChild(script);
11058
11059             this.trans = trans;
11060         }else{
11061             callback.call(scope||this, null, arg, false);
11062         }
11063     },
11064
11065     // private
11066     isLoading : function(){
11067         return this.trans ? true : false;
11068     },
11069
11070     /**
11071      * Abort the current server request.
11072      */
11073     abort : function(){
11074         if(this.isLoading()){
11075             this.destroyTrans(this.trans);
11076         }
11077     },
11078
11079     // private
11080     destroyTrans : function(trans, isLoaded){
11081         this.head.removeChild(document.getElementById(trans.scriptId));
11082         clearTimeout(trans.timeoutId);
11083         if(isLoaded){
11084             window[trans.cb] = undefined;
11085             try{
11086                 delete window[trans.cb];
11087             }catch(e){}
11088         }else{
11089             // if hasn't been loaded, wait for load to remove it to prevent script error
11090             window[trans.cb] = function(){
11091                 window[trans.cb] = undefined;
11092                 try{
11093                     delete window[trans.cb];
11094                 }catch(e){}
11095             };
11096         }
11097     },
11098
11099     // private
11100     handleResponse : function(o, trans){
11101         this.trans = false;
11102         this.destroyTrans(trans, true);
11103         var result;
11104         try {
11105             result = trans.reader.readRecords(o);
11106         }catch(e){
11107             this.fireEvent("loadexception", this, o, trans.arg, e);
11108             trans.callback.call(trans.scope||window, null, trans.arg, false);
11109             return;
11110         }
11111         this.fireEvent("load", this, o, trans.arg);
11112         trans.callback.call(trans.scope||window, result, trans.arg, true);
11113     },
11114
11115     // private
11116     handleFailure : function(trans){
11117         this.trans = false;
11118         this.destroyTrans(trans, false);
11119         this.fireEvent("loadexception", this, null, trans.arg);
11120         trans.callback.call(trans.scope||window, null, trans.arg, false);
11121     }
11122 });/*
11123  * Based on:
11124  * Ext JS Library 1.1.1
11125  * Copyright(c) 2006-2007, Ext JS, LLC.
11126  *
11127  * Originally Released Under LGPL - original licence link has changed is not relivant.
11128  *
11129  * Fork - LGPL
11130  * <script type="text/javascript">
11131  */
11132
11133 /**
11134  * @class Roo.data.JsonReader
11135  * @extends Roo.data.DataReader
11136  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11137  * based on mappings in a provided Roo.data.Record constructor.
11138  * 
11139  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11140  * in the reply previously. 
11141  * 
11142  * <p>
11143  * Example code:
11144  * <pre><code>
11145 var RecordDef = Roo.data.Record.create([
11146     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
11147     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
11148 ]);
11149 var myReader = new Roo.data.JsonReader({
11150     totalProperty: "results",    // The property which contains the total dataset size (optional)
11151     root: "rows",                // The property which contains an Array of row objects
11152     id: "id"                     // The property within each row object that provides an ID for the record (optional)
11153 }, RecordDef);
11154 </code></pre>
11155  * <p>
11156  * This would consume a JSON file like this:
11157  * <pre><code>
11158 { 'results': 2, 'rows': [
11159     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11160     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11161 }
11162 </code></pre>
11163  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11164  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11165  * paged from the remote server.
11166  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11167  * @cfg {String} root name of the property which contains the Array of row objects.
11168  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11169  * @cfg {Array} fields Array of field definition objects
11170  * @constructor
11171  * Create a new JsonReader
11172  * @param {Object} meta Metadata configuration options
11173  * @param {Object} recordType Either an Array of field definition objects,
11174  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11175  */
11176 Roo.data.JsonReader = function(meta, recordType){
11177     
11178     meta = meta || {};
11179     // set some defaults:
11180     Roo.applyIf(meta, {
11181         totalProperty: 'total',
11182         successProperty : 'success',
11183         root : 'data',
11184         id : 'id'
11185     });
11186     
11187     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11188 };
11189 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11190     
11191     /**
11192      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
11193      * Used by Store query builder to append _requestMeta to params.
11194      * 
11195      */
11196     metaFromRemote : false,
11197     /**
11198      * This method is only used by a DataProxy which has retrieved data from a remote server.
11199      * @param {Object} response The XHR object which contains the JSON data in its responseText.
11200      * @return {Object} data A data block which is used by an Roo.data.Store object as
11201      * a cache of Roo.data.Records.
11202      */
11203     read : function(response){
11204         var json = response.responseText;
11205        
11206         var o = /* eval:var:o */ eval("("+json+")");
11207         if(!o) {
11208             throw {message: "JsonReader.read: Json object not found"};
11209         }
11210         
11211         if(o.metaData){
11212             
11213             delete this.ef;
11214             this.metaFromRemote = true;
11215             this.meta = o.metaData;
11216             this.recordType = Roo.data.Record.create(o.metaData.fields);
11217             this.onMetaChange(this.meta, this.recordType, o);
11218         }
11219         return this.readRecords(o);
11220     },
11221
11222     // private function a store will implement
11223     onMetaChange : function(meta, recordType, o){
11224
11225     },
11226
11227     /**
11228          * @ignore
11229          */
11230     simpleAccess: function(obj, subsc) {
11231         return obj[subsc];
11232     },
11233
11234         /**
11235          * @ignore
11236          */
11237     getJsonAccessor: function(){
11238         var re = /[\[\.]/;
11239         return function(expr) {
11240             try {
11241                 return(re.test(expr))
11242                     ? new Function("obj", "return obj." + expr)
11243                     : function(obj){
11244                         return obj[expr];
11245                     };
11246             } catch(e){}
11247             return Roo.emptyFn;
11248         };
11249     }(),
11250
11251     /**
11252      * Create a data block containing Roo.data.Records from an XML document.
11253      * @param {Object} o An object which contains an Array of row objects in the property specified
11254      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11255      * which contains the total size of the dataset.
11256      * @return {Object} data A data block which is used by an Roo.data.Store object as
11257      * a cache of Roo.data.Records.
11258      */
11259     readRecords : function(o){
11260         /**
11261          * After any data loads, the raw JSON data is available for further custom processing.
11262          * @type Object
11263          */
11264         this.o = o;
11265         var s = this.meta, Record = this.recordType,
11266             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11267
11268 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
11269         if (!this.ef) {
11270             if(s.totalProperty) {
11271                     this.getTotal = this.getJsonAccessor(s.totalProperty);
11272                 }
11273                 if(s.successProperty) {
11274                     this.getSuccess = this.getJsonAccessor(s.successProperty);
11275                 }
11276                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11277                 if (s.id) {
11278                         var g = this.getJsonAccessor(s.id);
11279                         this.getId = function(rec) {
11280                                 var r = g(rec);  
11281                                 return (r === undefined || r === "") ? null : r;
11282                         };
11283                 } else {
11284                         this.getId = function(){return null;};
11285                 }
11286             this.ef = [];
11287             for(var jj = 0; jj < fl; jj++){
11288                 f = fi[jj];
11289                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11290                 this.ef[jj] = this.getJsonAccessor(map);
11291             }
11292         }
11293
11294         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11295         if(s.totalProperty){
11296             var vt = parseInt(this.getTotal(o), 10);
11297             if(!isNaN(vt)){
11298                 totalRecords = vt;
11299             }
11300         }
11301         if(s.successProperty){
11302             var vs = this.getSuccess(o);
11303             if(vs === false || vs === 'false'){
11304                 success = false;
11305             }
11306         }
11307         var records = [];
11308         for(var i = 0; i < c; i++){
11309                 var n = root[i];
11310             var values = {};
11311             var id = this.getId(n);
11312             for(var j = 0; j < fl; j++){
11313                 f = fi[j];
11314             var v = this.ef[j](n);
11315             if (!f.convert) {
11316                 Roo.log('missing convert for ' + f.name);
11317                 Roo.log(f);
11318                 continue;
11319             }
11320             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11321             }
11322             var record = new Record(values, id);
11323             record.json = n;
11324             records[i] = record;
11325         }
11326         return {
11327             raw : o,
11328             success : success,
11329             records : records,
11330             totalRecords : totalRecords
11331         };
11332     }
11333 });/*
11334  * Based on:
11335  * Ext JS Library 1.1.1
11336  * Copyright(c) 2006-2007, Ext JS, LLC.
11337  *
11338  * Originally Released Under LGPL - original licence link has changed is not relivant.
11339  *
11340  * Fork - LGPL
11341  * <script type="text/javascript">
11342  */
11343
11344 /**
11345  * @class Roo.data.ArrayReader
11346  * @extends Roo.data.DataReader
11347  * Data reader class to create an Array of Roo.data.Record objects from an Array.
11348  * Each element of that Array represents a row of data fields. The
11349  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11350  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11351  * <p>
11352  * Example code:.
11353  * <pre><code>
11354 var RecordDef = Roo.data.Record.create([
11355     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
11356     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
11357 ]);
11358 var myReader = new Roo.data.ArrayReader({
11359     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
11360 }, RecordDef);
11361 </code></pre>
11362  * <p>
11363  * This would consume an Array like this:
11364  * <pre><code>
11365 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11366   </code></pre>
11367  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11368  * @constructor
11369  * Create a new JsonReader
11370  * @param {Object} meta Metadata configuration options.
11371  * @param {Object} recordType Either an Array of field definition objects
11372  * as specified to {@link Roo.data.Record#create},
11373  * or an {@link Roo.data.Record} object
11374  * created using {@link Roo.data.Record#create}.
11375  */
11376 Roo.data.ArrayReader = function(meta, recordType){
11377     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11378 };
11379
11380 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11381     /**
11382      * Create a data block containing Roo.data.Records from an XML document.
11383      * @param {Object} o An Array of row objects which represents the dataset.
11384      * @return {Object} data A data block which is used by an Roo.data.Store object as
11385      * a cache of Roo.data.Records.
11386      */
11387     readRecords : function(o){
11388         var sid = this.meta ? this.meta.id : null;
11389         var recordType = this.recordType, fields = recordType.prototype.fields;
11390         var records = [];
11391         var root = o;
11392             for(var i = 0; i < root.length; i++){
11393                     var n = root[i];
11394                 var values = {};
11395                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11396                 for(var j = 0, jlen = fields.length; j < jlen; j++){
11397                 var f = fields.items[j];
11398                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11399                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11400                 v = f.convert(v);
11401                 values[f.name] = v;
11402             }
11403                 var record = new recordType(values, id);
11404                 record.json = n;
11405                 records[records.length] = record;
11406             }
11407             return {
11408                 records : records,
11409                 totalRecords : records.length
11410             };
11411     }
11412 });/*
11413  * - LGPL
11414  * * 
11415  */
11416
11417 /**
11418  * @class Roo.bootstrap.ComboBox
11419  * @extends Roo.bootstrap.TriggerField
11420  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11421  * @cfg {Boolean} append (true|false) default false
11422  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11423  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11424  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11425  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11426  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11427  * @cfg {Boolean} animate default true
11428  * @cfg {Boolean} emptyResultText only for touch device
11429  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11430  * @constructor
11431  * Create a new ComboBox.
11432  * @param {Object} config Configuration options
11433  */
11434 Roo.bootstrap.ComboBox = function(config){
11435     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11436     this.addEvents({
11437         /**
11438          * @event expand
11439          * Fires when the dropdown list is expanded
11440              * @param {Roo.bootstrap.ComboBox} combo This combo box
11441              */
11442         'expand' : true,
11443         /**
11444          * @event collapse
11445          * Fires when the dropdown list is collapsed
11446              * @param {Roo.bootstrap.ComboBox} combo This combo box
11447              */
11448         'collapse' : true,
11449         /**
11450          * @event beforeselect
11451          * Fires before a list item is selected. Return false to cancel the selection.
11452              * @param {Roo.bootstrap.ComboBox} combo This combo box
11453              * @param {Roo.data.Record} record The data record returned from the underlying store
11454              * @param {Number} index The index of the selected item in the dropdown list
11455              */
11456         'beforeselect' : true,
11457         /**
11458          * @event select
11459          * Fires when a list item is selected
11460              * @param {Roo.bootstrap.ComboBox} combo This combo box
11461              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11462              * @param {Number} index The index of the selected item in the dropdown list
11463              */
11464         'select' : true,
11465         /**
11466          * @event beforequery
11467          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11468          * The event object passed has these properties:
11469              * @param {Roo.bootstrap.ComboBox} combo This combo box
11470              * @param {String} query The query
11471              * @param {Boolean} forceAll true to force "all" query
11472              * @param {Boolean} cancel true to cancel the query
11473              * @param {Object} e The query event object
11474              */
11475         'beforequery': true,
11476          /**
11477          * @event add
11478          * Fires when the 'add' icon is pressed (add a listener to enable add button)
11479              * @param {Roo.bootstrap.ComboBox} combo This combo box
11480              */
11481         'add' : true,
11482         /**
11483          * @event edit
11484          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11485              * @param {Roo.bootstrap.ComboBox} combo This combo box
11486              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11487              */
11488         'edit' : true,
11489         /**
11490          * @event remove
11491          * Fires when the remove value from the combobox array
11492              * @param {Roo.bootstrap.ComboBox} combo This combo box
11493              */
11494         'remove' : true,
11495         /**
11496          * @event specialfilter
11497          * Fires when specialfilter
11498             * @param {Roo.bootstrap.ComboBox} combo This combo box
11499             */
11500         'specialfilter' : true,
11501         /**
11502          * @event tick
11503          * Fires when tick the element
11504             * @param {Roo.bootstrap.ComboBox} combo This combo box
11505             */
11506         'tick' : true
11507         
11508     });
11509     
11510     this.item = [];
11511     this.tickItems = [];
11512     
11513     this.selectedIndex = -1;
11514     if(this.mode == 'local'){
11515         if(config.queryDelay === undefined){
11516             this.queryDelay = 10;
11517         }
11518         if(config.minChars === undefined){
11519             this.minChars = 0;
11520         }
11521     }
11522 };
11523
11524 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11525      
11526     /**
11527      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11528      * rendering into an Roo.Editor, defaults to false)
11529      */
11530     /**
11531      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11532      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11533      */
11534     /**
11535      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11536      */
11537     /**
11538      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11539      * the dropdown list (defaults to undefined, with no header element)
11540      */
11541
11542      /**
11543      * @cfg {String/Roo.Template} tpl The template to use to render the output
11544      */
11545      
11546      /**
11547      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11548      */
11549     listWidth: undefined,
11550     /**
11551      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11552      * mode = 'remote' or 'text' if mode = 'local')
11553      */
11554     displayField: undefined,
11555     
11556     /**
11557      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11558      * mode = 'remote' or 'value' if mode = 'local'). 
11559      * Note: use of a valueField requires the user make a selection
11560      * in order for a value to be mapped.
11561      */
11562     valueField: undefined,
11563     
11564     
11565     /**
11566      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11567      * field's data value (defaults to the underlying DOM element's name)
11568      */
11569     hiddenName: undefined,
11570     /**
11571      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11572      */
11573     listClass: '',
11574     /**
11575      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11576      */
11577     selectedClass: 'active',
11578     
11579     /**
11580      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11581      */
11582     shadow:'sides',
11583     /**
11584      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11585      * anchor positions (defaults to 'tl-bl')
11586      */
11587     listAlign: 'tl-bl?',
11588     /**
11589      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11590      */
11591     maxHeight: 300,
11592     /**
11593      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
11594      * query specified by the allQuery config option (defaults to 'query')
11595      */
11596     triggerAction: 'query',
11597     /**
11598      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11599      * (defaults to 4, does not apply if editable = false)
11600      */
11601     minChars : 4,
11602     /**
11603      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11604      * delay (typeAheadDelay) if it matches a known value (defaults to false)
11605      */
11606     typeAhead: false,
11607     /**
11608      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11609      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11610      */
11611     queryDelay: 500,
11612     /**
11613      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11614      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
11615      */
11616     pageSize: 0,
11617     /**
11618      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
11619      * when editable = true (defaults to false)
11620      */
11621     selectOnFocus:false,
11622     /**
11623      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11624      */
11625     queryParam: 'query',
11626     /**
11627      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
11628      * when mode = 'remote' (defaults to 'Loading...')
11629      */
11630     loadingText: 'Loading...',
11631     /**
11632      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11633      */
11634     resizable: false,
11635     /**
11636      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11637      */
11638     handleHeight : 8,
11639     /**
11640      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11641      * traditional select (defaults to true)
11642      */
11643     editable: true,
11644     /**
11645      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11646      */
11647     allQuery: '',
11648     /**
11649      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11650      */
11651     mode: 'remote',
11652     /**
11653      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11654      * listWidth has a higher value)
11655      */
11656     minListWidth : 70,
11657     /**
11658      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11659      * allow the user to set arbitrary text into the field (defaults to false)
11660      */
11661     forceSelection:false,
11662     /**
11663      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11664      * if typeAhead = true (defaults to 250)
11665      */
11666     typeAheadDelay : 250,
11667     /**
11668      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11669      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11670      */
11671     valueNotFoundText : undefined,
11672     /**
11673      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11674      */
11675     blockFocus : false,
11676     
11677     /**
11678      * @cfg {Boolean} disableClear Disable showing of clear button.
11679      */
11680     disableClear : false,
11681     /**
11682      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
11683      */
11684     alwaysQuery : false,
11685     
11686     /**
11687      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
11688      */
11689     multiple : false,
11690     
11691     /**
11692      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11693      */
11694     invalidClass : "has-warning",
11695     
11696     /**
11697      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11698      */
11699     validClass : "has-success",
11700     
11701     /**
11702      * @cfg {Boolean} specialFilter (true|false) special filter default false
11703      */
11704     specialFilter : false,
11705     
11706     /**
11707      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
11708      */
11709     mobileTouchView : true,
11710     
11711     //private
11712     addicon : false,
11713     editicon: false,
11714     
11715     page: 0,
11716     hasQuery: false,
11717     append: false,
11718     loadNext: false,
11719     autoFocus : true,
11720     tickable : false,
11721     btnPosition : 'right',
11722     triggerList : true,
11723     showToggleBtn : true,
11724     animate : true,
11725     emptyResultText: 'Empty',
11726     triggerText : 'Select',
11727     
11728     // element that contains real text value.. (when hidden is used..)
11729     
11730     getAutoCreate : function()
11731     {
11732         var cfg = false;
11733         
11734         /*
11735          * Touch Devices
11736          */
11737         
11738         if(Roo.isTouch && this.mobileTouchView){
11739             cfg = this.getAutoCreateTouchView();
11740             return cfg;;
11741         }
11742         
11743         /*
11744          *  Normal ComboBox
11745          */
11746         if(!this.tickable){
11747             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11748             return cfg;
11749         }
11750         
11751         /*
11752          *  ComboBox with tickable selections
11753          */
11754              
11755         var align = this.labelAlign || this.parentLabelAlign();
11756         
11757         cfg = {
11758             cls : 'form-group roo-combobox-tickable' //input-group
11759         };
11760         
11761         var buttons = {
11762             tag : 'div',
11763             cls : 'tickable-buttons',
11764             cn : [
11765                 {
11766                     tag : 'button',
11767                     type : 'button',
11768                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11769                     html : this.triggerText
11770                 },
11771                 {
11772                     tag : 'button',
11773                     type : 'button',
11774                     name : 'ok',
11775                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11776                     html : 'Done'
11777                 },
11778                 {
11779                     tag : 'button',
11780                     type : 'button',
11781                     name : 'cancel',
11782                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11783                     html : 'Cancel'
11784                 }
11785             ]
11786         };
11787         
11788         if(this.editable){
11789             buttons.cn.unshift({
11790                 tag: 'input',
11791                 cls: 'select2-search-field-input'
11792             });
11793         }
11794         
11795         var _this = this;
11796         
11797         Roo.each(buttons.cn, function(c){
11798             if (_this.size) {
11799                 c.cls += ' btn-' + _this.size;
11800             }
11801
11802             if (_this.disabled) {
11803                 c.disabled = true;
11804             }
11805         });
11806         
11807         var box = {
11808             tag: 'div',
11809             cn: [
11810                 {
11811                     tag: 'input',
11812                     type : 'hidden',
11813                     cls: 'form-hidden-field'
11814                 },
11815                 {
11816                     tag: 'ul',
11817                     cls: 'select2-choices',
11818                     cn:[
11819                         {
11820                             tag: 'li',
11821                             cls: 'select2-search-field',
11822                             cn: [
11823
11824                                 buttons
11825                             ]
11826                         }
11827                     ]
11828                 }
11829             ]
11830         };
11831         
11832         var combobox = {
11833             cls: 'select2-container input-group select2-container-multi',
11834             cn: [
11835                 box
11836 //                {
11837 //                    tag: 'ul',
11838 //                    cls: 'typeahead typeahead-long dropdown-menu',
11839 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
11840 //                }
11841             ]
11842         };
11843         
11844         if(this.hasFeedback && !this.allowBlank){
11845             
11846             var feedback = {
11847                 tag: 'span',
11848                 cls: 'glyphicon form-control-feedback'
11849             };
11850
11851             combobox.cn.push(feedback);
11852         }
11853         
11854         if (align ==='left' && this.fieldLabel.length) {
11855             
11856                 Roo.log("left and has label");
11857                 cfg.cn = [
11858                     
11859                     {
11860                         tag: 'label',
11861                         'for' :  id,
11862                         cls : 'control-label col-sm-' + this.labelWidth,
11863                         html : this.fieldLabel
11864                         
11865                     },
11866                     {
11867                         cls : "col-sm-" + (12 - this.labelWidth), 
11868                         cn: [
11869                             combobox
11870                         ]
11871                     }
11872                     
11873                 ];
11874         } else if ( this.fieldLabel.length) {
11875                 Roo.log(" label");
11876                  cfg.cn = [
11877                    
11878                     {
11879                         tag: 'label',
11880                         //cls : 'input-group-addon',
11881                         html : this.fieldLabel
11882                         
11883                     },
11884                     
11885                     combobox
11886                     
11887                 ];
11888
11889         } else {
11890             
11891                 Roo.log(" no label && no align");
11892                 cfg = combobox
11893                      
11894                 
11895         }
11896          
11897         var settings=this;
11898         ['xs','sm','md','lg'].map(function(size){
11899             if (settings[size]) {
11900                 cfg.cls += ' col-' + size + '-' + settings[size];
11901             }
11902         });
11903         
11904         return cfg;
11905         
11906     },
11907     
11908     _initEventsCalled : false,
11909     
11910     // private
11911     initEvents: function()
11912     {
11913         
11914         if (this._initEventsCalled) { // as we call render... prevent looping...
11915             return;
11916         }
11917         this._initEventsCalled = true;
11918         
11919         if (!this.store) {
11920             throw "can not find store for combo";
11921         }
11922         
11923         this.store = Roo.factory(this.store, Roo.data);
11924         
11925         // if we are building from html. then this element is so complex, that we can not really
11926         // use the rendered HTML.
11927         // so we have to trash and replace the previous code.
11928         if (Roo.XComponent.build_from_html) {
11929             
11930             // remove this element....
11931             var e = this.el.dom, k=0;
11932             while (e ) { e = e.previousSibling;  ++k;}
11933
11934             this.el.remove();
11935             
11936             this.el=false;
11937             this.rendered = false;
11938             
11939             this.render(this.parent().getChildContainer(true), k);
11940             
11941             
11942             
11943         }
11944         
11945         
11946         /*
11947          * Touch Devices
11948          */
11949         
11950         if(Roo.isTouch && this.mobileTouchView){
11951             this.initTouchView();
11952             return;
11953         }
11954         
11955         if(this.tickable){
11956             this.initTickableEvents();
11957             return;
11958         }
11959         
11960         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11961         
11962         if(this.hiddenName){
11963             
11964             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11965             
11966             this.hiddenField.dom.value =
11967                 this.hiddenValue !== undefined ? this.hiddenValue :
11968                 this.value !== undefined ? this.value : '';
11969
11970             // prevent input submission
11971             this.el.dom.removeAttribute('name');
11972             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11973              
11974              
11975         }
11976         //if(Roo.isGecko){
11977         //    this.el.dom.setAttribute('autocomplete', 'off');
11978         //}
11979         
11980         var cls = 'x-combo-list';
11981         
11982         //this.list = new Roo.Layer({
11983         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
11984         //});
11985         
11986         var _this = this;
11987         
11988         (function(){
11989             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11990             _this.list.setWidth(lw);
11991         }).defer(100);
11992         
11993         this.list.on('mouseover', this.onViewOver, this);
11994         this.list.on('mousemove', this.onViewMove, this);
11995         
11996         this.list.on('scroll', this.onViewScroll, this);
11997         
11998         /*
11999         this.list.swallowEvent('mousewheel');
12000         this.assetHeight = 0;
12001
12002         if(this.title){
12003             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12004             this.assetHeight += this.header.getHeight();
12005         }
12006
12007         this.innerList = this.list.createChild({cls:cls+'-inner'});
12008         this.innerList.on('mouseover', this.onViewOver, this);
12009         this.innerList.on('mousemove', this.onViewMove, this);
12010         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12011         
12012         if(this.allowBlank && !this.pageSize && !this.disableClear){
12013             this.footer = this.list.createChild({cls:cls+'-ft'});
12014             this.pageTb = new Roo.Toolbar(this.footer);
12015            
12016         }
12017         if(this.pageSize){
12018             this.footer = this.list.createChild({cls:cls+'-ft'});
12019             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12020                     {pageSize: this.pageSize});
12021             
12022         }
12023         
12024         if (this.pageTb && this.allowBlank && !this.disableClear) {
12025             var _this = this;
12026             this.pageTb.add(new Roo.Toolbar.Fill(), {
12027                 cls: 'x-btn-icon x-btn-clear',
12028                 text: '&#160;',
12029                 handler: function()
12030                 {
12031                     _this.collapse();
12032                     _this.clearValue();
12033                     _this.onSelect(false, -1);
12034                 }
12035             });
12036         }
12037         if (this.footer) {
12038             this.assetHeight += this.footer.getHeight();
12039         }
12040         */
12041             
12042         if(!this.tpl){
12043             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12044         }
12045
12046         this.view = new Roo.View(this.list, this.tpl, {
12047             singleSelect:true, store: this.store, selectedClass: this.selectedClass
12048         });
12049         //this.view.wrapEl.setDisplayed(false);
12050         this.view.on('click', this.onViewClick, this);
12051         
12052         
12053         
12054         this.store.on('beforeload', this.onBeforeLoad, this);
12055         this.store.on('load', this.onLoad, this);
12056         this.store.on('loadexception', this.onLoadException, this);
12057         /*
12058         if(this.resizable){
12059             this.resizer = new Roo.Resizable(this.list,  {
12060                pinned:true, handles:'se'
12061             });
12062             this.resizer.on('resize', function(r, w, h){
12063                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12064                 this.listWidth = w;
12065                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12066                 this.restrictHeight();
12067             }, this);
12068             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12069         }
12070         */
12071         if(!this.editable){
12072             this.editable = true;
12073             this.setEditable(false);
12074         }
12075         
12076         /*
12077         
12078         if (typeof(this.events.add.listeners) != 'undefined') {
12079             
12080             this.addicon = this.wrap.createChild(
12081                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
12082        
12083             this.addicon.on('click', function(e) {
12084                 this.fireEvent('add', this);
12085             }, this);
12086         }
12087         if (typeof(this.events.edit.listeners) != 'undefined') {
12088             
12089             this.editicon = this.wrap.createChild(
12090                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
12091             if (this.addicon) {
12092                 this.editicon.setStyle('margin-left', '40px');
12093             }
12094             this.editicon.on('click', function(e) {
12095                 
12096                 // we fire even  if inothing is selected..
12097                 this.fireEvent('edit', this, this.lastData );
12098                 
12099             }, this);
12100         }
12101         */
12102         
12103         this.keyNav = new Roo.KeyNav(this.inputEl(), {
12104             "up" : function(e){
12105                 this.inKeyMode = true;
12106                 this.selectPrev();
12107             },
12108
12109             "down" : function(e){
12110                 if(!this.isExpanded()){
12111                     this.onTriggerClick();
12112                 }else{
12113                     this.inKeyMode = true;
12114                     this.selectNext();
12115                 }
12116             },
12117
12118             "enter" : function(e){
12119 //                this.onViewClick();
12120                 //return true;
12121                 this.collapse();
12122                 
12123                 if(this.fireEvent("specialkey", this, e)){
12124                     this.onViewClick(false);
12125                 }
12126                 
12127                 return true;
12128             },
12129
12130             "esc" : function(e){
12131                 this.collapse();
12132             },
12133
12134             "tab" : function(e){
12135                 this.collapse();
12136                 
12137                 if(this.fireEvent("specialkey", this, e)){
12138                     this.onViewClick(false);
12139                 }
12140                 
12141                 return true;
12142             },
12143
12144             scope : this,
12145
12146             doRelay : function(foo, bar, hname){
12147                 if(hname == 'down' || this.scope.isExpanded()){
12148                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12149                 }
12150                 return true;
12151             },
12152
12153             forceKeyDown: true
12154         });
12155         
12156         
12157         this.queryDelay = Math.max(this.queryDelay || 10,
12158                 this.mode == 'local' ? 10 : 250);
12159         
12160         
12161         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12162         
12163         if(this.typeAhead){
12164             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12165         }
12166         if(this.editable !== false){
12167             this.inputEl().on("keyup", this.onKeyUp, this);
12168         }
12169         if(this.forceSelection){
12170             this.inputEl().on('blur', this.doForce, this);
12171         }
12172         
12173         if(this.multiple){
12174             this.choices = this.el.select('ul.select2-choices', true).first();
12175             this.searchField = this.el.select('ul li.select2-search-field', true).first();
12176         }
12177     },
12178     
12179     initTickableEvents: function()
12180     {   
12181         this.createList();
12182         
12183         if(this.hiddenName){
12184             
12185             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12186             
12187             this.hiddenField.dom.value =
12188                 this.hiddenValue !== undefined ? this.hiddenValue :
12189                 this.value !== undefined ? this.value : '';
12190
12191             // prevent input submission
12192             this.el.dom.removeAttribute('name');
12193             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12194              
12195              
12196         }
12197         
12198 //        this.list = this.el.select('ul.dropdown-menu',true).first();
12199         
12200         this.choices = this.el.select('ul.select2-choices', true).first();
12201         this.searchField = this.el.select('ul li.select2-search-field', true).first();
12202         if(this.triggerList){
12203             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12204         }
12205          
12206         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12207         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12208         
12209         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12210         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12211         
12212         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12213         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12214         
12215         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12216         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12217         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12218         
12219         this.okBtn.hide();
12220         this.cancelBtn.hide();
12221         
12222         var _this = this;
12223         
12224         (function(){
12225             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12226             _this.list.setWidth(lw);
12227         }).defer(100);
12228         
12229         this.list.on('mouseover', this.onViewOver, this);
12230         this.list.on('mousemove', this.onViewMove, this);
12231         
12232         this.list.on('scroll', this.onViewScroll, this);
12233         
12234         if(!this.tpl){
12235             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>';
12236         }
12237
12238         this.view = new Roo.View(this.list, this.tpl, {
12239             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12240         });
12241         
12242         //this.view.wrapEl.setDisplayed(false);
12243         this.view.on('click', this.onViewClick, this);
12244         
12245         
12246         
12247         this.store.on('beforeload', this.onBeforeLoad, this);
12248         this.store.on('load', this.onLoad, this);
12249         this.store.on('loadexception', this.onLoadException, this);
12250         
12251         if(this.editable){
12252             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12253                 "up" : function(e){
12254                     this.inKeyMode = true;
12255                     this.selectPrev();
12256                 },
12257
12258                 "down" : function(e){
12259                     this.inKeyMode = true;
12260                     this.selectNext();
12261                 },
12262
12263                 "enter" : function(e){
12264                     if(this.fireEvent("specialkey", this, e)){
12265                         this.onViewClick(false);
12266                     }
12267                     
12268                     return true;
12269                 },
12270
12271                 "esc" : function(e){
12272                     this.onTickableFooterButtonClick(e, false, false);
12273                 },
12274
12275                 "tab" : function(e){
12276                     this.fireEvent("specialkey", this, e);
12277                     
12278                     this.onTickableFooterButtonClick(e, false, false);
12279                     
12280                     return true;
12281                 },
12282
12283                 scope : this,
12284
12285                 doRelay : function(e, fn, key){
12286                     if(this.scope.isExpanded()){
12287                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12288                     }
12289                     return true;
12290                 },
12291
12292                 forceKeyDown: true
12293             });
12294         }
12295         
12296         this.queryDelay = Math.max(this.queryDelay || 10,
12297                 this.mode == 'local' ? 10 : 250);
12298         
12299         
12300         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12301         
12302         if(this.typeAhead){
12303             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12304         }
12305         
12306         if(this.editable !== false){
12307             this.tickableInputEl().on("keyup", this.onKeyUp, this);
12308         }
12309         
12310     },
12311
12312     onDestroy : function(){
12313         if(this.view){
12314             this.view.setStore(null);
12315             this.view.el.removeAllListeners();
12316             this.view.el.remove();
12317             this.view.purgeListeners();
12318         }
12319         if(this.list){
12320             this.list.dom.innerHTML  = '';
12321         }
12322         
12323         if(this.store){
12324             this.store.un('beforeload', this.onBeforeLoad, this);
12325             this.store.un('load', this.onLoad, this);
12326             this.store.un('loadexception', this.onLoadException, this);
12327         }
12328         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12329     },
12330
12331     // private
12332     fireKey : function(e){
12333         if(e.isNavKeyPress() && !this.list.isVisible()){
12334             this.fireEvent("specialkey", this, e);
12335         }
12336     },
12337
12338     // private
12339     onResize: function(w, h){
12340 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12341 //        
12342 //        if(typeof w != 'number'){
12343 //            // we do not handle it!?!?
12344 //            return;
12345 //        }
12346 //        var tw = this.trigger.getWidth();
12347 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
12348 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
12349 //        var x = w - tw;
12350 //        this.inputEl().setWidth( this.adjustWidth('input', x));
12351 //            
12352 //        //this.trigger.setStyle('left', x+'px');
12353 //        
12354 //        if(this.list && this.listWidth === undefined){
12355 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12356 //            this.list.setWidth(lw);
12357 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12358 //        }
12359         
12360     
12361         
12362     },
12363
12364     /**
12365      * Allow or prevent the user from directly editing the field text.  If false is passed,
12366      * the user will only be able to select from the items defined in the dropdown list.  This method
12367      * is the runtime equivalent of setting the 'editable' config option at config time.
12368      * @param {Boolean} value True to allow the user to directly edit the field text
12369      */
12370     setEditable : function(value){
12371         if(value == this.editable){
12372             return;
12373         }
12374         this.editable = value;
12375         if(!value){
12376             this.inputEl().dom.setAttribute('readOnly', true);
12377             this.inputEl().on('mousedown', this.onTriggerClick,  this);
12378             this.inputEl().addClass('x-combo-noedit');
12379         }else{
12380             this.inputEl().dom.setAttribute('readOnly', false);
12381             this.inputEl().un('mousedown', this.onTriggerClick,  this);
12382             this.inputEl().removeClass('x-combo-noedit');
12383         }
12384     },
12385
12386     // private
12387     
12388     onBeforeLoad : function(combo,opts){
12389         if(!this.hasFocus){
12390             return;
12391         }
12392          if (!opts.add) {
12393             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12394          }
12395         this.restrictHeight();
12396         this.selectedIndex = -1;
12397     },
12398
12399     // private
12400     onLoad : function(){
12401         
12402         this.hasQuery = false;
12403         
12404         if(!this.hasFocus){
12405             return;
12406         }
12407         
12408         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12409             this.loading.hide();
12410         }
12411              
12412         if(this.store.getCount() > 0){
12413             this.expand();
12414             this.restrictHeight();
12415             if(this.lastQuery == this.allQuery){
12416                 if(this.editable && !this.tickable){
12417                     this.inputEl().dom.select();
12418                 }
12419                 
12420                 if(
12421                     !this.selectByValue(this.value, true) &&
12422                     this.autoFocus && 
12423                     (
12424                         !this.store.lastOptions ||
12425                         typeof(this.store.lastOptions.add) == 'undefined' || 
12426                         this.store.lastOptions.add != true
12427                     )
12428                 ){
12429                     this.select(0, true);
12430                 }
12431             }else{
12432                 if(this.autoFocus){
12433                     this.selectNext();
12434                 }
12435                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12436                     this.taTask.delay(this.typeAheadDelay);
12437                 }
12438             }
12439         }else{
12440             this.onEmptyResults();
12441         }
12442         
12443         //this.el.focus();
12444     },
12445     // private
12446     onLoadException : function()
12447     {
12448         this.hasQuery = false;
12449         
12450         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12451             this.loading.hide();
12452         }
12453         
12454         if(this.tickable && this.editable){
12455             return;
12456         }
12457         
12458         this.collapse();
12459         // only causes errors at present
12460         //Roo.log(this.store.reader.jsonData);
12461         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12462             // fixme
12463             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12464         //}
12465         
12466         
12467     },
12468     // private
12469     onTypeAhead : function(){
12470         if(this.store.getCount() > 0){
12471             var r = this.store.getAt(0);
12472             var newValue = r.data[this.displayField];
12473             var len = newValue.length;
12474             var selStart = this.getRawValue().length;
12475             
12476             if(selStart != len){
12477                 this.setRawValue(newValue);
12478                 this.selectText(selStart, newValue.length);
12479             }
12480         }
12481     },
12482
12483     // private
12484     onSelect : function(record, index){
12485         
12486         if(this.fireEvent('beforeselect', this, record, index) !== false){
12487         
12488             this.setFromData(index > -1 ? record.data : false);
12489             
12490             this.collapse();
12491             this.fireEvent('select', this, record, index);
12492         }
12493     },
12494
12495     /**
12496      * Returns the currently selected field value or empty string if no value is set.
12497      * @return {String} value The selected value
12498      */
12499     getValue : function(){
12500         
12501         if(this.multiple){
12502             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12503         }
12504         
12505         if(this.valueField){
12506             return typeof this.value != 'undefined' ? this.value : '';
12507         }else{
12508             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12509         }
12510     },
12511
12512     /**
12513      * Clears any text/value currently set in the field
12514      */
12515     clearValue : function(){
12516         if(this.hiddenField){
12517             this.hiddenField.dom.value = '';
12518         }
12519         this.value = '';
12520         this.setRawValue('');
12521         this.lastSelectionText = '';
12522         this.lastData = false;
12523         
12524         var close = this.closeTriggerEl();
12525         
12526         if(close){
12527             close.hide();
12528         }
12529         
12530     },
12531
12532     /**
12533      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
12534      * will be displayed in the field.  If the value does not match the data value of an existing item,
12535      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12536      * Otherwise the field will be blank (although the value will still be set).
12537      * @param {String} value The value to match
12538      */
12539     setValue : function(v){
12540         if(this.multiple){
12541             this.syncValue();
12542             return;
12543         }
12544         
12545         var text = v;
12546         if(this.valueField){
12547             var r = this.findRecord(this.valueField, v);
12548             if(r){
12549                 text = r.data[this.displayField];
12550             }else if(this.valueNotFoundText !== undefined){
12551                 text = this.valueNotFoundText;
12552             }
12553         }
12554         this.lastSelectionText = text;
12555         if(this.hiddenField){
12556             this.hiddenField.dom.value = v;
12557         }
12558         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12559         this.value = v;
12560         
12561         var close = this.closeTriggerEl();
12562         
12563         if(close){
12564             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12565         }
12566     },
12567     /**
12568      * @property {Object} the last set data for the element
12569      */
12570     
12571     lastData : false,
12572     /**
12573      * Sets the value of the field based on a object which is related to the record format for the store.
12574      * @param {Object} value the value to set as. or false on reset?
12575      */
12576     setFromData : function(o){
12577         
12578         if(this.multiple){
12579             this.addItem(o);
12580             return;
12581         }
12582             
12583         var dv = ''; // display value
12584         var vv = ''; // value value..
12585         this.lastData = o;
12586         if (this.displayField) {
12587             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12588         } else {
12589             // this is an error condition!!!
12590             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12591         }
12592         
12593         if(this.valueField){
12594             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12595         }
12596         
12597         var close = this.closeTriggerEl();
12598         
12599         if(close){
12600             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12601         }
12602         
12603         if(this.hiddenField){
12604             this.hiddenField.dom.value = vv;
12605             
12606             this.lastSelectionText = dv;
12607             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12608             this.value = vv;
12609             return;
12610         }
12611         // no hidden field.. - we store the value in 'value', but still display
12612         // display field!!!!
12613         this.lastSelectionText = dv;
12614         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12615         this.value = vv;
12616         
12617         
12618         
12619     },
12620     // private
12621     reset : function(){
12622         // overridden so that last data is reset..
12623         
12624         if(this.multiple){
12625             this.clearItem();
12626             return;
12627         }
12628         
12629         this.setValue(this.originalValue);
12630         this.clearInvalid();
12631         this.lastData = false;
12632         if (this.view) {
12633             this.view.clearSelections();
12634         }
12635     },
12636     // private
12637     findRecord : function(prop, value){
12638         var record;
12639         if(this.store.getCount() > 0){
12640             this.store.each(function(r){
12641                 if(r.data[prop] == value){
12642                     record = r;
12643                     return false;
12644                 }
12645                 return true;
12646             });
12647         }
12648         return record;
12649     },
12650     
12651     getName: function()
12652     {
12653         // returns hidden if it's set..
12654         if (!this.rendered) {return ''};
12655         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
12656         
12657     },
12658     // private
12659     onViewMove : function(e, t){
12660         this.inKeyMode = false;
12661     },
12662
12663     // private
12664     onViewOver : function(e, t){
12665         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12666             return;
12667         }
12668         var item = this.view.findItemFromChild(t);
12669         
12670         if(item){
12671             var index = this.view.indexOf(item);
12672             this.select(index, false);
12673         }
12674     },
12675
12676     // private
12677     onViewClick : function(view, doFocus, el, e)
12678     {
12679         var index = this.view.getSelectedIndexes()[0];
12680         
12681         var r = this.store.getAt(index);
12682         
12683         if(this.tickable){
12684             
12685             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12686                 return;
12687             }
12688             
12689             var rm = false;
12690             var _this = this;
12691             
12692             Roo.each(this.tickItems, function(v,k){
12693                 
12694                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12695                     Roo.log(v);
12696                     _this.tickItems.splice(k, 1);
12697                     
12698                     if(typeof(e) == 'undefined' && view == false){
12699                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12700                     }
12701                     
12702                     rm = true;
12703                     return;
12704                 }
12705             });
12706             
12707             if(rm){
12708                 return;
12709             }
12710             
12711             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
12712                 this.tickItems.push(r.data);
12713             }
12714             
12715             if(typeof(e) == 'undefined' && view == false){
12716                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12717             }
12718                     
12719             return;
12720         }
12721         
12722         if(r){
12723             this.onSelect(r, index);
12724         }
12725         if(doFocus !== false && !this.blockFocus){
12726             this.inputEl().focus();
12727         }
12728     },
12729
12730     // private
12731     restrictHeight : function(){
12732         //this.innerList.dom.style.height = '';
12733         //var inner = this.innerList.dom;
12734         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12735         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12736         //this.list.beginUpdate();
12737         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12738         this.list.alignTo(this.inputEl(), this.listAlign);
12739         this.list.alignTo(this.inputEl(), this.listAlign);
12740         //this.list.endUpdate();
12741     },
12742
12743     // private
12744     onEmptyResults : function(){
12745         
12746         if(this.tickable && this.editable){
12747             this.restrictHeight();
12748             return;
12749         }
12750         
12751         this.collapse();
12752     },
12753
12754     /**
12755      * Returns true if the dropdown list is expanded, else false.
12756      */
12757     isExpanded : function(){
12758         return this.list.isVisible();
12759     },
12760
12761     /**
12762      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12763      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12764      * @param {String} value The data value of the item to select
12765      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12766      * selected item if it is not currently in view (defaults to true)
12767      * @return {Boolean} True if the value matched an item in the list, else false
12768      */
12769     selectByValue : function(v, scrollIntoView){
12770         if(v !== undefined && v !== null){
12771             var r = this.findRecord(this.valueField || this.displayField, v);
12772             if(r){
12773                 this.select(this.store.indexOf(r), scrollIntoView);
12774                 return true;
12775             }
12776         }
12777         return false;
12778     },
12779
12780     /**
12781      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12782      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12783      * @param {Number} index The zero-based index of the list item to select
12784      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12785      * selected item if it is not currently in view (defaults to true)
12786      */
12787     select : function(index, scrollIntoView){
12788         this.selectedIndex = index;
12789         this.view.select(index);
12790         if(scrollIntoView !== false){
12791             var el = this.view.getNode(index);
12792             /*
12793              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12794              */
12795             if(el){
12796                 this.list.scrollChildIntoView(el, false);
12797             }
12798         }
12799     },
12800
12801     // private
12802     selectNext : function(){
12803         var ct = this.store.getCount();
12804         if(ct > 0){
12805             if(this.selectedIndex == -1){
12806                 this.select(0);
12807             }else if(this.selectedIndex < ct-1){
12808                 this.select(this.selectedIndex+1);
12809             }
12810         }
12811     },
12812
12813     // private
12814     selectPrev : function(){
12815         var ct = this.store.getCount();
12816         if(ct > 0){
12817             if(this.selectedIndex == -1){
12818                 this.select(0);
12819             }else if(this.selectedIndex != 0){
12820                 this.select(this.selectedIndex-1);
12821             }
12822         }
12823     },
12824
12825     // private
12826     onKeyUp : function(e){
12827         if(this.editable !== false && !e.isSpecialKey()){
12828             this.lastKey = e.getKey();
12829             this.dqTask.delay(this.queryDelay);
12830         }
12831     },
12832
12833     // private
12834     validateBlur : function(){
12835         return !this.list || !this.list.isVisible();   
12836     },
12837
12838     // private
12839     initQuery : function(){
12840         
12841         var v = this.getRawValue();
12842         
12843         if(this.tickable && this.editable){
12844             v = this.tickableInputEl().getValue();
12845         }
12846         
12847         this.doQuery(v);
12848     },
12849
12850     // private
12851     doForce : function(){
12852         if(this.inputEl().dom.value.length > 0){
12853             this.inputEl().dom.value =
12854                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12855              
12856         }
12857     },
12858
12859     /**
12860      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
12861      * query allowing the query action to be canceled if needed.
12862      * @param {String} query The SQL query to execute
12863      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12864      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
12865      * saved in the current store (defaults to false)
12866      */
12867     doQuery : function(q, forceAll){
12868         
12869         if(q === undefined || q === null){
12870             q = '';
12871         }
12872         var qe = {
12873             query: q,
12874             forceAll: forceAll,
12875             combo: this,
12876             cancel:false
12877         };
12878         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12879             return false;
12880         }
12881         q = qe.query;
12882         
12883         forceAll = qe.forceAll;
12884         if(forceAll === true || (q.length >= this.minChars)){
12885             
12886             this.hasQuery = true;
12887             
12888             if(this.lastQuery != q || this.alwaysQuery){
12889                 this.lastQuery = q;
12890                 if(this.mode == 'local'){
12891                     this.selectedIndex = -1;
12892                     if(forceAll){
12893                         this.store.clearFilter();
12894                     }else{
12895                         
12896                         if(this.specialFilter){
12897                             this.fireEvent('specialfilter', this);
12898                             this.onLoad();
12899                             return;
12900                         }
12901                         
12902                         this.store.filter(this.displayField, q);
12903                     }
12904                     
12905                     this.store.fireEvent("datachanged", this.store);
12906                     
12907                     this.onLoad();
12908                     
12909                     
12910                 }else{
12911                     
12912                     this.store.baseParams[this.queryParam] = q;
12913                     
12914                     var options = {params : this.getParams(q)};
12915                     
12916                     if(this.loadNext){
12917                         options.add = true;
12918                         options.params.start = this.page * this.pageSize;
12919                     }
12920                     
12921                     this.store.load(options);
12922                     
12923                     /*
12924                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
12925                      *  we should expand the list on onLoad
12926                      *  so command out it
12927                      */
12928 //                    this.expand();
12929                 }
12930             }else{
12931                 this.selectedIndex = -1;
12932                 this.onLoad();   
12933             }
12934         }
12935         
12936         this.loadNext = false;
12937     },
12938     
12939     // private
12940     getParams : function(q){
12941         var p = {};
12942         //p[this.queryParam] = q;
12943         
12944         if(this.pageSize){
12945             p.start = 0;
12946             p.limit = this.pageSize;
12947         }
12948         return p;
12949     },
12950
12951     /**
12952      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12953      */
12954     collapse : function(){
12955         if(!this.isExpanded()){
12956             return;
12957         }
12958         
12959         this.list.hide();
12960         
12961         if(this.tickable){
12962             this.hasFocus = false;
12963             this.okBtn.hide();
12964             this.cancelBtn.hide();
12965             this.trigger.show();
12966             
12967             if(this.editable){
12968                 this.tickableInputEl().dom.value = '';
12969                 this.tickableInputEl().blur();
12970             }
12971             
12972         }
12973         
12974         Roo.get(document).un('mousedown', this.collapseIf, this);
12975         Roo.get(document).un('mousewheel', this.collapseIf, this);
12976         if (!this.editable) {
12977             Roo.get(document).un('keydown', this.listKeyPress, this);
12978         }
12979         this.fireEvent('collapse', this);
12980     },
12981
12982     // private
12983     collapseIf : function(e){
12984         var in_combo  = e.within(this.el);
12985         var in_list =  e.within(this.list);
12986         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
12987         
12988         if (in_combo || in_list || is_list) {
12989             //e.stopPropagation();
12990             return;
12991         }
12992         
12993         if(this.tickable){
12994             this.onTickableFooterButtonClick(e, false, false);
12995         }
12996
12997         this.collapse();
12998         
12999     },
13000
13001     /**
13002      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13003      */
13004     expand : function(){
13005        
13006         if(this.isExpanded() || !this.hasFocus){
13007             return;
13008         }
13009         
13010         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13011         this.list.setWidth(lw);
13012         
13013         
13014          Roo.log('expand');
13015         
13016         this.list.show();
13017         
13018         this.restrictHeight();
13019         
13020         if(this.tickable){
13021             
13022             this.tickItems = Roo.apply([], this.item);
13023             
13024             this.okBtn.show();
13025             this.cancelBtn.show();
13026             this.trigger.hide();
13027             
13028             if(this.editable){
13029                 this.tickableInputEl().focus();
13030             }
13031             
13032         }
13033         
13034         Roo.get(document).on('mousedown', this.collapseIf, this);
13035         Roo.get(document).on('mousewheel', this.collapseIf, this);
13036         if (!this.editable) {
13037             Roo.get(document).on('keydown', this.listKeyPress, this);
13038         }
13039         
13040         this.fireEvent('expand', this);
13041     },
13042
13043     // private
13044     // Implements the default empty TriggerField.onTriggerClick function
13045     onTriggerClick : function(e)
13046     {
13047         Roo.log('trigger click');
13048         
13049         if(this.disabled || !this.triggerList){
13050             return;
13051         }
13052         
13053         this.page = 0;
13054         this.loadNext = false;
13055         
13056         if(this.isExpanded()){
13057             this.collapse();
13058             if (!this.blockFocus) {
13059                 this.inputEl().focus();
13060             }
13061             
13062         }else {
13063             this.hasFocus = true;
13064             if(this.triggerAction == 'all') {
13065                 this.doQuery(this.allQuery, true);
13066             } else {
13067                 this.doQuery(this.getRawValue());
13068             }
13069             if (!this.blockFocus) {
13070                 this.inputEl().focus();
13071             }
13072         }
13073     },
13074     
13075     onTickableTriggerClick : function(e)
13076     {
13077         if(this.disabled){
13078             return;
13079         }
13080         
13081         this.page = 0;
13082         this.loadNext = false;
13083         this.hasFocus = true;
13084         
13085         if(this.triggerAction == 'all') {
13086             this.doQuery(this.allQuery, true);
13087         } else {
13088             this.doQuery(this.getRawValue());
13089         }
13090     },
13091     
13092     onSearchFieldClick : function(e)
13093     {
13094         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13095             this.onTickableFooterButtonClick(e, false, false);
13096             return;
13097         }
13098         
13099         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13100             return;
13101         }
13102         
13103         this.page = 0;
13104         this.loadNext = false;
13105         this.hasFocus = true;
13106         
13107         if(this.triggerAction == 'all') {
13108             this.doQuery(this.allQuery, true);
13109         } else {
13110             this.doQuery(this.getRawValue());
13111         }
13112     },
13113     
13114     listKeyPress : function(e)
13115     {
13116         //Roo.log('listkeypress');
13117         // scroll to first matching element based on key pres..
13118         if (e.isSpecialKey()) {
13119             return false;
13120         }
13121         var k = String.fromCharCode(e.getKey()).toUpperCase();
13122         //Roo.log(k);
13123         var match  = false;
13124         var csel = this.view.getSelectedNodes();
13125         var cselitem = false;
13126         if (csel.length) {
13127             var ix = this.view.indexOf(csel[0]);
13128             cselitem  = this.store.getAt(ix);
13129             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13130                 cselitem = false;
13131             }
13132             
13133         }
13134         
13135         this.store.each(function(v) { 
13136             if (cselitem) {
13137                 // start at existing selection.
13138                 if (cselitem.id == v.id) {
13139                     cselitem = false;
13140                 }
13141                 return true;
13142             }
13143                 
13144             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13145                 match = this.store.indexOf(v);
13146                 return false;
13147             }
13148             return true;
13149         }, this);
13150         
13151         if (match === false) {
13152             return true; // no more action?
13153         }
13154         // scroll to?
13155         this.view.select(match);
13156         var sn = Roo.get(this.view.getSelectedNodes()[0]);
13157         sn.scrollIntoView(sn.dom.parentNode, false);
13158     },
13159     
13160     onViewScroll : function(e, t){
13161         
13162         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){
13163             return;
13164         }
13165         
13166         this.hasQuery = true;
13167         
13168         this.loading = this.list.select('.loading', true).first();
13169         
13170         if(this.loading === null){
13171             this.list.createChild({
13172                 tag: 'div',
13173                 cls: 'loading select2-more-results select2-active',
13174                 html: 'Loading more results...'
13175             });
13176             
13177             this.loading = this.list.select('.loading', true).first();
13178             
13179             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13180             
13181             this.loading.hide();
13182         }
13183         
13184         this.loading.show();
13185         
13186         var _combo = this;
13187         
13188         this.page++;
13189         this.loadNext = true;
13190         
13191         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13192         
13193         return;
13194     },
13195     
13196     addItem : function(o)
13197     {   
13198         var dv = ''; // display value
13199         
13200         if (this.displayField) {
13201             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13202         } else {
13203             // this is an error condition!!!
13204             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13205         }
13206         
13207         if(!dv.length){
13208             return;
13209         }
13210         
13211         var choice = this.choices.createChild({
13212             tag: 'li',
13213             cls: 'select2-search-choice',
13214             cn: [
13215                 {
13216                     tag: 'div',
13217                     html: dv
13218                 },
13219                 {
13220                     tag: 'a',
13221                     href: '#',
13222                     cls: 'select2-search-choice-close',
13223                     tabindex: '-1'
13224                 }
13225             ]
13226             
13227         }, this.searchField);
13228         
13229         var close = choice.select('a.select2-search-choice-close', true).first();
13230         
13231         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13232         
13233         this.item.push(o);
13234         
13235         this.lastData = o;
13236         
13237         this.syncValue();
13238         
13239         this.inputEl().dom.value = '';
13240         
13241         this.validate();
13242     },
13243     
13244     onRemoveItem : function(e, _self, o)
13245     {
13246         e.preventDefault();
13247         
13248         this.lastItem = Roo.apply([], this.item);
13249         
13250         var index = this.item.indexOf(o.data) * 1;
13251         
13252         if( index < 0){
13253             Roo.log('not this item?!');
13254             return;
13255         }
13256         
13257         this.item.splice(index, 1);
13258         o.item.remove();
13259         
13260         this.syncValue();
13261         
13262         this.fireEvent('remove', this, e);
13263         
13264         this.validate();
13265         
13266     },
13267     
13268     syncValue : function()
13269     {
13270         if(!this.item.length){
13271             this.clearValue();
13272             return;
13273         }
13274             
13275         var value = [];
13276         var _this = this;
13277         Roo.each(this.item, function(i){
13278             if(_this.valueField){
13279                 value.push(i[_this.valueField]);
13280                 return;
13281             }
13282
13283             value.push(i);
13284         });
13285
13286         this.value = value.join(',');
13287
13288         if(this.hiddenField){
13289             this.hiddenField.dom.value = this.value;
13290         }
13291         
13292         this.store.fireEvent("datachanged", this.store);
13293     },
13294     
13295     clearItem : function()
13296     {
13297         if(!this.multiple){
13298             return;
13299         }
13300         
13301         this.item = [];
13302         
13303         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
13304            c.remove();
13305         });
13306         
13307         this.syncValue();
13308         
13309         this.validate();
13310         
13311         if(this.tickable && !Roo.isTouch){
13312             this.view.refresh();
13313         }
13314     },
13315     
13316     inputEl: function ()
13317     {
13318         if(Roo.isTouch && this.mobileTouchView){
13319             return this.el.select('input.form-control',true).first();
13320         }
13321         
13322         if(this.tickable){
13323             return this.searchField;
13324         }
13325         
13326         return this.el.select('input.form-control',true).first();
13327     },
13328     
13329     
13330     onTickableFooterButtonClick : function(e, btn, el)
13331     {
13332         e.preventDefault();
13333         
13334         this.lastItem = Roo.apply([], this.item);
13335         
13336         if(btn && btn.name == 'cancel'){
13337             this.tickItems = Roo.apply([], this.item);
13338             this.collapse();
13339             return;
13340         }
13341         
13342         this.clearItem();
13343         
13344         var _this = this;
13345         
13346         Roo.each(this.tickItems, function(o){
13347             _this.addItem(o);
13348         });
13349         
13350         this.collapse();
13351         
13352     },
13353     
13354     validate : function()
13355     {
13356         var v = this.getRawValue();
13357         
13358         if(this.multiple){
13359             v = this.getValue();
13360         }
13361         
13362         if(this.disabled || this.allowBlank || v.length){
13363             this.markValid();
13364             return true;
13365         }
13366         
13367         this.markInvalid();
13368         return false;
13369     },
13370     
13371     tickableInputEl : function()
13372     {
13373         if(!this.tickable || !this.editable){
13374             return this.inputEl();
13375         }
13376         
13377         return this.inputEl().select('.select2-search-field-input', true).first();
13378     },
13379     
13380     
13381     getAutoCreateTouchView : function()
13382     {
13383         var id = Roo.id();
13384         
13385         var cfg = {
13386             cls: 'form-group' //input-group
13387         };
13388         
13389         var input =  {
13390             tag: 'input',
13391             id : id,
13392             type : this.inputType,
13393             cls : 'form-control x-combo-noedit',
13394             autocomplete: 'new-password',
13395             placeholder : this.placeholder || '',
13396             readonly : true
13397         };
13398         
13399         if (this.name) {
13400             input.name = this.name;
13401         }
13402         
13403         if (this.size) {
13404             input.cls += ' input-' + this.size;
13405         }
13406         
13407         if (this.disabled) {
13408             input.disabled = true;
13409         }
13410         
13411         var inputblock = {
13412             cls : '',
13413             cn : [
13414                 input
13415             ]
13416         };
13417         
13418         if(this.before){
13419             inputblock.cls += ' input-group';
13420             
13421             inputblock.cn.unshift({
13422                 tag :'span',
13423                 cls : 'input-group-addon',
13424                 html : this.before
13425             });
13426         }
13427         
13428         if(this.removable && !this.multiple){
13429             inputblock.cls += ' roo-removable';
13430             
13431             inputblock.cn.push({
13432                 tag: 'button',
13433                 html : 'x',
13434                 cls : 'roo-combo-removable-btn close'
13435             });
13436         }
13437
13438         if(this.hasFeedback && !this.allowBlank){
13439             
13440             inputblock.cls += ' has-feedback';
13441             
13442             inputblock.cn.push({
13443                 tag: 'span',
13444                 cls: 'glyphicon form-control-feedback'
13445             });
13446             
13447         }
13448         
13449         if (this.after) {
13450             
13451             inputblock.cls += (this.before) ? '' : ' input-group';
13452             
13453             inputblock.cn.push({
13454                 tag :'span',
13455                 cls : 'input-group-addon',
13456                 html : this.after
13457             });
13458         }
13459
13460         var box = {
13461             tag: 'div',
13462             cn: [
13463                 {
13464                     tag: 'input',
13465                     type : 'hidden',
13466                     cls: 'form-hidden-field'
13467                 },
13468                 inputblock
13469             ]
13470             
13471         };
13472         
13473         if(this.multiple){
13474             box = {
13475                 tag: 'div',
13476                 cn: [
13477                     {
13478                         tag: 'input',
13479                         type : 'hidden',
13480                         cls: 'form-hidden-field'
13481                     },
13482                     {
13483                         tag: 'ul',
13484                         cls: 'select2-choices',
13485                         cn:[
13486                             {
13487                                 tag: 'li',
13488                                 cls: 'select2-search-field',
13489                                 cn: [
13490
13491                                     inputblock
13492                                 ]
13493                             }
13494                         ]
13495                     }
13496                 ]
13497             }
13498         };
13499         
13500         var combobox = {
13501             cls: 'select2-container input-group',
13502             cn: [
13503                 box
13504             ]
13505         };
13506         
13507         if(this.multiple){
13508             combobox.cls += ' select2-container-multi';
13509         }
13510         
13511         var align = this.labelAlign || this.parentLabelAlign();
13512         
13513         cfg.cn = combobox;
13514         
13515         if(this.fieldLabel.length){
13516             
13517             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13518             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13519             
13520             cfg.cn = [
13521                 {
13522                     tag: 'label',
13523                     cls : 'control-label ' + lw,
13524                     html : this.fieldLabel
13525
13526                 },
13527                 {
13528                     cls : cw, 
13529                     cn: [
13530                         combobox
13531                     ]
13532                 }
13533             ];
13534         }
13535         
13536         var settings = this;
13537         
13538         ['xs','sm','md','lg'].map(function(size){
13539             if (settings[size]) {
13540                 cfg.cls += ' col-' + size + '-' + settings[size];
13541             }
13542         });
13543         
13544         return cfg;
13545     },
13546     
13547     initTouchView : function()
13548     {
13549         this.renderTouchView();
13550         
13551         this.touchViewEl.on('scroll', function(){
13552             this.el.dom.scrollTop = 0;
13553         }, this);
13554         
13555         this.inputEl().on("click", this.showTouchView, this);
13556         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13557         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13558         
13559         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13560         
13561         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13562         this.store.on('load', this.onTouchViewLoad, this);
13563         this.store.on('loadexception', this.onTouchViewLoadException, this);
13564         
13565         if(this.hiddenName){
13566             
13567             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13568             
13569             this.hiddenField.dom.value =
13570                 this.hiddenValue !== undefined ? this.hiddenValue :
13571                 this.value !== undefined ? this.value : '';
13572         
13573             this.el.dom.removeAttribute('name');
13574             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13575         }
13576         
13577         if(this.multiple){
13578             this.choices = this.el.select('ul.select2-choices', true).first();
13579             this.searchField = this.el.select('ul li.select2-search-field', true).first();
13580         }
13581         
13582         if(this.removable && !this.multiple){
13583             var close = this.closeTriggerEl();
13584             if(close){
13585                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13586                 close.on('click', this.removeBtnClick, this, close);
13587             }
13588         }
13589         
13590         return;
13591         
13592         
13593     },
13594     
13595     renderTouchView : function()
13596     {
13597         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13598         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13599         
13600         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13601         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13602         
13603         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13604         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13605         this.touchViewBodyEl.setStyle('overflow', 'auto');
13606         
13607         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13608         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13609         
13610         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13611         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13612         
13613     },
13614     
13615     showTouchView : function()
13616     {
13617         this.touchViewHeaderEl.hide();
13618
13619         if(this.fieldLabel.length){
13620             this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13621             this.touchViewHeaderEl.show();
13622         }
13623
13624         this.touchViewEl.show();
13625
13626         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13627         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13628
13629         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13630
13631         if(this.fieldLabel.length){
13632             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13633         }
13634         
13635         this.touchViewBodyEl.setHeight(bodyHeight);
13636
13637         if(this.animate){
13638             var _this = this;
13639             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13640         }else{
13641             this.touchViewEl.addClass('in');
13642         }
13643
13644         this.doTouchViewQuery();
13645         
13646     },
13647     
13648     hideTouchView : function()
13649     {
13650         this.touchViewEl.removeClass('in');
13651
13652         if(this.animate){
13653             var _this = this;
13654             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13655         }else{
13656             this.touchViewEl.setStyle('display', 'none');
13657         }
13658         
13659     },
13660     
13661     setTouchViewValue : function()
13662     {
13663         if(this.multiple){
13664             this.clearItem();
13665         
13666             var _this = this;
13667
13668             Roo.each(this.tickItems, function(o){
13669                 this.addItem(o);
13670             }, this);
13671         }
13672         
13673         this.hideTouchView();
13674     },
13675     
13676     doTouchViewQuery : function()
13677     {
13678         var qe = {
13679             query: '',
13680             forceAll: true,
13681             combo: this,
13682             cancel:false
13683         };
13684         
13685         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13686             return false;
13687         }
13688         
13689         if(!this.alwaysQuery || this.mode == 'local'){
13690             this.onTouchViewLoad();
13691             return;
13692         }
13693         
13694         this.store.load();
13695     },
13696     
13697     onTouchViewBeforeLoad : function(combo,opts)
13698     {
13699         return;
13700     },
13701
13702     // private
13703     onTouchViewLoad : function()
13704     {
13705         if(this.store.getCount() < 1){
13706             this.onTouchViewEmptyResults();
13707             return;
13708         }
13709         
13710         this.clearTouchView();
13711         
13712         var rawValue = this.getRawValue();
13713         
13714         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13715         
13716         this.tickItems = [];
13717         
13718         this.store.data.each(function(d, rowIndex){
13719             var row = this.touchViewListGroup.createChild(template);
13720             
13721             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13722                 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = d.data[this.displayField];
13723             }
13724             
13725             if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
13726                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13727             }
13728             
13729             if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
13730                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13731                 this.tickItems.push(d.data);
13732             }
13733             
13734             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
13735             
13736         }, this);
13737         
13738         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
13739         
13740         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13741
13742         if(this.fieldLabel.length){
13743             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13744         }
13745
13746         var listHeight = this.touchViewListGroup.getHeight();
13747         
13748         var _this = this;
13749         
13750         if(firstChecked && listHeight > bodyHeight){
13751             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
13752         }
13753         
13754     },
13755     
13756     onTouchViewLoadException : function()
13757     {
13758         this.hideTouchView();
13759     },
13760     
13761     onTouchViewEmptyResults : function()
13762     {
13763         this.clearTouchView();
13764         
13765         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
13766         
13767         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
13768         
13769     },
13770     
13771     clearTouchView : function()
13772     {
13773         this.touchViewListGroup.dom.innerHTML = '';
13774     },
13775     
13776     onTouchViewClick : function(e, el, o)
13777     {
13778         e.preventDefault();
13779         
13780         var row = o.row;
13781         var rowIndex = o.rowIndex;
13782         
13783         var r = this.store.getAt(rowIndex);
13784         
13785         if(!this.multiple){
13786             Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
13787                 c.dom.removeAttribute('checked');
13788             }, this);
13789             
13790             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13791         
13792             this.setFromData(r.data);
13793             
13794             var close = this.closeTriggerEl();
13795         
13796             if(close){
13797                 close.show();
13798             }
13799
13800             this.hideTouchView();
13801             
13802             this.fireEvent('select', this, r, rowIndex);
13803             
13804             return;
13805         }
13806         
13807         if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
13808             row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
13809             this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
13810             return;
13811         }
13812         
13813         row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13814         this.addItem(r.data);
13815         this.tickItems.push(r.data);
13816         
13817     }
13818     
13819
13820     /** 
13821     * @cfg {Boolean} grow 
13822     * @hide 
13823     */
13824     /** 
13825     * @cfg {Number} growMin 
13826     * @hide 
13827     */
13828     /** 
13829     * @cfg {Number} growMax 
13830     * @hide 
13831     */
13832     /**
13833      * @hide
13834      * @method autoSize
13835      */
13836 });
13837
13838 Roo.apply(Roo.bootstrap.ComboBox,  {
13839     
13840     header : {
13841         tag: 'div',
13842         cls: 'modal-header',
13843         cn: [
13844             {
13845                 tag: 'h4',
13846                 cls: 'modal-title'
13847             }
13848         ]
13849     },
13850     
13851     body : {
13852         tag: 'div',
13853         cls: 'modal-body',
13854         cn: [
13855             {
13856                 tag: 'ul',
13857                 cls: 'list-group'
13858             }
13859         ]
13860     },
13861     
13862     listItemRadio : {
13863         tag: 'li',
13864         cls: 'list-group-item',
13865         cn: [
13866             {
13867                 tag: 'span',
13868                 cls: 'roo-combobox-list-group-item-value'
13869             },
13870             {
13871                 tag: 'div',
13872                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
13873                 cn: [
13874                     {
13875                         tag: 'input',
13876                         type: 'radio'
13877                     },
13878                     {
13879                         tag: 'label'
13880                     }
13881                 ]
13882             }
13883         ]
13884     },
13885     
13886     listItemCheckbox : {
13887         tag: 'li',
13888         cls: 'list-group-item',
13889         cn: [
13890             {
13891                 tag: 'span',
13892                 cls: 'roo-combobox-list-group-item-value'
13893             },
13894             {
13895                 tag: 'div',
13896                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
13897                 cn: [
13898                     {
13899                         tag: 'input',
13900                         type: 'checkbox'
13901                     },
13902                     {
13903                         tag: 'label'
13904                     }
13905                 ]
13906             }
13907         ]
13908     },
13909     
13910     emptyResult : {
13911         tag: 'div',
13912         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
13913     },
13914     
13915     footer : {
13916         tag: 'div',
13917         cls: 'modal-footer',
13918         cn: [
13919             {
13920                 tag: 'div',
13921                 cls: 'row',
13922                 cn: [
13923                     {
13924                         tag: 'div',
13925                         cls: 'col-xs-6 text-left',
13926                         cn: {
13927                             tag: 'button',
13928                             cls: 'btn btn-danger roo-touch-view-cancel',
13929                             html: 'Cancel'
13930                         }
13931                     },
13932                     {
13933                         tag: 'div',
13934                         cls: 'col-xs-6 text-right',
13935                         cn: {
13936                             tag: 'button',
13937                             cls: 'btn btn-success roo-touch-view-ok',
13938                             html: 'OK'
13939                         }
13940                     }
13941                 ]
13942             }
13943         ]
13944         
13945     }
13946 });
13947
13948 Roo.apply(Roo.bootstrap.ComboBox,  {
13949     
13950     touchViewTemplate : {
13951         tag: 'div',
13952         cls: 'modal fade roo-combobox-touch-view',
13953         cn: [
13954             {
13955                 tag: 'div',
13956                 cls: 'modal-dialog',
13957                 cn: [
13958                     {
13959                         tag: 'div',
13960                         cls: 'modal-content',
13961                         cn: [
13962                             Roo.bootstrap.ComboBox.header,
13963                             Roo.bootstrap.ComboBox.body,
13964                             Roo.bootstrap.ComboBox.footer
13965                         ]
13966                     }
13967                 ]
13968             }
13969         ]
13970     }
13971 });/*
13972  * Based on:
13973  * Ext JS Library 1.1.1
13974  * Copyright(c) 2006-2007, Ext JS, LLC.
13975  *
13976  * Originally Released Under LGPL - original licence link has changed is not relivant.
13977  *
13978  * Fork - LGPL
13979  * <script type="text/javascript">
13980  */
13981
13982 /**
13983  * @class Roo.View
13984  * @extends Roo.util.Observable
13985  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
13986  * This class also supports single and multi selection modes. <br>
13987  * Create a data model bound view:
13988  <pre><code>
13989  var store = new Roo.data.Store(...);
13990
13991  var view = new Roo.View({
13992     el : "my-element",
13993     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
13994  
13995     singleSelect: true,
13996     selectedClass: "ydataview-selected",
13997     store: store
13998  });
13999
14000  // listen for node click?
14001  view.on("click", function(vw, index, node, e){
14002  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14003  });
14004
14005  // load XML data
14006  dataModel.load("foobar.xml");
14007  </code></pre>
14008  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14009  * <br><br>
14010  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14011  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14012  * 
14013  * Note: old style constructor is still suported (container, template, config)
14014  * 
14015  * @constructor
14016  * Create a new View
14017  * @param {Object} config The config object
14018  * 
14019  */
14020 Roo.View = function(config, depreciated_tpl, depreciated_config){
14021     
14022     this.parent = false;
14023     
14024     if (typeof(depreciated_tpl) == 'undefined') {
14025         // new way.. - universal constructor.
14026         Roo.apply(this, config);
14027         this.el  = Roo.get(this.el);
14028     } else {
14029         // old format..
14030         this.el  = Roo.get(config);
14031         this.tpl = depreciated_tpl;
14032         Roo.apply(this, depreciated_config);
14033     }
14034     this.wrapEl  = this.el.wrap().wrap();
14035     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14036     
14037     
14038     if(typeof(this.tpl) == "string"){
14039         this.tpl = new Roo.Template(this.tpl);
14040     } else {
14041         // support xtype ctors..
14042         this.tpl = new Roo.factory(this.tpl, Roo);
14043     }
14044     
14045     
14046     this.tpl.compile();
14047     
14048     /** @private */
14049     this.addEvents({
14050         /**
14051          * @event beforeclick
14052          * Fires before a click is processed. Returns false to cancel the default action.
14053          * @param {Roo.View} this
14054          * @param {Number} index The index of the target node
14055          * @param {HTMLElement} node The target node
14056          * @param {Roo.EventObject} e The raw event object
14057          */
14058             "beforeclick" : true,
14059         /**
14060          * @event click
14061          * Fires when a template node is clicked.
14062          * @param {Roo.View} this
14063          * @param {Number} index The index of the target node
14064          * @param {HTMLElement} node The target node
14065          * @param {Roo.EventObject} e The raw event object
14066          */
14067             "click" : true,
14068         /**
14069          * @event dblclick
14070          * Fires when a template node is double clicked.
14071          * @param {Roo.View} this
14072          * @param {Number} index The index of the target node
14073          * @param {HTMLElement} node The target node
14074          * @param {Roo.EventObject} e The raw event object
14075          */
14076             "dblclick" : true,
14077         /**
14078          * @event contextmenu
14079          * Fires when a template node is right clicked.
14080          * @param {Roo.View} this
14081          * @param {Number} index The index of the target node
14082          * @param {HTMLElement} node The target node
14083          * @param {Roo.EventObject} e The raw event object
14084          */
14085             "contextmenu" : true,
14086         /**
14087          * @event selectionchange
14088          * Fires when the selected nodes change.
14089          * @param {Roo.View} this
14090          * @param {Array} selections Array of the selected nodes
14091          */
14092             "selectionchange" : true,
14093     
14094         /**
14095          * @event beforeselect
14096          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14097          * @param {Roo.View} this
14098          * @param {HTMLElement} node The node to be selected
14099          * @param {Array} selections Array of currently selected nodes
14100          */
14101             "beforeselect" : true,
14102         /**
14103          * @event preparedata
14104          * Fires on every row to render, to allow you to change the data.
14105          * @param {Roo.View} this
14106          * @param {Object} data to be rendered (change this)
14107          */
14108           "preparedata" : true
14109           
14110           
14111         });
14112
14113
14114
14115     this.el.on({
14116         "click": this.onClick,
14117         "dblclick": this.onDblClick,
14118         "contextmenu": this.onContextMenu,
14119         scope:this
14120     });
14121
14122     this.selections = [];
14123     this.nodes = [];
14124     this.cmp = new Roo.CompositeElementLite([]);
14125     if(this.store){
14126         this.store = Roo.factory(this.store, Roo.data);
14127         this.setStore(this.store, true);
14128     }
14129     
14130     if ( this.footer && this.footer.xtype) {
14131            
14132          var fctr = this.wrapEl.appendChild(document.createElement("div"));
14133         
14134         this.footer.dataSource = this.store;
14135         this.footer.container = fctr;
14136         this.footer = Roo.factory(this.footer, Roo);
14137         fctr.insertFirst(this.el);
14138         
14139         // this is a bit insane - as the paging toolbar seems to detach the el..
14140 //        dom.parentNode.parentNode.parentNode
14141          // they get detached?
14142     }
14143     
14144     
14145     Roo.View.superclass.constructor.call(this);
14146     
14147     
14148 };
14149
14150 Roo.extend(Roo.View, Roo.util.Observable, {
14151     
14152      /**
14153      * @cfg {Roo.data.Store} store Data store to load data from.
14154      */
14155     store : false,
14156     
14157     /**
14158      * @cfg {String|Roo.Element} el The container element.
14159      */
14160     el : '',
14161     
14162     /**
14163      * @cfg {String|Roo.Template} tpl The template used by this View 
14164      */
14165     tpl : false,
14166     /**
14167      * @cfg {String} dataName the named area of the template to use as the data area
14168      *                          Works with domtemplates roo-name="name"
14169      */
14170     dataName: false,
14171     /**
14172      * @cfg {String} selectedClass The css class to add to selected nodes
14173      */
14174     selectedClass : "x-view-selected",
14175      /**
14176      * @cfg {String} emptyText The empty text to show when nothing is loaded.
14177      */
14178     emptyText : "",
14179     
14180     /**
14181      * @cfg {String} text to display on mask (default Loading)
14182      */
14183     mask : false,
14184     /**
14185      * @cfg {Boolean} multiSelect Allow multiple selection
14186      */
14187     multiSelect : false,
14188     /**
14189      * @cfg {Boolean} singleSelect Allow single selection
14190      */
14191     singleSelect:  false,
14192     
14193     /**
14194      * @cfg {Boolean} toggleSelect - selecting 
14195      */
14196     toggleSelect : false,
14197     
14198     /**
14199      * @cfg {Boolean} tickable - selecting 
14200      */
14201     tickable : false,
14202     
14203     /**
14204      * Returns the element this view is bound to.
14205      * @return {Roo.Element}
14206      */
14207     getEl : function(){
14208         return this.wrapEl;
14209     },
14210     
14211     
14212
14213     /**
14214      * Refreshes the view. - called by datachanged on the store. - do not call directly.
14215      */
14216     refresh : function(){
14217         //Roo.log('refresh');
14218         var t = this.tpl;
14219         
14220         // if we are using something like 'domtemplate', then
14221         // the what gets used is:
14222         // t.applySubtemplate(NAME, data, wrapping data..)
14223         // the outer template then get' applied with
14224         //     the store 'extra data'
14225         // and the body get's added to the
14226         //      roo-name="data" node?
14227         //      <span class='roo-tpl-{name}'></span> ?????
14228         
14229         
14230         
14231         this.clearSelections();
14232         this.el.update("");
14233         var html = [];
14234         var records = this.store.getRange();
14235         if(records.length < 1) {
14236             
14237             // is this valid??  = should it render a template??
14238             
14239             this.el.update(this.emptyText);
14240             return;
14241         }
14242         var el = this.el;
14243         if (this.dataName) {
14244             this.el.update(t.apply(this.store.meta)); //????
14245             el = this.el.child('.roo-tpl-' + this.dataName);
14246         }
14247         
14248         for(var i = 0, len = records.length; i < len; i++){
14249             var data = this.prepareData(records[i].data, i, records[i]);
14250             this.fireEvent("preparedata", this, data, i, records[i]);
14251             
14252             var d = Roo.apply({}, data);
14253             
14254             if(this.tickable){
14255                 Roo.apply(d, {'roo-id' : Roo.id()});
14256                 
14257                 var _this = this;
14258             
14259                 Roo.each(this.parent.item, function(item){
14260                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14261                         return;
14262                     }
14263                     Roo.apply(d, {'roo-data-checked' : 'checked'});
14264                 });
14265             }
14266             
14267             html[html.length] = Roo.util.Format.trim(
14268                 this.dataName ?
14269                     t.applySubtemplate(this.dataName, d, this.store.meta) :
14270                     t.apply(d)
14271             );
14272         }
14273         
14274         
14275         
14276         el.update(html.join(""));
14277         this.nodes = el.dom.childNodes;
14278         this.updateIndexes(0);
14279     },
14280     
14281
14282     /**
14283      * Function to override to reformat the data that is sent to
14284      * the template for each node.
14285      * DEPRICATED - use the preparedata event handler.
14286      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14287      * a JSON object for an UpdateManager bound view).
14288      */
14289     prepareData : function(data, index, record)
14290     {
14291         this.fireEvent("preparedata", this, data, index, record);
14292         return data;
14293     },
14294
14295     onUpdate : function(ds, record){
14296         // Roo.log('on update');   
14297         this.clearSelections();
14298         var index = this.store.indexOf(record);
14299         var n = this.nodes[index];
14300         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14301         n.parentNode.removeChild(n);
14302         this.updateIndexes(index, index);
14303     },
14304
14305     
14306     
14307 // --------- FIXME     
14308     onAdd : function(ds, records, index)
14309     {
14310         //Roo.log(['on Add', ds, records, index] );        
14311         this.clearSelections();
14312         if(this.nodes.length == 0){
14313             this.refresh();
14314             return;
14315         }
14316         var n = this.nodes[index];
14317         for(var i = 0, len = records.length; i < len; i++){
14318             var d = this.prepareData(records[i].data, i, records[i]);
14319             if(n){
14320                 this.tpl.insertBefore(n, d);
14321             }else{
14322                 
14323                 this.tpl.append(this.el, d);
14324             }
14325         }
14326         this.updateIndexes(index);
14327     },
14328
14329     onRemove : function(ds, record, index){
14330        // Roo.log('onRemove');
14331         this.clearSelections();
14332         var el = this.dataName  ?
14333             this.el.child('.roo-tpl-' + this.dataName) :
14334             this.el; 
14335         
14336         el.dom.removeChild(this.nodes[index]);
14337         this.updateIndexes(index);
14338     },
14339
14340     /**
14341      * Refresh an individual node.
14342      * @param {Number} index
14343      */
14344     refreshNode : function(index){
14345         this.onUpdate(this.store, this.store.getAt(index));
14346     },
14347
14348     updateIndexes : function(startIndex, endIndex){
14349         var ns = this.nodes;
14350         startIndex = startIndex || 0;
14351         endIndex = endIndex || ns.length - 1;
14352         for(var i = startIndex; i <= endIndex; i++){
14353             ns[i].nodeIndex = i;
14354         }
14355     },
14356
14357     /**
14358      * Changes the data store this view uses and refresh the view.
14359      * @param {Store} store
14360      */
14361     setStore : function(store, initial){
14362         if(!initial && this.store){
14363             this.store.un("datachanged", this.refresh);
14364             this.store.un("add", this.onAdd);
14365             this.store.un("remove", this.onRemove);
14366             this.store.un("update", this.onUpdate);
14367             this.store.un("clear", this.refresh);
14368             this.store.un("beforeload", this.onBeforeLoad);
14369             this.store.un("load", this.onLoad);
14370             this.store.un("loadexception", this.onLoad);
14371         }
14372         if(store){
14373           
14374             store.on("datachanged", this.refresh, this);
14375             store.on("add", this.onAdd, this);
14376             store.on("remove", this.onRemove, this);
14377             store.on("update", this.onUpdate, this);
14378             store.on("clear", this.refresh, this);
14379             store.on("beforeload", this.onBeforeLoad, this);
14380             store.on("load", this.onLoad, this);
14381             store.on("loadexception", this.onLoad, this);
14382         }
14383         
14384         if(store){
14385             this.refresh();
14386         }
14387     },
14388     /**
14389      * onbeforeLoad - masks the loading area.
14390      *
14391      */
14392     onBeforeLoad : function(store,opts)
14393     {
14394          //Roo.log('onBeforeLoad');   
14395         if (!opts.add) {
14396             this.el.update("");
14397         }
14398         this.el.mask(this.mask ? this.mask : "Loading" ); 
14399     },
14400     onLoad : function ()
14401     {
14402         this.el.unmask();
14403     },
14404     
14405
14406     /**
14407      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14408      * @param {HTMLElement} node
14409      * @return {HTMLElement} The template node
14410      */
14411     findItemFromChild : function(node){
14412         var el = this.dataName  ?
14413             this.el.child('.roo-tpl-' + this.dataName,true) :
14414             this.el.dom; 
14415         
14416         if(!node || node.parentNode == el){
14417                     return node;
14418             }
14419             var p = node.parentNode;
14420             while(p && p != el){
14421             if(p.parentNode == el){
14422                 return p;
14423             }
14424             p = p.parentNode;
14425         }
14426             return null;
14427     },
14428
14429     /** @ignore */
14430     onClick : function(e){
14431         var item = this.findItemFromChild(e.getTarget());
14432         if(item){
14433             var index = this.indexOf(item);
14434             if(this.onItemClick(item, index, e) !== false){
14435                 this.fireEvent("click", this, index, item, e);
14436             }
14437         }else{
14438             this.clearSelections();
14439         }
14440     },
14441
14442     /** @ignore */
14443     onContextMenu : function(e){
14444         var item = this.findItemFromChild(e.getTarget());
14445         if(item){
14446             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14447         }
14448     },
14449
14450     /** @ignore */
14451     onDblClick : function(e){
14452         var item = this.findItemFromChild(e.getTarget());
14453         if(item){
14454             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14455         }
14456     },
14457
14458     onItemClick : function(item, index, e)
14459     {
14460         if(this.fireEvent("beforeclick", this, index, item, e) === false){
14461             return false;
14462         }
14463         if (this.toggleSelect) {
14464             var m = this.isSelected(item) ? 'unselect' : 'select';
14465             //Roo.log(m);
14466             var _t = this;
14467             _t[m](item, true, false);
14468             return true;
14469         }
14470         if(this.multiSelect || this.singleSelect){
14471             if(this.multiSelect && e.shiftKey && this.lastSelection){
14472                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14473             }else{
14474                 this.select(item, this.multiSelect && e.ctrlKey);
14475                 this.lastSelection = item;
14476             }
14477             
14478             if(!this.tickable){
14479                 e.preventDefault();
14480             }
14481             
14482         }
14483         return true;
14484     },
14485
14486     /**
14487      * Get the number of selected nodes.
14488      * @return {Number}
14489      */
14490     getSelectionCount : function(){
14491         return this.selections.length;
14492     },
14493
14494     /**
14495      * Get the currently selected nodes.
14496      * @return {Array} An array of HTMLElements
14497      */
14498     getSelectedNodes : function(){
14499         return this.selections;
14500     },
14501
14502     /**
14503      * Get the indexes of the selected nodes.
14504      * @return {Array}
14505      */
14506     getSelectedIndexes : function(){
14507         var indexes = [], s = this.selections;
14508         for(var i = 0, len = s.length; i < len; i++){
14509             indexes.push(s[i].nodeIndex);
14510         }
14511         return indexes;
14512     },
14513
14514     /**
14515      * Clear all selections
14516      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14517      */
14518     clearSelections : function(suppressEvent){
14519         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14520             this.cmp.elements = this.selections;
14521             this.cmp.removeClass(this.selectedClass);
14522             this.selections = [];
14523             if(!suppressEvent){
14524                 this.fireEvent("selectionchange", this, this.selections);
14525             }
14526         }
14527     },
14528
14529     /**
14530      * Returns true if the passed node is selected
14531      * @param {HTMLElement/Number} node The node or node index
14532      * @return {Boolean}
14533      */
14534     isSelected : function(node){
14535         var s = this.selections;
14536         if(s.length < 1){
14537             return false;
14538         }
14539         node = this.getNode(node);
14540         return s.indexOf(node) !== -1;
14541     },
14542
14543     /**
14544      * Selects nodes.
14545      * @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
14546      * @param {Boolean} keepExisting (optional) true to keep existing selections
14547      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14548      */
14549     select : function(nodeInfo, keepExisting, suppressEvent){
14550         if(nodeInfo instanceof Array){
14551             if(!keepExisting){
14552                 this.clearSelections(true);
14553             }
14554             for(var i = 0, len = nodeInfo.length; i < len; i++){
14555                 this.select(nodeInfo[i], true, true);
14556             }
14557             return;
14558         } 
14559         var node = this.getNode(nodeInfo);
14560         if(!node || this.isSelected(node)){
14561             return; // already selected.
14562         }
14563         if(!keepExisting){
14564             this.clearSelections(true);
14565         }
14566         
14567         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14568             Roo.fly(node).addClass(this.selectedClass);
14569             this.selections.push(node);
14570             if(!suppressEvent){
14571                 this.fireEvent("selectionchange", this, this.selections);
14572             }
14573         }
14574         
14575         
14576     },
14577       /**
14578      * Unselects nodes.
14579      * @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
14580      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14581      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14582      */
14583     unselect : function(nodeInfo, keepExisting, suppressEvent)
14584     {
14585         if(nodeInfo instanceof Array){
14586             Roo.each(this.selections, function(s) {
14587                 this.unselect(s, nodeInfo);
14588             }, this);
14589             return;
14590         }
14591         var node = this.getNode(nodeInfo);
14592         if(!node || !this.isSelected(node)){
14593             //Roo.log("not selected");
14594             return; // not selected.
14595         }
14596         // fireevent???
14597         var ns = [];
14598         Roo.each(this.selections, function(s) {
14599             if (s == node ) {
14600                 Roo.fly(node).removeClass(this.selectedClass);
14601
14602                 return;
14603             }
14604             ns.push(s);
14605         },this);
14606         
14607         this.selections= ns;
14608         this.fireEvent("selectionchange", this, this.selections);
14609     },
14610
14611     /**
14612      * Gets a template node.
14613      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14614      * @return {HTMLElement} The node or null if it wasn't found
14615      */
14616     getNode : function(nodeInfo){
14617         if(typeof nodeInfo == "string"){
14618             return document.getElementById(nodeInfo);
14619         }else if(typeof nodeInfo == "number"){
14620             return this.nodes[nodeInfo];
14621         }
14622         return nodeInfo;
14623     },
14624
14625     /**
14626      * Gets a range template nodes.
14627      * @param {Number} startIndex
14628      * @param {Number} endIndex
14629      * @return {Array} An array of nodes
14630      */
14631     getNodes : function(start, end){
14632         var ns = this.nodes;
14633         start = start || 0;
14634         end = typeof end == "undefined" ? ns.length - 1 : end;
14635         var nodes = [];
14636         if(start <= end){
14637             for(var i = start; i <= end; i++){
14638                 nodes.push(ns[i]);
14639             }
14640         } else{
14641             for(var i = start; i >= end; i--){
14642                 nodes.push(ns[i]);
14643             }
14644         }
14645         return nodes;
14646     },
14647
14648     /**
14649      * Finds the index of the passed node
14650      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14651      * @return {Number} The index of the node or -1
14652      */
14653     indexOf : function(node){
14654         node = this.getNode(node);
14655         if(typeof node.nodeIndex == "number"){
14656             return node.nodeIndex;
14657         }
14658         var ns = this.nodes;
14659         for(var i = 0, len = ns.length; i < len; i++){
14660             if(ns[i] == node){
14661                 return i;
14662             }
14663         }
14664         return -1;
14665     }
14666 });
14667 /*
14668  * - LGPL
14669  *
14670  * based on jquery fullcalendar
14671  * 
14672  */
14673
14674 Roo.bootstrap = Roo.bootstrap || {};
14675 /**
14676  * @class Roo.bootstrap.Calendar
14677  * @extends Roo.bootstrap.Component
14678  * Bootstrap Calendar class
14679  * @cfg {Boolean} loadMask (true|false) default false
14680  * @cfg {Object} header generate the user specific header of the calendar, default false
14681
14682  * @constructor
14683  * Create a new Container
14684  * @param {Object} config The config object
14685  */
14686
14687
14688
14689 Roo.bootstrap.Calendar = function(config){
14690     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14691      this.addEvents({
14692         /**
14693              * @event select
14694              * Fires when a date is selected
14695              * @param {DatePicker} this
14696              * @param {Date} date The selected date
14697              */
14698         'select': true,
14699         /**
14700              * @event monthchange
14701              * Fires when the displayed month changes 
14702              * @param {DatePicker} this
14703              * @param {Date} date The selected month
14704              */
14705         'monthchange': true,
14706         /**
14707              * @event evententer
14708              * Fires when mouse over an event
14709              * @param {Calendar} this
14710              * @param {event} Event
14711              */
14712         'evententer': true,
14713         /**
14714              * @event eventleave
14715              * Fires when the mouse leaves an
14716              * @param {Calendar} this
14717              * @param {event}
14718              */
14719         'eventleave': true,
14720         /**
14721              * @event eventclick
14722              * Fires when the mouse click an
14723              * @param {Calendar} this
14724              * @param {event}
14725              */
14726         'eventclick': true
14727         
14728     });
14729
14730 };
14731
14732 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
14733     
14734      /**
14735      * @cfg {Number} startDay
14736      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
14737      */
14738     startDay : 0,
14739     
14740     loadMask : false,
14741     
14742     header : false,
14743       
14744     getAutoCreate : function(){
14745         
14746         
14747         var fc_button = function(name, corner, style, content ) {
14748             return Roo.apply({},{
14749                 tag : 'span',
14750                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
14751                          (corner.length ?
14752                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
14753                             ''
14754                         ),
14755                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
14756                 unselectable: 'on'
14757             });
14758         };
14759         
14760         var header = {};
14761         
14762         if(!this.header){
14763             header = {
14764                 tag : 'table',
14765                 cls : 'fc-header',
14766                 style : 'width:100%',
14767                 cn : [
14768                     {
14769                         tag: 'tr',
14770                         cn : [
14771                             {
14772                                 tag : 'td',
14773                                 cls : 'fc-header-left',
14774                                 cn : [
14775                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
14776                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
14777                                     { tag: 'span', cls: 'fc-header-space' },
14778                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
14779
14780
14781                                 ]
14782                             },
14783
14784                             {
14785                                 tag : 'td',
14786                                 cls : 'fc-header-center',
14787                                 cn : [
14788                                     {
14789                                         tag: 'span',
14790                                         cls: 'fc-header-title',
14791                                         cn : {
14792                                             tag: 'H2',
14793                                             html : 'month / year'
14794                                         }
14795                                     }
14796
14797                                 ]
14798                             },
14799                             {
14800                                 tag : 'td',
14801                                 cls : 'fc-header-right',
14802                                 cn : [
14803                               /*      fc_button('month', 'left', '', 'month' ),
14804                                     fc_button('week', '', '', 'week' ),
14805                                     fc_button('day', 'right', '', 'day' )
14806                                 */    
14807
14808                                 ]
14809                             }
14810
14811                         ]
14812                     }
14813                 ]
14814             };
14815         }
14816         
14817         header = this.header;
14818         
14819        
14820         var cal_heads = function() {
14821             var ret = [];
14822             // fixme - handle this.
14823             
14824             for (var i =0; i < Date.dayNames.length; i++) {
14825                 var d = Date.dayNames[i];
14826                 ret.push({
14827                     tag: 'th',
14828                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
14829                     html : d.substring(0,3)
14830                 });
14831                 
14832             }
14833             ret[0].cls += ' fc-first';
14834             ret[6].cls += ' fc-last';
14835             return ret;
14836         };
14837         var cal_cell = function(n) {
14838             return  {
14839                 tag: 'td',
14840                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
14841                 cn : [
14842                     {
14843                         cn : [
14844                             {
14845                                 cls: 'fc-day-number',
14846                                 html: 'D'
14847                             },
14848                             {
14849                                 cls: 'fc-day-content',
14850                              
14851                                 cn : [
14852                                      {
14853                                         style: 'position: relative;' // height: 17px;
14854                                     }
14855                                 ]
14856                             }
14857                             
14858                             
14859                         ]
14860                     }
14861                 ]
14862                 
14863             }
14864         };
14865         var cal_rows = function() {
14866             
14867             var ret = [];
14868             for (var r = 0; r < 6; r++) {
14869                 var row= {
14870                     tag : 'tr',
14871                     cls : 'fc-week',
14872                     cn : []
14873                 };
14874                 
14875                 for (var i =0; i < Date.dayNames.length; i++) {
14876                     var d = Date.dayNames[i];
14877                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
14878
14879                 }
14880                 row.cn[0].cls+=' fc-first';
14881                 row.cn[0].cn[0].style = 'min-height:90px';
14882                 row.cn[6].cls+=' fc-last';
14883                 ret.push(row);
14884                 
14885             }
14886             ret[0].cls += ' fc-first';
14887             ret[4].cls += ' fc-prev-last';
14888             ret[5].cls += ' fc-last';
14889             return ret;
14890             
14891         };
14892         
14893         var cal_table = {
14894             tag: 'table',
14895             cls: 'fc-border-separate',
14896             style : 'width:100%',
14897             cellspacing  : 0,
14898             cn : [
14899                 { 
14900                     tag: 'thead',
14901                     cn : [
14902                         { 
14903                             tag: 'tr',
14904                             cls : 'fc-first fc-last',
14905                             cn : cal_heads()
14906                         }
14907                     ]
14908                 },
14909                 { 
14910                     tag: 'tbody',
14911                     cn : cal_rows()
14912                 }
14913                   
14914             ]
14915         };
14916          
14917          var cfg = {
14918             cls : 'fc fc-ltr',
14919             cn : [
14920                 header,
14921                 {
14922                     cls : 'fc-content',
14923                     style : "position: relative;",
14924                     cn : [
14925                         {
14926                             cls : 'fc-view fc-view-month fc-grid',
14927                             style : 'position: relative',
14928                             unselectable : 'on',
14929                             cn : [
14930                                 {
14931                                     cls : 'fc-event-container',
14932                                     style : 'position:absolute;z-index:8;top:0;left:0;'
14933                                 },
14934                                 cal_table
14935                             ]
14936                         }
14937                     ]
14938     
14939                 }
14940            ] 
14941             
14942         };
14943         
14944          
14945         
14946         return cfg;
14947     },
14948     
14949     
14950     initEvents : function()
14951     {
14952         if(!this.store){
14953             throw "can not find store for calendar";
14954         }
14955         
14956         var mark = {
14957             tag: "div",
14958             cls:"x-dlg-mask",
14959             style: "text-align:center",
14960             cn: [
14961                 {
14962                     tag: "div",
14963                     style: "background-color:white;width:50%;margin:250 auto",
14964                     cn: [
14965                         {
14966                             tag: "img",
14967                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
14968                         },
14969                         {
14970                             tag: "span",
14971                             html: "Loading"
14972                         }
14973                         
14974                     ]
14975                 }
14976             ]
14977         };
14978         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
14979         
14980         var size = this.el.select('.fc-content', true).first().getSize();
14981         this.maskEl.setSize(size.width, size.height);
14982         this.maskEl.enableDisplayMode("block");
14983         if(!this.loadMask){
14984             this.maskEl.hide();
14985         }
14986         
14987         this.store = Roo.factory(this.store, Roo.data);
14988         this.store.on('load', this.onLoad, this);
14989         this.store.on('beforeload', this.onBeforeLoad, this);
14990         
14991         this.resize();
14992         
14993         this.cells = this.el.select('.fc-day',true);
14994         //Roo.log(this.cells);
14995         this.textNodes = this.el.query('.fc-day-number');
14996         this.cells.addClassOnOver('fc-state-hover');
14997         
14998         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
14999         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15000         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15001         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15002         
15003         this.on('monthchange', this.onMonthChange, this);
15004         
15005         this.update(new Date().clearTime());
15006     },
15007     
15008     resize : function() {
15009         var sz  = this.el.getSize();
15010         
15011         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15012         this.el.select('.fc-day-content div',true).setHeight(34);
15013     },
15014     
15015     
15016     // private
15017     showPrevMonth : function(e){
15018         this.update(this.activeDate.add("mo", -1));
15019     },
15020     showToday : function(e){
15021         this.update(new Date().clearTime());
15022     },
15023     // private
15024     showNextMonth : function(e){
15025         this.update(this.activeDate.add("mo", 1));
15026     },
15027
15028     // private
15029     showPrevYear : function(){
15030         this.update(this.activeDate.add("y", -1));
15031     },
15032
15033     // private
15034     showNextYear : function(){
15035         this.update(this.activeDate.add("y", 1));
15036     },
15037
15038     
15039    // private
15040     update : function(date)
15041     {
15042         var vd = this.activeDate;
15043         this.activeDate = date;
15044 //        if(vd && this.el){
15045 //            var t = date.getTime();
15046 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15047 //                Roo.log('using add remove');
15048 //                
15049 //                this.fireEvent('monthchange', this, date);
15050 //                
15051 //                this.cells.removeClass("fc-state-highlight");
15052 //                this.cells.each(function(c){
15053 //                   if(c.dateValue == t){
15054 //                       c.addClass("fc-state-highlight");
15055 //                       setTimeout(function(){
15056 //                            try{c.dom.firstChild.focus();}catch(e){}
15057 //                       }, 50);
15058 //                       return false;
15059 //                   }
15060 //                   return true;
15061 //                });
15062 //                return;
15063 //            }
15064 //        }
15065         
15066         var days = date.getDaysInMonth();
15067         
15068         var firstOfMonth = date.getFirstDateOfMonth();
15069         var startingPos = firstOfMonth.getDay()-this.startDay;
15070         
15071         if(startingPos < this.startDay){
15072             startingPos += 7;
15073         }
15074         
15075         var pm = date.add(Date.MONTH, -1);
15076         var prevStart = pm.getDaysInMonth()-startingPos;
15077 //        
15078         this.cells = this.el.select('.fc-day',true);
15079         this.textNodes = this.el.query('.fc-day-number');
15080         this.cells.addClassOnOver('fc-state-hover');
15081         
15082         var cells = this.cells.elements;
15083         var textEls = this.textNodes;
15084         
15085         Roo.each(cells, function(cell){
15086             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15087         });
15088         
15089         days += startingPos;
15090
15091         // convert everything to numbers so it's fast
15092         var day = 86400000;
15093         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15094         //Roo.log(d);
15095         //Roo.log(pm);
15096         //Roo.log(prevStart);
15097         
15098         var today = new Date().clearTime().getTime();
15099         var sel = date.clearTime().getTime();
15100         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15101         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15102         var ddMatch = this.disabledDatesRE;
15103         var ddText = this.disabledDatesText;
15104         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15105         var ddaysText = this.disabledDaysText;
15106         var format = this.format;
15107         
15108         var setCellClass = function(cal, cell){
15109             cell.row = 0;
15110             cell.events = [];
15111             cell.more = [];
15112             //Roo.log('set Cell Class');
15113             cell.title = "";
15114             var t = d.getTime();
15115             
15116             //Roo.log(d);
15117             
15118             cell.dateValue = t;
15119             if(t == today){
15120                 cell.className += " fc-today";
15121                 cell.className += " fc-state-highlight";
15122                 cell.title = cal.todayText;
15123             }
15124             if(t == sel){
15125                 // disable highlight in other month..
15126                 //cell.className += " fc-state-highlight";
15127                 
15128             }
15129             // disabling
15130             if(t < min) {
15131                 cell.className = " fc-state-disabled";
15132                 cell.title = cal.minText;
15133                 return;
15134             }
15135             if(t > max) {
15136                 cell.className = " fc-state-disabled";
15137                 cell.title = cal.maxText;
15138                 return;
15139             }
15140             if(ddays){
15141                 if(ddays.indexOf(d.getDay()) != -1){
15142                     cell.title = ddaysText;
15143                     cell.className = " fc-state-disabled";
15144                 }
15145             }
15146             if(ddMatch && format){
15147                 var fvalue = d.dateFormat(format);
15148                 if(ddMatch.test(fvalue)){
15149                     cell.title = ddText.replace("%0", fvalue);
15150                     cell.className = " fc-state-disabled";
15151                 }
15152             }
15153             
15154             if (!cell.initialClassName) {
15155                 cell.initialClassName = cell.dom.className;
15156             }
15157             
15158             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
15159         };
15160
15161         var i = 0;
15162         
15163         for(; i < startingPos; i++) {
15164             textEls[i].innerHTML = (++prevStart);
15165             d.setDate(d.getDate()+1);
15166             
15167             cells[i].className = "fc-past fc-other-month";
15168             setCellClass(this, cells[i]);
15169         }
15170         
15171         var intDay = 0;
15172         
15173         for(; i < days; i++){
15174             intDay = i - startingPos + 1;
15175             textEls[i].innerHTML = (intDay);
15176             d.setDate(d.getDate()+1);
15177             
15178             cells[i].className = ''; // "x-date-active";
15179             setCellClass(this, cells[i]);
15180         }
15181         var extraDays = 0;
15182         
15183         for(; i < 42; i++) {
15184             textEls[i].innerHTML = (++extraDays);
15185             d.setDate(d.getDate()+1);
15186             
15187             cells[i].className = "fc-future fc-other-month";
15188             setCellClass(this, cells[i]);
15189         }
15190         
15191         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15192         
15193         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15194         
15195         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15196         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15197         
15198         if(totalRows != 6){
15199             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15200             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15201         }
15202         
15203         this.fireEvent('monthchange', this, date);
15204         
15205         
15206         /*
15207         if(!this.internalRender){
15208             var main = this.el.dom.firstChild;
15209             var w = main.offsetWidth;
15210             this.el.setWidth(w + this.el.getBorderWidth("lr"));
15211             Roo.fly(main).setWidth(w);
15212             this.internalRender = true;
15213             // opera does not respect the auto grow header center column
15214             // then, after it gets a width opera refuses to recalculate
15215             // without a second pass
15216             if(Roo.isOpera && !this.secondPass){
15217                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15218                 this.secondPass = true;
15219                 this.update.defer(10, this, [date]);
15220             }
15221         }
15222         */
15223         
15224     },
15225     
15226     findCell : function(dt) {
15227         dt = dt.clearTime().getTime();
15228         var ret = false;
15229         this.cells.each(function(c){
15230             //Roo.log("check " +c.dateValue + '?=' + dt);
15231             if(c.dateValue == dt){
15232                 ret = c;
15233                 return false;
15234             }
15235             return true;
15236         });
15237         
15238         return ret;
15239     },
15240     
15241     findCells : function(ev) {
15242         var s = ev.start.clone().clearTime().getTime();
15243        // Roo.log(s);
15244         var e= ev.end.clone().clearTime().getTime();
15245        // Roo.log(e);
15246         var ret = [];
15247         this.cells.each(function(c){
15248              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15249             
15250             if(c.dateValue > e){
15251                 return ;
15252             }
15253             if(c.dateValue < s){
15254                 return ;
15255             }
15256             ret.push(c);
15257         });
15258         
15259         return ret;    
15260     },
15261     
15262 //    findBestRow: function(cells)
15263 //    {
15264 //        var ret = 0;
15265 //        
15266 //        for (var i =0 ; i < cells.length;i++) {
15267 //            ret  = Math.max(cells[i].rows || 0,ret);
15268 //        }
15269 //        return ret;
15270 //        
15271 //    },
15272     
15273     
15274     addItem : function(ev)
15275     {
15276         // look for vertical location slot in
15277         var cells = this.findCells(ev);
15278         
15279 //        ev.row = this.findBestRow(cells);
15280         
15281         // work out the location.
15282         
15283         var crow = false;
15284         var rows = [];
15285         for(var i =0; i < cells.length; i++) {
15286             
15287             cells[i].row = cells[0].row;
15288             
15289             if(i == 0){
15290                 cells[i].row = cells[i].row + 1;
15291             }
15292             
15293             if (!crow) {
15294                 crow = {
15295                     start : cells[i],
15296                     end :  cells[i]
15297                 };
15298                 continue;
15299             }
15300             if (crow.start.getY() == cells[i].getY()) {
15301                 // on same row.
15302                 crow.end = cells[i];
15303                 continue;
15304             }
15305             // different row.
15306             rows.push(crow);
15307             crow = {
15308                 start: cells[i],
15309                 end : cells[i]
15310             };
15311             
15312         }
15313         
15314         rows.push(crow);
15315         ev.els = [];
15316         ev.rows = rows;
15317         ev.cells = cells;
15318         
15319         cells[0].events.push(ev);
15320         
15321         this.calevents.push(ev);
15322     },
15323     
15324     clearEvents: function() {
15325         
15326         if(!this.calevents){
15327             return;
15328         }
15329         
15330         Roo.each(this.cells.elements, function(c){
15331             c.row = 0;
15332             c.events = [];
15333             c.more = [];
15334         });
15335         
15336         Roo.each(this.calevents, function(e) {
15337             Roo.each(e.els, function(el) {
15338                 el.un('mouseenter' ,this.onEventEnter, this);
15339                 el.un('mouseleave' ,this.onEventLeave, this);
15340                 el.remove();
15341             },this);
15342         },this);
15343         
15344         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
15345             e.remove();
15346         });
15347         
15348     },
15349     
15350     renderEvents: function()
15351     {   
15352         var _this = this;
15353         
15354         this.cells.each(function(c) {
15355             
15356             if(c.row < 5){
15357                 return;
15358             }
15359             
15360             var ev = c.events;
15361             
15362             var r = 4;
15363             if(c.row != c.events.length){
15364                 r = 4 - (4 - (c.row - c.events.length));
15365             }
15366             
15367             c.events = ev.slice(0, r);
15368             c.more = ev.slice(r);
15369             
15370             if(c.more.length && c.more.length == 1){
15371                 c.events.push(c.more.pop());
15372             }
15373             
15374             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15375             
15376         });
15377             
15378         this.cells.each(function(c) {
15379             
15380             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15381             
15382             
15383             for (var e = 0; e < c.events.length; e++){
15384                 var ev = c.events[e];
15385                 var rows = ev.rows;
15386                 
15387                 for(var i = 0; i < rows.length; i++) {
15388                 
15389                     // how many rows should it span..
15390
15391                     var  cfg = {
15392                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15393                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15394
15395                         unselectable : "on",
15396                         cn : [
15397                             {
15398                                 cls: 'fc-event-inner',
15399                                 cn : [
15400     //                                {
15401     //                                  tag:'span',
15402     //                                  cls: 'fc-event-time',
15403     //                                  html : cells.length > 1 ? '' : ev.time
15404     //                                },
15405                                     {
15406                                       tag:'span',
15407                                       cls: 'fc-event-title',
15408                                       html : String.format('{0}', ev.title)
15409                                     }
15410
15411
15412                                 ]
15413                             },
15414                             {
15415                                 cls: 'ui-resizable-handle ui-resizable-e',
15416                                 html : '&nbsp;&nbsp;&nbsp'
15417                             }
15418
15419                         ]
15420                     };
15421
15422                     if (i == 0) {
15423                         cfg.cls += ' fc-event-start';
15424                     }
15425                     if ((i+1) == rows.length) {
15426                         cfg.cls += ' fc-event-end';
15427                     }
15428
15429                     var ctr = _this.el.select('.fc-event-container',true).first();
15430                     var cg = ctr.createChild(cfg);
15431
15432                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15433                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15434
15435                     var r = (c.more.length) ? 1 : 0;
15436                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
15437                     cg.setWidth(ebox.right - sbox.x -2);
15438
15439                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15440                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15441                     cg.on('click', _this.onEventClick, _this, ev);
15442
15443                     ev.els.push(cg);
15444                     
15445                 }
15446                 
15447             }
15448             
15449             
15450             if(c.more.length){
15451                 var  cfg = {
15452                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15453                     style : 'position: absolute',
15454                     unselectable : "on",
15455                     cn : [
15456                         {
15457                             cls: 'fc-event-inner',
15458                             cn : [
15459                                 {
15460                                   tag:'span',
15461                                   cls: 'fc-event-title',
15462                                   html : 'More'
15463                                 }
15464
15465
15466                             ]
15467                         },
15468                         {
15469                             cls: 'ui-resizable-handle ui-resizable-e',
15470                             html : '&nbsp;&nbsp;&nbsp'
15471                         }
15472
15473                     ]
15474                 };
15475
15476                 var ctr = _this.el.select('.fc-event-container',true).first();
15477                 var cg = ctr.createChild(cfg);
15478
15479                 var sbox = c.select('.fc-day-content',true).first().getBox();
15480                 var ebox = c.select('.fc-day-content',true).first().getBox();
15481                 //Roo.log(cg);
15482                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
15483                 cg.setWidth(ebox.right - sbox.x -2);
15484
15485                 cg.on('click', _this.onMoreEventClick, _this, c.more);
15486                 
15487             }
15488             
15489         });
15490         
15491         
15492         
15493     },
15494     
15495     onEventEnter: function (e, el,event,d) {
15496         this.fireEvent('evententer', this, el, event);
15497     },
15498     
15499     onEventLeave: function (e, el,event,d) {
15500         this.fireEvent('eventleave', this, el, event);
15501     },
15502     
15503     onEventClick: function (e, el,event,d) {
15504         this.fireEvent('eventclick', this, el, event);
15505     },
15506     
15507     onMonthChange: function () {
15508         this.store.load();
15509     },
15510     
15511     onMoreEventClick: function(e, el, more)
15512     {
15513         var _this = this;
15514         
15515         this.calpopover.placement = 'right';
15516         this.calpopover.setTitle('More');
15517         
15518         this.calpopover.setContent('');
15519         
15520         var ctr = this.calpopover.el.select('.popover-content', true).first();
15521         
15522         Roo.each(more, function(m){
15523             var cfg = {
15524                 cls : 'fc-event-hori fc-event-draggable',
15525                 html : m.title
15526             };
15527             var cg = ctr.createChild(cfg);
15528             
15529             cg.on('click', _this.onEventClick, _this, m);
15530         });
15531         
15532         this.calpopover.show(el);
15533         
15534         
15535     },
15536     
15537     onLoad: function () 
15538     {   
15539         this.calevents = [];
15540         var cal = this;
15541         
15542         if(this.store.getCount() > 0){
15543             this.store.data.each(function(d){
15544                cal.addItem({
15545                     id : d.data.id,
15546                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15547                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15548                     time : d.data.start_time,
15549                     title : d.data.title,
15550                     description : d.data.description,
15551                     venue : d.data.venue
15552                 });
15553             });
15554         }
15555         
15556         this.renderEvents();
15557         
15558         if(this.calevents.length && this.loadMask){
15559             this.maskEl.hide();
15560         }
15561     },
15562     
15563     onBeforeLoad: function()
15564     {
15565         this.clearEvents();
15566         if(this.loadMask){
15567             this.maskEl.show();
15568         }
15569     }
15570 });
15571
15572  
15573  /*
15574  * - LGPL
15575  *
15576  * element
15577  * 
15578  */
15579
15580 /**
15581  * @class Roo.bootstrap.Popover
15582  * @extends Roo.bootstrap.Component
15583  * Bootstrap Popover class
15584  * @cfg {String} html contents of the popover   (or false to use children..)
15585  * @cfg {String} title of popover (or false to hide)
15586  * @cfg {String} placement how it is placed
15587  * @cfg {String} trigger click || hover (or false to trigger manually)
15588  * @cfg {String} over what (parent or false to trigger manually.)
15589  * @cfg {Number} delay - delay before showing
15590  
15591  * @constructor
15592  * Create a new Popover
15593  * @param {Object} config The config object
15594  */
15595
15596 Roo.bootstrap.Popover = function(config){
15597     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15598 };
15599
15600 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
15601     
15602     title: 'Fill in a title',
15603     html: false,
15604     
15605     placement : 'right',
15606     trigger : 'hover', // hover
15607     
15608     delay : 0,
15609     
15610     over: 'parent',
15611     
15612     can_build_overlaid : false,
15613     
15614     getChildContainer : function()
15615     {
15616         return this.el.select('.popover-content',true).first();
15617     },
15618     
15619     getAutoCreate : function(){
15620          Roo.log('make popover?');
15621         var cfg = {
15622            cls : 'popover roo-dynamic',
15623            style: 'display:block',
15624            cn : [
15625                 {
15626                     cls : 'arrow'
15627                 },
15628                 {
15629                     cls : 'popover-inner',
15630                     cn : [
15631                         {
15632                             tag: 'h3',
15633                             cls: 'popover-title',
15634                             html : this.title
15635                         },
15636                         {
15637                             cls : 'popover-content',
15638                             html : this.html
15639                         }
15640                     ]
15641                     
15642                 }
15643            ]
15644         };
15645         
15646         return cfg;
15647     },
15648     setTitle: function(str)
15649     {
15650         this.title = str;
15651         this.el.select('.popover-title',true).first().dom.innerHTML = str;
15652     },
15653     setContent: function(str)
15654     {
15655         this.html = str;
15656         this.el.select('.popover-content',true).first().dom.innerHTML = str;
15657     },
15658     // as it get's added to the bottom of the page.
15659     onRender : function(ct, position)
15660     {
15661         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15662         if(!this.el){
15663             var cfg = Roo.apply({},  this.getAutoCreate());
15664             cfg.id = Roo.id();
15665             
15666             if (this.cls) {
15667                 cfg.cls += ' ' + this.cls;
15668             }
15669             if (this.style) {
15670                 cfg.style = this.style;
15671             }
15672             //Roo.log("adding to ");
15673             this.el = Roo.get(document.body).createChild(cfg, position);
15674             Roo.log(this.el);
15675         }
15676         this.initEvents();
15677     },
15678     
15679     initEvents : function()
15680     {
15681         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15682         this.el.enableDisplayMode('block');
15683         this.el.hide();
15684         if (this.over === false) {
15685             return; 
15686         }
15687         if (this.triggers === false) {
15688             return;
15689         }
15690         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15691         var triggers = this.trigger ? this.trigger.split(' ') : [];
15692         Roo.each(triggers, function(trigger) {
15693         
15694             if (trigger == 'click') {
15695                 on_el.on('click', this.toggle, this);
15696             } else if (trigger != 'manual') {
15697                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
15698                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
15699       
15700                 on_el.on(eventIn  ,this.enter, this);
15701                 on_el.on(eventOut, this.leave, this);
15702             }
15703         }, this);
15704         
15705     },
15706     
15707     
15708     // private
15709     timeout : null,
15710     hoverState : null,
15711     
15712     toggle : function () {
15713         this.hoverState == 'in' ? this.leave() : this.enter();
15714     },
15715     
15716     enter : function () {
15717        
15718     
15719         clearTimeout(this.timeout);
15720     
15721         this.hoverState = 'in';
15722     
15723         if (!this.delay || !this.delay.show) {
15724             this.show();
15725             return;
15726         }
15727         var _t = this;
15728         this.timeout = setTimeout(function () {
15729             if (_t.hoverState == 'in') {
15730                 _t.show();
15731             }
15732         }, this.delay.show)
15733     },
15734     leave : function() {
15735         clearTimeout(this.timeout);
15736     
15737         this.hoverState = 'out';
15738     
15739         if (!this.delay || !this.delay.hide) {
15740             this.hide();
15741             return;
15742         }
15743         var _t = this;
15744         this.timeout = setTimeout(function () {
15745             if (_t.hoverState == 'out') {
15746                 _t.hide();
15747             }
15748         }, this.delay.hide)
15749     },
15750     
15751     show : function (on_el)
15752     {
15753         if (!on_el) {
15754             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15755         }
15756         // set content.
15757         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
15758         if (this.html !== false) {
15759             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
15760         }
15761         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
15762         if (!this.title.length) {
15763             this.el.select('.popover-title',true).hide();
15764         }
15765         
15766         var placement = typeof this.placement == 'function' ?
15767             this.placement.call(this, this.el, on_el) :
15768             this.placement;
15769             
15770         var autoToken = /\s?auto?\s?/i;
15771         var autoPlace = autoToken.test(placement);
15772         if (autoPlace) {
15773             placement = placement.replace(autoToken, '') || 'top';
15774         }
15775         
15776         //this.el.detach()
15777         //this.el.setXY([0,0]);
15778         this.el.show();
15779         this.el.dom.style.display='block';
15780         this.el.addClass(placement);
15781         
15782         //this.el.appendTo(on_el);
15783         
15784         var p = this.getPosition();
15785         var box = this.el.getBox();
15786         
15787         if (autoPlace) {
15788             // fixme..
15789         }
15790         var align = Roo.bootstrap.Popover.alignment[placement];
15791         this.el.alignTo(on_el, align[0],align[1]);
15792         //var arrow = this.el.select('.arrow',true).first();
15793         //arrow.set(align[2], 
15794         
15795         this.el.addClass('in');
15796         
15797         
15798         if (this.el.hasClass('fade')) {
15799             // fade it?
15800         }
15801         
15802     },
15803     hide : function()
15804     {
15805         this.el.setXY([0,0]);
15806         this.el.removeClass('in');
15807         this.el.hide();
15808         this.hoverState = null;
15809         
15810     }
15811     
15812 });
15813
15814 Roo.bootstrap.Popover.alignment = {
15815     'left' : ['r-l', [-10,0], 'right'],
15816     'right' : ['l-r', [10,0], 'left'],
15817     'bottom' : ['t-b', [0,10], 'top'],
15818     'top' : [ 'b-t', [0,-10], 'bottom']
15819 };
15820
15821  /*
15822  * - LGPL
15823  *
15824  * Progress
15825  * 
15826  */
15827
15828 /**
15829  * @class Roo.bootstrap.Progress
15830  * @extends Roo.bootstrap.Component
15831  * Bootstrap Progress class
15832  * @cfg {Boolean} striped striped of the progress bar
15833  * @cfg {Boolean} active animated of the progress bar
15834  * 
15835  * 
15836  * @constructor
15837  * Create a new Progress
15838  * @param {Object} config The config object
15839  */
15840
15841 Roo.bootstrap.Progress = function(config){
15842     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
15843 };
15844
15845 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
15846     
15847     striped : false,
15848     active: false,
15849     
15850     getAutoCreate : function(){
15851         var cfg = {
15852             tag: 'div',
15853             cls: 'progress'
15854         };
15855         
15856         
15857         if(this.striped){
15858             cfg.cls += ' progress-striped';
15859         }
15860       
15861         if(this.active){
15862             cfg.cls += ' active';
15863         }
15864         
15865         
15866         return cfg;
15867     }
15868    
15869 });
15870
15871  
15872
15873  /*
15874  * - LGPL
15875  *
15876  * ProgressBar
15877  * 
15878  */
15879
15880 /**
15881  * @class Roo.bootstrap.ProgressBar
15882  * @extends Roo.bootstrap.Component
15883  * Bootstrap ProgressBar class
15884  * @cfg {Number} aria_valuenow aria-value now
15885  * @cfg {Number} aria_valuemin aria-value min
15886  * @cfg {Number} aria_valuemax aria-value max
15887  * @cfg {String} label label for the progress bar
15888  * @cfg {String} panel (success | info | warning | danger )
15889  * @cfg {String} role role of the progress bar
15890  * @cfg {String} sr_only text
15891  * 
15892  * 
15893  * @constructor
15894  * Create a new ProgressBar
15895  * @param {Object} config The config object
15896  */
15897
15898 Roo.bootstrap.ProgressBar = function(config){
15899     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
15900 };
15901
15902 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
15903     
15904     aria_valuenow : 0,
15905     aria_valuemin : 0,
15906     aria_valuemax : 100,
15907     label : false,
15908     panel : false,
15909     role : false,
15910     sr_only: false,
15911     
15912     getAutoCreate : function()
15913     {
15914         
15915         var cfg = {
15916             tag: 'div',
15917             cls: 'progress-bar',
15918             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
15919         };
15920         
15921         if(this.sr_only){
15922             cfg.cn = {
15923                 tag: 'span',
15924                 cls: 'sr-only',
15925                 html: this.sr_only
15926             }
15927         }
15928         
15929         if(this.role){
15930             cfg.role = this.role;
15931         }
15932         
15933         if(this.aria_valuenow){
15934             cfg['aria-valuenow'] = this.aria_valuenow;
15935         }
15936         
15937         if(this.aria_valuemin){
15938             cfg['aria-valuemin'] = this.aria_valuemin;
15939         }
15940         
15941         if(this.aria_valuemax){
15942             cfg['aria-valuemax'] = this.aria_valuemax;
15943         }
15944         
15945         if(this.label && !this.sr_only){
15946             cfg.html = this.label;
15947         }
15948         
15949         if(this.panel){
15950             cfg.cls += ' progress-bar-' + this.panel;
15951         }
15952         
15953         return cfg;
15954     },
15955     
15956     update : function(aria_valuenow)
15957     {
15958         this.aria_valuenow = aria_valuenow;
15959         
15960         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
15961     }
15962    
15963 });
15964
15965  
15966
15967  /*
15968  * - LGPL
15969  *
15970  * column
15971  * 
15972  */
15973
15974 /**
15975  * @class Roo.bootstrap.TabGroup
15976  * @extends Roo.bootstrap.Column
15977  * Bootstrap Column class
15978  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
15979  * @cfg {Boolean} carousel true to make the group behave like a carousel
15980  * @cfg {Boolean} bullets show bullets for the panels
15981  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
15982  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
15983  * @cfg {Number} timer auto slide timer .. default 0 millisecond
15984  * 
15985  * @constructor
15986  * Create a new TabGroup
15987  * @param {Object} config The config object
15988  */
15989
15990 Roo.bootstrap.TabGroup = function(config){
15991     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
15992     if (!this.navId) {
15993         this.navId = Roo.id();
15994     }
15995     this.tabs = [];
15996     Roo.bootstrap.TabGroup.register(this);
15997     
15998 };
15999
16000 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
16001     
16002     carousel : false,
16003     transition : false,
16004     bullets : 0,
16005     timer : 0,
16006     autoslide : false,
16007     slideFn : false,
16008     slideOnTouch : false,
16009     
16010     getAutoCreate : function()
16011     {
16012         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16013         
16014         cfg.cls += ' tab-content';
16015         
16016         Roo.log('get auto create...............');
16017         
16018         if (this.carousel) {
16019             cfg.cls += ' carousel slide';
16020             
16021             cfg.cn = [{
16022                cls : 'carousel-inner'
16023             }];
16024         
16025             if(this.bullets  && !Roo.isTouch){
16026                 
16027                 var bullets = {
16028                     cls : 'carousel-bullets',
16029                     cn : []
16030                 };
16031                
16032                 if(this.bullets_cls){
16033                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16034                 }
16035                  /*
16036                 for (var i = 0; i < this.bullets; i++){
16037                     bullets.cn.push({
16038                         cls : 'bullet bullet-' + i
16039                     });
16040                 }
16041                 */
16042                 bullets.cn.push({
16043                     cls : 'clear'
16044                 });
16045                 
16046                 cfg.cn[0].cn = bullets;
16047             }
16048         }
16049         
16050         return cfg;
16051     },
16052     
16053     initEvents:  function()
16054     {
16055         Roo.log('-------- init events on tab group ---------');
16056         
16057         
16058         
16059         Roo.log(this);
16060         
16061         if(Roo.isTouch && this.slideOnTouch){
16062             this.el.on("touchstart", this.onTouchStart, this);
16063         }
16064         
16065         if(this.autoslide){
16066             var _this = this;
16067             
16068             this.slideFn = window.setInterval(function() {
16069                 _this.showPanelNext();
16070             }, this.timer);
16071         }
16072         
16073     },
16074     
16075     onTouchStart : function(e, el, o)
16076     {
16077         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
16078             return;
16079         }
16080         
16081         this.showPanelNext();
16082     },
16083     
16084     getChildContainer : function()
16085     {
16086         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
16087     },
16088     
16089     /**
16090     * register a Navigation item
16091     * @param {Roo.bootstrap.NavItem} the navitem to add
16092     */
16093     register : function(item)
16094     {
16095         this.tabs.push( item);
16096         item.navId = this.navId; // not really needed..
16097         this.addBullet();
16098     
16099     },
16100     
16101     getActivePanel : function()
16102     {
16103         var r = false;
16104         Roo.each(this.tabs, function(t) {
16105             if (t.active) {
16106                 r = t;
16107                 return false;
16108             }
16109             return null;
16110         });
16111         return r;
16112         
16113     },
16114     getPanelByName : function(n)
16115     {
16116         var r = false;
16117         Roo.each(this.tabs, function(t) {
16118             if (t.tabId == n) {
16119                 r = t;
16120                 return false;
16121             }
16122             return null;
16123         });
16124         return r;
16125     },
16126     indexOfPanel : function(p)
16127     {
16128         var r = false;
16129         Roo.each(this.tabs, function(t,i) {
16130             if (t.tabId == p.tabId) {
16131                 r = i;
16132                 return false;
16133             }
16134             return null;
16135         });
16136         return r;
16137     },
16138     /**
16139      * show a specific panel
16140      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16141      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16142      */
16143     showPanel : function (pan)
16144     {
16145         if(this.transition){
16146             Roo.log("waiting for the transitionend");
16147             return;
16148         }
16149         
16150         if (typeof(pan) == 'number') {
16151             pan = this.tabs[pan];
16152         }
16153         if (typeof(pan) == 'string') {
16154             pan = this.getPanelByName(pan);
16155         }
16156         if (pan.tabId == this.getActivePanel().tabId) {
16157             return true;
16158         }
16159         var cur = this.getActivePanel();
16160         
16161         if (false === cur.fireEvent('beforedeactivate')) {
16162             return false;
16163         }
16164         
16165         if(this.bullets > 0 && !Roo.isTouch){
16166             this.setActiveBullet(this.indexOfPanel(pan));
16167         }
16168         
16169         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16170             
16171             this.transition = true;
16172             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
16173             var lr = dir == 'next' ? 'left' : 'right';
16174             pan.el.addClass(dir); // or prev
16175             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16176             cur.el.addClass(lr); // or right
16177             pan.el.addClass(lr);
16178             
16179             var _this = this;
16180             cur.el.on('transitionend', function() {
16181                 Roo.log("trans end?");
16182                 
16183                 pan.el.removeClass([lr,dir]);
16184                 pan.setActive(true);
16185                 
16186                 cur.el.removeClass([lr]);
16187                 cur.setActive(false);
16188                 
16189                 _this.transition = false;
16190                 
16191             }, this, { single:  true } );
16192             
16193             return true;
16194         }
16195         
16196         cur.setActive(false);
16197         pan.setActive(true);
16198         
16199         return true;
16200         
16201     },
16202     showPanelNext : function()
16203     {
16204         var i = this.indexOfPanel(this.getActivePanel());
16205         
16206         if (i >= this.tabs.length - 1 && !this.autoslide) {
16207             return;
16208         }
16209         
16210         if (i >= this.tabs.length - 1 && this.autoslide) {
16211             i = -1;
16212         }
16213         
16214         this.showPanel(this.tabs[i+1]);
16215     },
16216     
16217     showPanelPrev : function()
16218     {
16219         var i = this.indexOfPanel(this.getActivePanel());
16220         
16221         if (i  < 1 && !this.autoslide) {
16222             return;
16223         }
16224         
16225         if (i < 1 && this.autoslide) {
16226             i = this.tabs.length;
16227         }
16228         
16229         this.showPanel(this.tabs[i-1]);
16230     },
16231     
16232     
16233     addBullet: function()
16234     {
16235         if(!this.bullets || Roo.isTouch){
16236             return;
16237         }
16238         var ctr = this.el.select('.carousel-bullets',true).first();
16239         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16240         var bullet = ctr.createChild({
16241             cls : 'bullet bullet-' + i
16242         },ctr.dom.lastChild);
16243         
16244         bullet.on('click', (function(e, el, o, ii, t){
16245
16246             e.preventDefault();
16247
16248             this.showPanel(ii);
16249
16250             if(this.autoslide && this.slideFn){
16251                 clearInterval(this.slideFn);
16252                 this.slideFn = window.setInterval(function() {
16253                     this.showPanelNext();
16254                 }, this.timer);
16255             }
16256
16257         }).createDelegate(this, [i, bullet], true));
16258                 
16259         
16260     },
16261      
16262     setActiveBullet : function(i)
16263     {
16264         if(Roo.isTouch){
16265             return;
16266         }
16267         
16268         Roo.each(this.el.select('.bullet', true).elements, function(el){
16269             el.removeClass('selected');
16270         });
16271
16272         var bullet = this.el.select('.bullet-' + i, true).first();
16273         
16274         if(!bullet){
16275             return;
16276         }
16277         
16278         bullet.addClass('selected');
16279     }
16280     
16281     
16282   
16283 });
16284
16285  
16286
16287  
16288  
16289 Roo.apply(Roo.bootstrap.TabGroup, {
16290     
16291     groups: {},
16292      /**
16293     * register a Navigation Group
16294     * @param {Roo.bootstrap.NavGroup} the navgroup to add
16295     */
16296     register : function(navgrp)
16297     {
16298         this.groups[navgrp.navId] = navgrp;
16299         
16300     },
16301     /**
16302     * fetch a Navigation Group based on the navigation ID
16303     * if one does not exist , it will get created.
16304     * @param {string} the navgroup to add
16305     * @returns {Roo.bootstrap.NavGroup} the navgroup 
16306     */
16307     get: function(navId) {
16308         if (typeof(this.groups[navId]) == 'undefined') {
16309             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
16310         }
16311         return this.groups[navId] ;
16312     }
16313     
16314     
16315     
16316 });
16317
16318  /*
16319  * - LGPL
16320  *
16321  * TabPanel
16322  * 
16323  */
16324
16325 /**
16326  * @class Roo.bootstrap.TabPanel
16327  * @extends Roo.bootstrap.Component
16328  * Bootstrap TabPanel class
16329  * @cfg {Boolean} active panel active
16330  * @cfg {String} html panel content
16331  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16332  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16333  * 
16334  * 
16335  * @constructor
16336  * Create a new TabPanel
16337  * @param {Object} config The config object
16338  */
16339
16340 Roo.bootstrap.TabPanel = function(config){
16341     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16342     this.addEvents({
16343         /**
16344              * @event changed
16345              * Fires when the active status changes
16346              * @param {Roo.bootstrap.TabPanel} this
16347              * @param {Boolean} state the new state
16348             
16349          */
16350         'changed': true,
16351         /**
16352              * @event beforedeactivate
16353              * Fires before a tab is de-activated - can be used to do validation on a form.
16354              * @param {Roo.bootstrap.TabPanel} this
16355              * @return {Boolean} false if there is an error
16356             
16357          */
16358         'beforedeactivate': true
16359      });
16360     
16361     this.tabId = this.tabId || Roo.id();
16362   
16363 };
16364
16365 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
16366     
16367     active: false,
16368     html: false,
16369     tabId: false,
16370     navId : false,
16371     
16372     getAutoCreate : function(){
16373         var cfg = {
16374             tag: 'div',
16375             // item is needed for carousel - not sure if it has any effect otherwise
16376             cls: 'tab-pane item',
16377             html: this.html || ''
16378         };
16379         
16380         if(this.active){
16381             cfg.cls += ' active';
16382         }
16383         
16384         if(this.tabId){
16385             cfg.tabId = this.tabId;
16386         }
16387         
16388         
16389         return cfg;
16390     },
16391     
16392     initEvents:  function()
16393     {
16394         Roo.log('-------- init events on tab panel ---------');
16395         
16396         var p = this.parent();
16397         this.navId = this.navId || p.navId;
16398         
16399         if (typeof(this.navId) != 'undefined') {
16400             // not really needed.. but just in case.. parent should be a NavGroup.
16401             var tg = Roo.bootstrap.TabGroup.get(this.navId);
16402             Roo.log(['register', tg, this]);
16403             tg.register(this);
16404             
16405             var i = tg.tabs.length - 1;
16406             
16407             if(this.active && tg.bullets > 0 && i < tg.bullets){
16408                 tg.setActiveBullet(i);
16409             }
16410         }
16411         
16412     },
16413     
16414     
16415     onRender : function(ct, position)
16416     {
16417        // Roo.log("Call onRender: " + this.xtype);
16418         
16419         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16420         
16421         
16422         
16423         
16424         
16425     },
16426     
16427     setActive: function(state)
16428     {
16429         Roo.log("panel - set active " + this.tabId + "=" + state);
16430         
16431         this.active = state;
16432         if (!state) {
16433             this.el.removeClass('active');
16434             
16435         } else  if (!this.el.hasClass('active')) {
16436             this.el.addClass('active');
16437         }
16438         
16439         this.fireEvent('changed', this, state);
16440     }
16441     
16442     
16443 });
16444  
16445
16446  
16447
16448  /*
16449  * - LGPL
16450  *
16451  * DateField
16452  * 
16453  */
16454
16455 /**
16456  * @class Roo.bootstrap.DateField
16457  * @extends Roo.bootstrap.Input
16458  * Bootstrap DateField class
16459  * @cfg {Number} weekStart default 0
16460  * @cfg {String} viewMode default empty, (months|years)
16461  * @cfg {String} minViewMode default empty, (months|years)
16462  * @cfg {Number} startDate default -Infinity
16463  * @cfg {Number} endDate default Infinity
16464  * @cfg {Boolean} todayHighlight default false
16465  * @cfg {Boolean} todayBtn default false
16466  * @cfg {Boolean} calendarWeeks default false
16467  * @cfg {Object} daysOfWeekDisabled default empty
16468  * @cfg {Boolean} singleMode default false (true | false)
16469  * 
16470  * @cfg {Boolean} keyboardNavigation default true
16471  * @cfg {String} language default en
16472  * 
16473  * @constructor
16474  * Create a new DateField
16475  * @param {Object} config The config object
16476  */
16477
16478 Roo.bootstrap.DateField = function(config){
16479     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16480      this.addEvents({
16481             /**
16482              * @event show
16483              * Fires when this field show.
16484              * @param {Roo.bootstrap.DateField} this
16485              * @param {Mixed} date The date value
16486              */
16487             show : true,
16488             /**
16489              * @event show
16490              * Fires when this field hide.
16491              * @param {Roo.bootstrap.DateField} this
16492              * @param {Mixed} date The date value
16493              */
16494             hide : true,
16495             /**
16496              * @event select
16497              * Fires when select a date.
16498              * @param {Roo.bootstrap.DateField} this
16499              * @param {Mixed} date The date value
16500              */
16501             select : true,
16502             /**
16503              * @event beforeselect
16504              * Fires when before select a date.
16505              * @param {Roo.bootstrap.DateField} this
16506              * @param {Mixed} date The date value
16507              */
16508             beforeselect : true
16509         });
16510 };
16511
16512 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
16513     
16514     /**
16515      * @cfg {String} format
16516      * The default date format string which can be overriden for localization support.  The format must be
16517      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16518      */
16519     format : "m/d/y",
16520     /**
16521      * @cfg {String} altFormats
16522      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16523      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16524      */
16525     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16526     
16527     weekStart : 0,
16528     
16529     viewMode : '',
16530     
16531     minViewMode : '',
16532     
16533     todayHighlight : false,
16534     
16535     todayBtn: false,
16536     
16537     language: 'en',
16538     
16539     keyboardNavigation: true,
16540     
16541     calendarWeeks: false,
16542     
16543     startDate: -Infinity,
16544     
16545     endDate: Infinity,
16546     
16547     daysOfWeekDisabled: [],
16548     
16549     _events: [],
16550     
16551     singleMode : false,
16552     
16553     UTCDate: function()
16554     {
16555         return new Date(Date.UTC.apply(Date, arguments));
16556     },
16557     
16558     UTCToday: function()
16559     {
16560         var today = new Date();
16561         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16562     },
16563     
16564     getDate: function() {
16565             var d = this.getUTCDate();
16566             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16567     },
16568     
16569     getUTCDate: function() {
16570             return this.date;
16571     },
16572     
16573     setDate: function(d) {
16574             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16575     },
16576     
16577     setUTCDate: function(d) {
16578             this.date = d;
16579             this.setValue(this.formatDate(this.date));
16580     },
16581         
16582     onRender: function(ct, position)
16583     {
16584         
16585         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16586         
16587         this.language = this.language || 'en';
16588         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16589         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16590         
16591         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16592         this.format = this.format || 'm/d/y';
16593         this.isInline = false;
16594         this.isInput = true;
16595         this.component = this.el.select('.add-on', true).first() || false;
16596         this.component = (this.component && this.component.length === 0) ? false : this.component;
16597         this.hasInput = this.component && this.inputEL().length;
16598         
16599         if (typeof(this.minViewMode === 'string')) {
16600             switch (this.minViewMode) {
16601                 case 'months':
16602                     this.minViewMode = 1;
16603                     break;
16604                 case 'years':
16605                     this.minViewMode = 2;
16606                     break;
16607                 default:
16608                     this.minViewMode = 0;
16609                     break;
16610             }
16611         }
16612         
16613         if (typeof(this.viewMode === 'string')) {
16614             switch (this.viewMode) {
16615                 case 'months':
16616                     this.viewMode = 1;
16617                     break;
16618                 case 'years':
16619                     this.viewMode = 2;
16620                     break;
16621                 default:
16622                     this.viewMode = 0;
16623                     break;
16624             }
16625         }
16626                 
16627         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16628         
16629 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16630         
16631         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16632         
16633         this.picker().on('mousedown', this.onMousedown, this);
16634         this.picker().on('click', this.onClick, this);
16635         
16636         this.picker().addClass('datepicker-dropdown');
16637         
16638         this.startViewMode = this.viewMode;
16639         
16640         if(this.singleMode){
16641             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16642                 v.setVisibilityMode(Roo.Element.DISPLAY);
16643                 v.hide();
16644             });
16645             
16646             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16647                 v.setStyle('width', '189px');
16648             });
16649         }
16650         
16651         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16652             if(!this.calendarWeeks){
16653                 v.remove();
16654                 return;
16655             }
16656             
16657             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16658             v.attr('colspan', function(i, val){
16659                 return parseInt(val) + 1;
16660             });
16661         });
16662                         
16663         
16664         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16665         
16666         this.setStartDate(this.startDate);
16667         this.setEndDate(this.endDate);
16668         
16669         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16670         
16671         this.fillDow();
16672         this.fillMonths();
16673         this.update();
16674         this.showMode();
16675         
16676         if(this.isInline) {
16677             this.show();
16678         }
16679     },
16680     
16681     picker : function()
16682     {
16683         return this.pickerEl;
16684 //        return this.el.select('.datepicker', true).first();
16685     },
16686     
16687     fillDow: function()
16688     {
16689         var dowCnt = this.weekStart;
16690         
16691         var dow = {
16692             tag: 'tr',
16693             cn: [
16694                 
16695             ]
16696         };
16697         
16698         if(this.calendarWeeks){
16699             dow.cn.push({
16700                 tag: 'th',
16701                 cls: 'cw',
16702                 html: '&nbsp;'
16703             })
16704         }
16705         
16706         while (dowCnt < this.weekStart + 7) {
16707             dow.cn.push({
16708                 tag: 'th',
16709                 cls: 'dow',
16710                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16711             });
16712         }
16713         
16714         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16715     },
16716     
16717     fillMonths: function()
16718     {    
16719         var i = 0;
16720         var months = this.picker().select('>.datepicker-months td', true).first();
16721         
16722         months.dom.innerHTML = '';
16723         
16724         while (i < 12) {
16725             var month = {
16726                 tag: 'span',
16727                 cls: 'month',
16728                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16729             };
16730             
16731             months.createChild(month);
16732         }
16733         
16734     },
16735     
16736     update: function()
16737     {
16738         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;
16739         
16740         if (this.date < this.startDate) {
16741             this.viewDate = new Date(this.startDate);
16742         } else if (this.date > this.endDate) {
16743             this.viewDate = new Date(this.endDate);
16744         } else {
16745             this.viewDate = new Date(this.date);
16746         }
16747         
16748         this.fill();
16749     },
16750     
16751     fill: function() 
16752     {
16753         var d = new Date(this.viewDate),
16754                 year = d.getUTCFullYear(),
16755                 month = d.getUTCMonth(),
16756                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16757                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16758                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16759                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16760                 currentDate = this.date && this.date.valueOf(),
16761                 today = this.UTCToday();
16762         
16763         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16764         
16765 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16766         
16767 //        this.picker.select('>tfoot th.today').
16768 //                                              .text(dates[this.language].today)
16769 //                                              .toggle(this.todayBtn !== false);
16770     
16771         this.updateNavArrows();
16772         this.fillMonths();
16773                                                 
16774         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16775         
16776         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16777          
16778         prevMonth.setUTCDate(day);
16779         
16780         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16781         
16782         var nextMonth = new Date(prevMonth);
16783         
16784         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16785         
16786         nextMonth = nextMonth.valueOf();
16787         
16788         var fillMonths = false;
16789         
16790         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16791         
16792         while(prevMonth.valueOf() < nextMonth) {
16793             var clsName = '';
16794             
16795             if (prevMonth.getUTCDay() === this.weekStart) {
16796                 if(fillMonths){
16797                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16798                 }
16799                     
16800                 fillMonths = {
16801                     tag: 'tr',
16802                     cn: []
16803                 };
16804                 
16805                 if(this.calendarWeeks){
16806                     // ISO 8601: First week contains first thursday.
16807                     // ISO also states week starts on Monday, but we can be more abstract here.
16808                     var
16809                     // Start of current week: based on weekstart/current date
16810                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16811                     // Thursday of this week
16812                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16813                     // First Thursday of year, year from thursday
16814                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16815                     // Calendar week: ms between thursdays, div ms per day, div 7 days
16816                     calWeek =  (th - yth) / 864e5 / 7 + 1;
16817                     
16818                     fillMonths.cn.push({
16819                         tag: 'td',
16820                         cls: 'cw',
16821                         html: calWeek
16822                     });
16823                 }
16824             }
16825             
16826             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16827                 clsName += ' old';
16828             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16829                 clsName += ' new';
16830             }
16831             if (this.todayHighlight &&
16832                 prevMonth.getUTCFullYear() == today.getFullYear() &&
16833                 prevMonth.getUTCMonth() == today.getMonth() &&
16834                 prevMonth.getUTCDate() == today.getDate()) {
16835                 clsName += ' today';
16836             }
16837             
16838             if (currentDate && prevMonth.valueOf() === currentDate) {
16839                 clsName += ' active';
16840             }
16841             
16842             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
16843                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
16844                     clsName += ' disabled';
16845             }
16846             
16847             fillMonths.cn.push({
16848                 tag: 'td',
16849                 cls: 'day ' + clsName,
16850                 html: prevMonth.getDate()
16851             });
16852             
16853             prevMonth.setDate(prevMonth.getDate()+1);
16854         }
16855           
16856         var currentYear = this.date && this.date.getUTCFullYear();
16857         var currentMonth = this.date && this.date.getUTCMonth();
16858         
16859         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
16860         
16861         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
16862             v.removeClass('active');
16863             
16864             if(currentYear === year && k === currentMonth){
16865                 v.addClass('active');
16866             }
16867             
16868             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
16869                 v.addClass('disabled');
16870             }
16871             
16872         });
16873         
16874         
16875         year = parseInt(year/10, 10) * 10;
16876         
16877         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
16878         
16879         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
16880         
16881         year -= 1;
16882         for (var i = -1; i < 11; i++) {
16883             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
16884                 tag: 'span',
16885                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
16886                 html: year
16887             });
16888             
16889             year += 1;
16890         }
16891     },
16892     
16893     showMode: function(dir) 
16894     {
16895         if (dir) {
16896             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
16897         }
16898         
16899         Roo.each(this.picker().select('>div',true).elements, function(v){
16900             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16901             v.hide();
16902         });
16903         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
16904     },
16905     
16906     place: function()
16907     {
16908         if(this.isInline) {
16909             return;
16910         }
16911         
16912         this.picker().removeClass(['bottom', 'top']);
16913         
16914         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16915             /*
16916              * place to the top of element!
16917              *
16918              */
16919             
16920             this.picker().addClass('top');
16921             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16922             
16923             return;
16924         }
16925         
16926         this.picker().addClass('bottom');
16927         
16928         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16929     },
16930     
16931     parseDate : function(value)
16932     {
16933         if(!value || value instanceof Date){
16934             return value;
16935         }
16936         var v = Date.parseDate(value, this.format);
16937         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
16938             v = Date.parseDate(value, 'Y-m-d');
16939         }
16940         if(!v && this.altFormats){
16941             if(!this.altFormatsArray){
16942                 this.altFormatsArray = this.altFormats.split("|");
16943             }
16944             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
16945                 v = Date.parseDate(value, this.altFormatsArray[i]);
16946             }
16947         }
16948         return v;
16949     },
16950     
16951     formatDate : function(date, fmt)
16952     {   
16953         return (!date || !(date instanceof Date)) ?
16954         date : date.dateFormat(fmt || this.format);
16955     },
16956     
16957     onFocus : function()
16958     {
16959         Roo.bootstrap.DateField.superclass.onFocus.call(this);
16960         this.show();
16961     },
16962     
16963     onBlur : function()
16964     {
16965         Roo.bootstrap.DateField.superclass.onBlur.call(this);
16966         
16967         var d = this.inputEl().getValue();
16968         
16969         this.setValue(d);
16970                 
16971         this.hide();
16972     },
16973     
16974     show : function()
16975     {
16976         this.picker().show();
16977         this.update();
16978         this.place();
16979         
16980         this.fireEvent('show', this, this.date);
16981     },
16982     
16983     hide : function()
16984     {
16985         if(this.isInline) {
16986             return;
16987         }
16988         this.picker().hide();
16989         this.viewMode = this.startViewMode;
16990         this.showMode();
16991         
16992         this.fireEvent('hide', this, this.date);
16993         
16994     },
16995     
16996     onMousedown: function(e)
16997     {
16998         e.stopPropagation();
16999         e.preventDefault();
17000     },
17001     
17002     keyup: function(e)
17003     {
17004         Roo.bootstrap.DateField.superclass.keyup.call(this);
17005         this.update();
17006     },
17007
17008     setValue: function(v)
17009     {
17010         if(this.fireEvent('beforeselect', this, v) !== false){
17011             var d = new Date(this.parseDate(v) ).clearTime();
17012         
17013             if(isNaN(d.getTime())){
17014                 this.date = this.viewDate = '';
17015                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17016                 return;
17017             }
17018
17019             v = this.formatDate(d);
17020
17021             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17022
17023             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17024
17025             this.update();
17026
17027             this.fireEvent('select', this, this.date);
17028         }
17029     },
17030     
17031     getValue: function()
17032     {
17033         return this.formatDate(this.date);
17034     },
17035     
17036     fireKey: function(e)
17037     {
17038         if (!this.picker().isVisible()){
17039             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17040                 this.show();
17041             }
17042             return;
17043         }
17044         
17045         var dateChanged = false,
17046         dir, day, month,
17047         newDate, newViewDate;
17048         
17049         switch(e.keyCode){
17050             case 27: // escape
17051                 this.hide();
17052                 e.preventDefault();
17053                 break;
17054             case 37: // left
17055             case 39: // right
17056                 if (!this.keyboardNavigation) {
17057                     break;
17058                 }
17059                 dir = e.keyCode == 37 ? -1 : 1;
17060                 
17061                 if (e.ctrlKey){
17062                     newDate = this.moveYear(this.date, dir);
17063                     newViewDate = this.moveYear(this.viewDate, dir);
17064                 } else if (e.shiftKey){
17065                     newDate = this.moveMonth(this.date, dir);
17066                     newViewDate = this.moveMonth(this.viewDate, dir);
17067                 } else {
17068                     newDate = new Date(this.date);
17069                     newDate.setUTCDate(this.date.getUTCDate() + dir);
17070                     newViewDate = new Date(this.viewDate);
17071                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
17072                 }
17073                 if (this.dateWithinRange(newDate)){
17074                     this.date = newDate;
17075                     this.viewDate = newViewDate;
17076                     this.setValue(this.formatDate(this.date));
17077 //                    this.update();
17078                     e.preventDefault();
17079                     dateChanged = true;
17080                 }
17081                 break;
17082             case 38: // up
17083             case 40: // down
17084                 if (!this.keyboardNavigation) {
17085                     break;
17086                 }
17087                 dir = e.keyCode == 38 ? -1 : 1;
17088                 if (e.ctrlKey){
17089                     newDate = this.moveYear(this.date, dir);
17090                     newViewDate = this.moveYear(this.viewDate, dir);
17091                 } else if (e.shiftKey){
17092                     newDate = this.moveMonth(this.date, dir);
17093                     newViewDate = this.moveMonth(this.viewDate, dir);
17094                 } else {
17095                     newDate = new Date(this.date);
17096                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
17097                     newViewDate = new Date(this.viewDate);
17098                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
17099                 }
17100                 if (this.dateWithinRange(newDate)){
17101                     this.date = newDate;
17102                     this.viewDate = newViewDate;
17103                     this.setValue(this.formatDate(this.date));
17104 //                    this.update();
17105                     e.preventDefault();
17106                     dateChanged = true;
17107                 }
17108                 break;
17109             case 13: // enter
17110                 this.setValue(this.formatDate(this.date));
17111                 this.hide();
17112                 e.preventDefault();
17113                 break;
17114             case 9: // tab
17115                 this.setValue(this.formatDate(this.date));
17116                 this.hide();
17117                 break;
17118             case 16: // shift
17119             case 17: // ctrl
17120             case 18: // alt
17121                 break;
17122             default :
17123                 this.hide();
17124                 
17125         }
17126     },
17127     
17128     
17129     onClick: function(e) 
17130     {
17131         e.stopPropagation();
17132         e.preventDefault();
17133         
17134         var target = e.getTarget();
17135         
17136         if(target.nodeName.toLowerCase() === 'i'){
17137             target = Roo.get(target).dom.parentNode;
17138         }
17139         
17140         var nodeName = target.nodeName;
17141         var className = target.className;
17142         var html = target.innerHTML;
17143         //Roo.log(nodeName);
17144         
17145         switch(nodeName.toLowerCase()) {
17146             case 'th':
17147                 switch(className) {
17148                     case 'switch':
17149                         this.showMode(1);
17150                         break;
17151                     case 'prev':
17152                     case 'next':
17153                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17154                         switch(this.viewMode){
17155                                 case 0:
17156                                         this.viewDate = this.moveMonth(this.viewDate, dir);
17157                                         break;
17158                                 case 1:
17159                                 case 2:
17160                                         this.viewDate = this.moveYear(this.viewDate, dir);
17161                                         break;
17162                         }
17163                         this.fill();
17164                         break;
17165                     case 'today':
17166                         var date = new Date();
17167                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17168 //                        this.fill()
17169                         this.setValue(this.formatDate(this.date));
17170                         
17171                         this.hide();
17172                         break;
17173                 }
17174                 break;
17175             case 'span':
17176                 if (className.indexOf('disabled') < 0) {
17177                     this.viewDate.setUTCDate(1);
17178                     if (className.indexOf('month') > -1) {
17179                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17180                     } else {
17181                         var year = parseInt(html, 10) || 0;
17182                         this.viewDate.setUTCFullYear(year);
17183                         
17184                     }
17185                     
17186                     if(this.singleMode){
17187                         this.setValue(this.formatDate(this.viewDate));
17188                         this.hide();
17189                         return;
17190                     }
17191                     
17192                     this.showMode(-1);
17193                     this.fill();
17194                 }
17195                 break;
17196                 
17197             case 'td':
17198                 //Roo.log(className);
17199                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17200                     var day = parseInt(html, 10) || 1;
17201                     var year = this.viewDate.getUTCFullYear(),
17202                         month = this.viewDate.getUTCMonth();
17203
17204                     if (className.indexOf('old') > -1) {
17205                         if(month === 0 ){
17206                             month = 11;
17207                             year -= 1;
17208                         }else{
17209                             month -= 1;
17210                         }
17211                     } else if (className.indexOf('new') > -1) {
17212                         if (month == 11) {
17213                             month = 0;
17214                             year += 1;
17215                         } else {
17216                             month += 1;
17217                         }
17218                     }
17219                     //Roo.log([year,month,day]);
17220                     this.date = this.UTCDate(year, month, day,0,0,0,0);
17221                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17222 //                    this.fill();
17223                     //Roo.log(this.formatDate(this.date));
17224                     this.setValue(this.formatDate(this.date));
17225                     this.hide();
17226                 }
17227                 break;
17228         }
17229     },
17230     
17231     setStartDate: function(startDate)
17232     {
17233         this.startDate = startDate || -Infinity;
17234         if (this.startDate !== -Infinity) {
17235             this.startDate = this.parseDate(this.startDate);
17236         }
17237         this.update();
17238         this.updateNavArrows();
17239     },
17240
17241     setEndDate: function(endDate)
17242     {
17243         this.endDate = endDate || Infinity;
17244         if (this.endDate !== Infinity) {
17245             this.endDate = this.parseDate(this.endDate);
17246         }
17247         this.update();
17248         this.updateNavArrows();
17249     },
17250     
17251     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17252     {
17253         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17254         if (typeof(this.daysOfWeekDisabled) !== 'object') {
17255             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17256         }
17257         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17258             return parseInt(d, 10);
17259         });
17260         this.update();
17261         this.updateNavArrows();
17262     },
17263     
17264     updateNavArrows: function() 
17265     {
17266         if(this.singleMode){
17267             return;
17268         }
17269         
17270         var d = new Date(this.viewDate),
17271         year = d.getUTCFullYear(),
17272         month = d.getUTCMonth();
17273         
17274         Roo.each(this.picker().select('.prev', true).elements, function(v){
17275             v.show();
17276             switch (this.viewMode) {
17277                 case 0:
17278
17279                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
17280                         v.hide();
17281                     }
17282                     break;
17283                 case 1:
17284                 case 2:
17285                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
17286                         v.hide();
17287                     }
17288                     break;
17289             }
17290         });
17291         
17292         Roo.each(this.picker().select('.next', true).elements, function(v){
17293             v.show();
17294             switch (this.viewMode) {
17295                 case 0:
17296
17297                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
17298                         v.hide();
17299                     }
17300                     break;
17301                 case 1:
17302                 case 2:
17303                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
17304                         v.hide();
17305                     }
17306                     break;
17307             }
17308         })
17309     },
17310     
17311     moveMonth: function(date, dir)
17312     {
17313         if (!dir) {
17314             return date;
17315         }
17316         var new_date = new Date(date.valueOf()),
17317         day = new_date.getUTCDate(),
17318         month = new_date.getUTCMonth(),
17319         mag = Math.abs(dir),
17320         new_month, test;
17321         dir = dir > 0 ? 1 : -1;
17322         if (mag == 1){
17323             test = dir == -1
17324             // If going back one month, make sure month is not current month
17325             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
17326             ? function(){
17327                 return new_date.getUTCMonth() == month;
17328             }
17329             // If going forward one month, make sure month is as expected
17330             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
17331             : function(){
17332                 return new_date.getUTCMonth() != new_month;
17333             };
17334             new_month = month + dir;
17335             new_date.setUTCMonth(new_month);
17336             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
17337             if (new_month < 0 || new_month > 11) {
17338                 new_month = (new_month + 12) % 12;
17339             }
17340         } else {
17341             // For magnitudes >1, move one month at a time...
17342             for (var i=0; i<mag; i++) {
17343                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
17344                 new_date = this.moveMonth(new_date, dir);
17345             }
17346             // ...then reset the day, keeping it in the new month
17347             new_month = new_date.getUTCMonth();
17348             new_date.setUTCDate(day);
17349             test = function(){
17350                 return new_month != new_date.getUTCMonth();
17351             };
17352         }
17353         // Common date-resetting loop -- if date is beyond end of month, make it
17354         // end of month
17355         while (test()){
17356             new_date.setUTCDate(--day);
17357             new_date.setUTCMonth(new_month);
17358         }
17359         return new_date;
17360     },
17361
17362     moveYear: function(date, dir)
17363     {
17364         return this.moveMonth(date, dir*12);
17365     },
17366
17367     dateWithinRange: function(date)
17368     {
17369         return date >= this.startDate && date <= this.endDate;
17370     },
17371
17372     
17373     remove: function() 
17374     {
17375         this.picker().remove();
17376     }
17377    
17378 });
17379
17380 Roo.apply(Roo.bootstrap.DateField,  {
17381     
17382     head : {
17383         tag: 'thead',
17384         cn: [
17385         {
17386             tag: 'tr',
17387             cn: [
17388             {
17389                 tag: 'th',
17390                 cls: 'prev',
17391                 html: '<i class="fa fa-arrow-left"/>'
17392             },
17393             {
17394                 tag: 'th',
17395                 cls: 'switch',
17396                 colspan: '5'
17397             },
17398             {
17399                 tag: 'th',
17400                 cls: 'next',
17401                 html: '<i class="fa fa-arrow-right"/>'
17402             }
17403
17404             ]
17405         }
17406         ]
17407     },
17408     
17409     content : {
17410         tag: 'tbody',
17411         cn: [
17412         {
17413             tag: 'tr',
17414             cn: [
17415             {
17416                 tag: 'td',
17417                 colspan: '7'
17418             }
17419             ]
17420         }
17421         ]
17422     },
17423     
17424     footer : {
17425         tag: 'tfoot',
17426         cn: [
17427         {
17428             tag: 'tr',
17429             cn: [
17430             {
17431                 tag: 'th',
17432                 colspan: '7',
17433                 cls: 'today'
17434             }
17435                     
17436             ]
17437         }
17438         ]
17439     },
17440     
17441     dates:{
17442         en: {
17443             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17444             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17445             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17446             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17447             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17448             today: "Today"
17449         }
17450     },
17451     
17452     modes: [
17453     {
17454         clsName: 'days',
17455         navFnc: 'Month',
17456         navStep: 1
17457     },
17458     {
17459         clsName: 'months',
17460         navFnc: 'FullYear',
17461         navStep: 1
17462     },
17463     {
17464         clsName: 'years',
17465         navFnc: 'FullYear',
17466         navStep: 10
17467     }]
17468 });
17469
17470 Roo.apply(Roo.bootstrap.DateField,  {
17471   
17472     template : {
17473         tag: 'div',
17474         cls: 'datepicker dropdown-menu roo-dynamic',
17475         cn: [
17476         {
17477             tag: 'div',
17478             cls: 'datepicker-days',
17479             cn: [
17480             {
17481                 tag: 'table',
17482                 cls: 'table-condensed',
17483                 cn:[
17484                 Roo.bootstrap.DateField.head,
17485                 {
17486                     tag: 'tbody'
17487                 },
17488                 Roo.bootstrap.DateField.footer
17489                 ]
17490             }
17491             ]
17492         },
17493         {
17494             tag: 'div',
17495             cls: 'datepicker-months',
17496             cn: [
17497             {
17498                 tag: 'table',
17499                 cls: 'table-condensed',
17500                 cn:[
17501                 Roo.bootstrap.DateField.head,
17502                 Roo.bootstrap.DateField.content,
17503                 Roo.bootstrap.DateField.footer
17504                 ]
17505             }
17506             ]
17507         },
17508         {
17509             tag: 'div',
17510             cls: 'datepicker-years',
17511             cn: [
17512             {
17513                 tag: 'table',
17514                 cls: 'table-condensed',
17515                 cn:[
17516                 Roo.bootstrap.DateField.head,
17517                 Roo.bootstrap.DateField.content,
17518                 Roo.bootstrap.DateField.footer
17519                 ]
17520             }
17521             ]
17522         }
17523         ]
17524     }
17525 });
17526
17527  
17528
17529  /*
17530  * - LGPL
17531  *
17532  * TimeField
17533  * 
17534  */
17535
17536 /**
17537  * @class Roo.bootstrap.TimeField
17538  * @extends Roo.bootstrap.Input
17539  * Bootstrap DateField class
17540  * 
17541  * 
17542  * @constructor
17543  * Create a new TimeField
17544  * @param {Object} config The config object
17545  */
17546
17547 Roo.bootstrap.TimeField = function(config){
17548     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17549     this.addEvents({
17550             /**
17551              * @event show
17552              * Fires when this field show.
17553              * @param {Roo.bootstrap.DateField} thisthis
17554              * @param {Mixed} date The date value
17555              */
17556             show : true,
17557             /**
17558              * @event show
17559              * Fires when this field hide.
17560              * @param {Roo.bootstrap.DateField} this
17561              * @param {Mixed} date The date value
17562              */
17563             hide : true,
17564             /**
17565              * @event select
17566              * Fires when select a date.
17567              * @param {Roo.bootstrap.DateField} this
17568              * @param {Mixed} date The date value
17569              */
17570             select : true
17571         });
17572 };
17573
17574 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
17575     
17576     /**
17577      * @cfg {String} format
17578      * The default time format string which can be overriden for localization support.  The format must be
17579      * valid according to {@link Date#parseDate} (defaults to 'H:i').
17580      */
17581     format : "H:i",
17582        
17583     onRender: function(ct, position)
17584     {
17585         
17586         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17587                 
17588         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17589         
17590         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17591         
17592         this.pop = this.picker().select('>.datepicker-time',true).first();
17593         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17594         
17595         this.picker().on('mousedown', this.onMousedown, this);
17596         this.picker().on('click', this.onClick, this);
17597         
17598         this.picker().addClass('datepicker-dropdown');
17599     
17600         this.fillTime();
17601         this.update();
17602             
17603         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17604         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17605         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17606         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17607         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17608         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17609
17610     },
17611     
17612     fireKey: function(e){
17613         if (!this.picker().isVisible()){
17614             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17615                 this.show();
17616             }
17617             return;
17618         }
17619
17620         e.preventDefault();
17621         
17622         switch(e.keyCode){
17623             case 27: // escape
17624                 this.hide();
17625                 break;
17626             case 37: // left
17627             case 39: // right
17628                 this.onTogglePeriod();
17629                 break;
17630             case 38: // up
17631                 this.onIncrementMinutes();
17632                 break;
17633             case 40: // down
17634                 this.onDecrementMinutes();
17635                 break;
17636             case 13: // enter
17637             case 9: // tab
17638                 this.setTime();
17639                 break;
17640         }
17641     },
17642     
17643     onClick: function(e) {
17644         e.stopPropagation();
17645         e.preventDefault();
17646     },
17647     
17648     picker : function()
17649     {
17650         return this.el.select('.datepicker', true).first();
17651     },
17652     
17653     fillTime: function()
17654     {    
17655         var time = this.pop.select('tbody', true).first();
17656         
17657         time.dom.innerHTML = '';
17658         
17659         time.createChild({
17660             tag: 'tr',
17661             cn: [
17662                 {
17663                     tag: 'td',
17664                     cn: [
17665                         {
17666                             tag: 'a',
17667                             href: '#',
17668                             cls: 'btn',
17669                             cn: [
17670                                 {
17671                                     tag: 'span',
17672                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
17673                                 }
17674                             ]
17675                         } 
17676                     ]
17677                 },
17678                 {
17679                     tag: 'td',
17680                     cls: 'separator'
17681                 },
17682                 {
17683                     tag: 'td',
17684                     cn: [
17685                         {
17686                             tag: 'a',
17687                             href: '#',
17688                             cls: 'btn',
17689                             cn: [
17690                                 {
17691                                     tag: 'span',
17692                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
17693                                 }
17694                             ]
17695                         }
17696                     ]
17697                 },
17698                 {
17699                     tag: 'td',
17700                     cls: 'separator'
17701                 }
17702             ]
17703         });
17704         
17705         time.createChild({
17706             tag: 'tr',
17707             cn: [
17708                 {
17709                     tag: 'td',
17710                     cn: [
17711                         {
17712                             tag: 'span',
17713                             cls: 'timepicker-hour',
17714                             html: '00'
17715                         }  
17716                     ]
17717                 },
17718                 {
17719                     tag: 'td',
17720                     cls: 'separator',
17721                     html: ':'
17722                 },
17723                 {
17724                     tag: 'td',
17725                     cn: [
17726                         {
17727                             tag: 'span',
17728                             cls: 'timepicker-minute',
17729                             html: '00'
17730                         }  
17731                     ]
17732                 },
17733                 {
17734                     tag: 'td',
17735                     cls: 'separator'
17736                 },
17737                 {
17738                     tag: 'td',
17739                     cn: [
17740                         {
17741                             tag: 'button',
17742                             type: 'button',
17743                             cls: 'btn btn-primary period',
17744                             html: 'AM'
17745                             
17746                         }
17747                     ]
17748                 }
17749             ]
17750         });
17751         
17752         time.createChild({
17753             tag: 'tr',
17754             cn: [
17755                 {
17756                     tag: 'td',
17757                     cn: [
17758                         {
17759                             tag: 'a',
17760                             href: '#',
17761                             cls: 'btn',
17762                             cn: [
17763                                 {
17764                                     tag: 'span',
17765                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
17766                                 }
17767                             ]
17768                         }
17769                     ]
17770                 },
17771                 {
17772                     tag: 'td',
17773                     cls: 'separator'
17774                 },
17775                 {
17776                     tag: 'td',
17777                     cn: [
17778                         {
17779                             tag: 'a',
17780                             href: '#',
17781                             cls: 'btn',
17782                             cn: [
17783                                 {
17784                                     tag: 'span',
17785                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
17786                                 }
17787                             ]
17788                         }
17789                     ]
17790                 },
17791                 {
17792                     tag: 'td',
17793                     cls: 'separator'
17794                 }
17795             ]
17796         });
17797         
17798     },
17799     
17800     update: function()
17801     {
17802         
17803         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17804         
17805         this.fill();
17806     },
17807     
17808     fill: function() 
17809     {
17810         var hours = this.time.getHours();
17811         var minutes = this.time.getMinutes();
17812         var period = 'AM';
17813         
17814         if(hours > 11){
17815             period = 'PM';
17816         }
17817         
17818         if(hours == 0){
17819             hours = 12;
17820         }
17821         
17822         
17823         if(hours > 12){
17824             hours = hours - 12;
17825         }
17826         
17827         if(hours < 10){
17828             hours = '0' + hours;
17829         }
17830         
17831         if(minutes < 10){
17832             minutes = '0' + minutes;
17833         }
17834         
17835         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
17836         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
17837         this.pop.select('button', true).first().dom.innerHTML = period;
17838         
17839     },
17840     
17841     place: function()
17842     {   
17843         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
17844         
17845         var cls = ['bottom'];
17846         
17847         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
17848             cls.pop();
17849             cls.push('top');
17850         }
17851         
17852         cls.push('right');
17853         
17854         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
17855             cls.pop();
17856             cls.push('left');
17857         }
17858         
17859         this.picker().addClass(cls.join('-'));
17860         
17861         var _this = this;
17862         
17863         Roo.each(cls, function(c){
17864             if(c == 'bottom'){
17865                 _this.picker().setTop(_this.inputEl().getHeight());
17866                 return;
17867             }
17868             if(c == 'top'){
17869                 _this.picker().setTop(0 - _this.picker().getHeight());
17870                 return;
17871             }
17872             
17873             if(c == 'left'){
17874                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
17875                 return;
17876             }
17877             if(c == 'right'){
17878                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
17879                 return;
17880             }
17881         });
17882         
17883     },
17884   
17885     onFocus : function()
17886     {
17887         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
17888         this.show();
17889     },
17890     
17891     onBlur : function()
17892     {
17893         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
17894         this.hide();
17895     },
17896     
17897     show : function()
17898     {
17899         this.picker().show();
17900         this.pop.show();
17901         this.update();
17902         this.place();
17903         
17904         this.fireEvent('show', this, this.date);
17905     },
17906     
17907     hide : function()
17908     {
17909         this.picker().hide();
17910         this.pop.hide();
17911         
17912         this.fireEvent('hide', this, this.date);
17913     },
17914     
17915     setTime : function()
17916     {
17917         this.hide();
17918         this.setValue(this.time.format(this.format));
17919         
17920         this.fireEvent('select', this, this.date);
17921         
17922         
17923     },
17924     
17925     onMousedown: function(e){
17926         e.stopPropagation();
17927         e.preventDefault();
17928     },
17929     
17930     onIncrementHours: function()
17931     {
17932         Roo.log('onIncrementHours');
17933         this.time = this.time.add(Date.HOUR, 1);
17934         this.update();
17935         
17936     },
17937     
17938     onDecrementHours: function()
17939     {
17940         Roo.log('onDecrementHours');
17941         this.time = this.time.add(Date.HOUR, -1);
17942         this.update();
17943     },
17944     
17945     onIncrementMinutes: function()
17946     {
17947         Roo.log('onIncrementMinutes');
17948         this.time = this.time.add(Date.MINUTE, 1);
17949         this.update();
17950     },
17951     
17952     onDecrementMinutes: function()
17953     {
17954         Roo.log('onDecrementMinutes');
17955         this.time = this.time.add(Date.MINUTE, -1);
17956         this.update();
17957     },
17958     
17959     onTogglePeriod: function()
17960     {
17961         Roo.log('onTogglePeriod');
17962         this.time = this.time.add(Date.HOUR, 12);
17963         this.update();
17964     }
17965     
17966    
17967 });
17968
17969 Roo.apply(Roo.bootstrap.TimeField,  {
17970     
17971     content : {
17972         tag: 'tbody',
17973         cn: [
17974             {
17975                 tag: 'tr',
17976                 cn: [
17977                 {
17978                     tag: 'td',
17979                     colspan: '7'
17980                 }
17981                 ]
17982             }
17983         ]
17984     },
17985     
17986     footer : {
17987         tag: 'tfoot',
17988         cn: [
17989             {
17990                 tag: 'tr',
17991                 cn: [
17992                 {
17993                     tag: 'th',
17994                     colspan: '7',
17995                     cls: '',
17996                     cn: [
17997                         {
17998                             tag: 'button',
17999                             cls: 'btn btn-info ok',
18000                             html: 'OK'
18001                         }
18002                     ]
18003                 }
18004
18005                 ]
18006             }
18007         ]
18008     }
18009 });
18010
18011 Roo.apply(Roo.bootstrap.TimeField,  {
18012   
18013     template : {
18014         tag: 'div',
18015         cls: 'datepicker dropdown-menu',
18016         cn: [
18017             {
18018                 tag: 'div',
18019                 cls: 'datepicker-time',
18020                 cn: [
18021                 {
18022                     tag: 'table',
18023                     cls: 'table-condensed',
18024                     cn:[
18025                     Roo.bootstrap.TimeField.content,
18026                     Roo.bootstrap.TimeField.footer
18027                     ]
18028                 }
18029                 ]
18030             }
18031         ]
18032     }
18033 });
18034
18035  
18036
18037  /*
18038  * - LGPL
18039  *
18040  * MonthField
18041  * 
18042  */
18043
18044 /**
18045  * @class Roo.bootstrap.MonthField
18046  * @extends Roo.bootstrap.Input
18047  * Bootstrap MonthField class
18048  * 
18049  * @cfg {String} language default en
18050  * 
18051  * @constructor
18052  * Create a new MonthField
18053  * @param {Object} config The config object
18054  */
18055
18056 Roo.bootstrap.MonthField = function(config){
18057     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
18058     
18059     this.addEvents({
18060         /**
18061          * @event show
18062          * Fires when this field show.
18063          * @param {Roo.bootstrap.MonthField} this
18064          * @param {Mixed} date The date value
18065          */
18066         show : true,
18067         /**
18068          * @event show
18069          * Fires when this field hide.
18070          * @param {Roo.bootstrap.MonthField} this
18071          * @param {Mixed} date The date value
18072          */
18073         hide : true,
18074         /**
18075          * @event select
18076          * Fires when select a date.
18077          * @param {Roo.bootstrap.MonthField} this
18078          * @param {String} oldvalue The old value
18079          * @param {String} newvalue The new value
18080          */
18081         select : true
18082     });
18083 };
18084
18085 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
18086     
18087     onRender: function(ct, position)
18088     {
18089         
18090         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
18091         
18092         this.language = this.language || 'en';
18093         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
18094         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
18095         
18096         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
18097         this.isInline = false;
18098         this.isInput = true;
18099         this.component = this.el.select('.add-on', true).first() || false;
18100         this.component = (this.component && this.component.length === 0) ? false : this.component;
18101         this.hasInput = this.component && this.inputEL().length;
18102         
18103         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
18104         
18105         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18106         
18107         this.picker().on('mousedown', this.onMousedown, this);
18108         this.picker().on('click', this.onClick, this);
18109         
18110         this.picker().addClass('datepicker-dropdown');
18111         
18112         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18113             v.setStyle('width', '189px');
18114         });
18115         
18116         this.fillMonths();
18117         
18118         this.update();
18119         
18120         if(this.isInline) {
18121             this.show();
18122         }
18123         
18124     },
18125     
18126     setValue: function(v, suppressEvent)
18127     {   
18128         var o = this.getValue();
18129         
18130         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
18131         
18132         this.update();
18133
18134         if(suppressEvent !== true){
18135             this.fireEvent('select', this, o, v);
18136         }
18137         
18138     },
18139     
18140     getValue: function()
18141     {
18142         return this.value;
18143     },
18144     
18145     onClick: function(e) 
18146     {
18147         e.stopPropagation();
18148         e.preventDefault();
18149         
18150         var target = e.getTarget();
18151         
18152         if(target.nodeName.toLowerCase() === 'i'){
18153             target = Roo.get(target).dom.parentNode;
18154         }
18155         
18156         var nodeName = target.nodeName;
18157         var className = target.className;
18158         var html = target.innerHTML;
18159         
18160         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18161             return;
18162         }
18163         
18164         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18165         
18166         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18167         
18168         this.hide();
18169                         
18170     },
18171     
18172     picker : function()
18173     {
18174         return this.pickerEl;
18175     },
18176     
18177     fillMonths: function()
18178     {    
18179         var i = 0;
18180         var months = this.picker().select('>.datepicker-months td', true).first();
18181         
18182         months.dom.innerHTML = '';
18183         
18184         while (i < 12) {
18185             var month = {
18186                 tag: 'span',
18187                 cls: 'month',
18188                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18189             };
18190             
18191             months.createChild(month);
18192         }
18193         
18194     },
18195     
18196     update: function()
18197     {
18198         var _this = this;
18199         
18200         if(typeof(this.vIndex) == 'undefined' && this.value.length){
18201             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18202         }
18203         
18204         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18205             e.removeClass('active');
18206             
18207             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18208                 e.addClass('active');
18209             }
18210         })
18211     },
18212     
18213     place: function()
18214     {
18215         if(this.isInline) {
18216             return;
18217         }
18218         
18219         this.picker().removeClass(['bottom', 'top']);
18220         
18221         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18222             /*
18223              * place to the top of element!
18224              *
18225              */
18226             
18227             this.picker().addClass('top');
18228             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18229             
18230             return;
18231         }
18232         
18233         this.picker().addClass('bottom');
18234         
18235         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18236     },
18237     
18238     onFocus : function()
18239     {
18240         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18241         this.show();
18242     },
18243     
18244     onBlur : function()
18245     {
18246         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18247         
18248         var d = this.inputEl().getValue();
18249         
18250         this.setValue(d);
18251                 
18252         this.hide();
18253     },
18254     
18255     show : function()
18256     {
18257         this.picker().show();
18258         this.picker().select('>.datepicker-months', true).first().show();
18259         this.update();
18260         this.place();
18261         
18262         this.fireEvent('show', this, this.date);
18263     },
18264     
18265     hide : function()
18266     {
18267         if(this.isInline) {
18268             return;
18269         }
18270         this.picker().hide();
18271         this.fireEvent('hide', this, this.date);
18272         
18273     },
18274     
18275     onMousedown: function(e)
18276     {
18277         e.stopPropagation();
18278         e.preventDefault();
18279     },
18280     
18281     keyup: function(e)
18282     {
18283         Roo.bootstrap.MonthField.superclass.keyup.call(this);
18284         this.update();
18285     },
18286
18287     fireKey: function(e)
18288     {
18289         if (!this.picker().isVisible()){
18290             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
18291                 this.show();
18292             }
18293             return;
18294         }
18295         
18296         var dir;
18297         
18298         switch(e.keyCode){
18299             case 27: // escape
18300                 this.hide();
18301                 e.preventDefault();
18302                 break;
18303             case 37: // left
18304             case 39: // right
18305                 dir = e.keyCode == 37 ? -1 : 1;
18306                 
18307                 this.vIndex = this.vIndex + dir;
18308                 
18309                 if(this.vIndex < 0){
18310                     this.vIndex = 0;
18311                 }
18312                 
18313                 if(this.vIndex > 11){
18314                     this.vIndex = 11;
18315                 }
18316                 
18317                 if(isNaN(this.vIndex)){
18318                     this.vIndex = 0;
18319                 }
18320                 
18321                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18322                 
18323                 break;
18324             case 38: // up
18325             case 40: // down
18326                 
18327                 dir = e.keyCode == 38 ? -1 : 1;
18328                 
18329                 this.vIndex = this.vIndex + dir * 4;
18330                 
18331                 if(this.vIndex < 0){
18332                     this.vIndex = 0;
18333                 }
18334                 
18335                 if(this.vIndex > 11){
18336                     this.vIndex = 11;
18337                 }
18338                 
18339                 if(isNaN(this.vIndex)){
18340                     this.vIndex = 0;
18341                 }
18342                 
18343                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18344                 break;
18345                 
18346             case 13: // enter
18347                 
18348                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18349                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18350                 }
18351                 
18352                 this.hide();
18353                 e.preventDefault();
18354                 break;
18355             case 9: // tab
18356                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18357                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18358                 }
18359                 this.hide();
18360                 break;
18361             case 16: // shift
18362             case 17: // ctrl
18363             case 18: // alt
18364                 break;
18365             default :
18366                 this.hide();
18367                 
18368         }
18369     },
18370     
18371     remove: function() 
18372     {
18373         this.picker().remove();
18374     }
18375    
18376 });
18377
18378 Roo.apply(Roo.bootstrap.MonthField,  {
18379     
18380     content : {
18381         tag: 'tbody',
18382         cn: [
18383         {
18384             tag: 'tr',
18385             cn: [
18386             {
18387                 tag: 'td',
18388                 colspan: '7'
18389             }
18390             ]
18391         }
18392         ]
18393     },
18394     
18395     dates:{
18396         en: {
18397             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18398             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18399         }
18400     }
18401 });
18402
18403 Roo.apply(Roo.bootstrap.MonthField,  {
18404   
18405     template : {
18406         tag: 'div',
18407         cls: 'datepicker dropdown-menu roo-dynamic',
18408         cn: [
18409             {
18410                 tag: 'div',
18411                 cls: 'datepicker-months',
18412                 cn: [
18413                 {
18414                     tag: 'table',
18415                     cls: 'table-condensed',
18416                     cn:[
18417                         Roo.bootstrap.DateField.content
18418                     ]
18419                 }
18420                 ]
18421             }
18422         ]
18423     }
18424 });
18425
18426  
18427
18428  
18429  /*
18430  * - LGPL
18431  *
18432  * CheckBox
18433  * 
18434  */
18435
18436 /**
18437  * @class Roo.bootstrap.CheckBox
18438  * @extends Roo.bootstrap.Input
18439  * Bootstrap CheckBox class
18440  * 
18441  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18442  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18443  * @cfg {String} boxLabel The text that appears beside the checkbox
18444  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18445  * @cfg {Boolean} checked initnal the element
18446  * @cfg {Boolean} inline inline the element (default false)
18447  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18448  * 
18449  * @constructor
18450  * Create a new CheckBox
18451  * @param {Object} config The config object
18452  */
18453
18454 Roo.bootstrap.CheckBox = function(config){
18455     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18456    
18457     this.addEvents({
18458         /**
18459         * @event check
18460         * Fires when the element is checked or unchecked.
18461         * @param {Roo.bootstrap.CheckBox} this This input
18462         * @param {Boolean} checked The new checked value
18463         */
18464        check : true
18465     });
18466     
18467 };
18468
18469 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
18470   
18471     inputType: 'checkbox',
18472     inputValue: 1,
18473     valueOff: 0,
18474     boxLabel: false,
18475     checked: false,
18476     weight : false,
18477     inline: false,
18478     
18479     getAutoCreate : function()
18480     {
18481         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18482         
18483         var id = Roo.id();
18484         
18485         var cfg = {};
18486         
18487         cfg.cls = 'form-group ' + this.inputType; //input-group
18488         
18489         if(this.inline){
18490             cfg.cls += ' ' + this.inputType + '-inline';
18491         }
18492         
18493         var input =  {
18494             tag: 'input',
18495             id : id,
18496             type : this.inputType,
18497             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18498             cls : 'roo-' + this.inputType, //'form-box',
18499             placeholder : this.placeholder || ''
18500             
18501         };
18502         
18503         if (this.weight) { // Validity check?
18504             cfg.cls += " " + this.inputType + "-" + this.weight;
18505         }
18506         
18507         if (this.disabled) {
18508             input.disabled=true;
18509         }
18510         
18511         if(this.checked){
18512             input.checked = this.checked;
18513         }
18514         
18515         if (this.name) {
18516             input.name = this.name;
18517         }
18518         
18519         if (this.size) {
18520             input.cls += ' input-' + this.size;
18521         }
18522         
18523         var settings=this;
18524         
18525         ['xs','sm','md','lg'].map(function(size){
18526             if (settings[size]) {
18527                 cfg.cls += ' col-' + size + '-' + settings[size];
18528             }
18529         });
18530         
18531         var inputblock = input;
18532          
18533         if (this.before || this.after) {
18534             
18535             inputblock = {
18536                 cls : 'input-group',
18537                 cn :  [] 
18538             };
18539             
18540             if (this.before) {
18541                 inputblock.cn.push({
18542                     tag :'span',
18543                     cls : 'input-group-addon',
18544                     html : this.before
18545                 });
18546             }
18547             
18548             inputblock.cn.push(input);
18549             
18550             if (this.after) {
18551                 inputblock.cn.push({
18552                     tag :'span',
18553                     cls : 'input-group-addon',
18554                     html : this.after
18555                 });
18556             }
18557             
18558         }
18559         
18560         if (align ==='left' && this.fieldLabel.length) {
18561                 Roo.log("left and has label");
18562                 cfg.cn = [
18563                     
18564                     {
18565                         tag: 'label',
18566                         'for' :  id,
18567                         cls : 'control-label col-md-' + this.labelWidth,
18568                         html : this.fieldLabel
18569                         
18570                     },
18571                     {
18572                         cls : "col-md-" + (12 - this.labelWidth), 
18573                         cn: [
18574                             inputblock
18575                         ]
18576                     }
18577                     
18578                 ];
18579         } else if ( this.fieldLabel.length) {
18580                 Roo.log(" label");
18581                 cfg.cn = [
18582                    
18583                     {
18584                         tag: this.boxLabel ? 'span' : 'label',
18585                         'for': id,
18586                         cls: 'control-label box-input-label',
18587                         //cls : 'input-group-addon',
18588                         html : this.fieldLabel
18589                         
18590                     },
18591                     
18592                     inputblock
18593                     
18594                 ];
18595
18596         } else {
18597             
18598                 Roo.log(" no label && no align");
18599                 cfg.cn = [  inputblock ] ;
18600                 
18601                 
18602         }
18603         if(this.boxLabel){
18604              var boxLabelCfg = {
18605                 tag: 'label',
18606                 //'for': id, // box label is handled by onclick - so no for...
18607                 cls: 'box-label',
18608                 html: this.boxLabel
18609             };
18610             
18611             if(this.tooltip){
18612                 boxLabelCfg.tooltip = this.tooltip;
18613             }
18614              
18615             cfg.cn.push(boxLabelCfg);
18616         }
18617         
18618         
18619        
18620         return cfg;
18621         
18622     },
18623     
18624     /**
18625      * return the real input element.
18626      */
18627     inputEl: function ()
18628     {
18629         return this.el.select('input.roo-' + this.inputType,true).first();
18630     },
18631     
18632     labelEl: function()
18633     {
18634         return this.el.select('label.control-label',true).first();
18635     },
18636     /* depricated... */
18637     
18638     label: function()
18639     {
18640         return this.labelEl();
18641     },
18642     
18643     initEvents : function()
18644     {
18645 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18646         
18647         this.inputEl().on('click', this.onClick,  this);
18648         
18649         if (this.boxLabel) { 
18650             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
18651         }
18652         
18653         this.startValue = this.getValue();
18654         
18655         if(this.groupId){
18656             Roo.bootstrap.CheckBox.register(this);
18657         }
18658     },
18659     
18660     onClick : function()
18661     {   
18662         this.setChecked(!this.checked);
18663     },
18664     
18665     setChecked : function(state,suppressEvent)
18666     {
18667         this.startValue = this.getValue();
18668         
18669         if(this.inputType == 'radio'){
18670             
18671             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18672                 e.dom.checked = false;
18673             });
18674             
18675             this.inputEl().dom.checked = true;
18676             
18677             this.inputEl().dom.value = this.inputValue;
18678             
18679             if(suppressEvent !== true){
18680                 this.fireEvent('check', this, true);
18681             }
18682             
18683             this.validate();
18684             
18685             return;
18686         }
18687         
18688         this.checked = state;
18689         
18690         this.inputEl().dom.checked = state;
18691         
18692         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18693         
18694         if(suppressEvent !== true){
18695             this.fireEvent('check', this, state);
18696         }
18697         
18698         this.validate();
18699     },
18700     
18701     getValue : function()
18702     {
18703         if(this.inputType == 'radio'){
18704             return this.getGroupValue();
18705         }
18706         
18707         return this.inputEl().getValue();
18708         
18709     },
18710     
18711     getGroupValue : function()
18712     {
18713         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18714             return '';
18715         }
18716         
18717         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18718     },
18719     
18720     setValue : function(v,suppressEvent)
18721     {
18722         if(this.inputType == 'radio'){
18723             this.setGroupValue(v, suppressEvent);
18724             return;
18725         }
18726         
18727         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18728         
18729         this.validate();
18730     },
18731     
18732     setGroupValue : function(v, suppressEvent)
18733     {
18734         this.startValue = this.getValue();
18735         
18736         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18737             e.dom.checked = false;
18738             
18739             if(e.dom.value == v){
18740                 e.dom.checked = true;
18741             }
18742         });
18743         
18744         if(suppressEvent !== true){
18745             this.fireEvent('check', this, true);
18746         }
18747
18748         this.validate();
18749         
18750         return;
18751     },
18752     
18753     validate : function()
18754     {
18755         if(
18756                 this.disabled || 
18757                 (this.inputType == 'radio' && this.validateRadio()) ||
18758                 (this.inputType == 'checkbox' && this.validateCheckbox())
18759         ){
18760             this.markValid();
18761             return true;
18762         }
18763         
18764         this.markInvalid();
18765         return false;
18766     },
18767     
18768     validateRadio : function()
18769     {
18770         var valid = false;
18771         
18772         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18773             if(!e.dom.checked){
18774                 return;
18775             }
18776             
18777             valid = true;
18778             
18779             return false;
18780         });
18781         
18782         return valid;
18783     },
18784     
18785     validateCheckbox : function()
18786     {
18787         if(!this.groupId){
18788             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18789         }
18790         
18791         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18792         
18793         if(!group){
18794             return false;
18795         }
18796         
18797         var r = false;
18798         
18799         for(var i in group){
18800             if(r){
18801                 break;
18802             }
18803             
18804             r = (group[i].getValue() == group[i].inputValue) ? true : false;
18805         }
18806         
18807         return r;
18808     },
18809     
18810     /**
18811      * Mark this field as valid
18812      */
18813     markValid : function()
18814     {
18815         if(this.allowBlank){
18816             return;
18817         }
18818         
18819         var _this = this;
18820         
18821         this.fireEvent('valid', this);
18822         
18823         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18824         
18825         if(this.groupId){
18826             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18827         }
18828         
18829         if(label){
18830             label.markValid();
18831         }
18832         
18833         if(this.inputType == 'radio'){
18834             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18835                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18836                 e.findParent('.form-group', false, true).addClass(_this.validClass);
18837             });
18838             
18839             return;
18840         }
18841         
18842         if(!this.groupId){
18843             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18844             this.el.findParent('.form-group', false, true).addClass(this.validClass);
18845             return;
18846         }
18847         
18848         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18849             
18850         if(!group){
18851             return;
18852         }
18853         
18854         for(var i in group){
18855             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18856             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
18857         }
18858     },
18859     
18860      /**
18861      * Mark this field as invalid
18862      * @param {String} msg The validation message
18863      */
18864     markInvalid : function(msg)
18865     {
18866         if(this.allowBlank){
18867             return;
18868         }
18869         
18870         var _this = this;
18871         
18872         this.fireEvent('invalid', this, msg);
18873         
18874         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18875         
18876         if(this.groupId){
18877             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18878         }
18879         
18880         if(label){
18881             label.markInvalid();
18882         }
18883             
18884         if(this.inputType == 'radio'){
18885             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18886                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18887                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
18888             });
18889             
18890             return;
18891         }
18892         
18893         if(!this.groupId){
18894             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18895             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
18896             return;
18897         }
18898         
18899         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18900         
18901         if(!group){
18902             return;
18903         }
18904         
18905         for(var i in group){
18906             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18907             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
18908         }
18909         
18910     }
18911     
18912 });
18913
18914 Roo.apply(Roo.bootstrap.CheckBox, {
18915     
18916     groups: {},
18917     
18918      /**
18919     * register a CheckBox Group
18920     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
18921     */
18922     register : function(checkbox)
18923     {
18924         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
18925             this.groups[checkbox.groupId] = {};
18926         }
18927         
18928         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
18929             return;
18930         }
18931         
18932         this.groups[checkbox.groupId][checkbox.name] = checkbox;
18933         
18934     },
18935     /**
18936     * fetch a CheckBox Group based on the group ID
18937     * @param {string} the group ID
18938     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
18939     */
18940     get: function(groupId) {
18941         if (typeof(this.groups[groupId]) == 'undefined') {
18942             return false;
18943         }
18944         
18945         return this.groups[groupId] ;
18946     }
18947     
18948     
18949 });
18950 /*
18951  * - LGPL
18952  *
18953  * Radio
18954  *
18955  *
18956  * not inline
18957  *<div class="radio">
18958   <label>
18959     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
18960     Option one is this and that&mdash;be sure to include why it's great
18961   </label>
18962 </div>
18963  *
18964  *
18965  *inline
18966  *<span>
18967  *<label class="radio-inline">fieldLabel</label>
18968  *<label class="radio-inline">
18969   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
18970 </label>
18971 <span>
18972  * 
18973  * 
18974  */
18975
18976 /**
18977  * @class Roo.bootstrap.Radio
18978  * @extends Roo.bootstrap.CheckBox
18979  * Bootstrap Radio class
18980
18981  * @constructor
18982  * Create a new Radio
18983  * @param {Object} config The config object
18984  */
18985
18986 Roo.bootstrap.Radio = function(config){
18987     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
18988    
18989 };
18990
18991 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
18992     
18993     inputType: 'radio',
18994     inputValue: '',
18995     valueOff: '',
18996     
18997     getAutoCreate : function()
18998     {
18999         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19000         align = align || 'left'; // default...
19001         
19002         
19003         
19004         var id = Roo.id();
19005         
19006         var cfg = {
19007                 tag : this.inline ? 'span' : 'div',
19008                 cls : '',
19009                 cn : []
19010         };
19011         
19012         var inline = this.inline ? ' radio-inline' : '';
19013         
19014         var lbl = {
19015                 tag: 'label' ,
19016                 // does not need for, as we wrap the input with it..
19017                 'for' : id,
19018                 cls : 'control-label box-label' + inline,
19019                 cn : []
19020         };
19021         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
19022         
19023         var fieldLabel = {
19024             tag: 'label' ,
19025             //cls : 'control-label' + inline,
19026             html : this.fieldLabel,
19027             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
19028         };
19029         
19030  
19031         
19032         
19033         var input =  {
19034             tag: 'input',
19035             id : id,
19036             type : this.inputType,
19037             //value : (!this.checked) ? this.valueOff : this.inputValue,
19038             value : this.inputValue,
19039             cls : 'roo-radio',
19040             placeholder : this.placeholder || '' // ?? needed????
19041             
19042         };
19043         if (this.weight) { // Validity check?
19044             input.cls += " radio-" + this.weight;
19045         }
19046         if (this.disabled) {
19047             input.disabled=true;
19048         }
19049         
19050         if(this.checked){
19051             input.checked = this.checked;
19052         }
19053         
19054         if (this.name) {
19055             input.name = this.name;
19056         }
19057         
19058         if (this.size) {
19059             input.cls += ' input-' + this.size;
19060         }
19061         
19062         //?? can span's inline have a width??
19063         
19064         var settings=this;
19065         ['xs','sm','md','lg'].map(function(size){
19066             if (settings[size]) {
19067                 cfg.cls += ' col-' + size + '-' + settings[size];
19068             }
19069         });
19070         
19071         var inputblock = input;
19072         
19073         if (this.before || this.after) {
19074             
19075             inputblock = {
19076                 cls : 'input-group',
19077                 tag : 'span',
19078                 cn :  [] 
19079             };
19080             if (this.before) {
19081                 inputblock.cn.push({
19082                     tag :'span',
19083                     cls : 'input-group-addon',
19084                     html : this.before
19085                 });
19086             }
19087             inputblock.cn.push(input);
19088             if (this.after) {
19089                 inputblock.cn.push({
19090                     tag :'span',
19091                     cls : 'input-group-addon',
19092                     html : this.after
19093                 });
19094             }
19095             
19096         };
19097         
19098         
19099         if (this.fieldLabel && this.fieldLabel.length) {
19100             cfg.cn.push(fieldLabel);
19101         }
19102        
19103         // normal bootstrap puts the input inside the label.
19104         // however with our styled version - it has to go after the input.
19105        
19106         //lbl.cn.push(inputblock);
19107         
19108         var lblwrap =  {
19109             tag: 'span',
19110             cls: 'radio' + inline,
19111             cn: [
19112                 inputblock,
19113                 lbl
19114             ]
19115         };
19116         
19117         cfg.cn.push( lblwrap);
19118         
19119         if(this.boxLabel){
19120             lbl.cn.push({
19121                 tag: 'span',
19122                 html: this.boxLabel
19123             })
19124         }
19125          
19126         
19127         return cfg;
19128         
19129     },
19130     
19131     initEvents : function()
19132     {
19133 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19134         
19135         this.inputEl().on('click', this.onClick,  this);
19136         if (this.boxLabel) {
19137             //Roo.log('find label');
19138             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
19139         }
19140         
19141     },
19142     
19143     inputEl: function ()
19144     {
19145         return this.el.select('input.roo-radio',true).first();
19146     },
19147     onClick : function()
19148     {   
19149         Roo.log("click");
19150         this.setChecked(true);
19151     },
19152     
19153     setChecked : function(state,suppressEvent)
19154     {
19155         if(state){
19156             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19157                 v.dom.checked = false;
19158             });
19159         }
19160         Roo.log(this.inputEl().dom);
19161         this.checked = state;
19162         this.inputEl().dom.checked = state;
19163         
19164         if(suppressEvent !== true){
19165             this.fireEvent('check', this, state);
19166         }
19167         
19168         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19169         
19170     },
19171     
19172     getGroupValue : function()
19173     {
19174         var value = '';
19175         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19176             if(v.dom.checked == true){
19177                 value = v.dom.value;
19178             }
19179         });
19180         
19181         return value;
19182     },
19183     
19184     /**
19185      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
19186      * @return {Mixed} value The field value
19187      */
19188     getValue : function(){
19189         return this.getGroupValue();
19190     }
19191     
19192 });
19193
19194  
19195 //<script type="text/javascript">
19196
19197 /*
19198  * Based  Ext JS Library 1.1.1
19199  * Copyright(c) 2006-2007, Ext JS, LLC.
19200  * LGPL
19201  *
19202  */
19203  
19204 /**
19205  * @class Roo.HtmlEditorCore
19206  * @extends Roo.Component
19207  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
19208  *
19209  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
19210  */
19211
19212 Roo.HtmlEditorCore = function(config){
19213     
19214     
19215     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
19216     
19217     
19218     this.addEvents({
19219         /**
19220          * @event initialize
19221          * Fires when the editor is fully initialized (including the iframe)
19222          * @param {Roo.HtmlEditorCore} this
19223          */
19224         initialize: true,
19225         /**
19226          * @event activate
19227          * Fires when the editor is first receives the focus. Any insertion must wait
19228          * until after this event.
19229          * @param {Roo.HtmlEditorCore} this
19230          */
19231         activate: true,
19232          /**
19233          * @event beforesync
19234          * Fires before the textarea is updated with content from the editor iframe. Return false
19235          * to cancel the sync.
19236          * @param {Roo.HtmlEditorCore} this
19237          * @param {String} html
19238          */
19239         beforesync: true,
19240          /**
19241          * @event beforepush
19242          * Fires before the iframe editor is updated with content from the textarea. Return false
19243          * to cancel the push.
19244          * @param {Roo.HtmlEditorCore} this
19245          * @param {String} html
19246          */
19247         beforepush: true,
19248          /**
19249          * @event sync
19250          * Fires when the textarea is updated with content from the editor iframe.
19251          * @param {Roo.HtmlEditorCore} this
19252          * @param {String} html
19253          */
19254         sync: true,
19255          /**
19256          * @event push
19257          * Fires when the iframe editor is updated with content from the textarea.
19258          * @param {Roo.HtmlEditorCore} this
19259          * @param {String} html
19260          */
19261         push: true,
19262         
19263         /**
19264          * @event editorevent
19265          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19266          * @param {Roo.HtmlEditorCore} this
19267          */
19268         editorevent: true
19269         
19270     });
19271     
19272     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
19273     
19274     // defaults : white / black...
19275     this.applyBlacklists();
19276     
19277     
19278     
19279 };
19280
19281
19282 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
19283
19284
19285      /**
19286      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
19287      */
19288     
19289     owner : false,
19290     
19291      /**
19292      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
19293      *                        Roo.resizable.
19294      */
19295     resizable : false,
19296      /**
19297      * @cfg {Number} height (in pixels)
19298      */   
19299     height: 300,
19300    /**
19301      * @cfg {Number} width (in pixels)
19302      */   
19303     width: 500,
19304     
19305     /**
19306      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19307      * 
19308      */
19309     stylesheets: false,
19310     
19311     // id of frame..
19312     frameId: false,
19313     
19314     // private properties
19315     validationEvent : false,
19316     deferHeight: true,
19317     initialized : false,
19318     activated : false,
19319     sourceEditMode : false,
19320     onFocus : Roo.emptyFn,
19321     iframePad:3,
19322     hideMode:'offsets',
19323     
19324     clearUp: true,
19325     
19326     // blacklist + whitelisted elements..
19327     black: false,
19328     white: false,
19329      
19330     
19331
19332     /**
19333      * Protected method that will not generally be called directly. It
19334      * is called when the editor initializes the iframe with HTML contents. Override this method if you
19335      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
19336      */
19337     getDocMarkup : function(){
19338         // body styles..
19339         var st = '';
19340         
19341         // inherit styels from page...?? 
19342         if (this.stylesheets === false) {
19343             
19344             Roo.get(document.head).select('style').each(function(node) {
19345                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19346             });
19347             
19348             Roo.get(document.head).select('link').each(function(node) { 
19349                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19350             });
19351             
19352         } else if (!this.stylesheets.length) {
19353                 // simple..
19354                 st = '<style type="text/css">' +
19355                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19356                    '</style>';
19357         } else { 
19358             
19359         }
19360         
19361         st +=  '<style type="text/css">' +
19362             'IMG { cursor: pointer } ' +
19363         '</style>';
19364
19365         
19366         return '<html><head>' + st  +
19367             //<style type="text/css">' +
19368             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19369             //'</style>' +
19370             ' </head><body class="roo-htmleditor-body"></body></html>';
19371     },
19372
19373     // private
19374     onRender : function(ct, position)
19375     {
19376         var _t = this;
19377         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19378         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19379         
19380         
19381         this.el.dom.style.border = '0 none';
19382         this.el.dom.setAttribute('tabIndex', -1);
19383         this.el.addClass('x-hidden hide');
19384         
19385         
19386         
19387         if(Roo.isIE){ // fix IE 1px bogus margin
19388             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19389         }
19390        
19391         
19392         this.frameId = Roo.id();
19393         
19394          
19395         
19396         var iframe = this.owner.wrap.createChild({
19397             tag: 'iframe',
19398             cls: 'form-control', // bootstrap..
19399             id: this.frameId,
19400             name: this.frameId,
19401             frameBorder : 'no',
19402             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
19403         }, this.el
19404         );
19405         
19406         
19407         this.iframe = iframe.dom;
19408
19409          this.assignDocWin();
19410         
19411         this.doc.designMode = 'on';
19412        
19413         this.doc.open();
19414         this.doc.write(this.getDocMarkup());
19415         this.doc.close();
19416
19417         
19418         var task = { // must defer to wait for browser to be ready
19419             run : function(){
19420                 //console.log("run task?" + this.doc.readyState);
19421                 this.assignDocWin();
19422                 if(this.doc.body || this.doc.readyState == 'complete'){
19423                     try {
19424                         this.doc.designMode="on";
19425                     } catch (e) {
19426                         return;
19427                     }
19428                     Roo.TaskMgr.stop(task);
19429                     this.initEditor.defer(10, this);
19430                 }
19431             },
19432             interval : 10,
19433             duration: 10000,
19434             scope: this
19435         };
19436         Roo.TaskMgr.start(task);
19437
19438     },
19439
19440     // private
19441     onResize : function(w, h)
19442     {
19443          Roo.log('resize: ' +w + ',' + h );
19444         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19445         if(!this.iframe){
19446             return;
19447         }
19448         if(typeof w == 'number'){
19449             
19450             this.iframe.style.width = w + 'px';
19451         }
19452         if(typeof h == 'number'){
19453             
19454             this.iframe.style.height = h + 'px';
19455             if(this.doc){
19456                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19457             }
19458         }
19459         
19460     },
19461
19462     /**
19463      * Toggles the editor between standard and source edit mode.
19464      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19465      */
19466     toggleSourceEdit : function(sourceEditMode){
19467         
19468         this.sourceEditMode = sourceEditMode === true;
19469         
19470         if(this.sourceEditMode){
19471  
19472             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
19473             
19474         }else{
19475             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19476             //this.iframe.className = '';
19477             this.deferFocus();
19478         }
19479         //this.setSize(this.owner.wrap.getSize());
19480         //this.fireEvent('editmodechange', this, this.sourceEditMode);
19481     },
19482
19483     
19484   
19485
19486     /**
19487      * Protected method that will not generally be called directly. If you need/want
19488      * custom HTML cleanup, this is the method you should override.
19489      * @param {String} html The HTML to be cleaned
19490      * return {String} The cleaned HTML
19491      */
19492     cleanHtml : function(html){
19493         html = String(html);
19494         if(html.length > 5){
19495             if(Roo.isSafari){ // strip safari nonsense
19496                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19497             }
19498         }
19499         if(html == '&nbsp;'){
19500             html = '';
19501         }
19502         return html;
19503     },
19504
19505     /**
19506      * HTML Editor -> Textarea
19507      * Protected method that will not generally be called directly. Syncs the contents
19508      * of the editor iframe with the textarea.
19509      */
19510     syncValue : function(){
19511         if(this.initialized){
19512             var bd = (this.doc.body || this.doc.documentElement);
19513             //this.cleanUpPaste(); -- this is done else where and causes havoc..
19514             var html = bd.innerHTML;
19515             if(Roo.isSafari){
19516                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19517                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19518                 if(m && m[1]){
19519                     html = '<div style="'+m[0]+'">' + html + '</div>';
19520                 }
19521             }
19522             html = this.cleanHtml(html);
19523             // fix up the special chars.. normaly like back quotes in word...
19524             // however we do not want to do this with chinese..
19525             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19526                 var cc = b.charCodeAt();
19527                 if (
19528                     (cc >= 0x4E00 && cc < 0xA000 ) ||
19529                     (cc >= 0x3400 && cc < 0x4E00 ) ||
19530                     (cc >= 0xf900 && cc < 0xfb00 )
19531                 ) {
19532                         return b;
19533                 }
19534                 return "&#"+cc+";" 
19535             });
19536             if(this.owner.fireEvent('beforesync', this, html) !== false){
19537                 this.el.dom.value = html;
19538                 this.owner.fireEvent('sync', this, html);
19539             }
19540         }
19541     },
19542
19543     /**
19544      * Protected method that will not generally be called directly. Pushes the value of the textarea
19545      * into the iframe editor.
19546      */
19547     pushValue : function(){
19548         if(this.initialized){
19549             var v = this.el.dom.value.trim();
19550             
19551 //            if(v.length < 1){
19552 //                v = '&#160;';
19553 //            }
19554             
19555             if(this.owner.fireEvent('beforepush', this, v) !== false){
19556                 var d = (this.doc.body || this.doc.documentElement);
19557                 d.innerHTML = v;
19558                 this.cleanUpPaste();
19559                 this.el.dom.value = d.innerHTML;
19560                 this.owner.fireEvent('push', this, v);
19561             }
19562         }
19563     },
19564
19565     // private
19566     deferFocus : function(){
19567         this.focus.defer(10, this);
19568     },
19569
19570     // doc'ed in Field
19571     focus : function(){
19572         if(this.win && !this.sourceEditMode){
19573             this.win.focus();
19574         }else{
19575             this.el.focus();
19576         }
19577     },
19578     
19579     assignDocWin: function()
19580     {
19581         var iframe = this.iframe;
19582         
19583          if(Roo.isIE){
19584             this.doc = iframe.contentWindow.document;
19585             this.win = iframe.contentWindow;
19586         } else {
19587 //            if (!Roo.get(this.frameId)) {
19588 //                return;
19589 //            }
19590 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19591 //            this.win = Roo.get(this.frameId).dom.contentWindow;
19592             
19593             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19594                 return;
19595             }
19596             
19597             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19598             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19599         }
19600     },
19601     
19602     // private
19603     initEditor : function(){
19604         //console.log("INIT EDITOR");
19605         this.assignDocWin();
19606         
19607         
19608         
19609         this.doc.designMode="on";
19610         this.doc.open();
19611         this.doc.write(this.getDocMarkup());
19612         this.doc.close();
19613         
19614         var dbody = (this.doc.body || this.doc.documentElement);
19615         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19616         // this copies styles from the containing element into thsi one..
19617         // not sure why we need all of this..
19618         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19619         
19620         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19621         //ss['background-attachment'] = 'fixed'; // w3c
19622         dbody.bgProperties = 'fixed'; // ie
19623         //Roo.DomHelper.applyStyles(dbody, ss);
19624         Roo.EventManager.on(this.doc, {
19625             //'mousedown': this.onEditorEvent,
19626             'mouseup': this.onEditorEvent,
19627             'dblclick': this.onEditorEvent,
19628             'click': this.onEditorEvent,
19629             'keyup': this.onEditorEvent,
19630             buffer:100,
19631             scope: this
19632         });
19633         if(Roo.isGecko){
19634             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19635         }
19636         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19637             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19638         }
19639         this.initialized = true;
19640
19641         this.owner.fireEvent('initialize', this);
19642         this.pushValue();
19643     },
19644
19645     // private
19646     onDestroy : function(){
19647         
19648         
19649         
19650         if(this.rendered){
19651             
19652             //for (var i =0; i < this.toolbars.length;i++) {
19653             //    // fixme - ask toolbars for heights?
19654             //    this.toolbars[i].onDestroy();
19655            // }
19656             
19657             //this.wrap.dom.innerHTML = '';
19658             //this.wrap.remove();
19659         }
19660     },
19661
19662     // private
19663     onFirstFocus : function(){
19664         
19665         this.assignDocWin();
19666         
19667         
19668         this.activated = true;
19669          
19670     
19671         if(Roo.isGecko){ // prevent silly gecko errors
19672             this.win.focus();
19673             var s = this.win.getSelection();
19674             if(!s.focusNode || s.focusNode.nodeType != 3){
19675                 var r = s.getRangeAt(0);
19676                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19677                 r.collapse(true);
19678                 this.deferFocus();
19679             }
19680             try{
19681                 this.execCmd('useCSS', true);
19682                 this.execCmd('styleWithCSS', false);
19683             }catch(e){}
19684         }
19685         this.owner.fireEvent('activate', this);
19686     },
19687
19688     // private
19689     adjustFont: function(btn){
19690         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19691         //if(Roo.isSafari){ // safari
19692         //    adjust *= 2;
19693        // }
19694         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19695         if(Roo.isSafari){ // safari
19696             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19697             v =  (v < 10) ? 10 : v;
19698             v =  (v > 48) ? 48 : v;
19699             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19700             
19701         }
19702         
19703         
19704         v = Math.max(1, v+adjust);
19705         
19706         this.execCmd('FontSize', v  );
19707     },
19708
19709     onEditorEvent : function(e)
19710     {
19711         this.owner.fireEvent('editorevent', this, e);
19712       //  this.updateToolbar();
19713         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19714     },
19715
19716     insertTag : function(tg)
19717     {
19718         // could be a bit smarter... -> wrap the current selected tRoo..
19719         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19720             
19721             range = this.createRange(this.getSelection());
19722             var wrappingNode = this.doc.createElement(tg.toLowerCase());
19723             wrappingNode.appendChild(range.extractContents());
19724             range.insertNode(wrappingNode);
19725
19726             return;
19727             
19728             
19729             
19730         }
19731         this.execCmd("formatblock",   tg);
19732         
19733     },
19734     
19735     insertText : function(txt)
19736     {
19737         
19738         
19739         var range = this.createRange();
19740         range.deleteContents();
19741                //alert(Sender.getAttribute('label'));
19742                
19743         range.insertNode(this.doc.createTextNode(txt));
19744     } ,
19745     
19746      
19747
19748     /**
19749      * Executes a Midas editor command on the editor document and performs necessary focus and
19750      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19751      * @param {String} cmd The Midas command
19752      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19753      */
19754     relayCmd : function(cmd, value){
19755         this.win.focus();
19756         this.execCmd(cmd, value);
19757         this.owner.fireEvent('editorevent', this);
19758         //this.updateToolbar();
19759         this.owner.deferFocus();
19760     },
19761
19762     /**
19763      * Executes a Midas editor command directly on the editor document.
19764      * For visual commands, you should use {@link #relayCmd} instead.
19765      * <b>This should only be called after the editor is initialized.</b>
19766      * @param {String} cmd The Midas command
19767      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19768      */
19769     execCmd : function(cmd, value){
19770         this.doc.execCommand(cmd, false, value === undefined ? null : value);
19771         this.syncValue();
19772     },
19773  
19774  
19775    
19776     /**
19777      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19778      * to insert tRoo.
19779      * @param {String} text | dom node.. 
19780      */
19781     insertAtCursor : function(text)
19782     {
19783         
19784         
19785         
19786         if(!this.activated){
19787             return;
19788         }
19789         /*
19790         if(Roo.isIE){
19791             this.win.focus();
19792             var r = this.doc.selection.createRange();
19793             if(r){
19794                 r.collapse(true);
19795                 r.pasteHTML(text);
19796                 this.syncValue();
19797                 this.deferFocus();
19798             
19799             }
19800             return;
19801         }
19802         */
19803         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19804             this.win.focus();
19805             
19806             
19807             // from jquery ui (MIT licenced)
19808             var range, node;
19809             var win = this.win;
19810             
19811             if (win.getSelection && win.getSelection().getRangeAt) {
19812                 range = win.getSelection().getRangeAt(0);
19813                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19814                 range.insertNode(node);
19815             } else if (win.document.selection && win.document.selection.createRange) {
19816                 // no firefox support
19817                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19818                 win.document.selection.createRange().pasteHTML(txt);
19819             } else {
19820                 // no firefox support
19821                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19822                 this.execCmd('InsertHTML', txt);
19823             } 
19824             
19825             this.syncValue();
19826             
19827             this.deferFocus();
19828         }
19829     },
19830  // private
19831     mozKeyPress : function(e){
19832         if(e.ctrlKey){
19833             var c = e.getCharCode(), cmd;
19834           
19835             if(c > 0){
19836                 c = String.fromCharCode(c).toLowerCase();
19837                 switch(c){
19838                     case 'b':
19839                         cmd = 'bold';
19840                         break;
19841                     case 'i':
19842                         cmd = 'italic';
19843                         break;
19844                     
19845                     case 'u':
19846                         cmd = 'underline';
19847                         break;
19848                     
19849                     case 'v':
19850                         this.cleanUpPaste.defer(100, this);
19851                         return;
19852                         
19853                 }
19854                 if(cmd){
19855                     this.win.focus();
19856                     this.execCmd(cmd);
19857                     this.deferFocus();
19858                     e.preventDefault();
19859                 }
19860                 
19861             }
19862         }
19863     },
19864
19865     // private
19866     fixKeys : function(){ // load time branching for fastest keydown performance
19867         if(Roo.isIE){
19868             return function(e){
19869                 var k = e.getKey(), r;
19870                 if(k == e.TAB){
19871                     e.stopEvent();
19872                     r = this.doc.selection.createRange();
19873                     if(r){
19874                         r.collapse(true);
19875                         r.pasteHTML('&#160;&#160;&#160;&#160;');
19876                         this.deferFocus();
19877                     }
19878                     return;
19879                 }
19880                 
19881                 if(k == e.ENTER){
19882                     r = this.doc.selection.createRange();
19883                     if(r){
19884                         var target = r.parentElement();
19885                         if(!target || target.tagName.toLowerCase() != 'li'){
19886                             e.stopEvent();
19887                             r.pasteHTML('<br />');
19888                             r.collapse(false);
19889                             r.select();
19890                         }
19891                     }
19892                 }
19893                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19894                     this.cleanUpPaste.defer(100, this);
19895                     return;
19896                 }
19897                 
19898                 
19899             };
19900         }else if(Roo.isOpera){
19901             return function(e){
19902                 var k = e.getKey();
19903                 if(k == e.TAB){
19904                     e.stopEvent();
19905                     this.win.focus();
19906                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
19907                     this.deferFocus();
19908                 }
19909                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19910                     this.cleanUpPaste.defer(100, this);
19911                     return;
19912                 }
19913                 
19914             };
19915         }else if(Roo.isSafari){
19916             return function(e){
19917                 var k = e.getKey();
19918                 
19919                 if(k == e.TAB){
19920                     e.stopEvent();
19921                     this.execCmd('InsertText','\t');
19922                     this.deferFocus();
19923                     return;
19924                 }
19925                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19926                     this.cleanUpPaste.defer(100, this);
19927                     return;
19928                 }
19929                 
19930              };
19931         }
19932     }(),
19933     
19934     getAllAncestors: function()
19935     {
19936         var p = this.getSelectedNode();
19937         var a = [];
19938         if (!p) {
19939             a.push(p); // push blank onto stack..
19940             p = this.getParentElement();
19941         }
19942         
19943         
19944         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
19945             a.push(p);
19946             p = p.parentNode;
19947         }
19948         a.push(this.doc.body);
19949         return a;
19950     },
19951     lastSel : false,
19952     lastSelNode : false,
19953     
19954     
19955     getSelection : function() 
19956     {
19957         this.assignDocWin();
19958         return Roo.isIE ? this.doc.selection : this.win.getSelection();
19959     },
19960     
19961     getSelectedNode: function() 
19962     {
19963         // this may only work on Gecko!!!
19964         
19965         // should we cache this!!!!
19966         
19967         
19968         
19969          
19970         var range = this.createRange(this.getSelection()).cloneRange();
19971         
19972         if (Roo.isIE) {
19973             var parent = range.parentElement();
19974             while (true) {
19975                 var testRange = range.duplicate();
19976                 testRange.moveToElementText(parent);
19977                 if (testRange.inRange(range)) {
19978                     break;
19979                 }
19980                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
19981                     break;
19982                 }
19983                 parent = parent.parentElement;
19984             }
19985             return parent;
19986         }
19987         
19988         // is ancestor a text element.
19989         var ac =  range.commonAncestorContainer;
19990         if (ac.nodeType == 3) {
19991             ac = ac.parentNode;
19992         }
19993         
19994         var ar = ac.childNodes;
19995          
19996         var nodes = [];
19997         var other_nodes = [];
19998         var has_other_nodes = false;
19999         for (var i=0;i<ar.length;i++) {
20000             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
20001                 continue;
20002             }
20003             // fullly contained node.
20004             
20005             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
20006                 nodes.push(ar[i]);
20007                 continue;
20008             }
20009             
20010             // probably selected..
20011             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
20012                 other_nodes.push(ar[i]);
20013                 continue;
20014             }
20015             // outer..
20016             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
20017                 continue;
20018             }
20019             
20020             
20021             has_other_nodes = true;
20022         }
20023         if (!nodes.length && other_nodes.length) {
20024             nodes= other_nodes;
20025         }
20026         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
20027             return false;
20028         }
20029         
20030         return nodes[0];
20031     },
20032     createRange: function(sel)
20033     {
20034         // this has strange effects when using with 
20035         // top toolbar - not sure if it's a great idea.
20036         //this.editor.contentWindow.focus();
20037         if (typeof sel != "undefined") {
20038             try {
20039                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
20040             } catch(e) {
20041                 return this.doc.createRange();
20042             }
20043         } else {
20044             return this.doc.createRange();
20045         }
20046     },
20047     getParentElement: function()
20048     {
20049         
20050         this.assignDocWin();
20051         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
20052         
20053         var range = this.createRange(sel);
20054          
20055         try {
20056             var p = range.commonAncestorContainer;
20057             while (p.nodeType == 3) { // text node
20058                 p = p.parentNode;
20059             }
20060             return p;
20061         } catch (e) {
20062             return null;
20063         }
20064     
20065     },
20066     /***
20067      *
20068      * Range intersection.. the hard stuff...
20069      *  '-1' = before
20070      *  '0' = hits..
20071      *  '1' = after.
20072      *         [ -- selected range --- ]
20073      *   [fail]                        [fail]
20074      *
20075      *    basically..
20076      *      if end is before start or  hits it. fail.
20077      *      if start is after end or hits it fail.
20078      *
20079      *   if either hits (but other is outside. - then it's not 
20080      *   
20081      *    
20082      **/
20083     
20084     
20085     // @see http://www.thismuchiknow.co.uk/?p=64.
20086     rangeIntersectsNode : function(range, node)
20087     {
20088         var nodeRange = node.ownerDocument.createRange();
20089         try {
20090             nodeRange.selectNode(node);
20091         } catch (e) {
20092             nodeRange.selectNodeContents(node);
20093         }
20094     
20095         var rangeStartRange = range.cloneRange();
20096         rangeStartRange.collapse(true);
20097     
20098         var rangeEndRange = range.cloneRange();
20099         rangeEndRange.collapse(false);
20100     
20101         var nodeStartRange = nodeRange.cloneRange();
20102         nodeStartRange.collapse(true);
20103     
20104         var nodeEndRange = nodeRange.cloneRange();
20105         nodeEndRange.collapse(false);
20106     
20107         return rangeStartRange.compareBoundaryPoints(
20108                  Range.START_TO_START, nodeEndRange) == -1 &&
20109                rangeEndRange.compareBoundaryPoints(
20110                  Range.START_TO_START, nodeStartRange) == 1;
20111         
20112          
20113     },
20114     rangeCompareNode : function(range, node)
20115     {
20116         var nodeRange = node.ownerDocument.createRange();
20117         try {
20118             nodeRange.selectNode(node);
20119         } catch (e) {
20120             nodeRange.selectNodeContents(node);
20121         }
20122         
20123         
20124         range.collapse(true);
20125     
20126         nodeRange.collapse(true);
20127      
20128         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
20129         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
20130          
20131         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
20132         
20133         var nodeIsBefore   =  ss == 1;
20134         var nodeIsAfter    = ee == -1;
20135         
20136         if (nodeIsBefore && nodeIsAfter) {
20137             return 0; // outer
20138         }
20139         if (!nodeIsBefore && nodeIsAfter) {
20140             return 1; //right trailed.
20141         }
20142         
20143         if (nodeIsBefore && !nodeIsAfter) {
20144             return 2;  // left trailed.
20145         }
20146         // fully contined.
20147         return 3;
20148     },
20149
20150     // private? - in a new class?
20151     cleanUpPaste :  function()
20152     {
20153         // cleans up the whole document..
20154         Roo.log('cleanuppaste');
20155         
20156         this.cleanUpChildren(this.doc.body);
20157         var clean = this.cleanWordChars(this.doc.body.innerHTML);
20158         if (clean != this.doc.body.innerHTML) {
20159             this.doc.body.innerHTML = clean;
20160         }
20161         
20162     },
20163     
20164     cleanWordChars : function(input) {// change the chars to hex code
20165         var he = Roo.HtmlEditorCore;
20166         
20167         var output = input;
20168         Roo.each(he.swapCodes, function(sw) { 
20169             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
20170             
20171             output = output.replace(swapper, sw[1]);
20172         });
20173         
20174         return output;
20175     },
20176     
20177     
20178     cleanUpChildren : function (n)
20179     {
20180         if (!n.childNodes.length) {
20181             return;
20182         }
20183         for (var i = n.childNodes.length-1; i > -1 ; i--) {
20184            this.cleanUpChild(n.childNodes[i]);
20185         }
20186     },
20187     
20188     
20189         
20190     
20191     cleanUpChild : function (node)
20192     {
20193         var ed = this;
20194         //console.log(node);
20195         if (node.nodeName == "#text") {
20196             // clean up silly Windows -- stuff?
20197             return; 
20198         }
20199         if (node.nodeName == "#comment") {
20200             node.parentNode.removeChild(node);
20201             // clean up silly Windows -- stuff?
20202             return; 
20203         }
20204         var lcname = node.tagName.toLowerCase();
20205         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
20206         // whitelist of tags..
20207         
20208         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
20209             // remove node.
20210             node.parentNode.removeChild(node);
20211             return;
20212             
20213         }
20214         
20215         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
20216         
20217         // remove <a name=....> as rendering on yahoo mailer is borked with this.
20218         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
20219         
20220         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
20221         //    remove_keep_children = true;
20222         //}
20223         
20224         if (remove_keep_children) {
20225             this.cleanUpChildren(node);
20226             // inserts everything just before this node...
20227             while (node.childNodes.length) {
20228                 var cn = node.childNodes[0];
20229                 node.removeChild(cn);
20230                 node.parentNode.insertBefore(cn, node);
20231             }
20232             node.parentNode.removeChild(node);
20233             return;
20234         }
20235         
20236         if (!node.attributes || !node.attributes.length) {
20237             this.cleanUpChildren(node);
20238             return;
20239         }
20240         
20241         function cleanAttr(n,v)
20242         {
20243             
20244             if (v.match(/^\./) || v.match(/^\//)) {
20245                 return;
20246             }
20247             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
20248                 return;
20249             }
20250             if (v.match(/^#/)) {
20251                 return;
20252             }
20253 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
20254             node.removeAttribute(n);
20255             
20256         }
20257         
20258         var cwhite = this.cwhite;
20259         var cblack = this.cblack;
20260             
20261         function cleanStyle(n,v)
20262         {
20263             if (v.match(/expression/)) { //XSS?? should we even bother..
20264                 node.removeAttribute(n);
20265                 return;
20266             }
20267             
20268             var parts = v.split(/;/);
20269             var clean = [];
20270             
20271             Roo.each(parts, function(p) {
20272                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
20273                 if (!p.length) {
20274                     return true;
20275                 }
20276                 var l = p.split(':').shift().replace(/\s+/g,'');
20277                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
20278                 
20279                 if ( cwhite.length && cblack.indexOf(l) > -1) {
20280 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20281                     //node.removeAttribute(n);
20282                     return true;
20283                 }
20284                 //Roo.log()
20285                 // only allow 'c whitelisted system attributes'
20286                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
20287 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20288                     //node.removeAttribute(n);
20289                     return true;
20290                 }
20291                 
20292                 
20293                  
20294                 
20295                 clean.push(p);
20296                 return true;
20297             });
20298             if (clean.length) { 
20299                 node.setAttribute(n, clean.join(';'));
20300             } else {
20301                 node.removeAttribute(n);
20302             }
20303             
20304         }
20305         
20306         
20307         for (var i = node.attributes.length-1; i > -1 ; i--) {
20308             var a = node.attributes[i];
20309             //console.log(a);
20310             
20311             if (a.name.toLowerCase().substr(0,2)=='on')  {
20312                 node.removeAttribute(a.name);
20313                 continue;
20314             }
20315             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
20316                 node.removeAttribute(a.name);
20317                 continue;
20318             }
20319             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
20320                 cleanAttr(a.name,a.value); // fixme..
20321                 continue;
20322             }
20323             if (a.name == 'style') {
20324                 cleanStyle(a.name,a.value);
20325                 continue;
20326             }
20327             /// clean up MS crap..
20328             // tecnically this should be a list of valid class'es..
20329             
20330             
20331             if (a.name == 'class') {
20332                 if (a.value.match(/^Mso/)) {
20333                     node.className = '';
20334                 }
20335                 
20336                 if (a.value.match(/body/)) {
20337                     node.className = '';
20338                 }
20339                 continue;
20340             }
20341             
20342             // style cleanup!?
20343             // class cleanup?
20344             
20345         }
20346         
20347         
20348         this.cleanUpChildren(node);
20349         
20350         
20351     },
20352     
20353     /**
20354      * Clean up MS wordisms...
20355      */
20356     cleanWord : function(node)
20357     {
20358         
20359         
20360         if (!node) {
20361             this.cleanWord(this.doc.body);
20362             return;
20363         }
20364         if (node.nodeName == "#text") {
20365             // clean up silly Windows -- stuff?
20366             return; 
20367         }
20368         if (node.nodeName == "#comment") {
20369             node.parentNode.removeChild(node);
20370             // clean up silly Windows -- stuff?
20371             return; 
20372         }
20373         
20374         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
20375             node.parentNode.removeChild(node);
20376             return;
20377         }
20378         
20379         // remove - but keep children..
20380         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20381             while (node.childNodes.length) {
20382                 var cn = node.childNodes[0];
20383                 node.removeChild(cn);
20384                 node.parentNode.insertBefore(cn, node);
20385             }
20386             node.parentNode.removeChild(node);
20387             this.iterateChildren(node, this.cleanWord);
20388             return;
20389         }
20390         // clean styles
20391         if (node.className.length) {
20392             
20393             var cn = node.className.split(/\W+/);
20394             var cna = [];
20395             Roo.each(cn, function(cls) {
20396                 if (cls.match(/Mso[a-zA-Z]+/)) {
20397                     return;
20398                 }
20399                 cna.push(cls);
20400             });
20401             node.className = cna.length ? cna.join(' ') : '';
20402             if (!cna.length) {
20403                 node.removeAttribute("class");
20404             }
20405         }
20406         
20407         if (node.hasAttribute("lang")) {
20408             node.removeAttribute("lang");
20409         }
20410         
20411         if (node.hasAttribute("style")) {
20412             
20413             var styles = node.getAttribute("style").split(";");
20414             var nstyle = [];
20415             Roo.each(styles, function(s) {
20416                 if (!s.match(/:/)) {
20417                     return;
20418                 }
20419                 var kv = s.split(":");
20420                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20421                     return;
20422                 }
20423                 // what ever is left... we allow.
20424                 nstyle.push(s);
20425             });
20426             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20427             if (!nstyle.length) {
20428                 node.removeAttribute('style');
20429             }
20430         }
20431         this.iterateChildren(node, this.cleanWord);
20432         
20433         
20434         
20435     },
20436     /**
20437      * iterateChildren of a Node, calling fn each time, using this as the scole..
20438      * @param {DomNode} node node to iterate children of.
20439      * @param {Function} fn method of this class to call on each item.
20440      */
20441     iterateChildren : function(node, fn)
20442     {
20443         if (!node.childNodes.length) {
20444                 return;
20445         }
20446         for (var i = node.childNodes.length-1; i > -1 ; i--) {
20447            fn.call(this, node.childNodes[i])
20448         }
20449     },
20450     
20451     
20452     /**
20453      * cleanTableWidths.
20454      *
20455      * Quite often pasting from word etc.. results in tables with column and widths.
20456      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20457      *
20458      */
20459     cleanTableWidths : function(node)
20460     {
20461          
20462          
20463         if (!node) {
20464             this.cleanTableWidths(this.doc.body);
20465             return;
20466         }
20467         
20468         // ignore list...
20469         if (node.nodeName == "#text" || node.nodeName == "#comment") {
20470             return; 
20471         }
20472         Roo.log(node.tagName);
20473         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20474             this.iterateChildren(node, this.cleanTableWidths);
20475             return;
20476         }
20477         if (node.hasAttribute('width')) {
20478             node.removeAttribute('width');
20479         }
20480         
20481          
20482         if (node.hasAttribute("style")) {
20483             // pretty basic...
20484             
20485             var styles = node.getAttribute("style").split(";");
20486             var nstyle = [];
20487             Roo.each(styles, function(s) {
20488                 if (!s.match(/:/)) {
20489                     return;
20490                 }
20491                 var kv = s.split(":");
20492                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20493                     return;
20494                 }
20495                 // what ever is left... we allow.
20496                 nstyle.push(s);
20497             });
20498             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20499             if (!nstyle.length) {
20500                 node.removeAttribute('style');
20501             }
20502         }
20503         
20504         this.iterateChildren(node, this.cleanTableWidths);
20505         
20506         
20507     },
20508     
20509     
20510     
20511     
20512     domToHTML : function(currentElement, depth, nopadtext) {
20513         
20514         depth = depth || 0;
20515         nopadtext = nopadtext || false;
20516     
20517         if (!currentElement) {
20518             return this.domToHTML(this.doc.body);
20519         }
20520         
20521         //Roo.log(currentElement);
20522         var j;
20523         var allText = false;
20524         var nodeName = currentElement.nodeName;
20525         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20526         
20527         if  (nodeName == '#text') {
20528             
20529             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20530         }
20531         
20532         
20533         var ret = '';
20534         if (nodeName != 'BODY') {
20535              
20536             var i = 0;
20537             // Prints the node tagName, such as <A>, <IMG>, etc
20538             if (tagName) {
20539                 var attr = [];
20540                 for(i = 0; i < currentElement.attributes.length;i++) {
20541                     // quoting?
20542                     var aname = currentElement.attributes.item(i).name;
20543                     if (!currentElement.attributes.item(i).value.length) {
20544                         continue;
20545                     }
20546                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20547                 }
20548                 
20549                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20550             } 
20551             else {
20552                 
20553                 // eack
20554             }
20555         } else {
20556             tagName = false;
20557         }
20558         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20559             return ret;
20560         }
20561         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20562             nopadtext = true;
20563         }
20564         
20565         
20566         // Traverse the tree
20567         i = 0;
20568         var currentElementChild = currentElement.childNodes.item(i);
20569         var allText = true;
20570         var innerHTML  = '';
20571         lastnode = '';
20572         while (currentElementChild) {
20573             // Formatting code (indent the tree so it looks nice on the screen)
20574             var nopad = nopadtext;
20575             if (lastnode == 'SPAN') {
20576                 nopad  = true;
20577             }
20578             // text
20579             if  (currentElementChild.nodeName == '#text') {
20580                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20581                 toadd = nopadtext ? toadd : toadd.trim();
20582                 if (!nopad && toadd.length > 80) {
20583                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
20584                 }
20585                 innerHTML  += toadd;
20586                 
20587                 i++;
20588                 currentElementChild = currentElement.childNodes.item(i);
20589                 lastNode = '';
20590                 continue;
20591             }
20592             allText = false;
20593             
20594             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
20595                 
20596             // Recursively traverse the tree structure of the child node
20597             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
20598             lastnode = currentElementChild.nodeName;
20599             i++;
20600             currentElementChild=currentElement.childNodes.item(i);
20601         }
20602         
20603         ret += innerHTML;
20604         
20605         if (!allText) {
20606                 // The remaining code is mostly for formatting the tree
20607             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
20608         }
20609         
20610         
20611         if (tagName) {
20612             ret+= "</"+tagName+">";
20613         }
20614         return ret;
20615         
20616     },
20617         
20618     applyBlacklists : function()
20619     {
20620         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
20621         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
20622         
20623         this.white = [];
20624         this.black = [];
20625         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20626             if (b.indexOf(tag) > -1) {
20627                 return;
20628             }
20629             this.white.push(tag);
20630             
20631         }, this);
20632         
20633         Roo.each(w, function(tag) {
20634             if (b.indexOf(tag) > -1) {
20635                 return;
20636             }
20637             if (this.white.indexOf(tag) > -1) {
20638                 return;
20639             }
20640             this.white.push(tag);
20641             
20642         }, this);
20643         
20644         
20645         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20646             if (w.indexOf(tag) > -1) {
20647                 return;
20648             }
20649             this.black.push(tag);
20650             
20651         }, this);
20652         
20653         Roo.each(b, function(tag) {
20654             if (w.indexOf(tag) > -1) {
20655                 return;
20656             }
20657             if (this.black.indexOf(tag) > -1) {
20658                 return;
20659             }
20660             this.black.push(tag);
20661             
20662         }, this);
20663         
20664         
20665         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
20666         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
20667         
20668         this.cwhite = [];
20669         this.cblack = [];
20670         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20671             if (b.indexOf(tag) > -1) {
20672                 return;
20673             }
20674             this.cwhite.push(tag);
20675             
20676         }, this);
20677         
20678         Roo.each(w, function(tag) {
20679             if (b.indexOf(tag) > -1) {
20680                 return;
20681             }
20682             if (this.cwhite.indexOf(tag) > -1) {
20683                 return;
20684             }
20685             this.cwhite.push(tag);
20686             
20687         }, this);
20688         
20689         
20690         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20691             if (w.indexOf(tag) > -1) {
20692                 return;
20693             }
20694             this.cblack.push(tag);
20695             
20696         }, this);
20697         
20698         Roo.each(b, function(tag) {
20699             if (w.indexOf(tag) > -1) {
20700                 return;
20701             }
20702             if (this.cblack.indexOf(tag) > -1) {
20703                 return;
20704             }
20705             this.cblack.push(tag);
20706             
20707         }, this);
20708     },
20709     
20710     setStylesheets : function(stylesheets)
20711     {
20712         if(typeof(stylesheets) == 'string'){
20713             Roo.get(this.iframe.contentDocument.head).createChild({
20714                 tag : 'link',
20715                 rel : 'stylesheet',
20716                 type : 'text/css',
20717                 href : stylesheets
20718             });
20719             
20720             return;
20721         }
20722         var _this = this;
20723      
20724         Roo.each(stylesheets, function(s) {
20725             if(!s.length){
20726                 return;
20727             }
20728             
20729             Roo.get(_this.iframe.contentDocument.head).createChild({
20730                 tag : 'link',
20731                 rel : 'stylesheet',
20732                 type : 'text/css',
20733                 href : s
20734             });
20735         });
20736
20737         
20738     },
20739     
20740     removeStylesheets : function()
20741     {
20742         var _this = this;
20743         
20744         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20745             s.remove();
20746         });
20747     }
20748     
20749     // hide stuff that is not compatible
20750     /**
20751      * @event blur
20752      * @hide
20753      */
20754     /**
20755      * @event change
20756      * @hide
20757      */
20758     /**
20759      * @event focus
20760      * @hide
20761      */
20762     /**
20763      * @event specialkey
20764      * @hide
20765      */
20766     /**
20767      * @cfg {String} fieldClass @hide
20768      */
20769     /**
20770      * @cfg {String} focusClass @hide
20771      */
20772     /**
20773      * @cfg {String} autoCreate @hide
20774      */
20775     /**
20776      * @cfg {String} inputType @hide
20777      */
20778     /**
20779      * @cfg {String} invalidClass @hide
20780      */
20781     /**
20782      * @cfg {String} invalidText @hide
20783      */
20784     /**
20785      * @cfg {String} msgFx @hide
20786      */
20787     /**
20788      * @cfg {String} validateOnBlur @hide
20789      */
20790 });
20791
20792 Roo.HtmlEditorCore.white = [
20793         'area', 'br', 'img', 'input', 'hr', 'wbr',
20794         
20795        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
20796        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
20797        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
20798        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
20799        'table',   'ul',         'xmp', 
20800        
20801        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
20802       'thead',   'tr', 
20803      
20804       'dir', 'menu', 'ol', 'ul', 'dl',
20805        
20806       'embed',  'object'
20807 ];
20808
20809
20810 Roo.HtmlEditorCore.black = [
20811     //    'embed',  'object', // enable - backend responsiblity to clean thiese
20812         'applet', // 
20813         'base',   'basefont', 'bgsound', 'blink',  'body', 
20814         'frame',  'frameset', 'head',    'html',   'ilayer', 
20815         'iframe', 'layer',  'link',     'meta',    'object',   
20816         'script', 'style' ,'title',  'xml' // clean later..
20817 ];
20818 Roo.HtmlEditorCore.clean = [
20819     'script', 'style', 'title', 'xml'
20820 ];
20821 Roo.HtmlEditorCore.remove = [
20822     'font'
20823 ];
20824 // attributes..
20825
20826 Roo.HtmlEditorCore.ablack = [
20827     'on'
20828 ];
20829     
20830 Roo.HtmlEditorCore.aclean = [ 
20831     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
20832 ];
20833
20834 // protocols..
20835 Roo.HtmlEditorCore.pwhite= [
20836         'http',  'https',  'mailto'
20837 ];
20838
20839 // white listed style attributes.
20840 Roo.HtmlEditorCore.cwhite= [
20841       //  'text-align', /// default is to allow most things..
20842       
20843          
20844 //        'font-size'//??
20845 ];
20846
20847 // black listed style attributes.
20848 Roo.HtmlEditorCore.cblack= [
20849       //  'font-size' -- this can be set by the project 
20850 ];
20851
20852
20853 Roo.HtmlEditorCore.swapCodes   =[ 
20854     [    8211, "--" ], 
20855     [    8212, "--" ], 
20856     [    8216,  "'" ],  
20857     [    8217, "'" ],  
20858     [    8220, '"' ],  
20859     [    8221, '"' ],  
20860     [    8226, "*" ],  
20861     [    8230, "..." ]
20862 ]; 
20863
20864     /*
20865  * - LGPL
20866  *
20867  * HtmlEditor
20868  * 
20869  */
20870
20871 /**
20872  * @class Roo.bootstrap.HtmlEditor
20873  * @extends Roo.bootstrap.TextArea
20874  * Bootstrap HtmlEditor class
20875
20876  * @constructor
20877  * Create a new HtmlEditor
20878  * @param {Object} config The config object
20879  */
20880
20881 Roo.bootstrap.HtmlEditor = function(config){
20882     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
20883     if (!this.toolbars) {
20884         this.toolbars = [];
20885     }
20886     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
20887     this.addEvents({
20888             /**
20889              * @event initialize
20890              * Fires when the editor is fully initialized (including the iframe)
20891              * @param {HtmlEditor} this
20892              */
20893             initialize: true,
20894             /**
20895              * @event activate
20896              * Fires when the editor is first receives the focus. Any insertion must wait
20897              * until after this event.
20898              * @param {HtmlEditor} this
20899              */
20900             activate: true,
20901              /**
20902              * @event beforesync
20903              * Fires before the textarea is updated with content from the editor iframe. Return false
20904              * to cancel the sync.
20905              * @param {HtmlEditor} this
20906              * @param {String} html
20907              */
20908             beforesync: true,
20909              /**
20910              * @event beforepush
20911              * Fires before the iframe editor is updated with content from the textarea. Return false
20912              * to cancel the push.
20913              * @param {HtmlEditor} this
20914              * @param {String} html
20915              */
20916             beforepush: true,
20917              /**
20918              * @event sync
20919              * Fires when the textarea is updated with content from the editor iframe.
20920              * @param {HtmlEditor} this
20921              * @param {String} html
20922              */
20923             sync: true,
20924              /**
20925              * @event push
20926              * Fires when the iframe editor is updated with content from the textarea.
20927              * @param {HtmlEditor} this
20928              * @param {String} html
20929              */
20930             push: true,
20931              /**
20932              * @event editmodechange
20933              * Fires when the editor switches edit modes
20934              * @param {HtmlEditor} this
20935              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
20936              */
20937             editmodechange: true,
20938             /**
20939              * @event editorevent
20940              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20941              * @param {HtmlEditor} this
20942              */
20943             editorevent: true,
20944             /**
20945              * @event firstfocus
20946              * Fires when on first focus - needed by toolbars..
20947              * @param {HtmlEditor} this
20948              */
20949             firstfocus: true,
20950             /**
20951              * @event autosave
20952              * Auto save the htmlEditor value as a file into Events
20953              * @param {HtmlEditor} this
20954              */
20955             autosave: true,
20956             /**
20957              * @event savedpreview
20958              * preview the saved version of htmlEditor
20959              * @param {HtmlEditor} this
20960              */
20961             savedpreview: true
20962         });
20963 };
20964
20965
20966 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
20967     
20968     
20969       /**
20970      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
20971      */
20972     toolbars : false,
20973    
20974      /**
20975      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20976      *                        Roo.resizable.
20977      */
20978     resizable : false,
20979      /**
20980      * @cfg {Number} height (in pixels)
20981      */   
20982     height: 300,
20983    /**
20984      * @cfg {Number} width (in pixels)
20985      */   
20986     width: false,
20987     
20988     /**
20989      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20990      * 
20991      */
20992     stylesheets: false,
20993     
20994     // id of frame..
20995     frameId: false,
20996     
20997     // private properties
20998     validationEvent : false,
20999     deferHeight: true,
21000     initialized : false,
21001     activated : false,
21002     
21003     onFocus : Roo.emptyFn,
21004     iframePad:3,
21005     hideMode:'offsets',
21006     
21007     
21008     tbContainer : false,
21009     
21010     toolbarContainer :function() {
21011         return this.wrap.select('.x-html-editor-tb',true).first();
21012     },
21013
21014     /**
21015      * Protected method that will not generally be called directly. It
21016      * is called when the editor creates its toolbar. Override this method if you need to
21017      * add custom toolbar buttons.
21018      * @param {HtmlEditor} editor
21019      */
21020     createToolbar : function(){
21021         
21022         Roo.log("create toolbars");
21023         
21024         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
21025         this.toolbars[0].render(this.toolbarContainer());
21026         
21027         return;
21028         
21029 //        if (!editor.toolbars || !editor.toolbars.length) {
21030 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
21031 //        }
21032 //        
21033 //        for (var i =0 ; i < editor.toolbars.length;i++) {
21034 //            editor.toolbars[i] = Roo.factory(
21035 //                    typeof(editor.toolbars[i]) == 'string' ?
21036 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
21037 //                Roo.bootstrap.HtmlEditor);
21038 //            editor.toolbars[i].init(editor);
21039 //        }
21040     },
21041
21042      
21043     // private
21044     onRender : function(ct, position)
21045     {
21046        // Roo.log("Call onRender: " + this.xtype);
21047         var _t = this;
21048         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
21049       
21050         this.wrap = this.inputEl().wrap({
21051             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
21052         });
21053         
21054         this.editorcore.onRender(ct, position);
21055          
21056         if (this.resizable) {
21057             this.resizeEl = new Roo.Resizable(this.wrap, {
21058                 pinned : true,
21059                 wrap: true,
21060                 dynamic : true,
21061                 minHeight : this.height,
21062                 height: this.height,
21063                 handles : this.resizable,
21064                 width: this.width,
21065                 listeners : {
21066                     resize : function(r, w, h) {
21067                         _t.onResize(w,h); // -something
21068                     }
21069                 }
21070             });
21071             
21072         }
21073         this.createToolbar(this);
21074        
21075         
21076         if(!this.width && this.resizable){
21077             this.setSize(this.wrap.getSize());
21078         }
21079         if (this.resizeEl) {
21080             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
21081             // should trigger onReize..
21082         }
21083         
21084     },
21085
21086     // private
21087     onResize : function(w, h)
21088     {
21089         Roo.log('resize: ' +w + ',' + h );
21090         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
21091         var ew = false;
21092         var eh = false;
21093         
21094         if(this.inputEl() ){
21095             if(typeof w == 'number'){
21096                 var aw = w - this.wrap.getFrameWidth('lr');
21097                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
21098                 ew = aw;
21099             }
21100             if(typeof h == 'number'){
21101                  var tbh = -11;  // fixme it needs to tool bar size!
21102                 for (var i =0; i < this.toolbars.length;i++) {
21103                     // fixme - ask toolbars for heights?
21104                     tbh += this.toolbars[i].el.getHeight();
21105                     //if (this.toolbars[i].footer) {
21106                     //    tbh += this.toolbars[i].footer.el.getHeight();
21107                     //}
21108                 }
21109               
21110                 
21111                 
21112                 
21113                 
21114                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
21115                 ah -= 5; // knock a few pixes off for look..
21116                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
21117                 var eh = ah;
21118             }
21119         }
21120         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
21121         this.editorcore.onResize(ew,eh);
21122         
21123     },
21124
21125     /**
21126      * Toggles the editor between standard and source edit mode.
21127      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21128      */
21129     toggleSourceEdit : function(sourceEditMode)
21130     {
21131         this.editorcore.toggleSourceEdit(sourceEditMode);
21132         
21133         if(this.editorcore.sourceEditMode){
21134             Roo.log('editor - showing textarea');
21135             
21136 //            Roo.log('in');
21137 //            Roo.log(this.syncValue());
21138             this.syncValue();
21139             this.inputEl().removeClass(['hide', 'x-hidden']);
21140             this.inputEl().dom.removeAttribute('tabIndex');
21141             this.inputEl().focus();
21142         }else{
21143             Roo.log('editor - hiding textarea');
21144 //            Roo.log('out')
21145 //            Roo.log(this.pushValue()); 
21146             this.pushValue();
21147             
21148             this.inputEl().addClass(['hide', 'x-hidden']);
21149             this.inputEl().dom.setAttribute('tabIndex', -1);
21150             //this.deferFocus();
21151         }
21152          
21153         if(this.resizable){
21154             this.setSize(this.wrap.getSize());
21155         }
21156         
21157         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
21158     },
21159  
21160     // private (for BoxComponent)
21161     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21162
21163     // private (for BoxComponent)
21164     getResizeEl : function(){
21165         return this.wrap;
21166     },
21167
21168     // private (for BoxComponent)
21169     getPositionEl : function(){
21170         return this.wrap;
21171     },
21172
21173     // private
21174     initEvents : function(){
21175         this.originalValue = this.getValue();
21176     },
21177
21178 //    /**
21179 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21180 //     * @method
21181 //     */
21182 //    markInvalid : Roo.emptyFn,
21183 //    /**
21184 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21185 //     * @method
21186 //     */
21187 //    clearInvalid : Roo.emptyFn,
21188
21189     setValue : function(v){
21190         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
21191         this.editorcore.pushValue();
21192     },
21193
21194      
21195     // private
21196     deferFocus : function(){
21197         this.focus.defer(10, this);
21198     },
21199
21200     // doc'ed in Field
21201     focus : function(){
21202         this.editorcore.focus();
21203         
21204     },
21205       
21206
21207     // private
21208     onDestroy : function(){
21209         
21210         
21211         
21212         if(this.rendered){
21213             
21214             for (var i =0; i < this.toolbars.length;i++) {
21215                 // fixme - ask toolbars for heights?
21216                 this.toolbars[i].onDestroy();
21217             }
21218             
21219             this.wrap.dom.innerHTML = '';
21220             this.wrap.remove();
21221         }
21222     },
21223
21224     // private
21225     onFirstFocus : function(){
21226         //Roo.log("onFirstFocus");
21227         this.editorcore.onFirstFocus();
21228          for (var i =0; i < this.toolbars.length;i++) {
21229             this.toolbars[i].onFirstFocus();
21230         }
21231         
21232     },
21233     
21234     // private
21235     syncValue : function()
21236     {   
21237         this.editorcore.syncValue();
21238     },
21239     
21240     pushValue : function()
21241     {   
21242         this.editorcore.pushValue();
21243     }
21244      
21245     
21246     // hide stuff that is not compatible
21247     /**
21248      * @event blur
21249      * @hide
21250      */
21251     /**
21252      * @event change
21253      * @hide
21254      */
21255     /**
21256      * @event focus
21257      * @hide
21258      */
21259     /**
21260      * @event specialkey
21261      * @hide
21262      */
21263     /**
21264      * @cfg {String} fieldClass @hide
21265      */
21266     /**
21267      * @cfg {String} focusClass @hide
21268      */
21269     /**
21270      * @cfg {String} autoCreate @hide
21271      */
21272     /**
21273      * @cfg {String} inputType @hide
21274      */
21275     /**
21276      * @cfg {String} invalidClass @hide
21277      */
21278     /**
21279      * @cfg {String} invalidText @hide
21280      */
21281     /**
21282      * @cfg {String} msgFx @hide
21283      */
21284     /**
21285      * @cfg {String} validateOnBlur @hide
21286      */
21287 });
21288  
21289     
21290    
21291    
21292    
21293       
21294 Roo.namespace('Roo.bootstrap.htmleditor');
21295 /**
21296  * @class Roo.bootstrap.HtmlEditorToolbar1
21297  * Basic Toolbar
21298  * 
21299  * Usage:
21300  *
21301  new Roo.bootstrap.HtmlEditor({
21302     ....
21303     toolbars : [
21304         new Roo.bootstrap.HtmlEditorToolbar1({
21305             disable : { fonts: 1 , format: 1, ..., ... , ...],
21306             btns : [ .... ]
21307         })
21308     }
21309      
21310  * 
21311  * @cfg {Object} disable List of elements to disable..
21312  * @cfg {Array} btns List of additional buttons.
21313  * 
21314  * 
21315  * NEEDS Extra CSS? 
21316  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
21317  */
21318  
21319 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
21320 {
21321     
21322     Roo.apply(this, config);
21323     
21324     // default disabled, based on 'good practice'..
21325     this.disable = this.disable || {};
21326     Roo.applyIf(this.disable, {
21327         fontSize : true,
21328         colors : true,
21329         specialElements : true
21330     });
21331     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
21332     
21333     this.editor = config.editor;
21334     this.editorcore = config.editor.editorcore;
21335     
21336     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
21337     
21338     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
21339     // dont call parent... till later.
21340 }
21341 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
21342      
21343     bar : true,
21344     
21345     editor : false,
21346     editorcore : false,
21347     
21348     
21349     formats : [
21350         "p" ,  
21351         "h1","h2","h3","h4","h5","h6", 
21352         "pre", "code", 
21353         "abbr", "acronym", "address", "cite", "samp", "var",
21354         'div','span'
21355     ],
21356     
21357     onRender : function(ct, position)
21358     {
21359        // Roo.log("Call onRender: " + this.xtype);
21360         
21361        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
21362        Roo.log(this.el);
21363        this.el.dom.style.marginBottom = '0';
21364        var _this = this;
21365        var editorcore = this.editorcore;
21366        var editor= this.editor;
21367        
21368        var children = [];
21369        var btn = function(id,cmd , toggle, handler){
21370        
21371             var  event = toggle ? 'toggle' : 'click';
21372        
21373             var a = {
21374                 size : 'sm',
21375                 xtype: 'Button',
21376                 xns: Roo.bootstrap,
21377                 glyphicon : id,
21378                 cmd : id || cmd,
21379                 enableToggle:toggle !== false,
21380                 //html : 'submit'
21381                 pressed : toggle ? false : null,
21382                 listeners : {}
21383             };
21384             a.listeners[toggle ? 'toggle' : 'click'] = function() {
21385                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
21386             };
21387             children.push(a);
21388             return a;
21389        }
21390         
21391         var style = {
21392                 xtype: 'Button',
21393                 size : 'sm',
21394                 xns: Roo.bootstrap,
21395                 glyphicon : 'font',
21396                 //html : 'submit'
21397                 menu : {
21398                     xtype: 'Menu',
21399                     xns: Roo.bootstrap,
21400                     items:  []
21401                 }
21402         };
21403         Roo.each(this.formats, function(f) {
21404             style.menu.items.push({
21405                 xtype :'MenuItem',
21406                 xns: Roo.bootstrap,
21407                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21408                 tagname : f,
21409                 listeners : {
21410                     click : function()
21411                     {
21412                         editorcore.insertTag(this.tagname);
21413                         editor.focus();
21414                     }
21415                 }
21416                 
21417             });
21418         });
21419          children.push(style);   
21420             
21421             
21422         btn('bold',false,true);
21423         btn('italic',false,true);
21424         btn('align-left', 'justifyleft',true);
21425         btn('align-center', 'justifycenter',true);
21426         btn('align-right' , 'justifyright',true);
21427         btn('link', false, false, function(btn) {
21428             //Roo.log("create link?");
21429             var url = prompt(this.createLinkText, this.defaultLinkValue);
21430             if(url && url != 'http:/'+'/'){
21431                 this.editorcore.relayCmd('createlink', url);
21432             }
21433         }),
21434         btn('list','insertunorderedlist',true);
21435         btn('pencil', false,true, function(btn){
21436                 Roo.log(this);
21437                 
21438                 this.toggleSourceEdit(btn.pressed);
21439         });
21440         /*
21441         var cog = {
21442                 xtype: 'Button',
21443                 size : 'sm',
21444                 xns: Roo.bootstrap,
21445                 glyphicon : 'cog',
21446                 //html : 'submit'
21447                 menu : {
21448                     xtype: 'Menu',
21449                     xns: Roo.bootstrap,
21450                     items:  []
21451                 }
21452         };
21453         
21454         cog.menu.items.push({
21455             xtype :'MenuItem',
21456             xns: Roo.bootstrap,
21457             html : Clean styles,
21458             tagname : f,
21459             listeners : {
21460                 click : function()
21461                 {
21462                     editorcore.insertTag(this.tagname);
21463                     editor.focus();
21464                 }
21465             }
21466             
21467         });
21468        */
21469         
21470          
21471        this.xtype = 'NavSimplebar';
21472         
21473         for(var i=0;i< children.length;i++) {
21474             
21475             this.buttons.add(this.addxtypeChild(children[i]));
21476             
21477         }
21478         
21479         editor.on('editorevent', this.updateToolbar, this);
21480     },
21481     onBtnClick : function(id)
21482     {
21483        this.editorcore.relayCmd(id);
21484        this.editorcore.focus();
21485     },
21486     
21487     /**
21488      * Protected method that will not generally be called directly. It triggers
21489      * a toolbar update by reading the markup state of the current selection in the editor.
21490      */
21491     updateToolbar: function(){
21492
21493         if(!this.editorcore.activated){
21494             this.editor.onFirstFocus(); // is this neeed?
21495             return;
21496         }
21497
21498         var btns = this.buttons; 
21499         var doc = this.editorcore.doc;
21500         btns.get('bold').setActive(doc.queryCommandState('bold'));
21501         btns.get('italic').setActive(doc.queryCommandState('italic'));
21502         //btns.get('underline').setActive(doc.queryCommandState('underline'));
21503         
21504         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21505         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21506         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21507         
21508         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21509         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21510          /*
21511         
21512         var ans = this.editorcore.getAllAncestors();
21513         if (this.formatCombo) {
21514             
21515             
21516             var store = this.formatCombo.store;
21517             this.formatCombo.setValue("");
21518             for (var i =0; i < ans.length;i++) {
21519                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21520                     // select it..
21521                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21522                     break;
21523                 }
21524             }
21525         }
21526         
21527         
21528         
21529         // hides menus... - so this cant be on a menu...
21530         Roo.bootstrap.MenuMgr.hideAll();
21531         */
21532         Roo.bootstrap.MenuMgr.hideAll();
21533         //this.editorsyncValue();
21534     },
21535     onFirstFocus: function() {
21536         this.buttons.each(function(item){
21537            item.enable();
21538         });
21539     },
21540     toggleSourceEdit : function(sourceEditMode){
21541         
21542           
21543         if(sourceEditMode){
21544             Roo.log("disabling buttons");
21545            this.buttons.each( function(item){
21546                 if(item.cmd != 'pencil'){
21547                     item.disable();
21548                 }
21549             });
21550           
21551         }else{
21552             Roo.log("enabling buttons");
21553             if(this.editorcore.initialized){
21554                 this.buttons.each( function(item){
21555                     item.enable();
21556                 });
21557             }
21558             
21559         }
21560         Roo.log("calling toggole on editor");
21561         // tell the editor that it's been pressed..
21562         this.editor.toggleSourceEdit(sourceEditMode);
21563        
21564     }
21565 });
21566
21567
21568
21569
21570
21571 /**
21572  * @class Roo.bootstrap.Table.AbstractSelectionModel
21573  * @extends Roo.util.Observable
21574  * Abstract base class for grid SelectionModels.  It provides the interface that should be
21575  * implemented by descendant classes.  This class should not be directly instantiated.
21576  * @constructor
21577  */
21578 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21579     this.locked = false;
21580     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21581 };
21582
21583
21584 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
21585     /** @ignore Called by the grid automatically. Do not call directly. */
21586     init : function(grid){
21587         this.grid = grid;
21588         this.initEvents();
21589     },
21590
21591     /**
21592      * Locks the selections.
21593      */
21594     lock : function(){
21595         this.locked = true;
21596     },
21597
21598     /**
21599      * Unlocks the selections.
21600      */
21601     unlock : function(){
21602         this.locked = false;
21603     },
21604
21605     /**
21606      * Returns true if the selections are locked.
21607      * @return {Boolean}
21608      */
21609     isLocked : function(){
21610         return this.locked;
21611     }
21612 });
21613 /**
21614  * @extends Roo.bootstrap.Table.AbstractSelectionModel
21615  * @class Roo.bootstrap.Table.RowSelectionModel
21616  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21617  * It supports multiple selections and keyboard selection/navigation. 
21618  * @constructor
21619  * @param {Object} config
21620  */
21621
21622 Roo.bootstrap.Table.RowSelectionModel = function(config){
21623     Roo.apply(this, config);
21624     this.selections = new Roo.util.MixedCollection(false, function(o){
21625         return o.id;
21626     });
21627
21628     this.last = false;
21629     this.lastActive = false;
21630
21631     this.addEvents({
21632         /**
21633              * @event selectionchange
21634              * Fires when the selection changes
21635              * @param {SelectionModel} this
21636              */
21637             "selectionchange" : true,
21638         /**
21639              * @event afterselectionchange
21640              * Fires after the selection changes (eg. by key press or clicking)
21641              * @param {SelectionModel} this
21642              */
21643             "afterselectionchange" : true,
21644         /**
21645              * @event beforerowselect
21646              * Fires when a row is selected being selected, return false to cancel.
21647              * @param {SelectionModel} this
21648              * @param {Number} rowIndex The selected index
21649              * @param {Boolean} keepExisting False if other selections will be cleared
21650              */
21651             "beforerowselect" : true,
21652         /**
21653              * @event rowselect
21654              * Fires when a row is selected.
21655              * @param {SelectionModel} this
21656              * @param {Number} rowIndex The selected index
21657              * @param {Roo.data.Record} r The record
21658              */
21659             "rowselect" : true,
21660         /**
21661              * @event rowdeselect
21662              * Fires when a row is deselected.
21663              * @param {SelectionModel} this
21664              * @param {Number} rowIndex The selected index
21665              */
21666         "rowdeselect" : true
21667     });
21668     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21669     this.locked = false;
21670 };
21671
21672 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
21673     /**
21674      * @cfg {Boolean} singleSelect
21675      * True to allow selection of only one row at a time (defaults to false)
21676      */
21677     singleSelect : false,
21678
21679     // private
21680     initEvents : function(){
21681
21682         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21683             this.grid.on("mousedown", this.handleMouseDown, this);
21684         }else{ // allow click to work like normal
21685             this.grid.on("rowclick", this.handleDragableRowClick, this);
21686         }
21687
21688         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21689             "up" : function(e){
21690                 if(!e.shiftKey){
21691                     this.selectPrevious(e.shiftKey);
21692                 }else if(this.last !== false && this.lastActive !== false){
21693                     var last = this.last;
21694                     this.selectRange(this.last,  this.lastActive-1);
21695                     this.grid.getView().focusRow(this.lastActive);
21696                     if(last !== false){
21697                         this.last = last;
21698                     }
21699                 }else{
21700                     this.selectFirstRow();
21701                 }
21702                 this.fireEvent("afterselectionchange", this);
21703             },
21704             "down" : function(e){
21705                 if(!e.shiftKey){
21706                     this.selectNext(e.shiftKey);
21707                 }else if(this.last !== false && this.lastActive !== false){
21708                     var last = this.last;
21709                     this.selectRange(this.last,  this.lastActive+1);
21710                     this.grid.getView().focusRow(this.lastActive);
21711                     if(last !== false){
21712                         this.last = last;
21713                     }
21714                 }else{
21715                     this.selectFirstRow();
21716                 }
21717                 this.fireEvent("afterselectionchange", this);
21718             },
21719             scope: this
21720         });
21721
21722         var view = this.grid.view;
21723         view.on("refresh", this.onRefresh, this);
21724         view.on("rowupdated", this.onRowUpdated, this);
21725         view.on("rowremoved", this.onRemove, this);
21726     },
21727
21728     // private
21729     onRefresh : function(){
21730         var ds = this.grid.dataSource, i, v = this.grid.view;
21731         var s = this.selections;
21732         s.each(function(r){
21733             if((i = ds.indexOfId(r.id)) != -1){
21734                 v.onRowSelect(i);
21735             }else{
21736                 s.remove(r);
21737             }
21738         });
21739     },
21740
21741     // private
21742     onRemove : function(v, index, r){
21743         this.selections.remove(r);
21744     },
21745
21746     // private
21747     onRowUpdated : function(v, index, r){
21748         if(this.isSelected(r)){
21749             v.onRowSelect(index);
21750         }
21751     },
21752
21753     /**
21754      * Select records.
21755      * @param {Array} records The records to select
21756      * @param {Boolean} keepExisting (optional) True to keep existing selections
21757      */
21758     selectRecords : function(records, keepExisting){
21759         if(!keepExisting){
21760             this.clearSelections();
21761         }
21762         var ds = this.grid.dataSource;
21763         for(var i = 0, len = records.length; i < len; i++){
21764             this.selectRow(ds.indexOf(records[i]), true);
21765         }
21766     },
21767
21768     /**
21769      * Gets the number of selected rows.
21770      * @return {Number}
21771      */
21772     getCount : function(){
21773         return this.selections.length;
21774     },
21775
21776     /**
21777      * Selects the first row in the grid.
21778      */
21779     selectFirstRow : function(){
21780         this.selectRow(0);
21781     },
21782
21783     /**
21784      * Select the last row.
21785      * @param {Boolean} keepExisting (optional) True to keep existing selections
21786      */
21787     selectLastRow : function(keepExisting){
21788         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21789     },
21790
21791     /**
21792      * Selects the row immediately following the last selected row.
21793      * @param {Boolean} keepExisting (optional) True to keep existing selections
21794      */
21795     selectNext : function(keepExisting){
21796         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21797             this.selectRow(this.last+1, keepExisting);
21798             this.grid.getView().focusRow(this.last);
21799         }
21800     },
21801
21802     /**
21803      * Selects the row that precedes the last selected row.
21804      * @param {Boolean} keepExisting (optional) True to keep existing selections
21805      */
21806     selectPrevious : function(keepExisting){
21807         if(this.last){
21808             this.selectRow(this.last-1, keepExisting);
21809             this.grid.getView().focusRow(this.last);
21810         }
21811     },
21812
21813     /**
21814      * Returns the selected records
21815      * @return {Array} Array of selected records
21816      */
21817     getSelections : function(){
21818         return [].concat(this.selections.items);
21819     },
21820
21821     /**
21822      * Returns the first selected record.
21823      * @return {Record}
21824      */
21825     getSelected : function(){
21826         return this.selections.itemAt(0);
21827     },
21828
21829
21830     /**
21831      * Clears all selections.
21832      */
21833     clearSelections : function(fast){
21834         if(this.locked) {
21835             return;
21836         }
21837         if(fast !== true){
21838             var ds = this.grid.dataSource;
21839             var s = this.selections;
21840             s.each(function(r){
21841                 this.deselectRow(ds.indexOfId(r.id));
21842             }, this);
21843             s.clear();
21844         }else{
21845             this.selections.clear();
21846         }
21847         this.last = false;
21848     },
21849
21850
21851     /**
21852      * Selects all rows.
21853      */
21854     selectAll : function(){
21855         if(this.locked) {
21856             return;
21857         }
21858         this.selections.clear();
21859         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
21860             this.selectRow(i, true);
21861         }
21862     },
21863
21864     /**
21865      * Returns True if there is a selection.
21866      * @return {Boolean}
21867      */
21868     hasSelection : function(){
21869         return this.selections.length > 0;
21870     },
21871
21872     /**
21873      * Returns True if the specified row is selected.
21874      * @param {Number/Record} record The record or index of the record to check
21875      * @return {Boolean}
21876      */
21877     isSelected : function(index){
21878         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
21879         return (r && this.selections.key(r.id) ? true : false);
21880     },
21881
21882     /**
21883      * Returns True if the specified record id is selected.
21884      * @param {String} id The id of record to check
21885      * @return {Boolean}
21886      */
21887     isIdSelected : function(id){
21888         return (this.selections.key(id) ? true : false);
21889     },
21890
21891     // private
21892     handleMouseDown : function(e, t){
21893         var view = this.grid.getView(), rowIndex;
21894         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
21895             return;
21896         };
21897         if(e.shiftKey && this.last !== false){
21898             var last = this.last;
21899             this.selectRange(last, rowIndex, e.ctrlKey);
21900             this.last = last; // reset the last
21901             view.focusRow(rowIndex);
21902         }else{
21903             var isSelected = this.isSelected(rowIndex);
21904             if(e.button !== 0 && isSelected){
21905                 view.focusRow(rowIndex);
21906             }else if(e.ctrlKey && isSelected){
21907                 this.deselectRow(rowIndex);
21908             }else if(!isSelected){
21909                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
21910                 view.focusRow(rowIndex);
21911             }
21912         }
21913         this.fireEvent("afterselectionchange", this);
21914     },
21915     // private
21916     handleDragableRowClick :  function(grid, rowIndex, e) 
21917     {
21918         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
21919             this.selectRow(rowIndex, false);
21920             grid.view.focusRow(rowIndex);
21921              this.fireEvent("afterselectionchange", this);
21922         }
21923     },
21924     
21925     /**
21926      * Selects multiple rows.
21927      * @param {Array} rows Array of the indexes of the row to select
21928      * @param {Boolean} keepExisting (optional) True to keep existing selections
21929      */
21930     selectRows : function(rows, keepExisting){
21931         if(!keepExisting){
21932             this.clearSelections();
21933         }
21934         for(var i = 0, len = rows.length; i < len; i++){
21935             this.selectRow(rows[i], true);
21936         }
21937     },
21938
21939     /**
21940      * Selects a range of rows. All rows in between startRow and endRow are also selected.
21941      * @param {Number} startRow The index of the first row in the range
21942      * @param {Number} endRow The index of the last row in the range
21943      * @param {Boolean} keepExisting (optional) True to retain existing selections
21944      */
21945     selectRange : function(startRow, endRow, keepExisting){
21946         if(this.locked) {
21947             return;
21948         }
21949         if(!keepExisting){
21950             this.clearSelections();
21951         }
21952         if(startRow <= endRow){
21953             for(var i = startRow; i <= endRow; i++){
21954                 this.selectRow(i, true);
21955             }
21956         }else{
21957             for(var i = startRow; i >= endRow; i--){
21958                 this.selectRow(i, true);
21959             }
21960         }
21961     },
21962
21963     /**
21964      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
21965      * @param {Number} startRow The index of the first row in the range
21966      * @param {Number} endRow The index of the last row in the range
21967      */
21968     deselectRange : function(startRow, endRow, preventViewNotify){
21969         if(this.locked) {
21970             return;
21971         }
21972         for(var i = startRow; i <= endRow; i++){
21973             this.deselectRow(i, preventViewNotify);
21974         }
21975     },
21976
21977     /**
21978      * Selects a row.
21979      * @param {Number} row The index of the row to select
21980      * @param {Boolean} keepExisting (optional) True to keep existing selections
21981      */
21982     selectRow : function(index, keepExisting, preventViewNotify){
21983         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
21984             return;
21985         }
21986         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
21987             if(!keepExisting || this.singleSelect){
21988                 this.clearSelections();
21989             }
21990             var r = this.grid.dataSource.getAt(index);
21991             this.selections.add(r);
21992             this.last = this.lastActive = index;
21993             if(!preventViewNotify){
21994                 this.grid.getView().onRowSelect(index);
21995             }
21996             this.fireEvent("rowselect", this, index, r);
21997             this.fireEvent("selectionchange", this);
21998         }
21999     },
22000
22001     /**
22002      * Deselects a row.
22003      * @param {Number} row The index of the row to deselect
22004      */
22005     deselectRow : function(index, preventViewNotify){
22006         if(this.locked) {
22007             return;
22008         }
22009         if(this.last == index){
22010             this.last = false;
22011         }
22012         if(this.lastActive == index){
22013             this.lastActive = false;
22014         }
22015         var r = this.grid.dataSource.getAt(index);
22016         this.selections.remove(r);
22017         if(!preventViewNotify){
22018             this.grid.getView().onRowDeselect(index);
22019         }
22020         this.fireEvent("rowdeselect", this, index);
22021         this.fireEvent("selectionchange", this);
22022     },
22023
22024     // private
22025     restoreLast : function(){
22026         if(this._last){
22027             this.last = this._last;
22028         }
22029     },
22030
22031     // private
22032     acceptsNav : function(row, col, cm){
22033         return !cm.isHidden(col) && cm.isCellEditable(col, row);
22034     },
22035
22036     // private
22037     onEditorKey : function(field, e){
22038         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22039         if(k == e.TAB){
22040             e.stopEvent();
22041             ed.completeEdit();
22042             if(e.shiftKey){
22043                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22044             }else{
22045                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22046             }
22047         }else if(k == e.ENTER && !e.ctrlKey){
22048             e.stopEvent();
22049             ed.completeEdit();
22050             if(e.shiftKey){
22051                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22052             }else{
22053                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22054             }
22055         }else if(k == e.ESC){
22056             ed.cancelEdit();
22057         }
22058         if(newCell){
22059             g.startEditing(newCell[0], newCell[1]);
22060         }
22061     }
22062 });/*
22063  * Based on:
22064  * Ext JS Library 1.1.1
22065  * Copyright(c) 2006-2007, Ext JS, LLC.
22066  *
22067  * Originally Released Under LGPL - original licence link has changed is not relivant.
22068  *
22069  * Fork - LGPL
22070  * <script type="text/javascript">
22071  */
22072  
22073 /**
22074  * @class Roo.bootstrap.PagingToolbar
22075  * @extends Roo.bootstrap.NavSimplebar
22076  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
22077  * @constructor
22078  * Create a new PagingToolbar
22079  * @param {Object} config The config object
22080  * @param {Roo.data.Store} store
22081  */
22082 Roo.bootstrap.PagingToolbar = function(config)
22083 {
22084     // old args format still supported... - xtype is prefered..
22085         // created from xtype...
22086     
22087     this.ds = config.dataSource;
22088     
22089     if (config.store && !this.ds) {
22090         this.store= Roo.factory(config.store, Roo.data);
22091         this.ds = this.store;
22092         this.ds.xmodule = this.xmodule || false;
22093     }
22094     
22095     this.toolbarItems = [];
22096     if (config.items) {
22097         this.toolbarItems = config.items;
22098     }
22099     
22100     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
22101     
22102     this.cursor = 0;
22103     
22104     if (this.ds) { 
22105         this.bind(this.ds);
22106     }
22107     
22108     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
22109     
22110 };
22111
22112 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
22113     /**
22114      * @cfg {Roo.data.Store} dataSource
22115      * The underlying data store providing the paged data
22116      */
22117     /**
22118      * @cfg {String/HTMLElement/Element} container
22119      * container The id or element that will contain the toolbar
22120      */
22121     /**
22122      * @cfg {Boolean} displayInfo
22123      * True to display the displayMsg (defaults to false)
22124      */
22125     /**
22126      * @cfg {Number} pageSize
22127      * The number of records to display per page (defaults to 20)
22128      */
22129     pageSize: 20,
22130     /**
22131      * @cfg {String} displayMsg
22132      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
22133      */
22134     displayMsg : 'Displaying {0} - {1} of {2}',
22135     /**
22136      * @cfg {String} emptyMsg
22137      * The message to display when no records are found (defaults to "No data to display")
22138      */
22139     emptyMsg : 'No data to display',
22140     /**
22141      * Customizable piece of the default paging text (defaults to "Page")
22142      * @type String
22143      */
22144     beforePageText : "Page",
22145     /**
22146      * Customizable piece of the default paging text (defaults to "of %0")
22147      * @type String
22148      */
22149     afterPageText : "of {0}",
22150     /**
22151      * Customizable piece of the default paging text (defaults to "First Page")
22152      * @type String
22153      */
22154     firstText : "First Page",
22155     /**
22156      * Customizable piece of the default paging text (defaults to "Previous Page")
22157      * @type String
22158      */
22159     prevText : "Previous Page",
22160     /**
22161      * Customizable piece of the default paging text (defaults to "Next Page")
22162      * @type String
22163      */
22164     nextText : "Next Page",
22165     /**
22166      * Customizable piece of the default paging text (defaults to "Last Page")
22167      * @type String
22168      */
22169     lastText : "Last Page",
22170     /**
22171      * Customizable piece of the default paging text (defaults to "Refresh")
22172      * @type String
22173      */
22174     refreshText : "Refresh",
22175
22176     buttons : false,
22177     // private
22178     onRender : function(ct, position) 
22179     {
22180         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
22181         this.navgroup.parentId = this.id;
22182         this.navgroup.onRender(this.el, null);
22183         // add the buttons to the navgroup
22184         
22185         if(this.displayInfo){
22186             Roo.log(this.el.select('ul.navbar-nav',true).first());
22187             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
22188             this.displayEl = this.el.select('.x-paging-info', true).first();
22189 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
22190 //            this.displayEl = navel.el.select('span',true).first();
22191         }
22192         
22193         var _this = this;
22194         
22195         if(this.buttons){
22196             Roo.each(_this.buttons, function(e){ // this might need to use render????
22197                Roo.factory(e).onRender(_this.el, null);
22198             });
22199         }
22200             
22201         Roo.each(_this.toolbarItems, function(e) {
22202             _this.navgroup.addItem(e);
22203         });
22204         
22205         
22206         this.first = this.navgroup.addItem({
22207             tooltip: this.firstText,
22208             cls: "prev",
22209             icon : 'fa fa-backward',
22210             disabled: true,
22211             preventDefault: true,
22212             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
22213         });
22214         
22215         this.prev =  this.navgroup.addItem({
22216             tooltip: this.prevText,
22217             cls: "prev",
22218             icon : 'fa fa-step-backward',
22219             disabled: true,
22220             preventDefault: true,
22221             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
22222         });
22223     //this.addSeparator();
22224         
22225         
22226         var field = this.navgroup.addItem( {
22227             tagtype : 'span',
22228             cls : 'x-paging-position',
22229             
22230             html : this.beforePageText  +
22231                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22232                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
22233          } ); //?? escaped?
22234         
22235         this.field = field.el.select('input', true).first();
22236         this.field.on("keydown", this.onPagingKeydown, this);
22237         this.field.on("focus", function(){this.dom.select();});
22238     
22239     
22240         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
22241         //this.field.setHeight(18);
22242         //this.addSeparator();
22243         this.next = this.navgroup.addItem({
22244             tooltip: this.nextText,
22245             cls: "next",
22246             html : ' <i class="fa fa-step-forward">',
22247             disabled: true,
22248             preventDefault: true,
22249             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
22250         });
22251         this.last = this.navgroup.addItem({
22252             tooltip: this.lastText,
22253             icon : 'fa fa-forward',
22254             cls: "next",
22255             disabled: true,
22256             preventDefault: true,
22257             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
22258         });
22259     //this.addSeparator();
22260         this.loading = this.navgroup.addItem({
22261             tooltip: this.refreshText,
22262             icon: 'fa fa-refresh',
22263             preventDefault: true,
22264             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
22265         });
22266         
22267     },
22268
22269     // private
22270     updateInfo : function(){
22271         if(this.displayEl){
22272             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
22273             var msg = count == 0 ?
22274                 this.emptyMsg :
22275                 String.format(
22276                     this.displayMsg,
22277                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
22278                 );
22279             this.displayEl.update(msg);
22280         }
22281     },
22282
22283     // private
22284     onLoad : function(ds, r, o){
22285        this.cursor = o.params ? o.params.start : 0;
22286        var d = this.getPageData(),
22287             ap = d.activePage,
22288             ps = d.pages;
22289         
22290        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
22291        this.field.dom.value = ap;
22292        this.first.setDisabled(ap == 1);
22293        this.prev.setDisabled(ap == 1);
22294        this.next.setDisabled(ap == ps);
22295        this.last.setDisabled(ap == ps);
22296        this.loading.enable();
22297        this.updateInfo();
22298     },
22299
22300     // private
22301     getPageData : function(){
22302         var total = this.ds.getTotalCount();
22303         return {
22304             total : total,
22305             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
22306             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
22307         };
22308     },
22309
22310     // private
22311     onLoadError : function(){
22312         this.loading.enable();
22313     },
22314
22315     // private
22316     onPagingKeydown : function(e){
22317         var k = e.getKey();
22318         var d = this.getPageData();
22319         if(k == e.RETURN){
22320             var v = this.field.dom.value, pageNum;
22321             if(!v || isNaN(pageNum = parseInt(v, 10))){
22322                 this.field.dom.value = d.activePage;
22323                 return;
22324             }
22325             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
22326             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22327             e.stopEvent();
22328         }
22329         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))
22330         {
22331           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
22332           this.field.dom.value = pageNum;
22333           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
22334           e.stopEvent();
22335         }
22336         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22337         {
22338           var v = this.field.dom.value, pageNum; 
22339           var increment = (e.shiftKey) ? 10 : 1;
22340           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
22341                 increment *= -1;
22342           }
22343           if(!v || isNaN(pageNum = parseInt(v, 10))) {
22344             this.field.dom.value = d.activePage;
22345             return;
22346           }
22347           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
22348           {
22349             this.field.dom.value = parseInt(v, 10) + increment;
22350             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
22351             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22352           }
22353           e.stopEvent();
22354         }
22355     },
22356
22357     // private
22358     beforeLoad : function(){
22359         if(this.loading){
22360             this.loading.disable();
22361         }
22362     },
22363
22364     // private
22365     onClick : function(which){
22366         
22367         var ds = this.ds;
22368         if (!ds) {
22369             return;
22370         }
22371         
22372         switch(which){
22373             case "first":
22374                 ds.load({params:{start: 0, limit: this.pageSize}});
22375             break;
22376             case "prev":
22377                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
22378             break;
22379             case "next":
22380                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
22381             break;
22382             case "last":
22383                 var total = ds.getTotalCount();
22384                 var extra = total % this.pageSize;
22385                 var lastStart = extra ? (total - extra) : total-this.pageSize;
22386                 ds.load({params:{start: lastStart, limit: this.pageSize}});
22387             break;
22388             case "refresh":
22389                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22390             break;
22391         }
22392     },
22393
22394     /**
22395      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22396      * @param {Roo.data.Store} store The data store to unbind
22397      */
22398     unbind : function(ds){
22399         ds.un("beforeload", this.beforeLoad, this);
22400         ds.un("load", this.onLoad, this);
22401         ds.un("loadexception", this.onLoadError, this);
22402         ds.un("remove", this.updateInfo, this);
22403         ds.un("add", this.updateInfo, this);
22404         this.ds = undefined;
22405     },
22406
22407     /**
22408      * Binds the paging toolbar to the specified {@link Roo.data.Store}
22409      * @param {Roo.data.Store} store The data store to bind
22410      */
22411     bind : function(ds){
22412         ds.on("beforeload", this.beforeLoad, this);
22413         ds.on("load", this.onLoad, this);
22414         ds.on("loadexception", this.onLoadError, this);
22415         ds.on("remove", this.updateInfo, this);
22416         ds.on("add", this.updateInfo, this);
22417         this.ds = ds;
22418     }
22419 });/*
22420  * - LGPL
22421  *
22422  * element
22423  * 
22424  */
22425
22426 /**
22427  * @class Roo.bootstrap.MessageBar
22428  * @extends Roo.bootstrap.Component
22429  * Bootstrap MessageBar class
22430  * @cfg {String} html contents of the MessageBar
22431  * @cfg {String} weight (info | success | warning | danger) default info
22432  * @cfg {String} beforeClass insert the bar before the given class
22433  * @cfg {Boolean} closable (true | false) default false
22434  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22435  * 
22436  * @constructor
22437  * Create a new Element
22438  * @param {Object} config The config object
22439  */
22440
22441 Roo.bootstrap.MessageBar = function(config){
22442     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22443 };
22444
22445 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
22446     
22447     html: '',
22448     weight: 'info',
22449     closable: false,
22450     fixed: false,
22451     beforeClass: 'bootstrap-sticky-wrap',
22452     
22453     getAutoCreate : function(){
22454         
22455         var cfg = {
22456             tag: 'div',
22457             cls: 'alert alert-dismissable alert-' + this.weight,
22458             cn: [
22459                 {
22460                     tag: 'span',
22461                     cls: 'message',
22462                     html: this.html || ''
22463                 }
22464             ]
22465         };
22466         
22467         if(this.fixed){
22468             cfg.cls += ' alert-messages-fixed';
22469         }
22470         
22471         if(this.closable){
22472             cfg.cn.push({
22473                 tag: 'button',
22474                 cls: 'close',
22475                 html: 'x'
22476             });
22477         }
22478         
22479         return cfg;
22480     },
22481     
22482     onRender : function(ct, position)
22483     {
22484         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22485         
22486         if(!this.el){
22487             var cfg = Roo.apply({},  this.getAutoCreate());
22488             cfg.id = Roo.id();
22489             
22490             if (this.cls) {
22491                 cfg.cls += ' ' + this.cls;
22492             }
22493             if (this.style) {
22494                 cfg.style = this.style;
22495             }
22496             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22497             
22498             this.el.setVisibilityMode(Roo.Element.DISPLAY);
22499         }
22500         
22501         this.el.select('>button.close').on('click', this.hide, this);
22502         
22503     },
22504     
22505     show : function()
22506     {
22507         if (!this.rendered) {
22508             this.render();
22509         }
22510         
22511         this.el.show();
22512         
22513         this.fireEvent('show', this);
22514         
22515     },
22516     
22517     hide : function()
22518     {
22519         if (!this.rendered) {
22520             this.render();
22521         }
22522         
22523         this.el.hide();
22524         
22525         this.fireEvent('hide', this);
22526     },
22527     
22528     update : function()
22529     {
22530 //        var e = this.el.dom.firstChild;
22531 //        
22532 //        if(this.closable){
22533 //            e = e.nextSibling;
22534 //        }
22535 //        
22536 //        e.data = this.html || '';
22537
22538         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22539     }
22540    
22541 });
22542
22543  
22544
22545      /*
22546  * - LGPL
22547  *
22548  * Graph
22549  * 
22550  */
22551
22552
22553 /**
22554  * @class Roo.bootstrap.Graph
22555  * @extends Roo.bootstrap.Component
22556  * Bootstrap Graph class
22557 > Prameters
22558  -sm {number} sm 4
22559  -md {number} md 5
22560  @cfg {String} graphtype  bar | vbar | pie
22561  @cfg {number} g_x coodinator | centre x (pie)
22562  @cfg {number} g_y coodinator | centre y (pie)
22563  @cfg {number} g_r radius (pie)
22564  @cfg {number} g_height height of the chart (respected by all elements in the set)
22565  @cfg {number} g_width width of the chart (respected by all elements in the set)
22566  @cfg {Object} title The title of the chart
22567     
22568  -{Array}  values
22569  -opts (object) options for the chart 
22570      o {
22571      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22572      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22573      o vgutter (number)
22574      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.
22575      o stacked (boolean) whether or not to tread values as in a stacked bar chart
22576      o to
22577      o stretch (boolean)
22578      o }
22579  -opts (object) options for the pie
22580      o{
22581      o cut
22582      o startAngle (number)
22583      o endAngle (number)
22584      } 
22585  *
22586  * @constructor
22587  * Create a new Input
22588  * @param {Object} config The config object
22589  */
22590
22591 Roo.bootstrap.Graph = function(config){
22592     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22593     
22594     this.addEvents({
22595         // img events
22596         /**
22597          * @event click
22598          * The img click event for the img.
22599          * @param {Roo.EventObject} e
22600          */
22601         "click" : true
22602     });
22603 };
22604
22605 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
22606     
22607     sm: 4,
22608     md: 5,
22609     graphtype: 'bar',
22610     g_height: 250,
22611     g_width: 400,
22612     g_x: 50,
22613     g_y: 50,
22614     g_r: 30,
22615     opts:{
22616         //g_colors: this.colors,
22617         g_type: 'soft',
22618         g_gutter: '20%'
22619
22620     },
22621     title : false,
22622
22623     getAutoCreate : function(){
22624         
22625         var cfg = {
22626             tag: 'div',
22627             html : null
22628         };
22629         
22630         
22631         return  cfg;
22632     },
22633
22634     onRender : function(ct,position){
22635         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22636         this.raphael = Raphael(this.el.dom);
22637         
22638                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22639                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22640                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22641                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22642                 /*
22643                 r.text(160, 10, "Single Series Chart").attr(txtattr);
22644                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22645                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22646                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22647                 
22648                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22649                 r.barchart(330, 10, 300, 220, data1);
22650                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22651                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22652                 */
22653                 
22654                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22655                 // r.barchart(30, 30, 560, 250,  xdata, {
22656                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22657                 //     axis : "0 0 1 1",
22658                 //     axisxlabels :  xdata
22659                 //     //yvalues : cols,
22660                    
22661                 // });
22662 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22663 //        
22664 //        this.load(null,xdata,{
22665 //                axis : "0 0 1 1",
22666 //                axisxlabels :  xdata
22667 //                });
22668
22669     },
22670
22671     load : function(graphtype,xdata,opts){
22672         this.raphael.clear();
22673         if(!graphtype) {
22674             graphtype = this.graphtype;
22675         }
22676         if(!opts){
22677             opts = this.opts;
22678         }
22679         var r = this.raphael,
22680             fin = function () {
22681                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22682             },
22683             fout = function () {
22684                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22685             },
22686             pfin = function() {
22687                 this.sector.stop();
22688                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22689
22690                 if (this.label) {
22691                     this.label[0].stop();
22692                     this.label[0].attr({ r: 7.5 });
22693                     this.label[1].attr({ "font-weight": 800 });
22694                 }
22695             },
22696             pfout = function() {
22697                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22698
22699                 if (this.label) {
22700                     this.label[0].animate({ r: 5 }, 500, "bounce");
22701                     this.label[1].attr({ "font-weight": 400 });
22702                 }
22703             };
22704
22705         switch(graphtype){
22706             case 'bar':
22707                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22708                 break;
22709             case 'hbar':
22710                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22711                 break;
22712             case 'pie':
22713 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
22714 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22715 //            
22716                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22717                 
22718                 break;
22719
22720         }
22721         
22722         if(this.title){
22723             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22724         }
22725         
22726     },
22727     
22728     setTitle: function(o)
22729     {
22730         this.title = o;
22731     },
22732     
22733     initEvents: function() {
22734         
22735         if(!this.href){
22736             this.el.on('click', this.onClick, this);
22737         }
22738     },
22739     
22740     onClick : function(e)
22741     {
22742         Roo.log('img onclick');
22743         this.fireEvent('click', this, e);
22744     }
22745    
22746 });
22747
22748  
22749 /*
22750  * - LGPL
22751  *
22752  * numberBox
22753  * 
22754  */
22755 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22756
22757 /**
22758  * @class Roo.bootstrap.dash.NumberBox
22759  * @extends Roo.bootstrap.Component
22760  * Bootstrap NumberBox class
22761  * @cfg {String} headline Box headline
22762  * @cfg {String} content Box content
22763  * @cfg {String} icon Box icon
22764  * @cfg {String} footer Footer text
22765  * @cfg {String} fhref Footer href
22766  * 
22767  * @constructor
22768  * Create a new NumberBox
22769  * @param {Object} config The config object
22770  */
22771
22772
22773 Roo.bootstrap.dash.NumberBox = function(config){
22774     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22775     
22776 };
22777
22778 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
22779     
22780     headline : '',
22781     content : '',
22782     icon : '',
22783     footer : '',
22784     fhref : '',
22785     ficon : '',
22786     
22787     getAutoCreate : function(){
22788         
22789         var cfg = {
22790             tag : 'div',
22791             cls : 'small-box ',
22792             cn : [
22793                 {
22794                     tag : 'div',
22795                     cls : 'inner',
22796                     cn :[
22797                         {
22798                             tag : 'h3',
22799                             cls : 'roo-headline',
22800                             html : this.headline
22801                         },
22802                         {
22803                             tag : 'p',
22804                             cls : 'roo-content',
22805                             html : this.content
22806                         }
22807                     ]
22808                 }
22809             ]
22810         };
22811         
22812         if(this.icon){
22813             cfg.cn.push({
22814                 tag : 'div',
22815                 cls : 'icon',
22816                 cn :[
22817                     {
22818                         tag : 'i',
22819                         cls : 'ion ' + this.icon
22820                     }
22821                 ]
22822             });
22823         }
22824         
22825         if(this.footer){
22826             var footer = {
22827                 tag : 'a',
22828                 cls : 'small-box-footer',
22829                 href : this.fhref || '#',
22830                 html : this.footer
22831             };
22832             
22833             cfg.cn.push(footer);
22834             
22835         }
22836         
22837         return  cfg;
22838     },
22839
22840     onRender : function(ct,position){
22841         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
22842
22843
22844        
22845                 
22846     },
22847
22848     setHeadline: function (value)
22849     {
22850         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
22851     },
22852     
22853     setFooter: function (value, href)
22854     {
22855         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
22856         
22857         if(href){
22858             this.el.select('a.small-box-footer',true).first().attr('href', href);
22859         }
22860         
22861     },
22862
22863     setContent: function (value)
22864     {
22865         this.el.select('.roo-content',true).first().dom.innerHTML = value;
22866     },
22867
22868     initEvents: function() 
22869     {   
22870         
22871     }
22872     
22873 });
22874
22875  
22876 /*
22877  * - LGPL
22878  *
22879  * TabBox
22880  * 
22881  */
22882 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22883
22884 /**
22885  * @class Roo.bootstrap.dash.TabBox
22886  * @extends Roo.bootstrap.Component
22887  * Bootstrap TabBox class
22888  * @cfg {String} title Title of the TabBox
22889  * @cfg {String} icon Icon of the TabBox
22890  * @cfg {Boolean} showtabs (true|false) show the tabs default true
22891  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
22892  * 
22893  * @constructor
22894  * Create a new TabBox
22895  * @param {Object} config The config object
22896  */
22897
22898
22899 Roo.bootstrap.dash.TabBox = function(config){
22900     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
22901     this.addEvents({
22902         // raw events
22903         /**
22904          * @event addpane
22905          * When a pane is added
22906          * @param {Roo.bootstrap.dash.TabPane} pane
22907          */
22908         "addpane" : true,
22909         /**
22910          * @event activatepane
22911          * When a pane is activated
22912          * @param {Roo.bootstrap.dash.TabPane} pane
22913          */
22914         "activatepane" : true
22915         
22916          
22917     });
22918     
22919     this.panes = [];
22920 };
22921
22922 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
22923
22924     title : '',
22925     icon : false,
22926     showtabs : true,
22927     tabScrollable : false,
22928     
22929     getChildContainer : function()
22930     {
22931         return this.el.select('.tab-content', true).first();
22932     },
22933     
22934     getAutoCreate : function(){
22935         
22936         var header = {
22937             tag: 'li',
22938             cls: 'pull-left header',
22939             html: this.title,
22940             cn : []
22941         };
22942         
22943         if(this.icon){
22944             header.cn.push({
22945                 tag: 'i',
22946                 cls: 'fa ' + this.icon
22947             });
22948         }
22949         
22950         var h = {
22951             tag: 'ul',
22952             cls: 'nav nav-tabs pull-right',
22953             cn: [
22954                 header
22955             ]
22956         };
22957         
22958         if(this.tabScrollable){
22959             h = {
22960                 tag: 'div',
22961                 cls: 'tab-header',
22962                 cn: [
22963                     {
22964                         tag: 'ul',
22965                         cls: 'nav nav-tabs pull-right',
22966                         cn: [
22967                             header
22968                         ]
22969                     }
22970                 ]
22971             };
22972         }
22973         
22974         var cfg = {
22975             tag: 'div',
22976             cls: 'nav-tabs-custom',
22977             cn: [
22978                 h,
22979                 {
22980                     tag: 'div',
22981                     cls: 'tab-content no-padding',
22982                     cn: []
22983                 }
22984             ]
22985         };
22986
22987         return  cfg;
22988     },
22989     initEvents : function()
22990     {
22991         //Roo.log('add add pane handler');
22992         this.on('addpane', this.onAddPane, this);
22993     },
22994      /**
22995      * Updates the box title
22996      * @param {String} html to set the title to.
22997      */
22998     setTitle : function(value)
22999     {
23000         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23001     },
23002     onAddPane : function(pane)
23003     {
23004         this.panes.push(pane);
23005         //Roo.log('addpane');
23006         //Roo.log(pane);
23007         // tabs are rendere left to right..
23008         if(!this.showtabs){
23009             return;
23010         }
23011         
23012         var ctr = this.el.select('.nav-tabs', true).first();
23013          
23014          
23015         var existing = ctr.select('.nav-tab',true);
23016         var qty = existing.getCount();;
23017         
23018         
23019         var tab = ctr.createChild({
23020             tag : 'li',
23021             cls : 'nav-tab' + (qty ? '' : ' active'),
23022             cn : [
23023                 {
23024                     tag : 'a',
23025                     href:'#',
23026                     html : pane.title
23027                 }
23028             ]
23029         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23030         pane.tab = tab;
23031         
23032         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23033         if (!qty) {
23034             pane.el.addClass('active');
23035         }
23036         
23037                 
23038     },
23039     onTabClick : function(ev,un,ob,pane)
23040     {
23041         //Roo.log('tab - prev default');
23042         ev.preventDefault();
23043         
23044         
23045         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23046         pane.tab.addClass('active');
23047         //Roo.log(pane.title);
23048         this.getChildContainer().select('.tab-pane',true).removeClass('active');
23049         // technically we should have a deactivate event.. but maybe add later.
23050         // and it should not de-activate the selected tab...
23051         this.fireEvent('activatepane', pane);
23052         pane.el.addClass('active');
23053         pane.fireEvent('activate');
23054         
23055         
23056     },
23057     
23058     getActivePane : function()
23059     {
23060         var r = false;
23061         Roo.each(this.panes, function(p) {
23062             if(p.el.hasClass('active')){
23063                 r = p;
23064                 return false;
23065             }
23066             
23067             return;
23068         });
23069         
23070         return r;
23071     }
23072     
23073     
23074 });
23075
23076  
23077 /*
23078  * - LGPL
23079  *
23080  * Tab pane
23081  * 
23082  */
23083 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23084 /**
23085  * @class Roo.bootstrap.TabPane
23086  * @extends Roo.bootstrap.Component
23087  * Bootstrap TabPane class
23088  * @cfg {Boolean} active (false | true) Default false
23089  * @cfg {String} title title of panel
23090
23091  * 
23092  * @constructor
23093  * Create a new TabPane
23094  * @param {Object} config The config object
23095  */
23096
23097 Roo.bootstrap.dash.TabPane = function(config){
23098     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
23099     
23100     this.addEvents({
23101         // raw events
23102         /**
23103          * @event activate
23104          * When a pane is activated
23105          * @param {Roo.bootstrap.dash.TabPane} pane
23106          */
23107         "activate" : true
23108          
23109     });
23110 };
23111
23112 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
23113     
23114     active : false,
23115     title : '',
23116     
23117     // the tabBox that this is attached to.
23118     tab : false,
23119      
23120     getAutoCreate : function() 
23121     {
23122         var cfg = {
23123             tag: 'div',
23124             cls: 'tab-pane'
23125         };
23126         
23127         if(this.active){
23128             cfg.cls += ' active';
23129         }
23130         
23131         return cfg;
23132     },
23133     initEvents  : function()
23134     {
23135         //Roo.log('trigger add pane handler');
23136         this.parent().fireEvent('addpane', this)
23137     },
23138     
23139      /**
23140      * Updates the tab title 
23141      * @param {String} html to set the title to.
23142      */
23143     setTitle: function(str)
23144     {
23145         if (!this.tab) {
23146             return;
23147         }
23148         this.title = str;
23149         this.tab.select('a', true).first().dom.innerHTML = str;
23150         
23151     }
23152     
23153     
23154     
23155 });
23156
23157  
23158
23159
23160  /*
23161  * - LGPL
23162  *
23163  * menu
23164  * 
23165  */
23166 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23167
23168 /**
23169  * @class Roo.bootstrap.menu.Menu
23170  * @extends Roo.bootstrap.Component
23171  * Bootstrap Menu class - container for Menu
23172  * @cfg {String} html Text of the menu
23173  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
23174  * @cfg {String} icon Font awesome icon
23175  * @cfg {String} pos Menu align to (top | bottom) default bottom
23176  * 
23177  * 
23178  * @constructor
23179  * Create a new Menu
23180  * @param {Object} config The config object
23181  */
23182
23183
23184 Roo.bootstrap.menu.Menu = function(config){
23185     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
23186     
23187     this.addEvents({
23188         /**
23189          * @event beforeshow
23190          * Fires before this menu is displayed
23191          * @param {Roo.bootstrap.menu.Menu} this
23192          */
23193         beforeshow : true,
23194         /**
23195          * @event beforehide
23196          * Fires before this menu is hidden
23197          * @param {Roo.bootstrap.menu.Menu} this
23198          */
23199         beforehide : true,
23200         /**
23201          * @event show
23202          * Fires after this menu is displayed
23203          * @param {Roo.bootstrap.menu.Menu} this
23204          */
23205         show : true,
23206         /**
23207          * @event hide
23208          * Fires after this menu is hidden
23209          * @param {Roo.bootstrap.menu.Menu} this
23210          */
23211         hide : true,
23212         /**
23213          * @event click
23214          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
23215          * @param {Roo.bootstrap.menu.Menu} this
23216          * @param {Roo.EventObject} e
23217          */
23218         click : true
23219     });
23220     
23221 };
23222
23223 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
23224     
23225     submenu : false,
23226     html : '',
23227     weight : 'default',
23228     icon : false,
23229     pos : 'bottom',
23230     
23231     
23232     getChildContainer : function() {
23233         if(this.isSubMenu){
23234             return this.el;
23235         }
23236         
23237         return this.el.select('ul.dropdown-menu', true).first();  
23238     },
23239     
23240     getAutoCreate : function()
23241     {
23242         var text = [
23243             {
23244                 tag : 'span',
23245                 cls : 'roo-menu-text',
23246                 html : this.html
23247             }
23248         ];
23249         
23250         if(this.icon){
23251             text.unshift({
23252                 tag : 'i',
23253                 cls : 'fa ' + this.icon
23254             })
23255         }
23256         
23257         
23258         var cfg = {
23259             tag : 'div',
23260             cls : 'btn-group',
23261             cn : [
23262                 {
23263                     tag : 'button',
23264                     cls : 'dropdown-button btn btn-' + this.weight,
23265                     cn : text
23266                 },
23267                 {
23268                     tag : 'button',
23269                     cls : 'dropdown-toggle btn btn-' + this.weight,
23270                     cn : [
23271                         {
23272                             tag : 'span',
23273                             cls : 'caret'
23274                         }
23275                     ]
23276                 },
23277                 {
23278                     tag : 'ul',
23279                     cls : 'dropdown-menu'
23280                 }
23281             ]
23282             
23283         };
23284         
23285         if(this.pos == 'top'){
23286             cfg.cls += ' dropup';
23287         }
23288         
23289         if(this.isSubMenu){
23290             cfg = {
23291                 tag : 'ul',
23292                 cls : 'dropdown-menu'
23293             }
23294         }
23295         
23296         return cfg;
23297     },
23298     
23299     onRender : function(ct, position)
23300     {
23301         this.isSubMenu = ct.hasClass('dropdown-submenu');
23302         
23303         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
23304     },
23305     
23306     initEvents : function() 
23307     {
23308         if(this.isSubMenu){
23309             return;
23310         }
23311         
23312         this.hidden = true;
23313         
23314         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
23315         this.triggerEl.on('click', this.onTriggerPress, this);
23316         
23317         this.buttonEl = this.el.select('button.dropdown-button', true).first();
23318         this.buttonEl.on('click', this.onClick, this);
23319         
23320     },
23321     
23322     list : function()
23323     {
23324         if(this.isSubMenu){
23325             return this.el;
23326         }
23327         
23328         return this.el.select('ul.dropdown-menu', true).first();
23329     },
23330     
23331     onClick : function(e)
23332     {
23333         this.fireEvent("click", this, e);
23334     },
23335     
23336     onTriggerPress  : function(e)
23337     {   
23338         if (this.isVisible()) {
23339             this.hide();
23340         } else {
23341             this.show();
23342         }
23343     },
23344     
23345     isVisible : function(){
23346         return !this.hidden;
23347     },
23348     
23349     show : function()
23350     {
23351         this.fireEvent("beforeshow", this);
23352         
23353         this.hidden = false;
23354         this.el.addClass('open');
23355         
23356         Roo.get(document).on("mouseup", this.onMouseUp, this);
23357         
23358         this.fireEvent("show", this);
23359         
23360         
23361     },
23362     
23363     hide : function()
23364     {
23365         this.fireEvent("beforehide", this);
23366         
23367         this.hidden = true;
23368         this.el.removeClass('open');
23369         
23370         Roo.get(document).un("mouseup", this.onMouseUp);
23371         
23372         this.fireEvent("hide", this);
23373     },
23374     
23375     onMouseUp : function()
23376     {
23377         this.hide();
23378     }
23379     
23380 });
23381
23382  
23383  /*
23384  * - LGPL
23385  *
23386  * menu item
23387  * 
23388  */
23389 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23390
23391 /**
23392  * @class Roo.bootstrap.menu.Item
23393  * @extends Roo.bootstrap.Component
23394  * Bootstrap MenuItem class
23395  * @cfg {Boolean} submenu (true | false) default false
23396  * @cfg {String} html text of the item
23397  * @cfg {String} href the link
23398  * @cfg {Boolean} disable (true | false) default false
23399  * @cfg {Boolean} preventDefault (true | false) default true
23400  * @cfg {String} icon Font awesome icon
23401  * @cfg {String} pos Submenu align to (left | right) default right 
23402  * 
23403  * 
23404  * @constructor
23405  * Create a new Item
23406  * @param {Object} config The config object
23407  */
23408
23409
23410 Roo.bootstrap.menu.Item = function(config){
23411     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23412     this.addEvents({
23413         /**
23414          * @event mouseover
23415          * Fires when the mouse is hovering over this menu
23416          * @param {Roo.bootstrap.menu.Item} this
23417          * @param {Roo.EventObject} e
23418          */
23419         mouseover : true,
23420         /**
23421          * @event mouseout
23422          * Fires when the mouse exits this menu
23423          * @param {Roo.bootstrap.menu.Item} this
23424          * @param {Roo.EventObject} e
23425          */
23426         mouseout : true,
23427         // raw events
23428         /**
23429          * @event click
23430          * The raw click event for the entire grid.
23431          * @param {Roo.EventObject} e
23432          */
23433         click : true
23434     });
23435 };
23436
23437 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
23438     
23439     submenu : false,
23440     href : '',
23441     html : '',
23442     preventDefault: true,
23443     disable : false,
23444     icon : false,
23445     pos : 'right',
23446     
23447     getAutoCreate : function()
23448     {
23449         var text = [
23450             {
23451                 tag : 'span',
23452                 cls : 'roo-menu-item-text',
23453                 html : this.html
23454             }
23455         ];
23456         
23457         if(this.icon){
23458             text.unshift({
23459                 tag : 'i',
23460                 cls : 'fa ' + this.icon
23461             })
23462         }
23463         
23464         var cfg = {
23465             tag : 'li',
23466             cn : [
23467                 {
23468                     tag : 'a',
23469                     href : this.href || '#',
23470                     cn : text
23471                 }
23472             ]
23473         };
23474         
23475         if(this.disable){
23476             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23477         }
23478         
23479         if(this.submenu){
23480             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23481             
23482             if(this.pos == 'left'){
23483                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23484             }
23485         }
23486         
23487         return cfg;
23488     },
23489     
23490     initEvents : function() 
23491     {
23492         this.el.on('mouseover', this.onMouseOver, this);
23493         this.el.on('mouseout', this.onMouseOut, this);
23494         
23495         this.el.select('a', true).first().on('click', this.onClick, this);
23496         
23497     },
23498     
23499     onClick : function(e)
23500     {
23501         if(this.preventDefault){
23502             e.preventDefault();
23503         }
23504         
23505         this.fireEvent("click", this, e);
23506     },
23507     
23508     onMouseOver : function(e)
23509     {
23510         if(this.submenu && this.pos == 'left'){
23511             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23512         }
23513         
23514         this.fireEvent("mouseover", this, e);
23515     },
23516     
23517     onMouseOut : function(e)
23518     {
23519         this.fireEvent("mouseout", this, e);
23520     }
23521 });
23522
23523  
23524
23525  /*
23526  * - LGPL
23527  *
23528  * menu separator
23529  * 
23530  */
23531 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23532
23533 /**
23534  * @class Roo.bootstrap.menu.Separator
23535  * @extends Roo.bootstrap.Component
23536  * Bootstrap Separator class
23537  * 
23538  * @constructor
23539  * Create a new Separator
23540  * @param {Object} config The config object
23541  */
23542
23543
23544 Roo.bootstrap.menu.Separator = function(config){
23545     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23546 };
23547
23548 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
23549     
23550     getAutoCreate : function(){
23551         var cfg = {
23552             tag : 'li',
23553             cls: 'divider'
23554         };
23555         
23556         return cfg;
23557     }
23558    
23559 });
23560
23561  
23562
23563  /*
23564  * - LGPL
23565  *
23566  * Tooltip
23567  * 
23568  */
23569
23570 /**
23571  * @class Roo.bootstrap.Tooltip
23572  * Bootstrap Tooltip class
23573  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23574  * to determine which dom element triggers the tooltip.
23575  * 
23576  * It needs to add support for additional attributes like tooltip-position
23577  * 
23578  * @constructor
23579  * Create a new Toolti
23580  * @param {Object} config The config object
23581  */
23582
23583 Roo.bootstrap.Tooltip = function(config){
23584     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23585 };
23586
23587 Roo.apply(Roo.bootstrap.Tooltip, {
23588     /**
23589      * @function init initialize tooltip monitoring.
23590      * @static
23591      */
23592     currentEl : false,
23593     currentTip : false,
23594     currentRegion : false,
23595     
23596     //  init : delay?
23597     
23598     init : function()
23599     {
23600         Roo.get(document).on('mouseover', this.enter ,this);
23601         Roo.get(document).on('mouseout', this.leave, this);
23602          
23603         
23604         this.currentTip = new Roo.bootstrap.Tooltip();
23605     },
23606     
23607     enter : function(ev)
23608     {
23609         var dom = ev.getTarget();
23610         
23611         //Roo.log(['enter',dom]);
23612         var el = Roo.fly(dom);
23613         if (this.currentEl) {
23614             //Roo.log(dom);
23615             //Roo.log(this.currentEl);
23616             //Roo.log(this.currentEl.contains(dom));
23617             if (this.currentEl == el) {
23618                 return;
23619             }
23620             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23621                 return;
23622             }
23623
23624         }
23625         
23626         if (this.currentTip.el) {
23627             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
23628         }    
23629         //Roo.log(ev);
23630         var bindEl = el;
23631         
23632         // you can not look for children, as if el is the body.. then everythign is the child..
23633         if (!el.attr('tooltip')) { //
23634             if (!el.select("[tooltip]").elements.length) {
23635                 return;
23636             }
23637             // is the mouse over this child...?
23638             bindEl = el.select("[tooltip]").first();
23639             var xy = ev.getXY();
23640             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23641                 //Roo.log("not in region.");
23642                 return;
23643             }
23644             //Roo.log("child element over..");
23645             
23646         }
23647         this.currentEl = bindEl;
23648         this.currentTip.bind(bindEl);
23649         this.currentRegion = Roo.lib.Region.getRegion(dom);
23650         this.currentTip.enter();
23651         
23652     },
23653     leave : function(ev)
23654     {
23655         var dom = ev.getTarget();
23656         //Roo.log(['leave',dom]);
23657         if (!this.currentEl) {
23658             return;
23659         }
23660         
23661         
23662         if (dom != this.currentEl.dom) {
23663             return;
23664         }
23665         var xy = ev.getXY();
23666         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
23667             return;
23668         }
23669         // only activate leave if mouse cursor is outside... bounding box..
23670         
23671         
23672         
23673         
23674         if (this.currentTip) {
23675             this.currentTip.leave();
23676         }
23677         //Roo.log('clear currentEl');
23678         this.currentEl = false;
23679         
23680         
23681     },
23682     alignment : {
23683         'left' : ['r-l', [-2,0], 'right'],
23684         'right' : ['l-r', [2,0], 'left'],
23685         'bottom' : ['t-b', [0,2], 'top'],
23686         'top' : [ 'b-t', [0,-2], 'bottom']
23687     }
23688     
23689 });
23690
23691
23692 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
23693     
23694     
23695     bindEl : false,
23696     
23697     delay : null, // can be { show : 300 , hide: 500}
23698     
23699     timeout : null,
23700     
23701     hoverState : null, //???
23702     
23703     placement : 'bottom', 
23704     
23705     getAutoCreate : function(){
23706     
23707         var cfg = {
23708            cls : 'tooltip',
23709            role : 'tooltip',
23710            cn : [
23711                 {
23712                     cls : 'tooltip-arrow'
23713                 },
23714                 {
23715                     cls : 'tooltip-inner'
23716                 }
23717            ]
23718         };
23719         
23720         return cfg;
23721     },
23722     bind : function(el)
23723     {
23724         this.bindEl = el;
23725     },
23726       
23727     
23728     enter : function () {
23729        
23730         if (this.timeout != null) {
23731             clearTimeout(this.timeout);
23732         }
23733         
23734         this.hoverState = 'in';
23735          //Roo.log("enter - show");
23736         if (!this.delay || !this.delay.show) {
23737             this.show();
23738             return;
23739         }
23740         var _t = this;
23741         this.timeout = setTimeout(function () {
23742             if (_t.hoverState == 'in') {
23743                 _t.show();
23744             }
23745         }, this.delay.show);
23746     },
23747     leave : function()
23748     {
23749         clearTimeout(this.timeout);
23750     
23751         this.hoverState = 'out';
23752          if (!this.delay || !this.delay.hide) {
23753             this.hide();
23754             return;
23755         }
23756        
23757         var _t = this;
23758         this.timeout = setTimeout(function () {
23759             //Roo.log("leave - timeout");
23760             
23761             if (_t.hoverState == 'out') {
23762                 _t.hide();
23763                 Roo.bootstrap.Tooltip.currentEl = false;
23764             }
23765         }, delay);
23766     },
23767     
23768     show : function ()
23769     {
23770         if (!this.el) {
23771             this.render(document.body);
23772         }
23773         // set content.
23774         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23775         
23776         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23777         
23778         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23779         
23780         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23781         
23782         var placement = typeof this.placement == 'function' ?
23783             this.placement.call(this, this.el, on_el) :
23784             this.placement;
23785             
23786         var autoToken = /\s?auto?\s?/i;
23787         var autoPlace = autoToken.test(placement);
23788         if (autoPlace) {
23789             placement = placement.replace(autoToken, '') || 'top';
23790         }
23791         
23792         //this.el.detach()
23793         //this.el.setXY([0,0]);
23794         this.el.show();
23795         //this.el.dom.style.display='block';
23796         
23797         //this.el.appendTo(on_el);
23798         
23799         var p = this.getPosition();
23800         var box = this.el.getBox();
23801         
23802         if (autoPlace) {
23803             // fixme..
23804         }
23805         
23806         var align = Roo.bootstrap.Tooltip.alignment[placement];
23807         
23808         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
23809         
23810         if(placement == 'top' || placement == 'bottom'){
23811             if(xy[0] < 0){
23812                 placement = 'right';
23813             }
23814             
23815             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
23816                 placement = 'left';
23817             }
23818         }
23819         
23820         align = Roo.bootstrap.Tooltip.alignment[placement];
23821         
23822         this.el.alignTo(this.bindEl, align[0],align[1]);
23823         //var arrow = this.el.select('.arrow',true).first();
23824         //arrow.set(align[2], 
23825         
23826         this.el.addClass(placement);
23827         
23828         this.el.addClass('in fade');
23829         
23830         this.hoverState = null;
23831         
23832         if (this.el.hasClass('fade')) {
23833             // fade it?
23834         }
23835         
23836     },
23837     hide : function()
23838     {
23839          
23840         if (!this.el) {
23841             return;
23842         }
23843         //this.el.setXY([0,0]);
23844         this.el.removeClass('in');
23845         //this.el.hide();
23846         
23847     }
23848     
23849 });
23850  
23851
23852  /*
23853  * - LGPL
23854  *
23855  * Location Picker
23856  * 
23857  */
23858
23859 /**
23860  * @class Roo.bootstrap.LocationPicker
23861  * @extends Roo.bootstrap.Component
23862  * Bootstrap LocationPicker class
23863  * @cfg {Number} latitude Position when init default 0
23864  * @cfg {Number} longitude Position when init default 0
23865  * @cfg {Number} zoom default 15
23866  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
23867  * @cfg {Boolean} mapTypeControl default false
23868  * @cfg {Boolean} disableDoubleClickZoom default false
23869  * @cfg {Boolean} scrollwheel default true
23870  * @cfg {Boolean} streetViewControl default false
23871  * @cfg {Number} radius default 0
23872  * @cfg {String} locationName
23873  * @cfg {Boolean} draggable default true
23874  * @cfg {Boolean} enableAutocomplete default false
23875  * @cfg {Boolean} enableReverseGeocode default true
23876  * @cfg {String} markerTitle
23877  * 
23878  * @constructor
23879  * Create a new LocationPicker
23880  * @param {Object} config The config object
23881  */
23882
23883
23884 Roo.bootstrap.LocationPicker = function(config){
23885     
23886     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
23887     
23888     this.addEvents({
23889         /**
23890          * @event initial
23891          * Fires when the picker initialized.
23892          * @param {Roo.bootstrap.LocationPicker} this
23893          * @param {Google Location} location
23894          */
23895         initial : true,
23896         /**
23897          * @event positionchanged
23898          * Fires when the picker position changed.
23899          * @param {Roo.bootstrap.LocationPicker} this
23900          * @param {Google Location} location
23901          */
23902         positionchanged : true,
23903         /**
23904          * @event resize
23905          * Fires when the map resize.
23906          * @param {Roo.bootstrap.LocationPicker} this
23907          */
23908         resize : true,
23909         /**
23910          * @event show
23911          * Fires when the map show.
23912          * @param {Roo.bootstrap.LocationPicker} this
23913          */
23914         show : true,
23915         /**
23916          * @event hide
23917          * Fires when the map hide.
23918          * @param {Roo.bootstrap.LocationPicker} this
23919          */
23920         hide : true,
23921         /**
23922          * @event mapClick
23923          * Fires when click the map.
23924          * @param {Roo.bootstrap.LocationPicker} this
23925          * @param {Map event} e
23926          */
23927         mapClick : true,
23928         /**
23929          * @event mapRightClick
23930          * Fires when right click the map.
23931          * @param {Roo.bootstrap.LocationPicker} this
23932          * @param {Map event} e
23933          */
23934         mapRightClick : true,
23935         /**
23936          * @event markerClick
23937          * Fires when click the marker.
23938          * @param {Roo.bootstrap.LocationPicker} this
23939          * @param {Map event} e
23940          */
23941         markerClick : true,
23942         /**
23943          * @event markerRightClick
23944          * Fires when right click the marker.
23945          * @param {Roo.bootstrap.LocationPicker} this
23946          * @param {Map event} e
23947          */
23948         markerRightClick : true,
23949         /**
23950          * @event OverlayViewDraw
23951          * Fires when OverlayView Draw
23952          * @param {Roo.bootstrap.LocationPicker} this
23953          */
23954         OverlayViewDraw : true,
23955         /**
23956          * @event OverlayViewOnAdd
23957          * Fires when OverlayView Draw
23958          * @param {Roo.bootstrap.LocationPicker} this
23959          */
23960         OverlayViewOnAdd : true,
23961         /**
23962          * @event OverlayViewOnRemove
23963          * Fires when OverlayView Draw
23964          * @param {Roo.bootstrap.LocationPicker} this
23965          */
23966         OverlayViewOnRemove : true,
23967         /**
23968          * @event OverlayViewShow
23969          * Fires when OverlayView Draw
23970          * @param {Roo.bootstrap.LocationPicker} this
23971          * @param {Pixel} cpx
23972          */
23973         OverlayViewShow : true,
23974         /**
23975          * @event OverlayViewHide
23976          * Fires when OverlayView Draw
23977          * @param {Roo.bootstrap.LocationPicker} this
23978          */
23979         OverlayViewHide : true,
23980         /**
23981          * @event loadexception
23982          * Fires when load google lib failed.
23983          * @param {Roo.bootstrap.LocationPicker} this
23984          */
23985         loadexception : true
23986     });
23987         
23988 };
23989
23990 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
23991     
23992     gMapContext: false,
23993     
23994     latitude: 0,
23995     longitude: 0,
23996     zoom: 15,
23997     mapTypeId: false,
23998     mapTypeControl: false,
23999     disableDoubleClickZoom: false,
24000     scrollwheel: true,
24001     streetViewControl: false,
24002     radius: 0,
24003     locationName: '',
24004     draggable: true,
24005     enableAutocomplete: false,
24006     enableReverseGeocode: true,
24007     markerTitle: '',
24008     
24009     getAutoCreate: function()
24010     {
24011
24012         var cfg = {
24013             tag: 'div',
24014             cls: 'roo-location-picker'
24015         };
24016         
24017         return cfg
24018     },
24019     
24020     initEvents: function(ct, position)
24021     {       
24022         if(!this.el.getWidth() || this.isApplied()){
24023             return;
24024         }
24025         
24026         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24027         
24028         this.initial();
24029     },
24030     
24031     initial: function()
24032     {
24033         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24034             this.fireEvent('loadexception', this);
24035             return;
24036         }
24037         
24038         if(!this.mapTypeId){
24039             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24040         }
24041         
24042         this.gMapContext = this.GMapContext();
24043         
24044         this.initOverlayView();
24045         
24046         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24047         
24048         var _this = this;
24049                 
24050         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24051             _this.setPosition(_this.gMapContext.marker.position);
24052         });
24053         
24054         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
24055             _this.fireEvent('mapClick', this, event);
24056             
24057         });
24058
24059         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
24060             _this.fireEvent('mapRightClick', this, event);
24061             
24062         });
24063         
24064         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
24065             _this.fireEvent('markerClick', this, event);
24066             
24067         });
24068
24069         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
24070             _this.fireEvent('markerRightClick', this, event);
24071             
24072         });
24073         
24074         this.setPosition(this.gMapContext.location);
24075         
24076         this.fireEvent('initial', this, this.gMapContext.location);
24077     },
24078     
24079     initOverlayView: function()
24080     {
24081         var _this = this;
24082         
24083         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
24084             
24085             draw: function()
24086             {
24087                 _this.fireEvent('OverlayViewDraw', _this);
24088             },
24089             
24090             onAdd: function()
24091             {
24092                 _this.fireEvent('OverlayViewOnAdd', _this);
24093             },
24094             
24095             onRemove: function()
24096             {
24097                 _this.fireEvent('OverlayViewOnRemove', _this);
24098             },
24099             
24100             show: function(cpx)
24101             {
24102                 _this.fireEvent('OverlayViewShow', _this, cpx);
24103             },
24104             
24105             hide: function()
24106             {
24107                 _this.fireEvent('OverlayViewHide', _this);
24108             }
24109             
24110         });
24111     },
24112     
24113     fromLatLngToContainerPixel: function(event)
24114     {
24115         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
24116     },
24117     
24118     isApplied: function() 
24119     {
24120         return this.getGmapContext() == false ? false : true;
24121     },
24122     
24123     getGmapContext: function() 
24124     {
24125         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
24126     },
24127     
24128     GMapContext: function() 
24129     {
24130         var position = new google.maps.LatLng(this.latitude, this.longitude);
24131         
24132         var _map = new google.maps.Map(this.el.dom, {
24133             center: position,
24134             zoom: this.zoom,
24135             mapTypeId: this.mapTypeId,
24136             mapTypeControl: this.mapTypeControl,
24137             disableDoubleClickZoom: this.disableDoubleClickZoom,
24138             scrollwheel: this.scrollwheel,
24139             streetViewControl: this.streetViewControl,
24140             locationName: this.locationName,
24141             draggable: this.draggable,
24142             enableAutocomplete: this.enableAutocomplete,
24143             enableReverseGeocode: this.enableReverseGeocode
24144         });
24145         
24146         var _marker = new google.maps.Marker({
24147             position: position,
24148             map: _map,
24149             title: this.markerTitle,
24150             draggable: this.draggable
24151         });
24152         
24153         return {
24154             map: _map,
24155             marker: _marker,
24156             circle: null,
24157             location: position,
24158             radius: this.radius,
24159             locationName: this.locationName,
24160             addressComponents: {
24161                 formatted_address: null,
24162                 addressLine1: null,
24163                 addressLine2: null,
24164                 streetName: null,
24165                 streetNumber: null,
24166                 city: null,
24167                 district: null,
24168                 state: null,
24169                 stateOrProvince: null
24170             },
24171             settings: this,
24172             domContainer: this.el.dom,
24173             geodecoder: new google.maps.Geocoder()
24174         };
24175     },
24176     
24177     drawCircle: function(center, radius, options) 
24178     {
24179         if (this.gMapContext.circle != null) {
24180             this.gMapContext.circle.setMap(null);
24181         }
24182         if (radius > 0) {
24183             radius *= 1;
24184             options = Roo.apply({}, options, {
24185                 strokeColor: "#0000FF",
24186                 strokeOpacity: .35,
24187                 strokeWeight: 2,
24188                 fillColor: "#0000FF",
24189                 fillOpacity: .2
24190             });
24191             
24192             options.map = this.gMapContext.map;
24193             options.radius = radius;
24194             options.center = center;
24195             this.gMapContext.circle = new google.maps.Circle(options);
24196             return this.gMapContext.circle;
24197         }
24198         
24199         return null;
24200     },
24201     
24202     setPosition: function(location) 
24203     {
24204         this.gMapContext.location = location;
24205         this.gMapContext.marker.setPosition(location);
24206         this.gMapContext.map.panTo(location);
24207         this.drawCircle(location, this.gMapContext.radius, {});
24208         
24209         var _this = this;
24210         
24211         if (this.gMapContext.settings.enableReverseGeocode) {
24212             this.gMapContext.geodecoder.geocode({
24213                 latLng: this.gMapContext.location
24214             }, function(results, status) {
24215                 
24216                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
24217                     _this.gMapContext.locationName = results[0].formatted_address;
24218                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
24219                     
24220                     _this.fireEvent('positionchanged', this, location);
24221                 }
24222             });
24223             
24224             return;
24225         }
24226         
24227         this.fireEvent('positionchanged', this, location);
24228     },
24229     
24230     resize: function()
24231     {
24232         google.maps.event.trigger(this.gMapContext.map, "resize");
24233         
24234         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24235         
24236         this.fireEvent('resize', this);
24237     },
24238     
24239     setPositionByLatLng: function(latitude, longitude)
24240     {
24241         this.setPosition(new google.maps.LatLng(latitude, longitude));
24242     },
24243     
24244     getCurrentPosition: function() 
24245     {
24246         return {
24247             latitude: this.gMapContext.location.lat(),
24248             longitude: this.gMapContext.location.lng()
24249         };
24250     },
24251     
24252     getAddressName: function() 
24253     {
24254         return this.gMapContext.locationName;
24255     },
24256     
24257     getAddressComponents: function() 
24258     {
24259         return this.gMapContext.addressComponents;
24260     },
24261     
24262     address_component_from_google_geocode: function(address_components) 
24263     {
24264         var result = {};
24265         
24266         for (var i = 0; i < address_components.length; i++) {
24267             var component = address_components[i];
24268             if (component.types.indexOf("postal_code") >= 0) {
24269                 result.postalCode = component.short_name;
24270             } else if (component.types.indexOf("street_number") >= 0) {
24271                 result.streetNumber = component.short_name;
24272             } else if (component.types.indexOf("route") >= 0) {
24273                 result.streetName = component.short_name;
24274             } else if (component.types.indexOf("neighborhood") >= 0) {
24275                 result.city = component.short_name;
24276             } else if (component.types.indexOf("locality") >= 0) {
24277                 result.city = component.short_name;
24278             } else if (component.types.indexOf("sublocality") >= 0) {
24279                 result.district = component.short_name;
24280             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
24281                 result.stateOrProvince = component.short_name;
24282             } else if (component.types.indexOf("country") >= 0) {
24283                 result.country = component.short_name;
24284             }
24285         }
24286         
24287         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
24288         result.addressLine2 = "";
24289         return result;
24290     },
24291     
24292     setZoomLevel: function(zoom)
24293     {
24294         this.gMapContext.map.setZoom(zoom);
24295     },
24296     
24297     show: function()
24298     {
24299         if(!this.el){
24300             return;
24301         }
24302         
24303         this.el.show();
24304         
24305         this.resize();
24306         
24307         this.fireEvent('show', this);
24308     },
24309     
24310     hide: function()
24311     {
24312         if(!this.el){
24313             return;
24314         }
24315         
24316         this.el.hide();
24317         
24318         this.fireEvent('hide', this);
24319     }
24320     
24321 });
24322
24323 Roo.apply(Roo.bootstrap.LocationPicker, {
24324     
24325     OverlayView : function(map, options)
24326     {
24327         options = options || {};
24328         
24329         this.setMap(map);
24330     }
24331     
24332     
24333 });/*
24334  * - LGPL
24335  *
24336  * Alert
24337  * 
24338  */
24339
24340 /**
24341  * @class Roo.bootstrap.Alert
24342  * @extends Roo.bootstrap.Component
24343  * Bootstrap Alert class
24344  * @cfg {String} title The title of alert
24345  * @cfg {String} html The content of alert
24346  * @cfg {String} weight (  success | info | warning | danger )
24347  * @cfg {String} faicon font-awesomeicon
24348  * 
24349  * @constructor
24350  * Create a new alert
24351  * @param {Object} config The config object
24352  */
24353
24354
24355 Roo.bootstrap.Alert = function(config){
24356     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
24357     
24358 };
24359
24360 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
24361     
24362     title: '',
24363     html: '',
24364     weight: false,
24365     faicon: false,
24366     
24367     getAutoCreate : function()
24368     {
24369         
24370         var cfg = {
24371             tag : 'div',
24372             cls : 'alert',
24373             cn : [
24374                 {
24375                     tag : 'i',
24376                     cls : 'roo-alert-icon'
24377                     
24378                 },
24379                 {
24380                     tag : 'b',
24381                     cls : 'roo-alert-title',
24382                     html : this.title
24383                 },
24384                 {
24385                     tag : 'span',
24386                     cls : 'roo-alert-text',
24387                     html : this.html
24388                 }
24389             ]
24390         };
24391         
24392         if(this.faicon){
24393             cfg.cn[0].cls += ' fa ' + this.faicon;
24394         }
24395         
24396         if(this.weight){
24397             cfg.cls += ' alert-' + this.weight;
24398         }
24399         
24400         return cfg;
24401     },
24402     
24403     initEvents: function() 
24404     {
24405         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24406     },
24407     
24408     setTitle : function(str)
24409     {
24410         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24411     },
24412     
24413     setText : function(str)
24414     {
24415         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24416     },
24417     
24418     setWeight : function(weight)
24419     {
24420         if(this.weight){
24421             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24422         }
24423         
24424         this.weight = weight;
24425         
24426         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24427     },
24428     
24429     setIcon : function(icon)
24430     {
24431         if(this.faicon){
24432             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24433         }
24434         
24435         this.faicon = icon;
24436         
24437         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24438     },
24439     
24440     hide: function() 
24441     {
24442         this.el.hide();   
24443     },
24444     
24445     show: function() 
24446     {  
24447         this.el.show();   
24448     }
24449     
24450 });
24451
24452  
24453 /*
24454 * Licence: LGPL
24455 */
24456
24457 /**
24458  * @class Roo.bootstrap.UploadCropbox
24459  * @extends Roo.bootstrap.Component
24460  * Bootstrap UploadCropbox class
24461  * @cfg {String} emptyText show when image has been loaded
24462  * @cfg {String} rotateNotify show when image too small to rotate
24463  * @cfg {Number} errorTimeout default 3000
24464  * @cfg {Number} minWidth default 300
24465  * @cfg {Number} minHeight default 300
24466  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24467  * @cfg {Boolean} isDocument (true|false) default false
24468  * @cfg {String} url action url
24469  * @cfg {String} paramName default 'imageUpload'
24470  * @cfg {String} method default POST
24471  * @cfg {Boolean} loadMask (true|false) default true
24472  * @cfg {Boolean} loadingText default 'Loading...'
24473  * 
24474  * @constructor
24475  * Create a new UploadCropbox
24476  * @param {Object} config The config object
24477  */
24478
24479 Roo.bootstrap.UploadCropbox = function(config){
24480     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
24481     
24482     this.addEvents({
24483         /**
24484          * @event beforeselectfile
24485          * Fire before select file
24486          * @param {Roo.bootstrap.UploadCropbox} this
24487          */
24488         "beforeselectfile" : true,
24489         /**
24490          * @event initial
24491          * Fire after initEvent
24492          * @param {Roo.bootstrap.UploadCropbox} this
24493          */
24494         "initial" : true,
24495         /**
24496          * @event crop
24497          * Fire after initEvent
24498          * @param {Roo.bootstrap.UploadCropbox} this
24499          * @param {String} data
24500          */
24501         "crop" : true,
24502         /**
24503          * @event prepare
24504          * Fire when preparing the file data
24505          * @param {Roo.bootstrap.UploadCropbox} this
24506          * @param {Object} file
24507          */
24508         "prepare" : true,
24509         /**
24510          * @event exception
24511          * Fire when get exception
24512          * @param {Roo.bootstrap.UploadCropbox} this
24513          * @param {XMLHttpRequest} xhr
24514          */
24515         "exception" : true,
24516         /**
24517          * @event beforeloadcanvas
24518          * Fire before load the canvas
24519          * @param {Roo.bootstrap.UploadCropbox} this
24520          * @param {String} src
24521          */
24522         "beforeloadcanvas" : true,
24523         /**
24524          * @event trash
24525          * Fire when trash image
24526          * @param {Roo.bootstrap.UploadCropbox} this
24527          */
24528         "trash" : true,
24529         /**
24530          * @event download
24531          * Fire when download the image
24532          * @param {Roo.bootstrap.UploadCropbox} this
24533          */
24534         "download" : true,
24535         /**
24536          * @event footerbuttonclick
24537          * Fire when footerbuttonclick
24538          * @param {Roo.bootstrap.UploadCropbox} this
24539          * @param {String} type
24540          */
24541         "footerbuttonclick" : true,
24542         /**
24543          * @event resize
24544          * Fire when resize
24545          * @param {Roo.bootstrap.UploadCropbox} this
24546          */
24547         "resize" : true,
24548         /**
24549          * @event rotate
24550          * Fire when rotate the image
24551          * @param {Roo.bootstrap.UploadCropbox} this
24552          * @param {String} pos
24553          */
24554         "rotate" : true,
24555         /**
24556          * @event inspect
24557          * Fire when inspect the file
24558          * @param {Roo.bootstrap.UploadCropbox} this
24559          * @param {Object} file
24560          */
24561         "inspect" : true,
24562         /**
24563          * @event upload
24564          * Fire when xhr upload the file
24565          * @param {Roo.bootstrap.UploadCropbox} this
24566          * @param {Object} data
24567          */
24568         "upload" : true,
24569         /**
24570          * @event arrange
24571          * Fire when arrange the file data
24572          * @param {Roo.bootstrap.UploadCropbox} this
24573          * @param {Object} formData
24574          */
24575         "arrange" : true
24576     });
24577     
24578     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24579 };
24580
24581 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
24582     
24583     emptyText : 'Click to upload image',
24584     rotateNotify : 'Image is too small to rotate',
24585     errorTimeout : 3000,
24586     scale : 0,
24587     baseScale : 1,
24588     rotate : 0,
24589     dragable : false,
24590     pinching : false,
24591     mouseX : 0,
24592     mouseY : 0,
24593     cropData : false,
24594     minWidth : 300,
24595     minHeight : 300,
24596     file : false,
24597     exif : {},
24598     baseRotate : 1,
24599     cropType : 'image/jpeg',
24600     buttons : false,
24601     canvasLoaded : false,
24602     isDocument : false,
24603     method : 'POST',
24604     paramName : 'imageUpload',
24605     loadMask : true,
24606     loadingText : 'Loading...',
24607     maskEl : false,
24608     
24609     getAutoCreate : function()
24610     {
24611         var cfg = {
24612             tag : 'div',
24613             cls : 'roo-upload-cropbox',
24614             cn : [
24615                 {
24616                     tag : 'input',
24617                     cls : 'roo-upload-cropbox-selector',
24618                     type : 'file'
24619                 },
24620                 {
24621                     tag : 'div',
24622                     cls : 'roo-upload-cropbox-body',
24623                     style : 'cursor:pointer',
24624                     cn : [
24625                         {
24626                             tag : 'div',
24627                             cls : 'roo-upload-cropbox-preview'
24628                         },
24629                         {
24630                             tag : 'div',
24631                             cls : 'roo-upload-cropbox-thumb'
24632                         },
24633                         {
24634                             tag : 'div',
24635                             cls : 'roo-upload-cropbox-empty-notify',
24636                             html : this.emptyText
24637                         },
24638                         {
24639                             tag : 'div',
24640                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
24641                             html : this.rotateNotify
24642                         }
24643                     ]
24644                 },
24645                 {
24646                     tag : 'div',
24647                     cls : 'roo-upload-cropbox-footer',
24648                     cn : {
24649                         tag : 'div',
24650                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24651                         cn : []
24652                     }
24653                 }
24654             ]
24655         };
24656         
24657         return cfg;
24658     },
24659     
24660     onRender : function(ct, position)
24661     {
24662         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
24663         
24664         if (this.buttons.length) {
24665             
24666             Roo.each(this.buttons, function(bb) {
24667                 
24668                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
24669                 
24670                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
24671                 
24672             }, this);
24673         }
24674         
24675         if(this.loadMask){
24676             this.maskEl = this.el;
24677         }
24678     },
24679     
24680     initEvents : function()
24681     {
24682         this.urlAPI = (window.createObjectURL && window) || 
24683                                 (window.URL && URL.revokeObjectURL && URL) || 
24684                                 (window.webkitURL && webkitURL);
24685                         
24686         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
24687         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24688         
24689         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
24690         this.selectorEl.hide();
24691         
24692         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
24693         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24694         
24695         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
24696         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24697         this.thumbEl.hide();
24698         
24699         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
24700         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24701         
24702         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
24703         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24704         this.errorEl.hide();
24705         
24706         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
24707         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24708         this.footerEl.hide();
24709         
24710         this.setThumbBoxSize();
24711         
24712         this.bind();
24713         
24714         this.resize();
24715         
24716         this.fireEvent('initial', this);
24717     },
24718
24719     bind : function()
24720     {
24721         var _this = this;
24722         
24723         window.addEventListener("resize", function() { _this.resize(); } );
24724         
24725         this.bodyEl.on('click', this.beforeSelectFile, this);
24726         
24727         if(Roo.isTouch){
24728             this.bodyEl.on('touchstart', this.onTouchStart, this);
24729             this.bodyEl.on('touchmove', this.onTouchMove, this);
24730             this.bodyEl.on('touchend', this.onTouchEnd, this);
24731         }
24732         
24733         if(!Roo.isTouch){
24734             this.bodyEl.on('mousedown', this.onMouseDown, this);
24735             this.bodyEl.on('mousemove', this.onMouseMove, this);
24736             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
24737             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
24738             Roo.get(document).on('mouseup', this.onMouseUp, this);
24739         }
24740         
24741         this.selectorEl.on('change', this.onFileSelected, this);
24742     },
24743     
24744     reset : function()
24745     {    
24746         this.scale = 0;
24747         this.baseScale = 1;
24748         this.rotate = 0;
24749         this.baseRotate = 1;
24750         this.dragable = false;
24751         this.pinching = false;
24752         this.mouseX = 0;
24753         this.mouseY = 0;
24754         this.cropData = false;
24755         this.notifyEl.dom.innerHTML = this.emptyText;
24756         
24757         this.selectorEl.dom.value = '';
24758         
24759     },
24760     
24761     resize : function()
24762     {
24763         if(this.fireEvent('resize', this) != false){
24764             this.setThumbBoxPosition();
24765             this.setCanvasPosition();
24766         }
24767     },
24768     
24769     onFooterButtonClick : function(e, el, o, type)
24770     {
24771         switch (type) {
24772             case 'rotate-left' :
24773                 this.onRotateLeft(e);
24774                 break;
24775             case 'rotate-right' :
24776                 this.onRotateRight(e);
24777                 break;
24778             case 'picture' :
24779                 this.beforeSelectFile(e);
24780                 break;
24781             case 'trash' :
24782                 this.trash(e);
24783                 break;
24784             case 'crop' :
24785                 this.crop(e);
24786                 break;
24787             case 'download' :
24788                 this.download(e);
24789                 break;
24790             default :
24791                 break;
24792         }
24793         
24794         this.fireEvent('footerbuttonclick', this, type);
24795     },
24796     
24797     beforeSelectFile : function(e)
24798     {
24799         e.preventDefault();
24800         
24801         if(this.fireEvent('beforeselectfile', this) != false){
24802             this.selectorEl.dom.click();
24803         }
24804     },
24805     
24806     onFileSelected : function(e)
24807     {
24808         e.preventDefault();
24809         
24810         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
24811             return;
24812         }
24813         
24814         var file = this.selectorEl.dom.files[0];
24815         
24816         if(this.fireEvent('inspect', this, file) != false){
24817             this.prepare(file);
24818         }
24819         
24820     },
24821     
24822     trash : function(e)
24823     {
24824         this.fireEvent('trash', this);
24825     },
24826     
24827     download : function(e)
24828     {
24829         this.fireEvent('download', this);
24830     },
24831     
24832     loadCanvas : function(src)
24833     {   
24834         if(this.fireEvent('beforeloadcanvas', this, src) != false){
24835             
24836             this.reset();
24837             
24838             this.imageEl = document.createElement('img');
24839             
24840             var _this = this;
24841             
24842             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
24843             
24844             this.imageEl.src = src;
24845         }
24846     },
24847     
24848     onLoadCanvas : function()
24849     {   
24850         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
24851         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
24852         
24853         this.bodyEl.un('click', this.beforeSelectFile, this);
24854         
24855         this.notifyEl.hide();
24856         this.thumbEl.show();
24857         this.footerEl.show();
24858         
24859         this.baseRotateLevel();
24860         
24861         if(this.isDocument){
24862             this.setThumbBoxSize();
24863         }
24864         
24865         this.setThumbBoxPosition();
24866         
24867         this.baseScaleLevel();
24868         
24869         this.draw();
24870         
24871         this.resize();
24872         
24873         this.canvasLoaded = true;
24874         
24875         if(this.loadMask){
24876             this.maskEl.unmask();
24877         }
24878         
24879     },
24880     
24881     setCanvasPosition : function()
24882     {   
24883         if(!this.canvasEl){
24884             return;
24885         }
24886         
24887         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
24888         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
24889         
24890         this.previewEl.setLeft(pw);
24891         this.previewEl.setTop(ph);
24892         
24893     },
24894     
24895     onMouseDown : function(e)
24896     {   
24897         e.stopEvent();
24898         
24899         this.dragable = true;
24900         this.pinching = false;
24901         
24902         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
24903             this.dragable = false;
24904             return;
24905         }
24906         
24907         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24908         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24909         
24910     },
24911     
24912     onMouseMove : function(e)
24913     {   
24914         e.stopEvent();
24915         
24916         if(!this.canvasLoaded){
24917             return;
24918         }
24919         
24920         if (!this.dragable){
24921             return;
24922         }
24923         
24924         var minX = Math.ceil(this.thumbEl.getLeft(true));
24925         var minY = Math.ceil(this.thumbEl.getTop(true));
24926         
24927         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
24928         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
24929         
24930         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24931         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24932         
24933         x = x - this.mouseX;
24934         y = y - this.mouseY;
24935         
24936         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
24937         var bgY = Math.ceil(y + this.previewEl.getTop(true));
24938         
24939         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
24940         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
24941         
24942         this.previewEl.setLeft(bgX);
24943         this.previewEl.setTop(bgY);
24944         
24945         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24946         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24947     },
24948     
24949     onMouseUp : function(e)
24950     {   
24951         e.stopEvent();
24952         
24953         this.dragable = false;
24954     },
24955     
24956     onMouseWheel : function(e)
24957     {   
24958         e.stopEvent();
24959         
24960         this.startScale = this.scale;
24961         
24962         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
24963         
24964         if(!this.zoomable()){
24965             this.scale = this.startScale;
24966             return;
24967         }
24968         
24969         this.draw();
24970         
24971         return;
24972     },
24973     
24974     zoomable : function()
24975     {
24976         var minScale = this.thumbEl.getWidth() / this.minWidth;
24977         
24978         if(this.minWidth < this.minHeight){
24979             minScale = this.thumbEl.getHeight() / this.minHeight;
24980         }
24981         
24982         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
24983         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
24984         
24985         if(
24986                 this.isDocument &&
24987                 (this.rotate == 0 || this.rotate == 180) && 
24988                 (
24989                     width > this.imageEl.OriginWidth || 
24990                     height > this.imageEl.OriginHeight ||
24991                     (width < this.minWidth && height < this.minHeight)
24992                 )
24993         ){
24994             return false;
24995         }
24996         
24997         if(
24998                 this.isDocument &&
24999                 (this.rotate == 90 || this.rotate == 270) && 
25000                 (
25001                     width > this.imageEl.OriginWidth || 
25002                     height > this.imageEl.OriginHeight ||
25003                     (width < this.minHeight && height < this.minWidth)
25004                 )
25005         ){
25006             return false;
25007         }
25008         
25009         if(
25010                 !this.isDocument &&
25011                 (this.rotate == 0 || this.rotate == 180) && 
25012                 (
25013                     width < this.minWidth || 
25014                     width > this.imageEl.OriginWidth || 
25015                     height < this.minHeight || 
25016                     height > this.imageEl.OriginHeight
25017                 )
25018         ){
25019             return false;
25020         }
25021         
25022         if(
25023                 !this.isDocument &&
25024                 (this.rotate == 90 || this.rotate == 270) && 
25025                 (
25026                     width < this.minHeight || 
25027                     width > this.imageEl.OriginWidth || 
25028                     height < this.minWidth || 
25029                     height > this.imageEl.OriginHeight
25030                 )
25031         ){
25032             return false;
25033         }
25034         
25035         return true;
25036         
25037     },
25038     
25039     onRotateLeft : function(e)
25040     {   
25041         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25042             
25043             var minScale = this.thumbEl.getWidth() / this.minWidth;
25044             
25045             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25046             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25047             
25048             this.startScale = this.scale;
25049             
25050             while (this.getScaleLevel() < minScale){
25051             
25052                 this.scale = this.scale + 1;
25053                 
25054                 if(!this.zoomable()){
25055                     break;
25056                 }
25057                 
25058                 if(
25059                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25060                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25061                 ){
25062                     continue;
25063                 }
25064                 
25065                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25066
25067                 this.draw();
25068                 
25069                 return;
25070             }
25071             
25072             this.scale = this.startScale;
25073             
25074             this.onRotateFail();
25075             
25076             return false;
25077         }
25078         
25079         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25080
25081         if(this.isDocument){
25082             this.setThumbBoxSize();
25083             this.setThumbBoxPosition();
25084             this.setCanvasPosition();
25085         }
25086         
25087         this.draw();
25088         
25089         this.fireEvent('rotate', this, 'left');
25090         
25091     },
25092     
25093     onRotateRight : function(e)
25094     {
25095         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25096             
25097             var minScale = this.thumbEl.getWidth() / this.minWidth;
25098         
25099             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25100             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25101             
25102             this.startScale = this.scale;
25103             
25104             while (this.getScaleLevel() < minScale){
25105             
25106                 this.scale = this.scale + 1;
25107                 
25108                 if(!this.zoomable()){
25109                     break;
25110                 }
25111                 
25112                 if(
25113                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25114                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25115                 ){
25116                     continue;
25117                 }
25118                 
25119                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25120
25121                 this.draw();
25122                 
25123                 return;
25124             }
25125             
25126             this.scale = this.startScale;
25127             
25128             this.onRotateFail();
25129             
25130             return false;
25131         }
25132         
25133         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25134
25135         if(this.isDocument){
25136             this.setThumbBoxSize();
25137             this.setThumbBoxPosition();
25138             this.setCanvasPosition();
25139         }
25140         
25141         this.draw();
25142         
25143         this.fireEvent('rotate', this, 'right');
25144     },
25145     
25146     onRotateFail : function()
25147     {
25148         this.errorEl.show(true);
25149         
25150         var _this = this;
25151         
25152         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
25153     },
25154     
25155     draw : function()
25156     {
25157         this.previewEl.dom.innerHTML = '';
25158         
25159         var canvasEl = document.createElement("canvas");
25160         
25161         var contextEl = canvasEl.getContext("2d");
25162         
25163         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25164         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25165         var center = this.imageEl.OriginWidth / 2;
25166         
25167         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
25168             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25169             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25170             center = this.imageEl.OriginHeight / 2;
25171         }
25172         
25173         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
25174         
25175         contextEl.translate(center, center);
25176         contextEl.rotate(this.rotate * Math.PI / 180);
25177
25178         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25179         
25180         this.canvasEl = document.createElement("canvas");
25181         
25182         this.contextEl = this.canvasEl.getContext("2d");
25183         
25184         switch (this.rotate) {
25185             case 0 :
25186                 
25187                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25188                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25189                 
25190                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25191                 
25192                 break;
25193             case 90 : 
25194                 
25195                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25196                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25197                 
25198                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25199                     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);
25200                     break;
25201                 }
25202                 
25203                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25204                 
25205                 break;
25206             case 180 :
25207                 
25208                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25209                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25210                 
25211                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25212                     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);
25213                     break;
25214                 }
25215                 
25216                 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);
25217                 
25218                 break;
25219             case 270 :
25220                 
25221                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25222                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25223         
25224                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25225                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25226                     break;
25227                 }
25228                 
25229                 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);
25230                 
25231                 break;
25232             default : 
25233                 break;
25234         }
25235         
25236         this.previewEl.appendChild(this.canvasEl);
25237         
25238         this.setCanvasPosition();
25239     },
25240     
25241     crop : function()
25242     {
25243         if(!this.canvasLoaded){
25244             return;
25245         }
25246         
25247         var imageCanvas = document.createElement("canvas");
25248         
25249         var imageContext = imageCanvas.getContext("2d");
25250         
25251         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25252         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25253         
25254         var center = imageCanvas.width / 2;
25255         
25256         imageContext.translate(center, center);
25257         
25258         imageContext.rotate(this.rotate * Math.PI / 180);
25259         
25260         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25261         
25262         var canvas = document.createElement("canvas");
25263         
25264         var context = canvas.getContext("2d");
25265                 
25266         canvas.width = this.minWidth;
25267         canvas.height = this.minHeight;
25268
25269         switch (this.rotate) {
25270             case 0 :
25271                 
25272                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25273                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25274                 
25275                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25276                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25277                 
25278                 var targetWidth = this.minWidth - 2 * x;
25279                 var targetHeight = this.minHeight - 2 * y;
25280                 
25281                 var scale = 1;
25282                 
25283                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25284                     scale = targetWidth / width;
25285                 }
25286                 
25287                 if(x > 0 && y == 0){
25288                     scale = targetHeight / height;
25289                 }
25290                 
25291                 if(x > 0 && y > 0){
25292                     scale = targetWidth / width;
25293                     
25294                     if(width < height){
25295                         scale = targetHeight / height;
25296                     }
25297                 }
25298                 
25299                 context.scale(scale, scale);
25300                 
25301                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25302                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25303
25304                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25305                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25306
25307                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25308                 
25309                 break;
25310             case 90 : 
25311                 
25312                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25313                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25314                 
25315                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25316                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25317                 
25318                 var targetWidth = this.minWidth - 2 * x;
25319                 var targetHeight = this.minHeight - 2 * y;
25320                 
25321                 var scale = 1;
25322                 
25323                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25324                     scale = targetWidth / width;
25325                 }
25326                 
25327                 if(x > 0 && y == 0){
25328                     scale = targetHeight / height;
25329                 }
25330                 
25331                 if(x > 0 && y > 0){
25332                     scale = targetWidth / width;
25333                     
25334                     if(width < height){
25335                         scale = targetHeight / height;
25336                     }
25337                 }
25338                 
25339                 context.scale(scale, scale);
25340                 
25341                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25342                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25343
25344                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25345                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25346                 
25347                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25348                 
25349                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25350                 
25351                 break;
25352             case 180 :
25353                 
25354                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25355                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25356                 
25357                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25358                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25359                 
25360                 var targetWidth = this.minWidth - 2 * x;
25361                 var targetHeight = this.minHeight - 2 * y;
25362                 
25363                 var scale = 1;
25364                 
25365                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25366                     scale = targetWidth / width;
25367                 }
25368                 
25369                 if(x > 0 && y == 0){
25370                     scale = targetHeight / height;
25371                 }
25372                 
25373                 if(x > 0 && y > 0){
25374                     scale = targetWidth / width;
25375                     
25376                     if(width < height){
25377                         scale = targetHeight / height;
25378                     }
25379                 }
25380                 
25381                 context.scale(scale, scale);
25382                 
25383                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25384                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25385
25386                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25387                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25388
25389                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25390                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25391                 
25392                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25393                 
25394                 break;
25395             case 270 :
25396                 
25397                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25398                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25399                 
25400                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25401                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25402                 
25403                 var targetWidth = this.minWidth - 2 * x;
25404                 var targetHeight = this.minHeight - 2 * y;
25405                 
25406                 var scale = 1;
25407                 
25408                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25409                     scale = targetWidth / width;
25410                 }
25411                 
25412                 if(x > 0 && y == 0){
25413                     scale = targetHeight / height;
25414                 }
25415                 
25416                 if(x > 0 && y > 0){
25417                     scale = targetWidth / width;
25418                     
25419                     if(width < height){
25420                         scale = targetHeight / height;
25421                     }
25422                 }
25423                 
25424                 context.scale(scale, scale);
25425                 
25426                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25427                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25428
25429                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25430                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25431                 
25432                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25433                 
25434                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25435                 
25436                 break;
25437             default : 
25438                 break;
25439         }
25440         
25441         this.cropData = canvas.toDataURL(this.cropType);
25442         
25443         if(this.fireEvent('crop', this, this.cropData) !== false){
25444             this.process(this.file, this.cropData);
25445         }
25446         
25447         return;
25448         
25449     },
25450     
25451     setThumbBoxSize : function()
25452     {
25453         var width, height;
25454         
25455         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
25456             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
25457             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
25458             
25459             this.minWidth = width;
25460             this.minHeight = height;
25461             
25462             if(this.rotate == 90 || this.rotate == 270){
25463                 this.minWidth = height;
25464                 this.minHeight = width;
25465             }
25466         }
25467         
25468         height = 300;
25469         width = Math.ceil(this.minWidth * height / this.minHeight);
25470         
25471         if(this.minWidth > this.minHeight){
25472             width = 300;
25473             height = Math.ceil(this.minHeight * width / this.minWidth);
25474         }
25475         
25476         this.thumbEl.setStyle({
25477             width : width + 'px',
25478             height : height + 'px'
25479         });
25480
25481         return;
25482             
25483     },
25484     
25485     setThumbBoxPosition : function()
25486     {
25487         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
25488         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
25489         
25490         this.thumbEl.setLeft(x);
25491         this.thumbEl.setTop(y);
25492         
25493     },
25494     
25495     baseRotateLevel : function()
25496     {
25497         this.baseRotate = 1;
25498         
25499         if(
25500                 typeof(this.exif) != 'undefined' &&
25501                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
25502                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
25503         ){
25504             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
25505         }
25506         
25507         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
25508         
25509     },
25510     
25511     baseScaleLevel : function()
25512     {
25513         var width, height;
25514         
25515         if(this.isDocument){
25516             
25517             if(this.baseRotate == 6 || this.baseRotate == 8){
25518             
25519                 height = this.thumbEl.getHeight();
25520                 this.baseScale = height / this.imageEl.OriginWidth;
25521
25522                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
25523                     width = this.thumbEl.getWidth();
25524                     this.baseScale = width / this.imageEl.OriginHeight;
25525                 }
25526
25527                 return;
25528             }
25529
25530             height = this.thumbEl.getHeight();
25531             this.baseScale = height / this.imageEl.OriginHeight;
25532
25533             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
25534                 width = this.thumbEl.getWidth();
25535                 this.baseScale = width / this.imageEl.OriginWidth;
25536             }
25537
25538             return;
25539         }
25540         
25541         if(this.baseRotate == 6 || this.baseRotate == 8){
25542             
25543             width = this.thumbEl.getHeight();
25544             this.baseScale = width / this.imageEl.OriginHeight;
25545             
25546             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
25547                 height = this.thumbEl.getWidth();
25548                 this.baseScale = height / this.imageEl.OriginHeight;
25549             }
25550             
25551             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25552                 height = this.thumbEl.getWidth();
25553                 this.baseScale = height / this.imageEl.OriginHeight;
25554                 
25555                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
25556                     width = this.thumbEl.getHeight();
25557                     this.baseScale = width / this.imageEl.OriginWidth;
25558                 }
25559             }
25560             
25561             return;
25562         }
25563         
25564         width = this.thumbEl.getWidth();
25565         this.baseScale = width / this.imageEl.OriginWidth;
25566         
25567         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
25568             height = this.thumbEl.getHeight();
25569             this.baseScale = height / this.imageEl.OriginHeight;
25570         }
25571         
25572         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25573             
25574             height = this.thumbEl.getHeight();
25575             this.baseScale = height / this.imageEl.OriginHeight;
25576             
25577             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
25578                 width = this.thumbEl.getWidth();
25579                 this.baseScale = width / this.imageEl.OriginWidth;
25580             }
25581             
25582         }
25583         
25584         return;
25585     },
25586     
25587     getScaleLevel : function()
25588     {
25589         return this.baseScale * Math.pow(1.1, this.scale);
25590     },
25591     
25592     onTouchStart : function(e)
25593     {
25594         if(!this.canvasLoaded){
25595             this.beforeSelectFile(e);
25596             return;
25597         }
25598         
25599         var touches = e.browserEvent.touches;
25600         
25601         if(!touches){
25602             return;
25603         }
25604         
25605         if(touches.length == 1){
25606             this.onMouseDown(e);
25607             return;
25608         }
25609         
25610         if(touches.length != 2){
25611             return;
25612         }
25613         
25614         var coords = [];
25615         
25616         for(var i = 0, finger; finger = touches[i]; i++){
25617             coords.push(finger.pageX, finger.pageY);
25618         }
25619         
25620         var x = Math.pow(coords[0] - coords[2], 2);
25621         var y = Math.pow(coords[1] - coords[3], 2);
25622         
25623         this.startDistance = Math.sqrt(x + y);
25624         
25625         this.startScale = this.scale;
25626         
25627         this.pinching = true;
25628         this.dragable = false;
25629         
25630     },
25631     
25632     onTouchMove : function(e)
25633     {
25634         if(!this.pinching && !this.dragable){
25635             return;
25636         }
25637         
25638         var touches = e.browserEvent.touches;
25639         
25640         if(!touches){
25641             return;
25642         }
25643         
25644         if(this.dragable){
25645             this.onMouseMove(e);
25646             return;
25647         }
25648         
25649         var coords = [];
25650         
25651         for(var i = 0, finger; finger = touches[i]; i++){
25652             coords.push(finger.pageX, finger.pageY);
25653         }
25654         
25655         var x = Math.pow(coords[0] - coords[2], 2);
25656         var y = Math.pow(coords[1] - coords[3], 2);
25657         
25658         this.endDistance = Math.sqrt(x + y);
25659         
25660         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
25661         
25662         if(!this.zoomable()){
25663             this.scale = this.startScale;
25664             return;
25665         }
25666         
25667         this.draw();
25668         
25669     },
25670     
25671     onTouchEnd : function(e)
25672     {
25673         this.pinching = false;
25674         this.dragable = false;
25675         
25676     },
25677     
25678     process : function(file, crop)
25679     {
25680         if(this.loadMask){
25681             this.maskEl.mask(this.loadingText);
25682         }
25683         
25684         this.xhr = new XMLHttpRequest();
25685         
25686         file.xhr = this.xhr;
25687
25688         this.xhr.open(this.method, this.url, true);
25689         
25690         var headers = {
25691             "Accept": "application/json",
25692             "Cache-Control": "no-cache",
25693             "X-Requested-With": "XMLHttpRequest"
25694         };
25695         
25696         for (var headerName in headers) {
25697             var headerValue = headers[headerName];
25698             if (headerValue) {
25699                 this.xhr.setRequestHeader(headerName, headerValue);
25700             }
25701         }
25702         
25703         var _this = this;
25704         
25705         this.xhr.onload = function()
25706         {
25707             _this.xhrOnLoad(_this.xhr);
25708         }
25709         
25710         this.xhr.onerror = function()
25711         {
25712             _this.xhrOnError(_this.xhr);
25713         }
25714         
25715         var formData = new FormData();
25716
25717         formData.append('returnHTML', 'NO');
25718         
25719         if(crop){
25720             formData.append('crop', crop);
25721         }
25722         
25723         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
25724             formData.append(this.paramName, file, file.name);
25725         }
25726         
25727         if(typeof(file.filename) != 'undefined'){
25728             formData.append('filename', file.filename);
25729         }
25730         
25731         if(typeof(file.mimetype) != 'undefined'){
25732             formData.append('mimetype', file.mimetype);
25733         }
25734         
25735         if(this.fireEvent('arrange', this, formData) != false){
25736             this.xhr.send(formData);
25737         };
25738     },
25739     
25740     xhrOnLoad : function(xhr)
25741     {
25742         if(this.loadMask){
25743             this.maskEl.unmask();
25744         }
25745         
25746         if (xhr.readyState !== 4) {
25747             this.fireEvent('exception', this, xhr);
25748             return;
25749         }
25750
25751         var response = Roo.decode(xhr.responseText);
25752         
25753         if(!response.success){
25754             this.fireEvent('exception', this, xhr);
25755             return;
25756         }
25757         
25758         var response = Roo.decode(xhr.responseText);
25759         
25760         this.fireEvent('upload', this, response);
25761         
25762     },
25763     
25764     xhrOnError : function()
25765     {
25766         if(this.loadMask){
25767             this.maskEl.unmask();
25768         }
25769         
25770         Roo.log('xhr on error');
25771         
25772         var response = Roo.decode(xhr.responseText);
25773           
25774         Roo.log(response);
25775         
25776     },
25777     
25778     prepare : function(file)
25779     {   
25780         if(this.loadMask){
25781             this.maskEl.mask(this.loadingText);
25782         }
25783         
25784         this.file = false;
25785         this.exif = {};
25786         
25787         if(typeof(file) === 'string'){
25788             this.loadCanvas(file);
25789             return;
25790         }
25791         
25792         if(!file || !this.urlAPI){
25793             return;
25794         }
25795         
25796         this.file = file;
25797         this.cropType = file.type;
25798         
25799         var _this = this;
25800         
25801         if(this.fireEvent('prepare', this, this.file) != false){
25802             
25803             var reader = new FileReader();
25804             
25805             reader.onload = function (e) {
25806                 if (e.target.error) {
25807                     Roo.log(e.target.error);
25808                     return;
25809                 }
25810                 
25811                 var buffer = e.target.result,
25812                     dataView = new DataView(buffer),
25813                     offset = 2,
25814                     maxOffset = dataView.byteLength - 4,
25815                     markerBytes,
25816                     markerLength;
25817                 
25818                 if (dataView.getUint16(0) === 0xffd8) {
25819                     while (offset < maxOffset) {
25820                         markerBytes = dataView.getUint16(offset);
25821                         
25822                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
25823                             markerLength = dataView.getUint16(offset + 2) + 2;
25824                             if (offset + markerLength > dataView.byteLength) {
25825                                 Roo.log('Invalid meta data: Invalid segment size.');
25826                                 break;
25827                             }
25828                             
25829                             if(markerBytes == 0xffe1){
25830                                 _this.parseExifData(
25831                                     dataView,
25832                                     offset,
25833                                     markerLength
25834                                 );
25835                             }
25836                             
25837                             offset += markerLength;
25838                             
25839                             continue;
25840                         }
25841                         
25842                         break;
25843                     }
25844                     
25845                 }
25846                 
25847                 var url = _this.urlAPI.createObjectURL(_this.file);
25848                 
25849                 _this.loadCanvas(url);
25850                 
25851                 return;
25852             }
25853             
25854             reader.readAsArrayBuffer(this.file);
25855             
25856         }
25857         
25858     },
25859     
25860     parseExifData : function(dataView, offset, length)
25861     {
25862         var tiffOffset = offset + 10,
25863             littleEndian,
25864             dirOffset;
25865     
25866         if (dataView.getUint32(offset + 4) !== 0x45786966) {
25867             // No Exif data, might be XMP data instead
25868             return;
25869         }
25870         
25871         // Check for the ASCII code for "Exif" (0x45786966):
25872         if (dataView.getUint32(offset + 4) !== 0x45786966) {
25873             // No Exif data, might be XMP data instead
25874             return;
25875         }
25876         if (tiffOffset + 8 > dataView.byteLength) {
25877             Roo.log('Invalid Exif data: Invalid segment size.');
25878             return;
25879         }
25880         // Check for the two null bytes:
25881         if (dataView.getUint16(offset + 8) !== 0x0000) {
25882             Roo.log('Invalid Exif data: Missing byte alignment offset.');
25883             return;
25884         }
25885         // Check the byte alignment:
25886         switch (dataView.getUint16(tiffOffset)) {
25887         case 0x4949:
25888             littleEndian = true;
25889             break;
25890         case 0x4D4D:
25891             littleEndian = false;
25892             break;
25893         default:
25894             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
25895             return;
25896         }
25897         // Check for the TIFF tag marker (0x002A):
25898         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
25899             Roo.log('Invalid Exif data: Missing TIFF marker.');
25900             return;
25901         }
25902         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
25903         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
25904         
25905         this.parseExifTags(
25906             dataView,
25907             tiffOffset,
25908             tiffOffset + dirOffset,
25909             littleEndian
25910         );
25911     },
25912     
25913     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
25914     {
25915         var tagsNumber,
25916             dirEndOffset,
25917             i;
25918         if (dirOffset + 6 > dataView.byteLength) {
25919             Roo.log('Invalid Exif data: Invalid directory offset.');
25920             return;
25921         }
25922         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
25923         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
25924         if (dirEndOffset + 4 > dataView.byteLength) {
25925             Roo.log('Invalid Exif data: Invalid directory size.');
25926             return;
25927         }
25928         for (i = 0; i < tagsNumber; i += 1) {
25929             this.parseExifTag(
25930                 dataView,
25931                 tiffOffset,
25932                 dirOffset + 2 + 12 * i, // tag offset
25933                 littleEndian
25934             );
25935         }
25936         // Return the offset to the next directory:
25937         return dataView.getUint32(dirEndOffset, littleEndian);
25938     },
25939     
25940     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
25941     {
25942         var tag = dataView.getUint16(offset, littleEndian);
25943         
25944         this.exif[tag] = this.getExifValue(
25945             dataView,
25946             tiffOffset,
25947             offset,
25948             dataView.getUint16(offset + 2, littleEndian), // tag type
25949             dataView.getUint32(offset + 4, littleEndian), // tag length
25950             littleEndian
25951         );
25952     },
25953     
25954     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
25955     {
25956         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
25957             tagSize,
25958             dataOffset,
25959             values,
25960             i,
25961             str,
25962             c;
25963     
25964         if (!tagType) {
25965             Roo.log('Invalid Exif data: Invalid tag type.');
25966             return;
25967         }
25968         
25969         tagSize = tagType.size * length;
25970         // Determine if the value is contained in the dataOffset bytes,
25971         // or if the value at the dataOffset is a pointer to the actual data:
25972         dataOffset = tagSize > 4 ?
25973                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
25974         if (dataOffset + tagSize > dataView.byteLength) {
25975             Roo.log('Invalid Exif data: Invalid data offset.');
25976             return;
25977         }
25978         if (length === 1) {
25979             return tagType.getValue(dataView, dataOffset, littleEndian);
25980         }
25981         values = [];
25982         for (i = 0; i < length; i += 1) {
25983             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
25984         }
25985         
25986         if (tagType.ascii) {
25987             str = '';
25988             // Concatenate the chars:
25989             for (i = 0; i < values.length; i += 1) {
25990                 c = values[i];
25991                 // Ignore the terminating NULL byte(s):
25992                 if (c === '\u0000') {
25993                     break;
25994                 }
25995                 str += c;
25996             }
25997             return str;
25998         }
25999         return values;
26000     }
26001     
26002 });
26003
26004 Roo.apply(Roo.bootstrap.UploadCropbox, {
26005     tags : {
26006         'Orientation': 0x0112
26007     },
26008     
26009     Orientation: {
26010             1: 0, //'top-left',
26011 //            2: 'top-right',
26012             3: 180, //'bottom-right',
26013 //            4: 'bottom-left',
26014 //            5: 'left-top',
26015             6: 90, //'right-top',
26016 //            7: 'right-bottom',
26017             8: 270 //'left-bottom'
26018     },
26019     
26020     exifTagTypes : {
26021         // byte, 8-bit unsigned int:
26022         1: {
26023             getValue: function (dataView, dataOffset) {
26024                 return dataView.getUint8(dataOffset);
26025             },
26026             size: 1
26027         },
26028         // ascii, 8-bit byte:
26029         2: {
26030             getValue: function (dataView, dataOffset) {
26031                 return String.fromCharCode(dataView.getUint8(dataOffset));
26032             },
26033             size: 1,
26034             ascii: true
26035         },
26036         // short, 16 bit int:
26037         3: {
26038             getValue: function (dataView, dataOffset, littleEndian) {
26039                 return dataView.getUint16(dataOffset, littleEndian);
26040             },
26041             size: 2
26042         },
26043         // long, 32 bit int:
26044         4: {
26045             getValue: function (dataView, dataOffset, littleEndian) {
26046                 return dataView.getUint32(dataOffset, littleEndian);
26047             },
26048             size: 4
26049         },
26050         // rational = two long values, first is numerator, second is denominator:
26051         5: {
26052             getValue: function (dataView, dataOffset, littleEndian) {
26053                 return dataView.getUint32(dataOffset, littleEndian) /
26054                     dataView.getUint32(dataOffset + 4, littleEndian);
26055             },
26056             size: 8
26057         },
26058         // slong, 32 bit signed int:
26059         9: {
26060             getValue: function (dataView, dataOffset, littleEndian) {
26061                 return dataView.getInt32(dataOffset, littleEndian);
26062             },
26063             size: 4
26064         },
26065         // srational, two slongs, first is numerator, second is denominator:
26066         10: {
26067             getValue: function (dataView, dataOffset, littleEndian) {
26068                 return dataView.getInt32(dataOffset, littleEndian) /
26069                     dataView.getInt32(dataOffset + 4, littleEndian);
26070             },
26071             size: 8
26072         }
26073     },
26074     
26075     footer : {
26076         STANDARD : [
26077             {
26078                 tag : 'div',
26079                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26080                 action : 'rotate-left',
26081                 cn : [
26082                     {
26083                         tag : 'button',
26084                         cls : 'btn btn-default',
26085                         html : '<i class="fa fa-undo"></i>'
26086                     }
26087                 ]
26088             },
26089             {
26090                 tag : 'div',
26091                 cls : 'btn-group roo-upload-cropbox-picture',
26092                 action : 'picture',
26093                 cn : [
26094                     {
26095                         tag : 'button',
26096                         cls : 'btn btn-default',
26097                         html : '<i class="fa fa-picture-o"></i>'
26098                     }
26099                 ]
26100             },
26101             {
26102                 tag : 'div',
26103                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26104                 action : 'rotate-right',
26105                 cn : [
26106                     {
26107                         tag : 'button',
26108                         cls : 'btn btn-default',
26109                         html : '<i class="fa fa-repeat"></i>'
26110                     }
26111                 ]
26112             }
26113         ],
26114         DOCUMENT : [
26115             {
26116                 tag : 'div',
26117                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26118                 action : 'rotate-left',
26119                 cn : [
26120                     {
26121                         tag : 'button',
26122                         cls : 'btn btn-default',
26123                         html : '<i class="fa fa-undo"></i>'
26124                     }
26125                 ]
26126             },
26127             {
26128                 tag : 'div',
26129                 cls : 'btn-group roo-upload-cropbox-download',
26130                 action : 'download',
26131                 cn : [
26132                     {
26133                         tag : 'button',
26134                         cls : 'btn btn-default',
26135                         html : '<i class="fa fa-download"></i>'
26136                     }
26137                 ]
26138             },
26139             {
26140                 tag : 'div',
26141                 cls : 'btn-group roo-upload-cropbox-crop',
26142                 action : 'crop',
26143                 cn : [
26144                     {
26145                         tag : 'button',
26146                         cls : 'btn btn-default',
26147                         html : '<i class="fa fa-crop"></i>'
26148                     }
26149                 ]
26150             },
26151             {
26152                 tag : 'div',
26153                 cls : 'btn-group roo-upload-cropbox-trash',
26154                 action : 'trash',
26155                 cn : [
26156                     {
26157                         tag : 'button',
26158                         cls : 'btn btn-default',
26159                         html : '<i class="fa fa-trash"></i>'
26160                     }
26161                 ]
26162             },
26163             {
26164                 tag : 'div',
26165                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26166                 action : 'rotate-right',
26167                 cn : [
26168                     {
26169                         tag : 'button',
26170                         cls : 'btn btn-default',
26171                         html : '<i class="fa fa-repeat"></i>'
26172                     }
26173                 ]
26174             }
26175         ],
26176         ROTATOR : [
26177             {
26178                 tag : 'div',
26179                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26180                 action : 'rotate-left',
26181                 cn : [
26182                     {
26183                         tag : 'button',
26184                         cls : 'btn btn-default',
26185                         html : '<i class="fa fa-undo"></i>'
26186                     }
26187                 ]
26188             },
26189             {
26190                 tag : 'div',
26191                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26192                 action : 'rotate-right',
26193                 cn : [
26194                     {
26195                         tag : 'button',
26196                         cls : 'btn btn-default',
26197                         html : '<i class="fa fa-repeat"></i>'
26198                     }
26199                 ]
26200             }
26201         ]
26202     }
26203 });
26204
26205 /*
26206 * Licence: LGPL
26207 */
26208
26209 /**
26210  * @class Roo.bootstrap.DocumentManager
26211  * @extends Roo.bootstrap.Component
26212  * Bootstrap DocumentManager class
26213  * @cfg {String} paramName default 'imageUpload'
26214  * @cfg {String} method default POST
26215  * @cfg {String} url action url
26216  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
26217  * @cfg {Boolean} multiple multiple upload default true
26218  * @cfg {Number} thumbSize default 300
26219  * @cfg {String} fieldLabel
26220  * @cfg {Number} labelWidth default 4
26221  * @cfg {String} labelAlign (left|top) default left
26222  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
26223  * 
26224  * @constructor
26225  * Create a new DocumentManager
26226  * @param {Object} config The config object
26227  */
26228
26229 Roo.bootstrap.DocumentManager = function(config){
26230     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26231     
26232     this.addEvents({
26233         /**
26234          * @event initial
26235          * Fire when initial the DocumentManager
26236          * @param {Roo.bootstrap.DocumentManager} this
26237          */
26238         "initial" : true,
26239         /**
26240          * @event inspect
26241          * inspect selected file
26242          * @param {Roo.bootstrap.DocumentManager} this
26243          * @param {File} file
26244          */
26245         "inspect" : true,
26246         /**
26247          * @event exception
26248          * Fire when xhr load exception
26249          * @param {Roo.bootstrap.DocumentManager} this
26250          * @param {XMLHttpRequest} xhr
26251          */
26252         "exception" : true,
26253         /**
26254          * @event prepare
26255          * prepare the form data
26256          * @param {Roo.bootstrap.DocumentManager} this
26257          * @param {Object} formData
26258          */
26259         "prepare" : true,
26260         /**
26261          * @event remove
26262          * Fire when remove the file
26263          * @param {Roo.bootstrap.DocumentManager} this
26264          * @param {Object} file
26265          */
26266         "remove" : true,
26267         /**
26268          * @event refresh
26269          * Fire after refresh the file
26270          * @param {Roo.bootstrap.DocumentManager} this
26271          */
26272         "refresh" : true,
26273         /**
26274          * @event click
26275          * Fire after click the image
26276          * @param {Roo.bootstrap.DocumentManager} this
26277          * @param {Object} file
26278          */
26279         "click" : true,
26280         /**
26281          * @event edit
26282          * Fire when upload a image and editable set to true
26283          * @param {Roo.bootstrap.DocumentManager} this
26284          * @param {Object} file
26285          */
26286         "edit" : true,
26287         /**
26288          * @event beforeselectfile
26289          * Fire before select file
26290          * @param {Roo.bootstrap.DocumentManager} this
26291          */
26292         "beforeselectfile" : true,
26293         /**
26294          * @event process
26295          * Fire before process file
26296          * @param {Roo.bootstrap.DocumentManager} this
26297          * @param {Object} file
26298          */
26299         "process" : true
26300         
26301     });
26302 };
26303
26304 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
26305     
26306     boxes : 0,
26307     inputName : '',
26308     thumbSize : 300,
26309     multiple : true,
26310     files : [],
26311     method : 'POST',
26312     url : '',
26313     paramName : 'imageUpload',
26314     fieldLabel : '',
26315     labelWidth : 4,
26316     labelAlign : 'left',
26317     editable : true,
26318     delegates : [],
26319     
26320     
26321     xhr : false, 
26322     
26323     getAutoCreate : function()
26324     {   
26325         var managerWidget = {
26326             tag : 'div',
26327             cls : 'roo-document-manager',
26328             cn : [
26329                 {
26330                     tag : 'input',
26331                     cls : 'roo-document-manager-selector',
26332                     type : 'file'
26333                 },
26334                 {
26335                     tag : 'div',
26336                     cls : 'roo-document-manager-uploader',
26337                     cn : [
26338                         {
26339                             tag : 'div',
26340                             cls : 'roo-document-manager-upload-btn',
26341                             html : '<i class="fa fa-plus"></i>'
26342                         }
26343                     ]
26344                     
26345                 }
26346             ]
26347         };
26348         
26349         var content = [
26350             {
26351                 tag : 'div',
26352                 cls : 'column col-md-12',
26353                 cn : managerWidget
26354             }
26355         ];
26356         
26357         if(this.fieldLabel.length){
26358             
26359             content = [
26360                 {
26361                     tag : 'div',
26362                     cls : 'column col-md-12',
26363                     html : this.fieldLabel
26364                 },
26365                 {
26366                     tag : 'div',
26367                     cls : 'column col-md-12',
26368                     cn : managerWidget
26369                 }
26370             ];
26371
26372             if(this.labelAlign == 'left'){
26373                 content = [
26374                     {
26375                         tag : 'div',
26376                         cls : 'column col-md-' + this.labelWidth,
26377                         html : this.fieldLabel
26378                     },
26379                     {
26380                         tag : 'div',
26381                         cls : 'column col-md-' + (12 - this.labelWidth),
26382                         cn : managerWidget
26383                     }
26384                 ];
26385                 
26386             }
26387         }
26388         
26389         var cfg = {
26390             tag : 'div',
26391             cls : 'row clearfix',
26392             cn : content
26393         };
26394         
26395         return cfg;
26396         
26397     },
26398     
26399     initEvents : function()
26400     {
26401         this.managerEl = this.el.select('.roo-document-manager', true).first();
26402         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26403         
26404         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
26405         this.selectorEl.hide();
26406         
26407         if(this.multiple){
26408             this.selectorEl.attr('multiple', 'multiple');
26409         }
26410         
26411         this.selectorEl.on('change', this.onFileSelected, this);
26412         
26413         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
26414         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26415         
26416         this.uploader.on('click', this.onUploaderClick, this);
26417         
26418         this.renderProgressDialog();
26419         
26420         var _this = this;
26421         
26422         window.addEventListener("resize", function() { _this.refresh(); } );
26423         
26424         this.fireEvent('initial', this);
26425     },
26426     
26427     renderProgressDialog : function()
26428     {
26429         var _this = this;
26430         
26431         this.progressDialog = new Roo.bootstrap.Modal({
26432             cls : 'roo-document-manager-progress-dialog',
26433             allow_close : false,
26434             title : '',
26435             buttons : [
26436                 {
26437                     name  :'cancel',
26438                     weight : 'danger',
26439                     html : 'Cancel'
26440                 }
26441             ], 
26442             listeners : { 
26443                 btnclick : function() {
26444                     _this.uploadCancel();
26445                     this.hide();
26446                 }
26447             }
26448         });
26449          
26450         this.progressDialog.render(Roo.get(document.body));
26451          
26452         this.progress = new Roo.bootstrap.Progress({
26453             cls : 'roo-document-manager-progress',
26454             active : true,
26455             striped : true
26456         });
26457         
26458         this.progress.render(this.progressDialog.getChildContainer());
26459         
26460         this.progressBar = new Roo.bootstrap.ProgressBar({
26461             cls : 'roo-document-manager-progress-bar',
26462             aria_valuenow : 0,
26463             aria_valuemin : 0,
26464             aria_valuemax : 12,
26465             panel : 'success'
26466         });
26467         
26468         this.progressBar.render(this.progress.getChildContainer());
26469     },
26470     
26471     onUploaderClick : function(e)
26472     {
26473         e.preventDefault();
26474      
26475         if(this.fireEvent('beforeselectfile', this) != false){
26476             this.selectorEl.dom.click();
26477         }
26478         
26479     },
26480     
26481     onFileSelected : function(e)
26482     {
26483         e.preventDefault();
26484         
26485         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26486             return;
26487         }
26488         
26489         Roo.each(this.selectorEl.dom.files, function(file){
26490             if(this.fireEvent('inspect', this, file) != false){
26491                 this.files.push(file);
26492             }
26493         }, this);
26494         
26495         this.queue();
26496         
26497     },
26498     
26499     queue : function()
26500     {
26501         this.selectorEl.dom.value = '';
26502         
26503         if(!this.files.length){
26504             return;
26505         }
26506         
26507         if(this.boxes > 0 && this.files.length > this.boxes){
26508             this.files = this.files.slice(0, this.boxes);
26509         }
26510         
26511         this.uploader.show();
26512         
26513         if(this.boxes > 0 && this.files.length > this.boxes - 1){
26514             this.uploader.hide();
26515         }
26516         
26517         var _this = this;
26518         
26519         var files = [];
26520         
26521         var docs = [];
26522         
26523         Roo.each(this.files, function(file){
26524             
26525             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26526                 var f = this.renderPreview(file);
26527                 files.push(f);
26528                 return;
26529             }
26530             
26531             if(file.type.indexOf('image') != -1){
26532                 this.delegates.push(
26533                     (function(){
26534                         _this.process(file);
26535                     }).createDelegate(this)
26536                 );
26537         
26538                 return;
26539             }
26540             
26541             docs.push(
26542                 (function(){
26543                     _this.process(file);
26544                 }).createDelegate(this)
26545             );
26546             
26547         }, this);
26548         
26549         this.files = files;
26550         
26551         this.delegates = this.delegates.concat(docs);
26552         
26553         if(!this.delegates.length){
26554             this.refresh();
26555             return;
26556         }
26557         
26558         this.progressBar.aria_valuemax = this.delegates.length;
26559         
26560         this.arrange();
26561         
26562         return;
26563     },
26564     
26565     arrange : function()
26566     {
26567         if(!this.delegates.length){
26568             this.progressDialog.hide();
26569             this.refresh();
26570             return;
26571         }
26572         
26573         var delegate = this.delegates.shift();
26574         
26575         this.progressDialog.show();
26576         
26577         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
26578         
26579         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
26580         
26581         delegate();
26582     },
26583     
26584     refresh : function()
26585     {
26586         this.uploader.show();
26587         
26588         if(this.boxes > 0 && this.files.length > this.boxes - 1){
26589             this.uploader.hide();
26590         }
26591         
26592         Roo.isTouch ? this.closable(false) : this.closable(true);
26593         
26594         this.fireEvent('refresh', this);
26595     },
26596     
26597     onRemove : function(e, el, o)
26598     {
26599         e.preventDefault();
26600         
26601         this.fireEvent('remove', this, o);
26602         
26603     },
26604     
26605     remove : function(o)
26606     {
26607         var files = [];
26608         
26609         Roo.each(this.files, function(file){
26610             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
26611                 files.push(file);
26612                 return;
26613             }
26614
26615             o.target.remove();
26616
26617         }, this);
26618         
26619         this.files = files;
26620         
26621         this.refresh();
26622     },
26623     
26624     clear : function()
26625     {
26626         Roo.each(this.files, function(file){
26627             if(!file.target){
26628                 return;
26629             }
26630             
26631             file.target.remove();
26632
26633         }, this);
26634         
26635         this.files = [];
26636         
26637         this.refresh();
26638     },
26639     
26640     onClick : function(e, el, o)
26641     {
26642         e.preventDefault();
26643         
26644         this.fireEvent('click', this, o);
26645         
26646     },
26647     
26648     closable : function(closable)
26649     {
26650         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
26651             
26652             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26653             
26654             if(closable){
26655                 el.show();
26656                 return;
26657             }
26658             
26659             el.hide();
26660             
26661         }, this);
26662     },
26663     
26664     xhrOnLoad : function(xhr)
26665     {
26666         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26667             el.remove();
26668         }, this);
26669         
26670         if (xhr.readyState !== 4) {
26671             this.arrange();
26672             this.fireEvent('exception', this, xhr);
26673             return;
26674         }
26675
26676         var response = Roo.decode(xhr.responseText);
26677         
26678         if(!response.success){
26679             this.arrange();
26680             this.fireEvent('exception', this, xhr);
26681             return;
26682         }
26683         
26684         var file = this.renderPreview(response.data);
26685         
26686         this.files.push(file);
26687         
26688         this.arrange();
26689         
26690     },
26691     
26692     xhrOnError : function()
26693     {
26694         Roo.log('xhr on error');
26695         
26696         var response = Roo.decode(xhr.responseText);
26697           
26698         Roo.log(response);
26699         
26700         this.arrange();
26701     },
26702     
26703     process : function(file)
26704     {
26705         if(this.fireEvent('process', this, file) !== false){
26706             if(this.editable && file.type.indexOf('image') != -1){
26707                 this.fireEvent('edit', this, file);
26708                 return;
26709             }
26710
26711             this.uploadStart(file, false);
26712
26713             return;
26714         }
26715         
26716     },
26717     
26718     uploadStart : function(file, crop)
26719     {
26720         this.xhr = new XMLHttpRequest();
26721         
26722         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26723             this.arrange();
26724             return;
26725         }
26726         
26727         file.xhr = this.xhr;
26728             
26729         this.managerEl.createChild({
26730             tag : 'div',
26731             cls : 'roo-document-manager-loading',
26732             cn : [
26733                 {
26734                     tag : 'div',
26735                     tooltip : file.name,
26736                     cls : 'roo-document-manager-thumb',
26737                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26738                 }
26739             ]
26740
26741         });
26742
26743         this.xhr.open(this.method, this.url, true);
26744         
26745         var headers = {
26746             "Accept": "application/json",
26747             "Cache-Control": "no-cache",
26748             "X-Requested-With": "XMLHttpRequest"
26749         };
26750         
26751         for (var headerName in headers) {
26752             var headerValue = headers[headerName];
26753             if (headerValue) {
26754                 this.xhr.setRequestHeader(headerName, headerValue);
26755             }
26756         }
26757         
26758         var _this = this;
26759         
26760         this.xhr.onload = function()
26761         {
26762             _this.xhrOnLoad(_this.xhr);
26763         }
26764         
26765         this.xhr.onerror = function()
26766         {
26767             _this.xhrOnError(_this.xhr);
26768         }
26769         
26770         var formData = new FormData();
26771
26772         formData.append('returnHTML', 'NO');
26773         
26774         if(crop){
26775             formData.append('crop', crop);
26776         }
26777         
26778         formData.append(this.paramName, file, file.name);
26779         
26780         if(this.fireEvent('prepare', this, formData) != false){
26781             this.xhr.send(formData);
26782         };
26783     },
26784     
26785     uploadCancel : function()
26786     {
26787         if (this.xhr) {
26788             this.xhr.abort();
26789         }
26790         
26791         
26792         this.delegates = [];
26793         
26794         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26795             el.remove();
26796         }, this);
26797         
26798         this.arrange();
26799     },
26800     
26801     renderPreview : function(file)
26802     {
26803         if(typeof(file.target) != 'undefined' && file.target){
26804             return file;
26805         }
26806         
26807         var previewEl = this.managerEl.createChild({
26808             tag : 'div',
26809             cls : 'roo-document-manager-preview',
26810             cn : [
26811                 {
26812                     tag : 'div',
26813                     tooltip : file.filename,
26814                     cls : 'roo-document-manager-thumb',
26815                     html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
26816                 },
26817                 {
26818                     tag : 'button',
26819                     cls : 'close',
26820                     html : '<i class="fa fa-times-circle"></i>'
26821                 }
26822             ]
26823         });
26824
26825         var close = previewEl.select('button.close', true).first();
26826
26827         close.on('click', this.onRemove, this, file);
26828
26829         file.target = previewEl;
26830
26831         var image = previewEl.select('img', true).first();
26832         
26833         var _this = this;
26834         
26835         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
26836         
26837         image.on('click', this.onClick, this, file);
26838         
26839         return file;
26840         
26841     },
26842     
26843     onPreviewLoad : function(file, image)
26844     {
26845         if(typeof(file.target) == 'undefined' || !file.target){
26846             return;
26847         }
26848         
26849         var width = image.dom.naturalWidth || image.dom.width;
26850         var height = image.dom.naturalHeight || image.dom.height;
26851         
26852         if(width > height){
26853             file.target.addClass('wide');
26854             return;
26855         }
26856         
26857         file.target.addClass('tall');
26858         return;
26859         
26860     },
26861     
26862     uploadFromSource : function(file, crop)
26863     {
26864         this.xhr = new XMLHttpRequest();
26865         
26866         this.managerEl.createChild({
26867             tag : 'div',
26868             cls : 'roo-document-manager-loading',
26869             cn : [
26870                 {
26871                     tag : 'div',
26872                     tooltip : file.name,
26873                     cls : 'roo-document-manager-thumb',
26874                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26875                 }
26876             ]
26877
26878         });
26879
26880         this.xhr.open(this.method, this.url, true);
26881         
26882         var headers = {
26883             "Accept": "application/json",
26884             "Cache-Control": "no-cache",
26885             "X-Requested-With": "XMLHttpRequest"
26886         };
26887         
26888         for (var headerName in headers) {
26889             var headerValue = headers[headerName];
26890             if (headerValue) {
26891                 this.xhr.setRequestHeader(headerName, headerValue);
26892             }
26893         }
26894         
26895         var _this = this;
26896         
26897         this.xhr.onload = function()
26898         {
26899             _this.xhrOnLoad(_this.xhr);
26900         }
26901         
26902         this.xhr.onerror = function()
26903         {
26904             _this.xhrOnError(_this.xhr);
26905         }
26906         
26907         var formData = new FormData();
26908
26909         formData.append('returnHTML', 'NO');
26910         
26911         formData.append('crop', crop);
26912         
26913         if(typeof(file.filename) != 'undefined'){
26914             formData.append('filename', file.filename);
26915         }
26916         
26917         if(typeof(file.mimetype) != 'undefined'){
26918             formData.append('mimetype', file.mimetype);
26919         }
26920         
26921         if(this.fireEvent('prepare', this, formData) != false){
26922             this.xhr.send(formData);
26923         };
26924     }
26925 });
26926
26927 /*
26928 * Licence: LGPL
26929 */
26930
26931 /**
26932  * @class Roo.bootstrap.DocumentViewer
26933  * @extends Roo.bootstrap.Component
26934  * Bootstrap DocumentViewer class
26935  * 
26936  * @constructor
26937  * Create a new DocumentViewer
26938  * @param {Object} config The config object
26939  */
26940
26941 Roo.bootstrap.DocumentViewer = function(config){
26942     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
26943     
26944     this.addEvents({
26945         /**
26946          * @event initial
26947          * Fire after initEvent
26948          * @param {Roo.bootstrap.DocumentViewer} this
26949          */
26950         "initial" : true,
26951         /**
26952          * @event click
26953          * Fire after click
26954          * @param {Roo.bootstrap.DocumentViewer} this
26955          */
26956         "click" : true,
26957         /**
26958          * @event trash
26959          * Fire after trash button
26960          * @param {Roo.bootstrap.DocumentViewer} this
26961          */
26962         "trash" : true
26963         
26964     });
26965 };
26966
26967 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
26968     
26969     getAutoCreate : function()
26970     {
26971         var cfg = {
26972             tag : 'div',
26973             cls : 'roo-document-viewer',
26974             cn : [
26975                 {
26976                     tag : 'div',
26977                     cls : 'roo-document-viewer-body',
26978                     cn : [
26979                         {
26980                             tag : 'div',
26981                             cls : 'roo-document-viewer-thumb',
26982                             cn : [
26983                                 {
26984                                     tag : 'img',
26985                                     cls : 'roo-document-viewer-image'
26986                                 }
26987                             ]
26988                         }
26989                     ]
26990                 },
26991                 {
26992                     tag : 'div',
26993                     cls : 'roo-document-viewer-footer',
26994                     cn : {
26995                         tag : 'div',
26996                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
26997                         cn : [
26998                             {
26999                                 tag : 'div',
27000                                 cls : 'btn-group',
27001                                 cn : [
27002                                     {
27003                                         tag : 'button',
27004                                         cls : 'btn btn-default roo-document-viewer-trash',
27005                                         html : '<i class="fa fa-trash"></i>'
27006                                     }
27007                                 ]
27008                             }
27009                         ]
27010                     }
27011                 }
27012             ]
27013         };
27014         
27015         return cfg;
27016     },
27017     
27018     initEvents : function()
27019     {
27020         
27021         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27022         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27023         
27024         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27025         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27026         
27027         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27028         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27029         
27030         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27031         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27032         
27033         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27034         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27035         
27036         this.bodyEl.on('click', this.onClick, this);
27037         
27038         this.trashBtn.on('click', this.onTrash, this);
27039         
27040     },
27041     
27042     initial : function()
27043     {
27044 //        this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27045         
27046         
27047         this.fireEvent('initial', this);
27048         
27049     },
27050     
27051     onClick : function(e)
27052     {
27053         e.preventDefault();
27054         
27055         this.fireEvent('click', this);
27056     },
27057     
27058     onTrash : function(e)
27059     {
27060         e.preventDefault();
27061         
27062         this.fireEvent('trash', this);
27063     }
27064     
27065 });
27066 /*
27067  * - LGPL
27068  *
27069  * nav progress bar
27070  * 
27071  */
27072
27073 /**
27074  * @class Roo.bootstrap.NavProgressBar
27075  * @extends Roo.bootstrap.Component
27076  * Bootstrap NavProgressBar class
27077  * 
27078  * @constructor
27079  * Create a new nav progress bar
27080  * @param {Object} config The config object
27081  */
27082
27083 Roo.bootstrap.NavProgressBar = function(config){
27084     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
27085
27086     this.bullets = this.bullets || [];
27087    
27088 //    Roo.bootstrap.NavProgressBar.register(this);
27089      this.addEvents({
27090         /**
27091              * @event changed
27092              * Fires when the active item changes
27093              * @param {Roo.bootstrap.NavProgressBar} this
27094              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
27095              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
27096          */
27097         'changed': true
27098      });
27099     
27100 };
27101
27102 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
27103     
27104     bullets : [],
27105     barItems : [],
27106     
27107     getAutoCreate : function()
27108     {
27109         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
27110         
27111         cfg = {
27112             tag : 'div',
27113             cls : 'roo-navigation-bar-group',
27114             cn : [
27115                 {
27116                     tag : 'div',
27117                     cls : 'roo-navigation-top-bar'
27118                 },
27119                 {
27120                     tag : 'div',
27121                     cls : 'roo-navigation-bullets-bar',
27122                     cn : [
27123                         {
27124                             tag : 'ul',
27125                             cls : 'roo-navigation-bar'
27126                         }
27127                     ]
27128                 },
27129                 
27130                 {
27131                     tag : 'div',
27132                     cls : 'roo-navigation-bottom-bar'
27133                 }
27134             ]
27135             
27136         };
27137         
27138         return cfg;
27139         
27140     },
27141     
27142     initEvents: function() 
27143     {
27144         
27145     },
27146     
27147     onRender : function(ct, position) 
27148     {
27149         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27150         
27151         if(this.bullets.length){
27152             Roo.each(this.bullets, function(b){
27153                this.addItem(b);
27154             }, this);
27155         }
27156         
27157         this.format();
27158         
27159     },
27160     
27161     addItem : function(cfg)
27162     {
27163         var item = new Roo.bootstrap.NavProgressItem(cfg);
27164         
27165         item.parentId = this.id;
27166         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
27167         
27168         if(cfg.html){
27169             var top = new Roo.bootstrap.Element({
27170                 tag : 'div',
27171                 cls : 'roo-navigation-bar-text'
27172             });
27173             
27174             var bottom = new Roo.bootstrap.Element({
27175                 tag : 'div',
27176                 cls : 'roo-navigation-bar-text'
27177             });
27178             
27179             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
27180             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
27181             
27182             var topText = new Roo.bootstrap.Element({
27183                 tag : 'span',
27184                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
27185             });
27186             
27187             var bottomText = new Roo.bootstrap.Element({
27188                 tag : 'span',
27189                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
27190             });
27191             
27192             topText.onRender(top.el, null);
27193             bottomText.onRender(bottom.el, null);
27194             
27195             item.topEl = top;
27196             item.bottomEl = bottom;
27197         }
27198         
27199         this.barItems.push(item);
27200         
27201         return item;
27202     },
27203     
27204     getActive : function()
27205     {
27206         var active = false;
27207         
27208         Roo.each(this.barItems, function(v){
27209             
27210             if (!v.isActive()) {
27211                 return;
27212             }
27213             
27214             active = v;
27215             return false;
27216             
27217         });
27218         
27219         return active;
27220     },
27221     
27222     setActiveItem : function(item)
27223     {
27224         var prev = false;
27225         
27226         Roo.each(this.barItems, function(v){
27227             if (v.rid == item.rid) {
27228                 return ;
27229             }
27230             
27231             if (v.isActive()) {
27232                 v.setActive(false);
27233                 prev = v;
27234             }
27235         });
27236
27237         item.setActive(true);
27238         
27239         this.fireEvent('changed', this, item, prev);
27240     },
27241     
27242     getBarItem: function(rid)
27243     {
27244         var ret = false;
27245         
27246         Roo.each(this.barItems, function(e) {
27247             if (e.rid != rid) {
27248                 return;
27249             }
27250             
27251             ret =  e;
27252             return false;
27253         });
27254         
27255         return ret;
27256     },
27257     
27258     indexOfItem : function(item)
27259     {
27260         var index = false;
27261         
27262         Roo.each(this.barItems, function(v, i){
27263             
27264             if (v.rid != item.rid) {
27265                 return;
27266             }
27267             
27268             index = i;
27269             return false
27270         });
27271         
27272         return index;
27273     },
27274     
27275     setActiveNext : function()
27276     {
27277         var i = this.indexOfItem(this.getActive());
27278         
27279         if (i > this.barItems.length) {
27280             return;
27281         }
27282         
27283         this.setActiveItem(this.barItems[i+1]);
27284     },
27285     
27286     setActivePrev : function()
27287     {
27288         var i = this.indexOfItem(this.getActive());
27289         
27290         if (i  < 1) {
27291             return;
27292         }
27293         
27294         this.setActiveItem(this.barItems[i-1]);
27295     },
27296     
27297     format : function()
27298     {
27299         if(!this.barItems.length){
27300             return;
27301         }
27302      
27303         var width = 100 / this.barItems.length;
27304         
27305         Roo.each(this.barItems, function(i){
27306             i.el.setStyle('width', width + '%');
27307             i.topEl.el.setStyle('width', width + '%');
27308             i.bottomEl.el.setStyle('width', width + '%');
27309         }, this);
27310         
27311     }
27312     
27313 });
27314 /*
27315  * - LGPL
27316  *
27317  * Nav Progress Item
27318  * 
27319  */
27320
27321 /**
27322  * @class Roo.bootstrap.NavProgressItem
27323  * @extends Roo.bootstrap.Component
27324  * Bootstrap NavProgressItem class
27325  * @cfg {String} rid the reference id
27326  * @cfg {Boolean} active (true|false) Is item active default false
27327  * @cfg {Boolean} disabled (true|false) Is item active default false
27328  * @cfg {String} html
27329  * @cfg {String} position (top|bottom) text position default bottom
27330  * @cfg {String} icon show icon instead of number
27331  * 
27332  * @constructor
27333  * Create a new NavProgressItem
27334  * @param {Object} config The config object
27335  */
27336 Roo.bootstrap.NavProgressItem = function(config){
27337     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
27338     this.addEvents({
27339         // raw events
27340         /**
27341          * @event click
27342          * The raw click event for the entire grid.
27343          * @param {Roo.bootstrap.NavProgressItem} this
27344          * @param {Roo.EventObject} e
27345          */
27346         "click" : true
27347     });
27348    
27349 };
27350
27351 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
27352     
27353     rid : '',
27354     active : false,
27355     disabled : false,
27356     html : '',
27357     position : 'bottom',
27358     icon : false,
27359     
27360     getAutoCreate : function()
27361     {
27362         var iconCls = 'roo-navigation-bar-item-icon';
27363         
27364         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
27365         
27366         var cfg = {
27367             tag: 'li',
27368             cls: 'roo-navigation-bar-item',
27369             cn : [
27370                 {
27371                     tag : 'i',
27372                     cls : iconCls
27373                 }
27374             ]
27375         };
27376         
27377         if(this.active){
27378             cfg.cls += ' active';
27379         }
27380         if(this.disabled){
27381             cfg.cls += ' disabled';
27382         }
27383         
27384         return cfg;
27385     },
27386     
27387     disable : function()
27388     {
27389         this.setDisabled(true);
27390     },
27391     
27392     enable : function()
27393     {
27394         this.setDisabled(false);
27395     },
27396     
27397     initEvents: function() 
27398     {
27399         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
27400         
27401         this.iconEl.on('click', this.onClick, this);
27402     },
27403     
27404     onClick : function(e)
27405     {
27406         e.preventDefault();
27407         
27408         if(this.disabled){
27409             return;
27410         }
27411         
27412         if(this.fireEvent('click', this, e) === false){
27413             return;
27414         };
27415         
27416         this.parent().setActiveItem(this);
27417     },
27418     
27419     isActive: function () 
27420     {
27421         return this.active;
27422     },
27423     
27424     setActive : function(state)
27425     {
27426         if(this.active == state){
27427             return;
27428         }
27429         
27430         this.active = state;
27431         
27432         if (state) {
27433             this.el.addClass('active');
27434             return;
27435         }
27436         
27437         this.el.removeClass('active');
27438         
27439         return;
27440     },
27441     
27442     setDisabled : function(state)
27443     {
27444         if(this.disabled == state){
27445             return;
27446         }
27447         
27448         this.disabled = state;
27449         
27450         if (state) {
27451             this.el.addClass('disabled');
27452             return;
27453         }
27454         
27455         this.el.removeClass('disabled');
27456     },
27457     
27458     tooltipEl : function()
27459     {
27460         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
27461     }
27462 });
27463  
27464
27465  /*
27466  * - LGPL
27467  *
27468  * FieldLabel
27469  * 
27470  */
27471
27472 /**
27473  * @class Roo.bootstrap.FieldLabel
27474  * @extends Roo.bootstrap.Component
27475  * Bootstrap FieldLabel class
27476  * @cfg {String} html contents of the element
27477  * @cfg {String} tag tag of the element default label
27478  * @cfg {String} cls class of the element
27479  * @cfg {String} target label target 
27480  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
27481  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
27482  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
27483  * @cfg {String} iconTooltip default "This field is required"
27484  * 
27485  * @constructor
27486  * Create a new FieldLabel
27487  * @param {Object} config The config object
27488  */
27489
27490 Roo.bootstrap.FieldLabel = function(config){
27491     Roo.bootstrap.Element.superclass.constructor.call(this, config);
27492     
27493     this.addEvents({
27494             /**
27495              * @event invalid
27496              * Fires after the field has been marked as invalid.
27497              * @param {Roo.form.FieldLabel} this
27498              * @param {String} msg The validation message
27499              */
27500             invalid : true,
27501             /**
27502              * @event valid
27503              * Fires after the field has been validated with no errors.
27504              * @param {Roo.form.FieldLabel} this
27505              */
27506             valid : true
27507         });
27508 };
27509
27510 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
27511     
27512     tag: 'label',
27513     cls: '',
27514     html: '',
27515     target: '',
27516     allowBlank : true,
27517     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
27518     validClass : 'text-success fa fa-lg fa-check',
27519     iconTooltip : 'This field is required',
27520     
27521     getAutoCreate : function(){
27522         
27523         var cfg = {
27524             tag : this.tag,
27525             cls : 'roo-bootstrap-field-label ' + this.cls,
27526             for : this.target,
27527             cn : [
27528                 {
27529                     tag : 'i',
27530                     cls : '',
27531                     tooltip : this.iconTooltip
27532                 },
27533                 {
27534                     tag : 'span',
27535                     html : this.html
27536                 }
27537             ] 
27538         };
27539         
27540         return cfg;
27541     },
27542     
27543     initEvents: function() 
27544     {
27545         Roo.bootstrap.Element.superclass.initEvents.call(this);
27546         
27547         this.iconEl = this.el.select('i', true).first();
27548         
27549         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
27550         
27551         Roo.bootstrap.FieldLabel.register(this);
27552     },
27553     
27554     /**
27555      * Mark this field as valid
27556      */
27557     markValid : function()
27558     {
27559         this.iconEl.show();
27560         
27561         this.iconEl.removeClass(this.invalidClass);
27562         
27563         this.iconEl.addClass(this.validClass);
27564         
27565         this.fireEvent('valid', this);
27566     },
27567     
27568     /**
27569      * Mark this field as invalid
27570      * @param {String} msg The validation message
27571      */
27572     markInvalid : function(msg)
27573     {
27574         this.iconEl.show();
27575         
27576         this.iconEl.removeClass(this.validClass);
27577         
27578         this.iconEl.addClass(this.invalidClass);
27579         
27580         this.fireEvent('invalid', this, msg);
27581     }
27582     
27583    
27584 });
27585
27586 Roo.apply(Roo.bootstrap.FieldLabel, {
27587     
27588     groups: {},
27589     
27590      /**
27591     * register a FieldLabel Group
27592     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
27593     */
27594     register : function(label)
27595     {
27596         if(this.groups.hasOwnProperty(label.target)){
27597             return;
27598         }
27599      
27600         this.groups[label.target] = label;
27601         
27602     },
27603     /**
27604     * fetch a FieldLabel Group based on the target
27605     * @param {string} target
27606     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
27607     */
27608     get: function(target) {
27609         if (typeof(this.groups[target]) == 'undefined') {
27610             return false;
27611         }
27612         
27613         return this.groups[target] ;
27614     }
27615 });
27616
27617  
27618
27619  /*
27620  * - LGPL
27621  *
27622  * page DateSplitField.
27623  * 
27624  */
27625
27626
27627 /**
27628  * @class Roo.bootstrap.DateSplitField
27629  * @extends Roo.bootstrap.Component
27630  * Bootstrap DateSplitField class
27631  * @cfg {string} fieldLabel - the label associated
27632  * @cfg {Number} labelWidth set the width of label (0-12)
27633  * @cfg {String} labelAlign (top|left)
27634  * @cfg {Boolean} dayAllowBlank (true|false) default false
27635  * @cfg {Boolean} monthAllowBlank (true|false) default false
27636  * @cfg {Boolean} yearAllowBlank (true|false) default false
27637  * @cfg {string} dayPlaceholder 
27638  * @cfg {string} monthPlaceholder
27639  * @cfg {string} yearPlaceholder
27640  * @cfg {string} dayFormat default 'd'
27641  * @cfg {string} monthFormat default 'm'
27642  * @cfg {string} yearFormat default 'Y'
27643
27644  *     
27645  * @constructor
27646  * Create a new DateSplitField
27647  * @param {Object} config The config object
27648  */
27649
27650 Roo.bootstrap.DateSplitField = function(config){
27651     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
27652     
27653     this.addEvents({
27654         // raw events
27655          /**
27656          * @event years
27657          * getting the data of years
27658          * @param {Roo.bootstrap.DateSplitField} this
27659          * @param {Object} years
27660          */
27661         "years" : true,
27662         /**
27663          * @event days
27664          * getting the data of days
27665          * @param {Roo.bootstrap.DateSplitField} this
27666          * @param {Object} days
27667          */
27668         "days" : true,
27669         /**
27670          * @event invalid
27671          * Fires after the field has been marked as invalid.
27672          * @param {Roo.form.Field} this
27673          * @param {String} msg The validation message
27674          */
27675         invalid : true,
27676        /**
27677          * @event valid
27678          * Fires after the field has been validated with no errors.
27679          * @param {Roo.form.Field} this
27680          */
27681         valid : true
27682     });
27683 };
27684
27685 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
27686     
27687     fieldLabel : '',
27688     labelAlign : 'top',
27689     labelWidth : 3,
27690     dayAllowBlank : false,
27691     monthAllowBlank : false,
27692     yearAllowBlank : false,
27693     dayPlaceholder : '',
27694     monthPlaceholder : '',
27695     yearPlaceholder : '',
27696     dayFormat : 'd',
27697     monthFormat : 'm',
27698     yearFormat : 'Y',
27699     isFormField : true,
27700     
27701     getAutoCreate : function()
27702     {
27703         var cfg = {
27704             tag : 'div',
27705             cls : 'row roo-date-split-field-group',
27706             cn : [
27707                 {
27708                     tag : 'input',
27709                     type : 'hidden',
27710                     cls : 'form-hidden-field roo-date-split-field-group-value',
27711                     name : this.name
27712                 }
27713             ]
27714         };
27715         
27716         if(this.fieldLabel){
27717             cfg.cn.push({
27718                 tag : 'div',
27719                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
27720                 cn : [
27721                     {
27722                         tag : 'label',
27723                         html : this.fieldLabel
27724                     }
27725                 ]
27726             });
27727         }
27728         
27729         Roo.each(['day', 'month', 'year'], function(t){
27730             cfg.cn.push({
27731                 tag : 'div',
27732                 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
27733             });
27734         }, this);
27735         
27736         return cfg;
27737     },
27738     
27739     inputEl: function ()
27740     {
27741         return this.el.select('.roo-date-split-field-group-value', true).first();
27742     },
27743     
27744     onRender : function(ct, position) 
27745     {
27746         var _this = this;
27747         
27748         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27749         
27750         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
27751         
27752         this.dayField = new Roo.bootstrap.ComboBox({
27753             allowBlank : this.dayAllowBlank,
27754             alwaysQuery : true,
27755             displayField : 'value',
27756             editable : false,
27757             fieldLabel : '',
27758             forceSelection : true,
27759             mode : 'local',
27760             placeholder : this.dayPlaceholder,
27761             selectOnFocus : true,
27762             tpl : '<div class="select2-result"><b>{value}</b></div>',
27763             triggerAction : 'all',
27764             typeAhead : true,
27765             valueField : 'value',
27766             store : new Roo.data.SimpleStore({
27767                 data : (function() {    
27768                     var days = [];
27769                     _this.fireEvent('days', _this, days);
27770                     return days;
27771                 })(),
27772                 fields : [ 'value' ]
27773             }),
27774             listeners : {
27775                 select : function (_self, record, index)
27776                 {
27777                     _this.setValue(_this.getValue());
27778                 }
27779             }
27780         });
27781
27782         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
27783         
27784         this.monthField = new Roo.bootstrap.MonthField({
27785             after : '<i class=\"fa fa-calendar\"></i>',
27786             allowBlank : this.monthAllowBlank,
27787             placeholder : this.monthPlaceholder,
27788             readOnly : true,
27789             listeners : {
27790                 render : function (_self)
27791                 {
27792                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
27793                         e.preventDefault();
27794                         _self.focus();
27795                     });
27796                 },
27797                 select : function (_self, oldvalue, newvalue)
27798                 {
27799                     _this.setValue(_this.getValue());
27800                 }
27801             }
27802         });
27803         
27804         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
27805         
27806         this.yearField = new Roo.bootstrap.ComboBox({
27807             allowBlank : this.yearAllowBlank,
27808             alwaysQuery : true,
27809             displayField : 'value',
27810             editable : false,
27811             fieldLabel : '',
27812             forceSelection : true,
27813             mode : 'local',
27814             placeholder : this.yearPlaceholder,
27815             selectOnFocus : true,
27816             tpl : '<div class="select2-result"><b>{value}</b></div>',
27817             triggerAction : 'all',
27818             typeAhead : true,
27819             valueField : 'value',
27820             store : new Roo.data.SimpleStore({
27821                 data : (function() {
27822                     var years = [];
27823                     _this.fireEvent('years', _this, years);
27824                     return years;
27825                 })(),
27826                 fields : [ 'value' ]
27827             }),
27828             listeners : {
27829                 select : function (_self, record, index)
27830                 {
27831                     _this.setValue(_this.getValue());
27832                 }
27833             }
27834         });
27835
27836         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
27837     },
27838     
27839     setValue : function(v, format)
27840     {
27841         this.inputEl.dom.value = v;
27842         
27843         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
27844         
27845         var d = Date.parseDate(v, f);
27846         
27847         if(!d){
27848             this.validate();
27849             return;
27850         }
27851         
27852         this.setDay(d.format(this.dayFormat));
27853         this.setMonth(d.format(this.monthFormat));
27854         this.setYear(d.format(this.yearFormat));
27855         
27856         this.validate();
27857         
27858         return;
27859     },
27860     
27861     setDay : function(v)
27862     {
27863         this.dayField.setValue(v);
27864         this.inputEl.dom.value = this.getValue();
27865         this.validate();
27866         return;
27867     },
27868     
27869     setMonth : function(v)
27870     {
27871         this.monthField.setValue(v, true);
27872         this.inputEl.dom.value = this.getValue();
27873         this.validate();
27874         return;
27875     },
27876     
27877     setYear : function(v)
27878     {
27879         this.yearField.setValue(v);
27880         this.inputEl.dom.value = this.getValue();
27881         this.validate();
27882         return;
27883     },
27884     
27885     getDay : function()
27886     {
27887         return this.dayField.getValue();
27888     },
27889     
27890     getMonth : function()
27891     {
27892         return this.monthField.getValue();
27893     },
27894     
27895     getYear : function()
27896     {
27897         return this.yearField.getValue();
27898     },
27899     
27900     getValue : function()
27901     {
27902         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
27903         
27904         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
27905         
27906         return date;
27907     },
27908     
27909     reset : function()
27910     {
27911         this.setDay('');
27912         this.setMonth('');
27913         this.setYear('');
27914         this.inputEl.dom.value = '';
27915         this.validate();
27916         return;
27917     },
27918     
27919     validate : function()
27920     {
27921         var d = this.dayField.validate();
27922         var m = this.monthField.validate();
27923         var y = this.yearField.validate();
27924         
27925         var valid = true;
27926         
27927         if(
27928                 (!this.dayAllowBlank && !d) ||
27929                 (!this.monthAllowBlank && !m) ||
27930                 (!this.yearAllowBlank && !y)
27931         ){
27932             valid = false;
27933         }
27934         
27935         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
27936             return valid;
27937         }
27938         
27939         if(valid){
27940             this.markValid();
27941             return valid;
27942         }
27943         
27944         this.markInvalid();
27945         
27946         return valid;
27947     },
27948     
27949     markValid : function()
27950     {
27951         
27952         var label = this.el.select('label', true).first();
27953         var icon = this.el.select('i.fa-star', true).first();
27954
27955         if(label && icon){
27956             icon.remove();
27957         }
27958         
27959         this.fireEvent('valid', this);
27960     },
27961     
27962      /**
27963      * Mark this field as invalid
27964      * @param {String} msg The validation message
27965      */
27966     markInvalid : function(msg)
27967     {
27968         
27969         var label = this.el.select('label', true).first();
27970         var icon = this.el.select('i.fa-star', true).first();
27971
27972         if(label && !icon){
27973             this.el.select('.roo-date-split-field-label', true).createChild({
27974                 tag : 'i',
27975                 cls : 'text-danger fa fa-lg fa-star',
27976                 tooltip : 'This field is required',
27977                 style : 'margin-right:5px;'
27978             }, label, true);
27979         }
27980         
27981         this.fireEvent('invalid', this, msg);
27982     },
27983     
27984     clearInvalid : function()
27985     {
27986         var label = this.el.select('label', true).first();
27987         var icon = this.el.select('i.fa-star', true).first();
27988
27989         if(label && icon){
27990             icon.remove();
27991         }
27992         
27993         this.fireEvent('valid', this);
27994     },
27995     
27996     getName: function()
27997     {
27998         return this.name;
27999     }
28000     
28001 });
28002
28003