Roo/bootstrap/ComboBox.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  * @cfg {Boolean} clickable (true|false) default false
995
996  *     
997  * @constructor
998  * Create a new Container
999  * @param {Object} config The config object
1000  */
1001
1002 Roo.bootstrap.Container = function(config){
1003     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1004     
1005     this.addEvents({
1006         // raw events
1007          /**
1008          * @event expand
1009          * After the panel has been expand
1010          * 
1011          * @param {Roo.bootstrap.Container} this
1012          */
1013         "expand" : true,
1014         /**
1015          * @event collapse
1016          * After the panel has been collapsed
1017          * 
1018          * @param {Roo.bootstrap.Container} this
1019          */
1020         "collapse" : true,
1021         /**
1022          * @event click
1023          * When a element is chick
1024          * @param {Roo.bootstrap.Container} this
1025          * @param {Roo.EventObject} e
1026          */
1027         "click" : true
1028     });
1029 };
1030
1031 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1032     
1033     jumbotron : false,
1034     well: '',
1035     panel : '',
1036     header: '',
1037     footer : '',
1038     sticky: '',
1039     tag : false,
1040     alert : false,
1041     fa: false,
1042     icon : false,
1043     expandable : false,
1044     rheader : '',
1045     expanded : true,
1046     clickable: false,
1047   
1048      
1049     getChildContainer : function() {
1050         
1051         if(!this.el){
1052             return false;
1053         }
1054         
1055         if (this.panel.length) {
1056             return this.el.select('.panel-body',true).first();
1057         }
1058         
1059         return this.el;
1060     },
1061     
1062     
1063     getAutoCreate : function(){
1064         
1065         var cfg = {
1066             tag : this.tag || 'div',
1067             html : '',
1068             cls : ''
1069         };
1070         if (this.jumbotron) {
1071             cfg.cls = 'jumbotron';
1072         }
1073         
1074         
1075         
1076         // - this is applied by the parent..
1077         //if (this.cls) {
1078         //    cfg.cls = this.cls + '';
1079         //}
1080         
1081         if (this.sticky.length) {
1082             
1083             var bd = Roo.get(document.body);
1084             if (!bd.hasClass('bootstrap-sticky')) {
1085                 bd.addClass('bootstrap-sticky');
1086                 Roo.select('html',true).setStyle('height', '100%');
1087             }
1088              
1089             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1090         }
1091         
1092         
1093         if (this.well.length) {
1094             switch (this.well) {
1095                 case 'lg':
1096                 case 'sm':
1097                     cfg.cls +=' well well-' +this.well;
1098                     break;
1099                 default:
1100                     cfg.cls +=' well';
1101                     break;
1102             }
1103         }
1104         
1105         if (this.hidden) {
1106             cfg.cls += ' hidden';
1107         }
1108         
1109         
1110         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1111             cfg.cls +=' alert alert-' + this.alert;
1112         }
1113         
1114         var body = cfg;
1115         
1116         if (this.panel.length) {
1117             cfg.cls += ' panel panel-' + this.panel;
1118             cfg.cn = [];
1119             if (this.header.length) {
1120                 
1121                 var h = [];
1122                 
1123                 if(this.expandable){
1124                     
1125                     cfg.cls = cfg.cls + ' expandable';
1126                     
1127                     h.push({
1128                         tag: 'i',
1129                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1130                     });
1131                     
1132                 }
1133                 
1134                 h.push(
1135                     {
1136                         tag: 'span',
1137                         cls : 'panel-title',
1138                         html : (this.expandable ? '&nbsp;' : '') + this.header
1139                     },
1140                     {
1141                         tag: 'span',
1142                         cls: 'panel-header-right',
1143                         html: this.rheader
1144                     }
1145                 );
1146                 
1147                 cfg.cn.push({
1148                     cls : 'panel-heading',
1149                     style : this.expandable ? 'cursor: pointer' : '',
1150                     cn : h
1151                 });
1152                 
1153             }
1154             
1155             body = false;
1156             cfg.cn.push({
1157                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1158                 html : this.html
1159             });
1160             
1161             
1162             if (this.footer.length) {
1163                 cfg.cn.push({
1164                     cls : 'panel-footer',
1165                     html : this.footer
1166                     
1167                 });
1168             }
1169             
1170         }
1171         
1172         if (body) {
1173             body.html = this.html || cfg.html;
1174             // prefix with the icons..
1175             if (this.fa) {
1176                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1177             }
1178             if (this.icon) {
1179                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1180             }
1181             
1182             
1183         }
1184         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1185             cfg.cls =  'container';
1186         }
1187         
1188         return cfg;
1189     },
1190     
1191     initEvents: function() 
1192     {
1193         if(this.expandable){
1194             var headerEl = this.headerEl();
1195         
1196             if(headerEl){
1197                 headerEl.on('click', this.onToggleClick, this);
1198             }
1199         }
1200         
1201         if(this.clickable){
1202             this.el.on('click', this.onClick, this);
1203         }
1204         
1205     },
1206     
1207     onToggleClick : function()
1208     {
1209         var headerEl = this.headerEl();
1210         
1211         if(!headerEl){
1212             return;
1213         }
1214         
1215         if(this.expanded){
1216             this.collapse();
1217             return;
1218         }
1219         
1220         this.expand();
1221     },
1222     
1223     expand : function()
1224     {
1225         if(this.fireEvent('expand', this)) {
1226             
1227             this.expanded = true;
1228             
1229             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1230             
1231             this.el.select('.panel-body',true).first().removeClass('hide');
1232             
1233             var toggleEl = this.toggleEl();
1234
1235             if(!toggleEl){
1236                 return;
1237             }
1238
1239             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1240         }
1241         
1242     },
1243     
1244     collapse : function()
1245     {
1246         if(this.fireEvent('collapse', this)) {
1247             
1248             this.expanded = false;
1249             
1250             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1251             this.el.select('.panel-body',true).first().addClass('hide');
1252         
1253             var toggleEl = this.toggleEl();
1254
1255             if(!toggleEl){
1256                 return;
1257             }
1258
1259             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1260         }
1261     },
1262     
1263     toggleEl : function()
1264     {
1265         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1266             return;
1267         }
1268         
1269         return this.el.select('.panel-heading .fa',true).first();
1270     },
1271     
1272     headerEl : function()
1273     {
1274         if(!this.el || !this.panel.length || !this.header.length){
1275             return;
1276         }
1277         
1278         return this.el.select('.panel-heading',true).first()
1279     },
1280     
1281     titleEl : function()
1282     {
1283         if(!this.el || !this.panel.length || !this.header.length){
1284             return;
1285         }
1286         
1287         return this.el.select('.panel-title',true).first();
1288     },
1289     
1290     setTitle : function(v)
1291     {
1292         var titleEl = this.titleEl();
1293         
1294         if(!titleEl){
1295             return;
1296         }
1297         
1298         titleEl.dom.innerHTML = v;
1299     },
1300     
1301     getTitle : function()
1302     {
1303         
1304         var titleEl = this.titleEl();
1305         
1306         if(!titleEl){
1307             return '';
1308         }
1309         
1310         return titleEl.dom.innerHTML;
1311     },
1312     
1313     setRightTitle : function(v)
1314     {
1315         var t = this.el.select('.panel-header-right',true).first();
1316         
1317         if(!t){
1318             return;
1319         }
1320         
1321         t.dom.innerHTML = v;
1322     },
1323     
1324     onClick : function(e)
1325     {
1326         e.preventDefault();
1327         
1328         this.fireEvent('click', this, e);
1329     }
1330    
1331 });
1332
1333  /*
1334  * - LGPL
1335  *
1336  * image
1337  * 
1338  */
1339
1340
1341 /**
1342  * @class Roo.bootstrap.Img
1343  * @extends Roo.bootstrap.Component
1344  * Bootstrap Img class
1345  * @cfg {Boolean} imgResponsive false | true
1346  * @cfg {String} border rounded | circle | thumbnail
1347  * @cfg {String} src image source
1348  * @cfg {String} alt image alternative text
1349  * @cfg {String} href a tag href
1350  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1351  * @cfg {String} xsUrl xs image source
1352  * @cfg {String} smUrl sm image source
1353  * @cfg {String} mdUrl md image source
1354  * @cfg {String} lgUrl lg image source
1355  * 
1356  * @constructor
1357  * Create a new Input
1358  * @param {Object} config The config object
1359  */
1360
1361 Roo.bootstrap.Img = function(config){
1362     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1363     
1364     this.addEvents({
1365         // img events
1366         /**
1367          * @event click
1368          * The img click event for the img.
1369          * @param {Roo.EventObject} e
1370          */
1371         "click" : true
1372     });
1373 };
1374
1375 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1376     
1377     imgResponsive: true,
1378     border: '',
1379     src: 'about:blank',
1380     href: false,
1381     target: false,
1382     xsUrl: '',
1383     smUrl: '',
1384     mdUrl: '',
1385     lgUrl: '',
1386
1387     getAutoCreate : function()
1388     {   
1389         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1390             return this.createSingleImg();
1391         }
1392         
1393         var cfg = {
1394             tag: 'div',
1395             cls: 'roo-image-responsive-group',
1396             cn: []
1397         };
1398         var _this = this;
1399         
1400         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1401             
1402             if(!_this[size + 'Url']){
1403                 return;
1404             }
1405             
1406             var img = {
1407                 tag: 'img',
1408                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1409                 html: _this.html || cfg.html,
1410                 src: _this[size + 'Url']
1411             };
1412             
1413             img.cls += ' roo-image-responsive-' + size;
1414             
1415             var s = ['xs', 'sm', 'md', 'lg'];
1416             
1417             s.splice(s.indexOf(size), 1);
1418             
1419             Roo.each(s, function(ss){
1420                 img.cls += ' hidden-' + ss;
1421             });
1422             
1423             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1424                 cfg.cls += ' img-' + _this.border;
1425             }
1426             
1427             if(_this.alt){
1428                 cfg.alt = _this.alt;
1429             }
1430             
1431             if(_this.href){
1432                 var a = {
1433                     tag: 'a',
1434                     href: _this.href,
1435                     cn: [
1436                         img
1437                     ]
1438                 };
1439
1440                 if(this.target){
1441                     a.target = _this.target;
1442                 }
1443             }
1444             
1445             cfg.cn.push((_this.href) ? a : img);
1446             
1447         });
1448         
1449         return cfg;
1450     },
1451     
1452     createSingleImg : function()
1453     {
1454         var cfg = {
1455             tag: 'img',
1456             cls: (this.imgResponsive) ? 'img-responsive' : '',
1457             html : null,
1458             src : 'about:blank'  // just incase src get's set to undefined?!?
1459         };
1460         
1461         cfg.html = this.html || cfg.html;
1462         
1463         cfg.src = this.src || cfg.src;
1464         
1465         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1466             cfg.cls += ' img-' + this.border;
1467         }
1468         
1469         if(this.alt){
1470             cfg.alt = this.alt;
1471         }
1472         
1473         if(this.href){
1474             var a = {
1475                 tag: 'a',
1476                 href: this.href,
1477                 cn: [
1478                     cfg
1479                 ]
1480             };
1481             
1482             if(this.target){
1483                 a.target = this.target;
1484             }
1485             
1486         }
1487         
1488         return (this.href) ? a : cfg;
1489     },
1490     
1491     initEvents: function() 
1492     {
1493         if(!this.href){
1494             this.el.on('click', this.onClick, this);
1495         }
1496         
1497     },
1498     
1499     onClick : function(e)
1500     {
1501         Roo.log('img onclick');
1502         this.fireEvent('click', this, e);
1503     }
1504    
1505 });
1506
1507  /*
1508  * - LGPL
1509  *
1510  * image
1511  * 
1512  */
1513
1514
1515 /**
1516  * @class Roo.bootstrap.Link
1517  * @extends Roo.bootstrap.Component
1518  * Bootstrap Link Class
1519  * @cfg {String} alt image alternative text
1520  * @cfg {String} href a tag href
1521  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1522  * @cfg {String} html the content of the link.
1523  * @cfg {String} anchor name for the anchor link
1524
1525  * @cfg {Boolean} preventDefault (true | false) default false
1526
1527  * 
1528  * @constructor
1529  * Create a new Input
1530  * @param {Object} config The config object
1531  */
1532
1533 Roo.bootstrap.Link = function(config){
1534     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1535     
1536     this.addEvents({
1537         // img events
1538         /**
1539          * @event click
1540          * The img click event for the img.
1541          * @param {Roo.EventObject} e
1542          */
1543         "click" : true
1544     });
1545 };
1546
1547 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1548     
1549     href: false,
1550     target: false,
1551     preventDefault: false,
1552     anchor : false,
1553     alt : false,
1554
1555     getAutoCreate : function()
1556     {
1557         
1558         var cfg = {
1559             tag: 'a'
1560         };
1561         // anchor's do not require html/href...
1562         if (this.anchor === false) {
1563             cfg.html = this.html || '';
1564             cfg.href = this.href || '#';
1565         } else {
1566             cfg.name = this.anchor;
1567             if (this.html !== false) {
1568                 cfg.html = this.html;
1569             }
1570             if (this.href !== false) {
1571                 cfg.href = this.href;
1572             }
1573         }
1574         
1575         if(this.alt !== false){
1576             cfg.alt = this.alt;
1577         }
1578         
1579         
1580         if(this.target !== false) {
1581             cfg.target = this.target;
1582         }
1583         
1584         return cfg;
1585     },
1586     
1587     initEvents: function() {
1588         
1589         if(!this.href || this.preventDefault){
1590             this.el.on('click', this.onClick, this);
1591         }
1592     },
1593     
1594     onClick : function(e)
1595     {
1596         if(this.preventDefault){
1597             e.preventDefault();
1598         }
1599         //Roo.log('img onclick');
1600         this.fireEvent('click', this, e);
1601     }
1602    
1603 });
1604
1605  /*
1606  * - LGPL
1607  *
1608  * header
1609  * 
1610  */
1611
1612 /**
1613  * @class Roo.bootstrap.Header
1614  * @extends Roo.bootstrap.Component
1615  * Bootstrap Header class
1616  * @cfg {String} html content of header
1617  * @cfg {Number} level (1|2|3|4|5|6) default 1
1618  * 
1619  * @constructor
1620  * Create a new Header
1621  * @param {Object} config The config object
1622  */
1623
1624
1625 Roo.bootstrap.Header  = function(config){
1626     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1627 };
1628
1629 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1630     
1631     //href : false,
1632     html : false,
1633     level : 1,
1634     
1635     
1636     
1637     getAutoCreate : function(){
1638         
1639         
1640         
1641         var cfg = {
1642             tag: 'h' + (1 *this.level),
1643             html: this.html || ''
1644         } ;
1645         
1646         return cfg;
1647     }
1648    
1649 });
1650
1651  
1652
1653  /*
1654  * Based on:
1655  * Ext JS Library 1.1.1
1656  * Copyright(c) 2006-2007, Ext JS, LLC.
1657  *
1658  * Originally Released Under LGPL - original licence link has changed is not relivant.
1659  *
1660  * Fork - LGPL
1661  * <script type="text/javascript">
1662  */
1663  
1664 /**
1665  * @class Roo.bootstrap.MenuMgr
1666  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1667  * @singleton
1668  */
1669 Roo.bootstrap.MenuMgr = function(){
1670    var menus, active, groups = {}, attached = false, lastShow = new Date();
1671
1672    // private - called when first menu is created
1673    function init(){
1674        menus = {};
1675        active = new Roo.util.MixedCollection();
1676        Roo.get(document).addKeyListener(27, function(){
1677            if(active.length > 0){
1678                hideAll();
1679            }
1680        });
1681    }
1682
1683    // private
1684    function hideAll(){
1685        if(active && active.length > 0){
1686            var c = active.clone();
1687            c.each(function(m){
1688                m.hide();
1689            });
1690        }
1691    }
1692
1693    // private
1694    function onHide(m){
1695        active.remove(m);
1696        if(active.length < 1){
1697            Roo.get(document).un("mouseup", onMouseDown);
1698             
1699            attached = false;
1700        }
1701    }
1702
1703    // private
1704    function onShow(m){
1705        var last = active.last();
1706        lastShow = new Date();
1707        active.add(m);
1708        if(!attached){
1709           Roo.get(document).on("mouseup", onMouseDown);
1710            
1711            attached = true;
1712        }
1713        if(m.parentMenu){
1714           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1715           m.parentMenu.activeChild = m;
1716        }else if(last && last.isVisible()){
1717           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1718        }
1719    }
1720
1721    // private
1722    function onBeforeHide(m){
1723        if(m.activeChild){
1724            m.activeChild.hide();
1725        }
1726        if(m.autoHideTimer){
1727            clearTimeout(m.autoHideTimer);
1728            delete m.autoHideTimer;
1729        }
1730    }
1731
1732    // private
1733    function onBeforeShow(m){
1734        var pm = m.parentMenu;
1735        if(!pm && !m.allowOtherMenus){
1736            hideAll();
1737        }else if(pm && pm.activeChild && active != m){
1738            pm.activeChild.hide();
1739        }
1740    }
1741
1742    // private this should really trigger on mouseup..
1743    function onMouseDown(e){
1744         Roo.log("on Mouse Up");
1745         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu") && !e.getTarget('.user-menu')){
1746             Roo.log("hideAll");
1747             hideAll();
1748             e.stopEvent();
1749         }
1750         
1751         
1752    }
1753
1754    // private
1755    function onBeforeCheck(mi, state){
1756        if(state){
1757            var g = groups[mi.group];
1758            for(var i = 0, l = g.length; i < l; i++){
1759                if(g[i] != mi){
1760                    g[i].setChecked(false);
1761                }
1762            }
1763        }
1764    }
1765
1766    return {
1767
1768        /**
1769         * Hides all menus that are currently visible
1770         */
1771        hideAll : function(){
1772             hideAll();  
1773        },
1774
1775        // private
1776        register : function(menu){
1777            if(!menus){
1778                init();
1779            }
1780            menus[menu.id] = menu;
1781            menu.on("beforehide", onBeforeHide);
1782            menu.on("hide", onHide);
1783            menu.on("beforeshow", onBeforeShow);
1784            menu.on("show", onShow);
1785            var g = menu.group;
1786            if(g && menu.events["checkchange"]){
1787                if(!groups[g]){
1788                    groups[g] = [];
1789                }
1790                groups[g].push(menu);
1791                menu.on("checkchange", onCheck);
1792            }
1793        },
1794
1795         /**
1796          * Returns a {@link Roo.menu.Menu} object
1797          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1798          * be used to generate and return a new Menu instance.
1799          */
1800        get : function(menu){
1801            if(typeof menu == "string"){ // menu id
1802                return menus[menu];
1803            }else if(menu.events){  // menu instance
1804                return menu;
1805            }
1806            /*else if(typeof menu.length == 'number'){ // array of menu items?
1807                return new Roo.bootstrap.Menu({items:menu});
1808            }else{ // otherwise, must be a config
1809                return new Roo.bootstrap.Menu(menu);
1810            }
1811            */
1812            return false;
1813        },
1814
1815        // private
1816        unregister : function(menu){
1817            delete menus[menu.id];
1818            menu.un("beforehide", onBeforeHide);
1819            menu.un("hide", onHide);
1820            menu.un("beforeshow", onBeforeShow);
1821            menu.un("show", onShow);
1822            var g = menu.group;
1823            if(g && menu.events["checkchange"]){
1824                groups[g].remove(menu);
1825                menu.un("checkchange", onCheck);
1826            }
1827        },
1828
1829        // private
1830        registerCheckable : function(menuItem){
1831            var g = menuItem.group;
1832            if(g){
1833                if(!groups[g]){
1834                    groups[g] = [];
1835                }
1836                groups[g].push(menuItem);
1837                menuItem.on("beforecheckchange", onBeforeCheck);
1838            }
1839        },
1840
1841        // private
1842        unregisterCheckable : function(menuItem){
1843            var g = menuItem.group;
1844            if(g){
1845                groups[g].remove(menuItem);
1846                menuItem.un("beforecheckchange", onBeforeCheck);
1847            }
1848        }
1849    };
1850 }();/*
1851  * - LGPL
1852  *
1853  * menu
1854  * 
1855  */
1856
1857 /**
1858  * @class Roo.bootstrap.Menu
1859  * @extends Roo.bootstrap.Component
1860  * Bootstrap Menu class - container for MenuItems
1861  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1862  * 
1863  * @constructor
1864  * Create a new Menu
1865  * @param {Object} config The config object
1866  */
1867
1868
1869 Roo.bootstrap.Menu = function(config){
1870     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1871     if (this.registerMenu) {
1872         Roo.bootstrap.MenuMgr.register(this);
1873     }
1874     this.addEvents({
1875         /**
1876          * @event beforeshow
1877          * Fires before this menu is displayed
1878          * @param {Roo.menu.Menu} this
1879          */
1880         beforeshow : true,
1881         /**
1882          * @event beforehide
1883          * Fires before this menu is hidden
1884          * @param {Roo.menu.Menu} this
1885          */
1886         beforehide : true,
1887         /**
1888          * @event show
1889          * Fires after this menu is displayed
1890          * @param {Roo.menu.Menu} this
1891          */
1892         show : true,
1893         /**
1894          * @event hide
1895          * Fires after this menu is hidden
1896          * @param {Roo.menu.Menu} this
1897          */
1898         hide : true,
1899         /**
1900          * @event click
1901          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1902          * @param {Roo.menu.Menu} this
1903          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1904          * @param {Roo.EventObject} e
1905          */
1906         click : true,
1907         /**
1908          * @event mouseover
1909          * Fires when the mouse is hovering over this menu
1910          * @param {Roo.menu.Menu} this
1911          * @param {Roo.EventObject} e
1912          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1913          */
1914         mouseover : true,
1915         /**
1916          * @event mouseout
1917          * Fires when the mouse exits this menu
1918          * @param {Roo.menu.Menu} this
1919          * @param {Roo.EventObject} e
1920          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1921          */
1922         mouseout : true,
1923         /**
1924          * @event itemclick
1925          * Fires when a menu item contained in this menu is clicked
1926          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1927          * @param {Roo.EventObject} e
1928          */
1929         itemclick: true
1930     });
1931     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1932 };
1933
1934 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1935     
1936    /// html : false,
1937     //align : '',
1938     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1939     type: false,
1940     /**
1941      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1942      */
1943     registerMenu : true,
1944     
1945     menuItems :false, // stores the menu items..
1946     
1947     hidden:true,
1948     
1949     parentMenu : false,
1950     
1951     getChildContainer : function() {
1952         return this.el;  
1953     },
1954     
1955     getAutoCreate : function(){
1956          
1957         //if (['right'].indexOf(this.align)!==-1) {
1958         //    cfg.cn[1].cls += ' pull-right'
1959         //}
1960         
1961         
1962         var cfg = {
1963             tag : 'ul',
1964             cls : 'dropdown-menu' ,
1965             style : 'z-index:1000'
1966             
1967         };
1968         
1969         if (this.type === 'submenu') {
1970             cfg.cls = 'submenu active';
1971         }
1972         if (this.type === 'treeview') {
1973             cfg.cls = 'treeview-menu';
1974         }
1975         
1976         return cfg;
1977     },
1978     initEvents : function() {
1979         
1980        // Roo.log("ADD event");
1981        // Roo.log(this.triggerEl.dom);
1982         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
1983         
1984         this.triggerEl.addClass('dropdown-toggle');
1985         
1986         if (Roo.isTouch) {
1987             this.el.on('touchstart'  , this.onTouch, this);
1988         }
1989         this.el.on('click' , this.onClick, this);
1990
1991         this.el.on("mouseover", this.onMouseOver, this);
1992         this.el.on("mouseout", this.onMouseOut, this);
1993         
1994     },
1995     
1996     findTargetItem : function(e)
1997     {
1998         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1999         if(!t){
2000             return false;
2001         }
2002         //Roo.log(t);         Roo.log(t.id);
2003         if(t && t.id){
2004             //Roo.log(this.menuitems);
2005             return this.menuitems.get(t.id);
2006             
2007             //return this.items.get(t.menuItemId);
2008         }
2009         
2010         return false;
2011     },
2012     
2013     onTouch : function(e) 
2014     {
2015         //e.stopEvent(); this make the user popdown broken
2016         this.onClick(e);
2017     },
2018     
2019     onClick : function(e)
2020     {
2021         Roo.log("menu.onClick");
2022         var t = this.findTargetItem(e);
2023         if(!t || t.isContainer){
2024             return;
2025         }
2026         Roo.log(e);
2027         /*
2028         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2029             if(t == this.activeItem && t.shouldDeactivate(e)){
2030                 this.activeItem.deactivate();
2031                 delete this.activeItem;
2032                 return;
2033             }
2034             if(t.canActivate){
2035                 this.setActiveItem(t, true);
2036             }
2037             return;
2038             
2039             
2040         }
2041         */
2042        
2043         Roo.log('pass click event');
2044         
2045         t.onClick(e);
2046         
2047         this.fireEvent("click", this, t, e);
2048         
2049         this.hide();
2050     },
2051      onMouseOver : function(e){
2052         var t  = this.findTargetItem(e);
2053         //Roo.log(t);
2054         //if(t){
2055         //    if(t.canActivate && !t.disabled){
2056         //        this.setActiveItem(t, true);
2057         //    }
2058         //}
2059         
2060         this.fireEvent("mouseover", this, e, t);
2061     },
2062     isVisible : function(){
2063         return !this.hidden;
2064     },
2065      onMouseOut : function(e){
2066         var t  = this.findTargetItem(e);
2067         
2068         //if(t ){
2069         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2070         //        this.activeItem.deactivate();
2071         //        delete this.activeItem;
2072         //    }
2073         //}
2074         this.fireEvent("mouseout", this, e, t);
2075     },
2076     
2077     
2078     /**
2079      * Displays this menu relative to another element
2080      * @param {String/HTMLElement/Roo.Element} element The element to align to
2081      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2082      * the element (defaults to this.defaultAlign)
2083      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2084      */
2085     show : function(el, pos, parentMenu){
2086         this.parentMenu = parentMenu;
2087         if(!this.el){
2088             this.render();
2089         }
2090         this.fireEvent("beforeshow", this);
2091         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2092     },
2093      /**
2094      * Displays this menu at a specific xy position
2095      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2096      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2097      */
2098     showAt : function(xy, parentMenu, /* private: */_e){
2099         this.parentMenu = parentMenu;
2100         if(!this.el){
2101             this.render();
2102         }
2103         if(_e !== false){
2104             this.fireEvent("beforeshow", this);
2105             //xy = this.el.adjustForConstraints(xy);
2106         }
2107         
2108         //this.el.show();
2109         this.hideMenuItems();
2110         this.hidden = false;
2111         this.triggerEl.addClass('open');
2112         
2113         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2114             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2115         }
2116         
2117         this.el.setXY(xy);
2118         this.focus();
2119         this.fireEvent("show", this);
2120     },
2121     
2122     focus : function(){
2123         return;
2124         if(!this.hidden){
2125             this.doFocus.defer(50, this);
2126         }
2127     },
2128
2129     doFocus : function(){
2130         if(!this.hidden){
2131             this.focusEl.focus();
2132         }
2133     },
2134
2135     /**
2136      * Hides this menu and optionally all parent menus
2137      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2138      */
2139     hide : function(deep){
2140         
2141         this.hideMenuItems();
2142         if(this.el && this.isVisible()){
2143             this.fireEvent("beforehide", this);
2144             if(this.activeItem){
2145                 this.activeItem.deactivate();
2146                 this.activeItem = null;
2147             }
2148             this.triggerEl.removeClass('open');;
2149             this.hidden = true;
2150             this.fireEvent("hide", this);
2151         }
2152         if(deep === true && this.parentMenu){
2153             this.parentMenu.hide(true);
2154         }
2155     },
2156     
2157     onTriggerPress  : function(e)
2158     {
2159         
2160         Roo.log('trigger press');
2161         //Roo.log(e.getTarget());
2162        // Roo.log(this.triggerEl.dom);
2163         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
2164             return;
2165         }
2166         
2167         if (this.isVisible()) {
2168             Roo.log('hide');
2169             this.hide();
2170         } else {
2171             Roo.log('show');
2172             this.show(this.triggerEl, false, false);
2173         }
2174         
2175         e.stopEvent();
2176     },
2177     
2178          
2179        
2180     
2181     hideMenuItems : function()
2182     {
2183         //$(backdrop).remove()
2184         Roo.select('.open',true).each(function(aa) {
2185             
2186             aa.removeClass('open');
2187           //var parent = getParent($(this))
2188           //var relatedTarget = { relatedTarget: this }
2189           
2190            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2191           //if (e.isDefaultPrevented()) return
2192            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2193         })
2194     },
2195     addxtypeChild : function (tree, cntr) {
2196         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2197           
2198         this.menuitems.add(comp);
2199         return comp;
2200
2201     },
2202     getEl : function()
2203     {
2204         Roo.log(this.el);
2205         return this.el;
2206     }
2207 });
2208
2209  
2210  /*
2211  * - LGPL
2212  *
2213  * menu item
2214  * 
2215  */
2216
2217
2218 /**
2219  * @class Roo.bootstrap.MenuItem
2220  * @extends Roo.bootstrap.Component
2221  * Bootstrap MenuItem class
2222  * @cfg {String} html the menu label
2223  * @cfg {String} href the link
2224  * @cfg {Boolean} preventDefault (true | false) default true
2225  * @cfg {Boolean} isContainer (true | false) default false
2226  * 
2227  * 
2228  * @constructor
2229  * Create a new MenuItem
2230  * @param {Object} config The config object
2231  */
2232
2233
2234 Roo.bootstrap.MenuItem = function(config){
2235     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2236     this.addEvents({
2237         // raw events
2238         /**
2239          * @event click
2240          * The raw click event for the entire grid.
2241          * @param {Roo.bootstrap.MenuItem} this
2242          * @param {Roo.EventObject} e
2243          */
2244         "click" : true
2245     });
2246 };
2247
2248 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2249     
2250     href : false,
2251     html : false,
2252     preventDefault: true,
2253     isContainer : false,
2254     
2255     getAutoCreate : function(){
2256         
2257         if(this.isContainer){
2258             return {
2259                 tag: 'li',
2260                 cls: 'dropdown-menu-item'
2261             };
2262         }
2263         
2264         var cfg= {
2265             tag: 'li',
2266             cls: 'dropdown-menu-item',
2267             cn: [
2268                     {
2269                         tag : 'a',
2270                         href : '#',
2271                         html : 'Link'
2272                     }
2273                 ]
2274         };
2275         if (this.parent().type == 'treeview') {
2276             cfg.cls = 'treeview-menu';
2277         }
2278         
2279         cfg.cn[0].href = this.href || cfg.cn[0].href ;
2280         cfg.cn[0].html = this.html || cfg.cn[0].html ;
2281         return cfg;
2282     },
2283     
2284     initEvents: function() {
2285         
2286         //this.el.select('a').on('click', this.onClick, this);
2287         
2288     },
2289     onClick : function(e)
2290     {
2291         Roo.log('item on click ');
2292         //if(this.preventDefault){
2293         //    e.preventDefault();
2294         //}
2295         //this.parent().hideMenuItems();
2296         
2297         this.fireEvent('click', this, e);
2298     },
2299     getEl : function()
2300     {
2301         return this.el;
2302     }
2303 });
2304
2305  
2306
2307  /*
2308  * - LGPL
2309  *
2310  * menu separator
2311  * 
2312  */
2313
2314
2315 /**
2316  * @class Roo.bootstrap.MenuSeparator
2317  * @extends Roo.bootstrap.Component
2318  * Bootstrap MenuSeparator class
2319  * 
2320  * @constructor
2321  * Create a new MenuItem
2322  * @param {Object} config The config object
2323  */
2324
2325
2326 Roo.bootstrap.MenuSeparator = function(config){
2327     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2328 };
2329
2330 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2331     
2332     getAutoCreate : function(){
2333         var cfg = {
2334             cls: 'divider',
2335             tag : 'li'
2336         };
2337         
2338         return cfg;
2339     }
2340    
2341 });
2342
2343  
2344
2345  
2346 /*
2347 * Licence: LGPL
2348 */
2349
2350 /**
2351  * @class Roo.bootstrap.Modal
2352  * @extends Roo.bootstrap.Component
2353  * Bootstrap Modal class
2354  * @cfg {String} title Title of dialog
2355  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2356  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn 
2357  * @cfg {Boolean} specificTitle default false
2358  * @cfg {Array} buttons Array of buttons or standard button set..
2359  * @cfg {String} buttonPosition (left|right|center) default right
2360  * @cfg {Boolean} animate default true
2361  * @cfg {Boolean} allow_close default true
2362  * 
2363  * @constructor
2364  * Create a new Modal Dialog
2365  * @param {Object} config The config object
2366  */
2367
2368 Roo.bootstrap.Modal = function(config){
2369     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2370     this.addEvents({
2371         // raw events
2372         /**
2373          * @event btnclick
2374          * The raw btnclick event for the button
2375          * @param {Roo.EventObject} e
2376          */
2377         "btnclick" : true
2378     });
2379     this.buttons = this.buttons || [];
2380      
2381     if (this.tmpl) {
2382         this.tmpl = Roo.factory(this.tmpl);
2383     }
2384     
2385 };
2386
2387 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2388     
2389     title : 'test dialog',
2390    
2391     buttons : false,
2392     
2393     // set on load...
2394      
2395     html: false,
2396     
2397     tmp: false,
2398     
2399     specificTitle: false,
2400     
2401     buttonPosition: 'right',
2402     
2403     allow_close : true,
2404     
2405     animate : true,
2406     
2407     
2408      // private
2409     bodyEl:  false,
2410     footerEl:  false,
2411     titleEl:  false,
2412     closeEl:  false,
2413     
2414     
2415     onRender : function(ct, position)
2416     {
2417         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2418      
2419         if(!this.el){
2420             var cfg = Roo.apply({},  this.getAutoCreate());
2421             cfg.id = Roo.id();
2422             //if(!cfg.name){
2423             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2424             //}
2425             //if (!cfg.name.length) {
2426             //    delete cfg.name;
2427            // }
2428             if (this.cls) {
2429                 cfg.cls += ' ' + this.cls;
2430             }
2431             if (this.style) {
2432                 cfg.style = this.style;
2433             }
2434             this.el = Roo.get(document.body).createChild(cfg, position);
2435         }
2436         //var type = this.el.dom.type;
2437         
2438         
2439         if(this.tabIndex !== undefined){
2440             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2441         }
2442         
2443         
2444         this.bodyEl = this.el.select('.modal-body',true).first();
2445         this.closeEl = this.el.select('.modal-header .close', true).first();
2446         this.footerEl = this.el.select('.modal-footer',true).first();
2447         this.titleEl = this.el.select('.modal-title',true).first();
2448         
2449         
2450          
2451         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2452         this.maskEl.enableDisplayMode("block");
2453         this.maskEl.hide();
2454         //this.el.addClass("x-dlg-modal");
2455     
2456         if (this.buttons.length) {
2457             Roo.each(this.buttons, function(bb) {
2458                 var b = Roo.apply({}, bb);
2459                 b.xns = b.xns || Roo.bootstrap;
2460                 b.xtype = b.xtype || 'Button';
2461                 if (typeof(b.listeners) == 'undefined') {
2462                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2463                 }
2464                 
2465                 var btn = Roo.factory(b);
2466                 
2467                 btn.onRender(this.el.select('.modal-footer div').first());
2468                 
2469             },this);
2470         }
2471         // render the children.
2472         var nitems = [];
2473         
2474         if(typeof(this.items) != 'undefined'){
2475             var items = this.items;
2476             delete this.items;
2477
2478             for(var i =0;i < items.length;i++) {
2479                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2480             }
2481         }
2482         
2483         this.items = nitems;
2484         
2485         // where are these used - they used to be body/close/footer
2486         
2487        
2488         this.initEvents();
2489         //this.el.addClass([this.fieldClass, this.cls]);
2490         
2491     },
2492     
2493     getAutoCreate : function(){
2494         
2495         
2496         var bdy = {
2497                 cls : 'modal-body',
2498                 html : this.html || ''
2499         };
2500         
2501         var title = {
2502             tag: 'h4',
2503             cls : 'modal-title',
2504             html : this.title
2505         };
2506         
2507         if(this.specificTitle){
2508             title = this.title;
2509             
2510         };
2511         
2512         var header = [];
2513         if (this.allow_close) {
2514             header.push({
2515                 tag: 'button',
2516                 cls : 'close',
2517                 html : '&times'
2518             });
2519         }
2520         header.push(title);
2521         
2522         var modal = {
2523             cls: "modal",
2524             style : 'display: none',
2525             cn : [
2526                 {
2527                     cls: "modal-dialog",
2528                     cn : [
2529                         {
2530                             cls : "modal-content",
2531                             cn : [
2532                                 {
2533                                     cls : 'modal-header',
2534                                     cn : header
2535                                 },
2536                                 bdy,
2537                                 {
2538                                     cls : 'modal-footer',
2539                                     cn : [
2540                                         {
2541                                             tag: 'div',
2542                                             cls: 'btn-' + this.buttonPosition
2543                                         }
2544                                     ]
2545                                     
2546                                 }
2547                                 
2548                                 
2549                             ]
2550                             
2551                         }
2552                     ]
2553                         
2554                 }
2555             ]
2556         };
2557         
2558         if(this.animate){
2559             modal.cls += ' fade';
2560         }
2561         
2562         return modal;
2563           
2564     },
2565     getChildContainer : function() {
2566          
2567          return this.bodyEl;
2568         
2569     },
2570     getButtonContainer : function() {
2571          return this.el.select('.modal-footer div',true).first();
2572         
2573     },
2574     initEvents : function()
2575     {
2576         if (this.allow_close) {
2577             this.closeEl.on('click', this.hide, this);
2578         }
2579         
2580         var _this = this;
2581         
2582         window.addEventListener("resize", function() { _this.resize(); } );
2583
2584     },
2585     
2586     resize : function()
2587     {
2588         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2589     },
2590     
2591     show : function() {
2592         
2593         if (!this.rendered) {
2594             this.render();
2595         }
2596         
2597         this.el.setStyle('display', 'block');
2598         
2599         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2600             var _this = this;
2601             (function(){
2602                 this.el.addClass('in');
2603             }).defer(50, this);
2604         }else{
2605             this.el.addClass('in');
2606             
2607         }
2608         
2609         // not sure how we can show data in here.. 
2610         //if (this.tmpl) {
2611         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2612         //}
2613         
2614         Roo.get(document.body).addClass("x-body-masked");
2615         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2616         this.maskEl.show();
2617         this.el.setStyle('zIndex', '10001');
2618        
2619         this.fireEvent('show', this);
2620          
2621         
2622         
2623     },
2624     hide : function()
2625     {
2626         this.maskEl.hide();
2627         Roo.get(document.body).removeClass("x-body-masked");
2628         this.el.removeClass('in');
2629         this.el.select('.modal-dialog', true).first().setStyle('transform','');
2630         
2631         if(this.animate){ // why
2632             var _this = this;
2633             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2634         }else{
2635             this.el.setStyle('display', 'none');
2636         }
2637         
2638         this.fireEvent('hide', this);
2639     },
2640     
2641     addButton : function(str, cb)
2642     {
2643          
2644         
2645         var b = Roo.apply({}, { html : str } );
2646         b.xns = b.xns || Roo.bootstrap;
2647         b.xtype = b.xtype || 'Button';
2648         if (typeof(b.listeners) == 'undefined') {
2649             b.listeners = { click : cb.createDelegate(this)  };
2650         }
2651         
2652         var btn = Roo.factory(b);
2653            
2654         btn.onRender(this.el.select('.modal-footer div').first());
2655         
2656         return btn;   
2657        
2658     },
2659     
2660     setDefaultButton : function(btn)
2661     {
2662         //this.el.select('.modal-footer').()
2663     },
2664     resizeTo: function(w,h)
2665     {
2666         // skip..
2667     },
2668     setContentSize  : function(w, h)
2669     {
2670         
2671     },
2672     onButtonClick: function(btn,e)
2673     {
2674         //Roo.log([a,b,c]);
2675         this.fireEvent('btnclick', btn.name, e);
2676     },
2677      /**
2678      * Set the title of the Dialog
2679      * @param {String} str new Title
2680      */
2681     setTitle: function(str) {
2682         this.titleEl.dom.innerHTML = str;    
2683     },
2684     /**
2685      * Set the body of the Dialog
2686      * @param {String} str new Title
2687      */
2688     setBody: function(str) {
2689         this.bodyEl.dom.innerHTML = str;    
2690     },
2691     /**
2692      * Set the body of the Dialog using the template
2693      * @param {Obj} data - apply this data to the template and replace the body contents.
2694      */
2695     applyBody: function(obj)
2696     {
2697         if (!this.tmpl) {
2698             Roo.log("Error - using apply Body without a template");
2699             //code
2700         }
2701         this.tmpl.overwrite(this.bodyEl, obj);
2702     }
2703     
2704 });
2705
2706
2707 Roo.apply(Roo.bootstrap.Modal,  {
2708     /**
2709          * Button config that displays a single OK button
2710          * @type Object
2711          */
2712         OK :  [{
2713             name : 'ok',
2714             weight : 'primary',
2715             html : 'OK'
2716         }], 
2717         /**
2718          * Button config that displays Yes and No buttons
2719          * @type Object
2720          */
2721         YESNO : [
2722             {
2723                 name  : 'no',
2724                 html : 'No'
2725             },
2726             {
2727                 name  :'yes',
2728                 weight : 'primary',
2729                 html : 'Yes'
2730             }
2731         ],
2732         
2733         /**
2734          * Button config that displays OK and Cancel buttons
2735          * @type Object
2736          */
2737         OKCANCEL : [
2738             {
2739                name : 'cancel',
2740                 html : 'Cancel'
2741             },
2742             {
2743                 name : 'ok',
2744                 weight : 'primary',
2745                 html : 'OK'
2746             }
2747         ],
2748         /**
2749          * Button config that displays Yes, No and Cancel buttons
2750          * @type Object
2751          */
2752         YESNOCANCEL : [
2753             {
2754                 name : 'yes',
2755                 weight : 'primary',
2756                 html : 'Yes'
2757             },
2758             {
2759                 name : 'no',
2760                 html : 'No'
2761             },
2762             {
2763                 name : 'cancel',
2764                 html : 'Cancel'
2765             }
2766         ]
2767 });
2768  
2769  /*
2770  * - LGPL
2771  *
2772  * messagebox - can be used as a replace
2773  * 
2774  */
2775 /**
2776  * @class Roo.MessageBox
2777  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2778  * Example usage:
2779  *<pre><code>
2780 // Basic alert:
2781 Roo.Msg.alert('Status', 'Changes saved successfully.');
2782
2783 // Prompt for user data:
2784 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2785     if (btn == 'ok'){
2786         // process text value...
2787     }
2788 });
2789
2790 // Show a dialog using config options:
2791 Roo.Msg.show({
2792    title:'Save Changes?',
2793    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2794    buttons: Roo.Msg.YESNOCANCEL,
2795    fn: processResult,
2796    animEl: 'elId'
2797 });
2798 </code></pre>
2799  * @singleton
2800  */
2801 Roo.bootstrap.MessageBox = function(){
2802     var dlg, opt, mask, waitTimer;
2803     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2804     var buttons, activeTextEl, bwidth;
2805
2806     
2807     // private
2808     var handleButton = function(button){
2809         dlg.hide();
2810         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2811     };
2812
2813     // private
2814     var handleHide = function(){
2815         if(opt && opt.cls){
2816             dlg.el.removeClass(opt.cls);
2817         }
2818         //if(waitTimer){
2819         //    Roo.TaskMgr.stop(waitTimer);
2820         //    waitTimer = null;
2821         //}
2822     };
2823
2824     // private
2825     var updateButtons = function(b){
2826         var width = 0;
2827         if(!b){
2828             buttons["ok"].hide();
2829             buttons["cancel"].hide();
2830             buttons["yes"].hide();
2831             buttons["no"].hide();
2832             //dlg.footer.dom.style.display = 'none';
2833             return width;
2834         }
2835         dlg.footerEl.dom.style.display = '';
2836         for(var k in buttons){
2837             if(typeof buttons[k] != "function"){
2838                 if(b[k]){
2839                     buttons[k].show();
2840                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2841                     width += buttons[k].el.getWidth()+15;
2842                 }else{
2843                     buttons[k].hide();
2844                 }
2845             }
2846         }
2847         return width;
2848     };
2849
2850     // private
2851     var handleEsc = function(d, k, e){
2852         if(opt && opt.closable !== false){
2853             dlg.hide();
2854         }
2855         if(e){
2856             e.stopEvent();
2857         }
2858     };
2859
2860     return {
2861         /**
2862          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2863          * @return {Roo.BasicDialog} The BasicDialog element
2864          */
2865         getDialog : function(){
2866            if(!dlg){
2867                 dlg = new Roo.bootstrap.Modal( {
2868                     //draggable: true,
2869                     //resizable:false,
2870                     //constraintoviewport:false,
2871                     //fixedcenter:true,
2872                     //collapsible : false,
2873                     //shim:true,
2874                     //modal: true,
2875                   //  width:400,
2876                   //  height:100,
2877                     //buttonAlign:"center",
2878                     closeClick : function(){
2879                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2880                             handleButton("no");
2881                         }else{
2882                             handleButton("cancel");
2883                         }
2884                     }
2885                 });
2886                 dlg.render();
2887                 dlg.on("hide", handleHide);
2888                 mask = dlg.mask;
2889                 //dlg.addKeyListener(27, handleEsc);
2890                 buttons = {};
2891                 this.buttons = buttons;
2892                 var bt = this.buttonText;
2893                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2894                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2895                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2896                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2897                 //Roo.log(buttons);
2898                 bodyEl = dlg.bodyEl.createChild({
2899
2900                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2901                         '<textarea class="roo-mb-textarea"></textarea>' +
2902                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2903                 });
2904                 msgEl = bodyEl.dom.firstChild;
2905                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2906                 textboxEl.enableDisplayMode();
2907                 textboxEl.addKeyListener([10,13], function(){
2908                     if(dlg.isVisible() && opt && opt.buttons){
2909                         if(opt.buttons.ok){
2910                             handleButton("ok");
2911                         }else if(opt.buttons.yes){
2912                             handleButton("yes");
2913                         }
2914                     }
2915                 });
2916                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2917                 textareaEl.enableDisplayMode();
2918                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2919                 progressEl.enableDisplayMode();
2920                 var pf = progressEl.dom.firstChild;
2921                 if (pf) {
2922                     pp = Roo.get(pf.firstChild);
2923                     pp.setHeight(pf.offsetHeight);
2924                 }
2925                 
2926             }
2927             return dlg;
2928         },
2929
2930         /**
2931          * Updates the message box body text
2932          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2933          * the XHTML-compliant non-breaking space character '&amp;#160;')
2934          * @return {Roo.MessageBox} This message box
2935          */
2936         updateText : function(text){
2937             if(!dlg.isVisible() && !opt.width){
2938                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2939             }
2940             msgEl.innerHTML = text || '&#160;';
2941       
2942             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2943             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2944             var w = Math.max(
2945                     Math.min(opt.width || cw , this.maxWidth), 
2946                     Math.max(opt.minWidth || this.minWidth, bwidth)
2947             );
2948             if(opt.prompt){
2949                 activeTextEl.setWidth(w);
2950             }
2951             if(dlg.isVisible()){
2952                 dlg.fixedcenter = false;
2953             }
2954             // to big, make it scroll. = But as usual stupid IE does not support
2955             // !important..
2956             
2957             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2958                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2959                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2960             } else {
2961                 bodyEl.dom.style.height = '';
2962                 bodyEl.dom.style.overflowY = '';
2963             }
2964             if (cw > w) {
2965                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2966             } else {
2967                 bodyEl.dom.style.overflowX = '';
2968             }
2969             
2970             dlg.setContentSize(w, bodyEl.getHeight());
2971             if(dlg.isVisible()){
2972                 dlg.fixedcenter = true;
2973             }
2974             return this;
2975         },
2976
2977         /**
2978          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2979          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2980          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2981          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2982          * @return {Roo.MessageBox} This message box
2983          */
2984         updateProgress : function(value, text){
2985             if(text){
2986                 this.updateText(text);
2987             }
2988             if (pp) { // weird bug on my firefox - for some reason this is not defined
2989                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2990             }
2991             return this;
2992         },        
2993
2994         /**
2995          * Returns true if the message box is currently displayed
2996          * @return {Boolean} True if the message box is visible, else false
2997          */
2998         isVisible : function(){
2999             return dlg && dlg.isVisible();  
3000         },
3001
3002         /**
3003          * Hides the message box if it is displayed
3004          */
3005         hide : function(){
3006             if(this.isVisible()){
3007                 dlg.hide();
3008             }  
3009         },
3010
3011         /**
3012          * Displays a new message box, or reinitializes an existing message box, based on the config options
3013          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3014          * The following config object properties are supported:
3015          * <pre>
3016 Property    Type             Description
3017 ----------  ---------------  ------------------------------------------------------------------------------------
3018 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3019                                    closes (defaults to undefined)
3020 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3021                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3022 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3023                                    progress and wait dialogs will ignore this property and always hide the
3024                                    close button as they can only be closed programmatically.
3025 cls               String           A custom CSS class to apply to the message box element
3026 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3027                                    displayed (defaults to 75)
3028 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3029                                    function will be btn (the name of the button that was clicked, if applicable,
3030                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3031                                    Progress and wait dialogs will ignore this option since they do not respond to
3032                                    user actions and can only be closed programmatically, so any required function
3033                                    should be called by the same code after it closes the dialog.
3034 icon              String           A CSS class that provides a background image to be used as an icon for
3035                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3036 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3037 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3038 modal             Boolean          False to allow user interaction with the page while the message box is
3039                                    displayed (defaults to true)
3040 msg               String           A string that will replace the existing message box body text (defaults
3041                                    to the XHTML-compliant non-breaking space character '&#160;')
3042 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3043 progress          Boolean          True to display a progress bar (defaults to false)
3044 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3045 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3046 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3047 title             String           The title text
3048 value             String           The string value to set into the active textbox element if displayed
3049 wait              Boolean          True to display a progress bar (defaults to false)
3050 width             Number           The width of the dialog in pixels
3051 </pre>
3052          *
3053          * Example usage:
3054          * <pre><code>
3055 Roo.Msg.show({
3056    title: 'Address',
3057    msg: 'Please enter your address:',
3058    width: 300,
3059    buttons: Roo.MessageBox.OKCANCEL,
3060    multiline: true,
3061    fn: saveAddress,
3062    animEl: 'addAddressBtn'
3063 });
3064 </code></pre>
3065          * @param {Object} config Configuration options
3066          * @return {Roo.MessageBox} This message box
3067          */
3068         show : function(options)
3069         {
3070             
3071             // this causes nightmares if you show one dialog after another
3072             // especially on callbacks..
3073              
3074             if(this.isVisible()){
3075                 
3076                 this.hide();
3077                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3078                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3079                 Roo.log("New Dialog Message:" +  options.msg )
3080                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3081                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3082                 
3083             }
3084             var d = this.getDialog();
3085             opt = options;
3086             d.setTitle(opt.title || "&#160;");
3087             d.closeEl.setDisplayed(opt.closable !== false);
3088             activeTextEl = textboxEl;
3089             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3090             if(opt.prompt){
3091                 if(opt.multiline){
3092                     textboxEl.hide();
3093                     textareaEl.show();
3094                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3095                         opt.multiline : this.defaultTextHeight);
3096                     activeTextEl = textareaEl;
3097                 }else{
3098                     textboxEl.show();
3099                     textareaEl.hide();
3100                 }
3101             }else{
3102                 textboxEl.hide();
3103                 textareaEl.hide();
3104             }
3105             progressEl.setDisplayed(opt.progress === true);
3106             this.updateProgress(0);
3107             activeTextEl.dom.value = opt.value || "";
3108             if(opt.prompt){
3109                 dlg.setDefaultButton(activeTextEl);
3110             }else{
3111                 var bs = opt.buttons;
3112                 var db = null;
3113                 if(bs && bs.ok){
3114                     db = buttons["ok"];
3115                 }else if(bs && bs.yes){
3116                     db = buttons["yes"];
3117                 }
3118                 dlg.setDefaultButton(db);
3119             }
3120             bwidth = updateButtons(opt.buttons);
3121             this.updateText(opt.msg);
3122             if(opt.cls){
3123                 d.el.addClass(opt.cls);
3124             }
3125             d.proxyDrag = opt.proxyDrag === true;
3126             d.modal = opt.modal !== false;
3127             d.mask = opt.modal !== false ? mask : false;
3128             if(!d.isVisible()){
3129                 // force it to the end of the z-index stack so it gets a cursor in FF
3130                 document.body.appendChild(dlg.el.dom);
3131                 d.animateTarget = null;
3132                 d.show(options.animEl);
3133             }
3134             return this;
3135         },
3136
3137         /**
3138          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3139          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3140          * and closing the message box when the process is complete.
3141          * @param {String} title The title bar text
3142          * @param {String} msg The message box body text
3143          * @return {Roo.MessageBox} This message box
3144          */
3145         progress : function(title, msg){
3146             this.show({
3147                 title : title,
3148                 msg : msg,
3149                 buttons: false,
3150                 progress:true,
3151                 closable:false,
3152                 minWidth: this.minProgressWidth,
3153                 modal : true
3154             });
3155             return this;
3156         },
3157
3158         /**
3159          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3160          * If a callback function is passed it will be called after the user clicks the button, and the
3161          * id of the button that was clicked will be passed as the only parameter to the callback
3162          * (could also be the top-right close button).
3163          * @param {String} title The title bar text
3164          * @param {String} msg The message box body text
3165          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3166          * @param {Object} scope (optional) The scope of the callback function
3167          * @return {Roo.MessageBox} This message box
3168          */
3169         alert : function(title, msg, fn, scope){
3170             this.show({
3171                 title : title,
3172                 msg : msg,
3173                 buttons: this.OK,
3174                 fn: fn,
3175                 scope : scope,
3176                 modal : true
3177             });
3178             return this;
3179         },
3180
3181         /**
3182          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3183          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3184          * You are responsible for closing the message box when the process is complete.
3185          * @param {String} msg The message box body text
3186          * @param {String} title (optional) The title bar text
3187          * @return {Roo.MessageBox} This message box
3188          */
3189         wait : function(msg, title){
3190             this.show({
3191                 title : title,
3192                 msg : msg,
3193                 buttons: false,
3194                 closable:false,
3195                 progress:true,
3196                 modal:true,
3197                 width:300,
3198                 wait:true
3199             });
3200             waitTimer = Roo.TaskMgr.start({
3201                 run: function(i){
3202                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3203                 },
3204                 interval: 1000
3205             });
3206             return this;
3207         },
3208
3209         /**
3210          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3211          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3212          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3213          * @param {String} title The title bar text
3214          * @param {String} msg The message box body text
3215          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3216          * @param {Object} scope (optional) The scope of the callback function
3217          * @return {Roo.MessageBox} This message box
3218          */
3219         confirm : function(title, msg, fn, scope){
3220             this.show({
3221                 title : title,
3222                 msg : msg,
3223                 buttons: this.YESNO,
3224                 fn: fn,
3225                 scope : scope,
3226                 modal : true
3227             });
3228             return this;
3229         },
3230
3231         /**
3232          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3233          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3234          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3235          * (could also be the top-right close button) and the text that was entered will be passed as the two
3236          * parameters to the callback.
3237          * @param {String} title The title bar text
3238          * @param {String} msg The message box body text
3239          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3240          * @param {Object} scope (optional) The scope of the callback function
3241          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3242          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3243          * @return {Roo.MessageBox} This message box
3244          */
3245         prompt : function(title, msg, fn, scope, multiline){
3246             this.show({
3247                 title : title,
3248                 msg : msg,
3249                 buttons: this.OKCANCEL,
3250                 fn: fn,
3251                 minWidth:250,
3252                 scope : scope,
3253                 prompt:true,
3254                 multiline: multiline,
3255                 modal : true
3256             });
3257             return this;
3258         },
3259
3260         /**
3261          * Button config that displays a single OK button
3262          * @type Object
3263          */
3264         OK : {ok:true},
3265         /**
3266          * Button config that displays Yes and No buttons
3267          * @type Object
3268          */
3269         YESNO : {yes:true, no:true},
3270         /**
3271          * Button config that displays OK and Cancel buttons
3272          * @type Object
3273          */
3274         OKCANCEL : {ok:true, cancel:true},
3275         /**
3276          * Button config that displays Yes, No and Cancel buttons
3277          * @type Object
3278          */
3279         YESNOCANCEL : {yes:true, no:true, cancel:true},
3280
3281         /**
3282          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3283          * @type Number
3284          */
3285         defaultTextHeight : 75,
3286         /**
3287          * The maximum width in pixels of the message box (defaults to 600)
3288          * @type Number
3289          */
3290         maxWidth : 600,
3291         /**
3292          * The minimum width in pixels of the message box (defaults to 100)
3293          * @type Number
3294          */
3295         minWidth : 100,
3296         /**
3297          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3298          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3299          * @type Number
3300          */
3301         minProgressWidth : 250,
3302         /**
3303          * An object containing the default button text strings that can be overriden for localized language support.
3304          * Supported properties are: ok, cancel, yes and no.
3305          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3306          * @type Object
3307          */
3308         buttonText : {
3309             ok : "OK",
3310             cancel : "Cancel",
3311             yes : "Yes",
3312             no : "No"
3313         }
3314     };
3315 }();
3316
3317 /**
3318  * Shorthand for {@link Roo.MessageBox}
3319  */
3320 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3321 Roo.Msg = Roo.Msg || Roo.MessageBox;
3322 /*
3323  * - LGPL
3324  *
3325  * navbar
3326  * 
3327  */
3328
3329 /**
3330  * @class Roo.bootstrap.Navbar
3331  * @extends Roo.bootstrap.Component
3332  * Bootstrap Navbar class
3333
3334  * @constructor
3335  * Create a new Navbar
3336  * @param {Object} config The config object
3337  */
3338
3339
3340 Roo.bootstrap.Navbar = function(config){
3341     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3342     
3343 };
3344
3345 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3346     
3347     
3348    
3349     // private
3350     navItems : false,
3351     loadMask : false,
3352     
3353     
3354     getAutoCreate : function(){
3355         
3356         
3357         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3358         
3359     },
3360     
3361     initEvents :function ()
3362     {
3363         //Roo.log(this.el.select('.navbar-toggle',true));
3364         this.el.select('.navbar-toggle',true).on('click', function() {
3365            // Roo.log('click');
3366             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3367         }, this);
3368         
3369         var mark = {
3370             tag: "div",
3371             cls:"x-dlg-mask"
3372         };
3373         
3374         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3375         
3376         var size = this.el.getSize();
3377         this.maskEl.setSize(size.width, size.height);
3378         this.maskEl.enableDisplayMode("block");
3379         this.maskEl.hide();
3380         
3381         if(this.loadMask){
3382             this.maskEl.show();
3383         }
3384     },
3385     
3386     
3387     getChildContainer : function()
3388     {
3389         if (this.el.select('.collapse').getCount()) {
3390             return this.el.select('.collapse',true).first();
3391         }
3392         
3393         return this.el;
3394     },
3395     
3396     mask : function()
3397     {
3398         this.maskEl.show();
3399     },
3400     
3401     unmask : function()
3402     {
3403         this.maskEl.hide();
3404     } 
3405     
3406     
3407     
3408     
3409 });
3410
3411
3412
3413  
3414
3415  /*
3416  * - LGPL
3417  *
3418  * navbar
3419  * 
3420  */
3421
3422 /**
3423  * @class Roo.bootstrap.NavSimplebar
3424  * @extends Roo.bootstrap.Navbar
3425  * Bootstrap Sidebar class
3426  *
3427  * @cfg {Boolean} inverse is inverted color
3428  * 
3429  * @cfg {String} type (nav | pills | tabs)
3430  * @cfg {Boolean} arrangement stacked | justified
3431  * @cfg {String} align (left | right) alignment
3432  * 
3433  * @cfg {Boolean} main (true|false) main nav bar? default false
3434  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3435  * 
3436  * @cfg {String} tag (header|footer|nav|div) default is nav 
3437
3438  * 
3439  * 
3440  * 
3441  * @constructor
3442  * Create a new Sidebar
3443  * @param {Object} config The config object
3444  */
3445
3446
3447 Roo.bootstrap.NavSimplebar = function(config){
3448     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3449 };
3450
3451 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3452     
3453     inverse: false,
3454     
3455     type: false,
3456     arrangement: '',
3457     align : false,
3458     
3459     
3460     
3461     main : false,
3462     
3463     
3464     tag : false,
3465     
3466     
3467     getAutoCreate : function(){
3468         
3469         
3470         var cfg = {
3471             tag : this.tag || 'div',
3472             cls : 'navbar'
3473         };
3474           
3475         
3476         cfg.cn = [
3477             {
3478                 cls: 'nav',
3479                 tag : 'ul'
3480             }
3481         ];
3482         
3483          
3484         this.type = this.type || 'nav';
3485         if (['tabs','pills'].indexOf(this.type)!==-1) {
3486             cfg.cn[0].cls += ' nav-' + this.type
3487         
3488         
3489         } else {
3490             if (this.type!=='nav') {
3491                 Roo.log('nav type must be nav/tabs/pills')
3492             }
3493             cfg.cn[0].cls += ' navbar-nav'
3494         }
3495         
3496         
3497         
3498         
3499         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3500             cfg.cn[0].cls += ' nav-' + this.arrangement;
3501         }
3502         
3503         
3504         if (this.align === 'right') {
3505             cfg.cn[0].cls += ' navbar-right';
3506         }
3507         
3508         if (this.inverse) {
3509             cfg.cls += ' navbar-inverse';
3510             
3511         }
3512         
3513         
3514         return cfg;
3515     
3516         
3517     }
3518     
3519     
3520     
3521 });
3522
3523
3524
3525  
3526
3527  
3528        /*
3529  * - LGPL
3530  *
3531  * navbar
3532  * 
3533  */
3534
3535 /**
3536  * @class Roo.bootstrap.NavHeaderbar
3537  * @extends Roo.bootstrap.NavSimplebar
3538  * Bootstrap Sidebar class
3539  *
3540  * @cfg {String} brand what is brand
3541  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3542  * @cfg {String} brand_href href of the brand
3543  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3544  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3545  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3546  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3547  * 
3548  * @constructor
3549  * Create a new Sidebar
3550  * @param {Object} config The config object
3551  */
3552
3553
3554 Roo.bootstrap.NavHeaderbar = function(config){
3555     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3556       
3557 };
3558
3559 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3560     
3561     position: '',
3562     brand: '',
3563     brand_href: false,
3564     srButton : true,
3565     autohide : false,
3566     desktopCenter : false,
3567    
3568     
3569     getAutoCreate : function(){
3570         
3571         var   cfg = {
3572             tag: this.nav || 'nav',
3573             cls: 'navbar',
3574             role: 'navigation',
3575             cn: []
3576         };
3577         
3578         var cn = cfg.cn;
3579         if (this.desktopCenter) {
3580             cn.push({cls : 'container', cn : []});
3581             cn = cn[0].cn;
3582         }
3583         
3584         if(this.srButton){
3585             cn.push({
3586                 tag: 'div',
3587                 cls: 'navbar-header',
3588                 cn: [
3589                     {
3590                         tag: 'button',
3591                         type: 'button',
3592                         cls: 'navbar-toggle',
3593                         'data-toggle': 'collapse',
3594                         cn: [
3595                             {
3596                                 tag: 'span',
3597                                 cls: 'sr-only',
3598                                 html: 'Toggle navigation'
3599                             },
3600                             {
3601                                 tag: 'span',
3602                                 cls: 'icon-bar'
3603                             },
3604                             {
3605                                 tag: 'span',
3606                                 cls: 'icon-bar'
3607                             },
3608                             {
3609                                 tag: 'span',
3610                                 cls: 'icon-bar'
3611                             }
3612                         ]
3613                     }
3614                 ]
3615             });
3616         }
3617         
3618         cn.push({
3619             tag: 'div',
3620             cls: 'collapse navbar-collapse',
3621             cn : []
3622         });
3623         
3624         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3625         
3626         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3627             cfg.cls += ' navbar-' + this.position;
3628             
3629             // tag can override this..
3630             
3631             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3632         }
3633         
3634         if (this.brand !== '') {
3635             cn[0].cn.push({
3636                 tag: 'a',
3637                 href: this.brand_href ? this.brand_href : '#',
3638                 cls: 'navbar-brand',
3639                 cn: [
3640                 this.brand
3641                 ]
3642             });
3643         }
3644         
3645         if(this.main){
3646             cfg.cls += ' main-nav';
3647         }
3648         
3649         
3650         return cfg;
3651
3652         
3653     },
3654     getHeaderChildContainer : function()
3655     {
3656         if (this.el.select('.navbar-header').getCount()) {
3657             return this.el.select('.navbar-header',true).first();
3658         }
3659         
3660         return this.getChildContainer();
3661     },
3662     
3663     
3664     initEvents : function()
3665     {
3666         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3667         
3668         if (this.autohide) {
3669             
3670             var prevScroll = 0;
3671             var ft = this.el;
3672             
3673             Roo.get(document).on('scroll',function(e) {
3674                 var ns = Roo.get(document).getScroll().top;
3675                 var os = prevScroll;
3676                 prevScroll = ns;
3677                 
3678                 if(ns > os){
3679                     ft.removeClass('slideDown');
3680                     ft.addClass('slideUp');
3681                     return;
3682                 }
3683                 ft.removeClass('slideUp');
3684                 ft.addClass('slideDown');
3685                  
3686               
3687           },this);
3688         }
3689     }    
3690     
3691 });
3692
3693
3694
3695  
3696
3697  /*
3698  * - LGPL
3699  *
3700  * navbar
3701  * 
3702  */
3703
3704 /**
3705  * @class Roo.bootstrap.NavSidebar
3706  * @extends Roo.bootstrap.Navbar
3707  * Bootstrap Sidebar class
3708  * 
3709  * @constructor
3710  * Create a new Sidebar
3711  * @param {Object} config The config object
3712  */
3713
3714
3715 Roo.bootstrap.NavSidebar = function(config){
3716     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3717 };
3718
3719 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3720     
3721     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3722     
3723     getAutoCreate : function(){
3724         
3725         
3726         return  {
3727             tag: 'div',
3728             cls: 'sidebar sidebar-nav'
3729         };
3730     
3731         
3732     }
3733     
3734     
3735     
3736 });
3737
3738
3739
3740  
3741
3742  /*
3743  * - LGPL
3744  *
3745  * nav group
3746  * 
3747  */
3748
3749 /**
3750  * @class Roo.bootstrap.NavGroup
3751  * @extends Roo.bootstrap.Component
3752  * Bootstrap NavGroup class
3753  * @cfg {String} align (left|right)
3754  * @cfg {Boolean} inverse
3755  * @cfg {String} type (nav|pills|tab) default nav
3756  * @cfg {String} navId - reference Id for navbar.
3757
3758  * 
3759  * @constructor
3760  * Create a new nav group
3761  * @param {Object} config The config object
3762  */
3763
3764 Roo.bootstrap.NavGroup = function(config){
3765     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3766     this.navItems = [];
3767    
3768     Roo.bootstrap.NavGroup.register(this);
3769      this.addEvents({
3770         /**
3771              * @event changed
3772              * Fires when the active item changes
3773              * @param {Roo.bootstrap.NavGroup} this
3774              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3775              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3776          */
3777         'changed': true
3778      });
3779     
3780 };
3781
3782 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3783     
3784     align: '',
3785     inverse: false,
3786     form: false,
3787     type: 'nav',
3788     navId : '',
3789     // private
3790     
3791     navItems : false, 
3792     
3793     getAutoCreate : function()
3794     {
3795         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3796         
3797         cfg = {
3798             tag : 'ul',
3799             cls: 'nav' 
3800         };
3801         
3802         if (['tabs','pills'].indexOf(this.type)!==-1) {
3803             cfg.cls += ' nav-' + this.type
3804         } else {
3805             if (this.type!=='nav') {
3806                 Roo.log('nav type must be nav/tabs/pills')
3807             }
3808             cfg.cls += ' navbar-nav'
3809         }
3810         
3811         if (this.parent().sidebar) {
3812             cfg = {
3813                 tag: 'ul',
3814                 cls: 'dashboard-menu sidebar-menu'
3815             };
3816             
3817             return cfg;
3818         }
3819         
3820         if (this.form === true) {
3821             cfg = {
3822                 tag: 'form',
3823                 cls: 'navbar-form'
3824             };
3825             
3826             if (this.align === 'right') {
3827                 cfg.cls += ' navbar-right';
3828             } else {
3829                 cfg.cls += ' navbar-left';
3830             }
3831         }
3832         
3833         if (this.align === 'right') {
3834             cfg.cls += ' navbar-right';
3835         }
3836         
3837         if (this.inverse) {
3838             cfg.cls += ' navbar-inverse';
3839             
3840         }
3841         
3842         
3843         return cfg;
3844     },
3845     /**
3846     * sets the active Navigation item
3847     * @param {Roo.bootstrap.NavItem} the new current navitem
3848     */
3849     setActiveItem : function(item)
3850     {
3851         var prev = false;
3852         Roo.each(this.navItems, function(v){
3853             if (v == item) {
3854                 return ;
3855             }
3856             if (v.isActive()) {
3857                 v.setActive(false, true);
3858                 prev = v;
3859                 
3860             }
3861             
3862         });
3863
3864         item.setActive(true, true);
3865         this.fireEvent('changed', this, item, prev);
3866         
3867         
3868     },
3869     /**
3870     * gets the active Navigation item
3871     * @return {Roo.bootstrap.NavItem} the current navitem
3872     */
3873     getActive : function()
3874     {
3875         
3876         var prev = false;
3877         Roo.each(this.navItems, function(v){
3878             
3879             if (v.isActive()) {
3880                 prev = v;
3881                 
3882             }
3883             
3884         });
3885         return prev;
3886     },
3887     
3888     indexOfNav : function()
3889     {
3890         
3891         var prev = false;
3892         Roo.each(this.navItems, function(v,i){
3893             
3894             if (v.isActive()) {
3895                 prev = i;
3896                 
3897             }
3898             
3899         });
3900         return prev;
3901     },
3902     /**
3903     * adds a Navigation item
3904     * @param {Roo.bootstrap.NavItem} the navitem to add
3905     */
3906     addItem : function(cfg)
3907     {
3908         var cn = new Roo.bootstrap.NavItem(cfg);
3909         this.register(cn);
3910         cn.parentId = this.id;
3911         cn.onRender(this.el, null);
3912         return cn;
3913     },
3914     /**
3915     * register a Navigation item
3916     * @param {Roo.bootstrap.NavItem} the navitem to add
3917     */
3918     register : function(item)
3919     {
3920         this.navItems.push( item);
3921         item.navId = this.navId;
3922     
3923     },
3924     
3925     /**
3926     * clear all the Navigation item
3927     */
3928    
3929     clearAll : function()
3930     {
3931         this.navItems = [];
3932         this.el.dom.innerHTML = '';
3933     },
3934     
3935     getNavItem: function(tabId)
3936     {
3937         var ret = false;
3938         Roo.each(this.navItems, function(e) {
3939             if (e.tabId == tabId) {
3940                ret =  e;
3941                return false;
3942             }
3943             return true;
3944             
3945         });
3946         return ret;
3947     },
3948     
3949     setActiveNext : function()
3950     {
3951         var i = this.indexOfNav(this.getActive());
3952         if (i > this.navItems.length) {
3953             return;
3954         }
3955         this.setActiveItem(this.navItems[i+1]);
3956     },
3957     setActivePrev : function()
3958     {
3959         var i = this.indexOfNav(this.getActive());
3960         if (i  < 1) {
3961             return;
3962         }
3963         this.setActiveItem(this.navItems[i-1]);
3964     },
3965     clearWasActive : function(except) {
3966         Roo.each(this.navItems, function(e) {
3967             if (e.tabId != except.tabId && e.was_active) {
3968                e.was_active = false;
3969                return false;
3970             }
3971             return true;
3972             
3973         });
3974     },
3975     getWasActive : function ()
3976     {
3977         var r = false;
3978         Roo.each(this.navItems, function(e) {
3979             if (e.was_active) {
3980                r = e;
3981                return false;
3982             }
3983             return true;
3984             
3985         });
3986         return r;
3987     }
3988     
3989     
3990 });
3991
3992  
3993 Roo.apply(Roo.bootstrap.NavGroup, {
3994     
3995     groups: {},
3996      /**
3997     * register a Navigation Group
3998     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3999     */
4000     register : function(navgrp)
4001     {
4002         this.groups[navgrp.navId] = navgrp;
4003         
4004     },
4005     /**
4006     * fetch a Navigation Group based on the navigation ID
4007     * @param {string} the navgroup to add
4008     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4009     */
4010     get: function(navId) {
4011         if (typeof(this.groups[navId]) == 'undefined') {
4012             return false;
4013             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4014         }
4015         return this.groups[navId] ;
4016     }
4017     
4018     
4019     
4020 });
4021
4022  /*
4023  * - LGPL
4024  *
4025  * row
4026  * 
4027  */
4028
4029 /**
4030  * @class Roo.bootstrap.NavItem
4031  * @extends Roo.bootstrap.Component
4032  * Bootstrap Navbar.NavItem class
4033  * @cfg {String} href  link to
4034  * @cfg {String} html content of button
4035  * @cfg {String} badge text inside badge
4036  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4037  * @cfg {String} glyphicon name of glyphicon
4038  * @cfg {String} icon name of font awesome icon
4039  * @cfg {Boolean} active Is item active
4040  * @cfg {Boolean} disabled Is item disabled
4041  
4042  * @cfg {Boolean} preventDefault (true | false) default false
4043  * @cfg {String} tabId the tab that this item activates.
4044  * @cfg {String} tagtype (a|span) render as a href or span?
4045  * @cfg {Boolean} animateRef (true|false) link to element default false  
4046   
4047  * @constructor
4048  * Create a new Navbar Item
4049  * @param {Object} config The config object
4050  */
4051 Roo.bootstrap.NavItem = function(config){
4052     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4053     this.addEvents({
4054         // raw events
4055         /**
4056          * @event click
4057          * The raw click event for the entire grid.
4058          * @param {Roo.EventObject} e
4059          */
4060         "click" : true,
4061          /**
4062             * @event changed
4063             * Fires when the active item active state changes
4064             * @param {Roo.bootstrap.NavItem} this
4065             * @param {boolean} state the new state
4066              
4067          */
4068         'changed': true,
4069         /**
4070             * @event scrollto
4071             * Fires when scroll to element
4072             * @param {Roo.bootstrap.NavItem} this
4073             * @param {Object} options
4074             * @param {Roo.EventObject} e
4075              
4076          */
4077         'scrollto': true
4078     });
4079    
4080 };
4081
4082 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4083     
4084     href: false,
4085     html: '',
4086     badge: '',
4087     icon: false,
4088     glyphicon: false,
4089     active: false,
4090     preventDefault : false,
4091     tabId : false,
4092     tagtype : 'a',
4093     disabled : false,
4094     animateRef : false,
4095     was_active : false,
4096     
4097     getAutoCreate : function(){
4098          
4099         var cfg = {
4100             tag: 'li',
4101             cls: 'nav-item'
4102             
4103         };
4104         
4105         if (this.active) {
4106             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4107         }
4108         if (this.disabled) {
4109             cfg.cls += ' disabled';
4110         }
4111         
4112         if (this.href || this.html || this.glyphicon || this.icon) {
4113             cfg.cn = [
4114                 {
4115                     tag: this.tagtype,
4116                     href : this.href || "#",
4117                     html: this.html || ''
4118                 }
4119             ];
4120             
4121             if (this.icon) {
4122                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4123             }
4124
4125             if(this.glyphicon) {
4126                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4127             }
4128             
4129             if (this.menu) {
4130                 
4131                 cfg.cn[0].html += " <span class='caret'></span>";
4132              
4133             }
4134             
4135             if (this.badge !== '') {
4136                  
4137                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4138             }
4139         }
4140         
4141         
4142         
4143         return cfg;
4144     },
4145     initEvents: function() 
4146     {
4147         if (typeof (this.menu) != 'undefined') {
4148             this.menu.parentType = this.xtype;
4149             this.menu.triggerEl = this.el;
4150             this.menu = this.addxtype(Roo.apply({}, this.menu));
4151         }
4152         
4153         this.el.select('a',true).on('click', this.onClick, this);
4154         
4155         if(this.tagtype == 'span'){
4156             this.el.select('span',true).on('click', this.onClick, this);
4157         }
4158        
4159         // at this point parent should be available..
4160         this.parent().register(this);
4161     },
4162     
4163     onClick : function(e)
4164     {
4165         if(
4166                 this.preventDefault || 
4167                 this.href == '#' 
4168         ){
4169             
4170             e.preventDefault();
4171         }
4172         
4173         if (this.disabled) {
4174             return;
4175         }
4176         
4177         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4178         if (tg && tg.transition) {
4179             Roo.log("waiting for the transitionend");
4180             return;
4181         }
4182         
4183         
4184         
4185         //Roo.log("fire event clicked");
4186         if(this.fireEvent('click', this, e) === false){
4187             return;
4188         };
4189         
4190         if(this.tagtype == 'span'){
4191             return;
4192         }
4193         
4194         //Roo.log(this.href);
4195         var ael = this.el.select('a',true).first();
4196         //Roo.log(ael);
4197         
4198         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4199             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4200             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4201                 return; // ignore... - it's a 'hash' to another page.
4202             }
4203             
4204             e.preventDefault();
4205             this.scrollToElement(e);
4206         }
4207         
4208         
4209         var p =  this.parent();
4210    
4211         if (['tabs','pills'].indexOf(p.type)!==-1) {
4212             if (typeof(p.setActiveItem) !== 'undefined') {
4213                 p.setActiveItem(this);
4214             }
4215         }
4216         
4217         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4218         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4219             // remove the collapsed menu expand...
4220             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4221         }
4222     },
4223     
4224     isActive: function () {
4225         return this.active
4226     },
4227     setActive : function(state, fire, is_was_active)
4228     {
4229         if (this.active && !state && this.navId) {
4230             this.was_active = true;
4231             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4232             if (nv) {
4233                 nv.clearWasActive(this);
4234             }
4235             
4236         }
4237         this.active = state;
4238         
4239         if (!state ) {
4240             this.el.removeClass('active');
4241         } else if (!this.el.hasClass('active')) {
4242             this.el.addClass('active');
4243         }
4244         if (fire) {
4245             this.fireEvent('changed', this, state);
4246         }
4247         
4248         // show a panel if it's registered and related..
4249         
4250         if (!this.navId || !this.tabId || !state || is_was_active) {
4251             return;
4252         }
4253         
4254         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4255         if (!tg) {
4256             return;
4257         }
4258         var pan = tg.getPanelByName(this.tabId);
4259         if (!pan) {
4260             return;
4261         }
4262         // if we can not flip to new panel - go back to old nav highlight..
4263         if (false == tg.showPanel(pan)) {
4264             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4265             if (nv) {
4266                 var onav = nv.getWasActive();
4267                 if (onav) {
4268                     onav.setActive(true, false, true);
4269                 }
4270             }
4271             
4272         }
4273         
4274         
4275         
4276     },
4277      // this should not be here...
4278     setDisabled : function(state)
4279     {
4280         this.disabled = state;
4281         if (!state ) {
4282             this.el.removeClass('disabled');
4283         } else if (!this.el.hasClass('disabled')) {
4284             this.el.addClass('disabled');
4285         }
4286         
4287     },
4288     
4289     /**
4290      * Fetch the element to display the tooltip on.
4291      * @return {Roo.Element} defaults to this.el
4292      */
4293     tooltipEl : function()
4294     {
4295         return this.el.select('' + this.tagtype + '', true).first();
4296     },
4297     
4298     scrollToElement : function(e)
4299     {
4300         var c = document.body;
4301         
4302         /*
4303          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4304          */
4305         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4306             c = document.documentElement;
4307         }
4308         
4309         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4310         
4311         if(!target){
4312             return;
4313         }
4314
4315         var o = target.calcOffsetsTo(c);
4316         
4317         var options = {
4318             target : target,
4319             value : o[1]
4320         };
4321         
4322         this.fireEvent('scrollto', this, options, e);
4323         
4324         Roo.get(c).scrollTo('top', options.value, true);
4325         
4326         return;
4327     }
4328 });
4329  
4330
4331  /*
4332  * - LGPL
4333  *
4334  * sidebar item
4335  *
4336  *  li
4337  *    <span> icon </span>
4338  *    <span> text </span>
4339  *    <span>badge </span>
4340  */
4341
4342 /**
4343  * @class Roo.bootstrap.NavSidebarItem
4344  * @extends Roo.bootstrap.NavItem
4345  * Bootstrap Navbar.NavSidebarItem class
4346  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4347  * @constructor
4348  * Create a new Navbar Button
4349  * @param {Object} config The config object
4350  */
4351 Roo.bootstrap.NavSidebarItem = function(config){
4352     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4353     this.addEvents({
4354         // raw events
4355         /**
4356          * @event click
4357          * The raw click event for the entire grid.
4358          * @param {Roo.EventObject} e
4359          */
4360         "click" : true,
4361          /**
4362             * @event changed
4363             * Fires when the active item active state changes
4364             * @param {Roo.bootstrap.NavSidebarItem} this
4365             * @param {boolean} state the new state
4366              
4367          */
4368         'changed': true
4369     });
4370    
4371 };
4372
4373 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4374     
4375     badgeWeight : 'default',
4376     
4377     getAutoCreate : function(){
4378         
4379         
4380         var a = {
4381                 tag: 'a',
4382                 href : this.href || '#',
4383                 cls: '',
4384                 html : '',
4385                 cn : []
4386         };
4387         var cfg = {
4388             tag: 'li',
4389             cls: '',
4390             cn: [ a ]
4391         };
4392         var span = {
4393             tag: 'span',
4394             html : this.html || ''
4395         };
4396         
4397         
4398         if (this.active) {
4399             cfg.cls += ' active';
4400         }
4401         
4402         if (this.disabled) {
4403             cfg.cls += ' disabled';
4404         }
4405         
4406         // left icon..
4407         if (this.glyphicon || this.icon) {
4408             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4409             a.cn.push({ tag : 'i', cls : c }) ;
4410         }
4411         // html..
4412         a.cn.push(span);
4413         // then badge..
4414         if (this.badge !== '') {
4415             
4416             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4417         }
4418         // fi
4419         if (this.menu) {
4420             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4421             a.cls += 'dropdown-toggle treeview' ;
4422             
4423         }
4424         
4425         
4426         
4427         return cfg;
4428          
4429            
4430     },
4431     
4432     initEvents : function()
4433     { 
4434         this.el.on('click', this.onClick, this);
4435        
4436     
4437         if(this.badge !== ''){
4438  
4439             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4440         }
4441         
4442     },
4443     
4444     onClick : function(e)
4445     {
4446         if(this.disabled){
4447             e.preventDefault();
4448             return;
4449         }
4450         
4451         if(this.preventDefault){
4452             e.preventDefault();
4453         }
4454         
4455         this.fireEvent('click', this);
4456     },
4457     
4458     disable : function()
4459     {
4460         this.setDisabled(true);
4461     },
4462     
4463     enable : function()
4464     {
4465         this.setDisabled(false);
4466     },
4467     
4468     setDisabled : function(state)
4469     {
4470         if(this.disabled == state){
4471             return;
4472         }
4473         
4474         this.disabled = state;
4475         
4476         if (state) {
4477             this.el.addClass('disabled');
4478             return;
4479         }
4480         
4481         this.el.removeClass('disabled');
4482         
4483         return;
4484     },
4485     
4486     setActive : function(state)
4487     {
4488         if(this.active == state){
4489             return;
4490         }
4491         
4492         this.active = state;
4493         
4494         if (state) {
4495             this.el.addClass('active');
4496             return;
4497         }
4498         
4499         this.el.removeClass('active');
4500         
4501         return;
4502     },
4503     
4504     isActive: function () 
4505     {
4506         return this.active;
4507     },
4508     
4509     setBadge : function(str)
4510     {
4511         if(!this.badgeEl){
4512             return;
4513         }
4514         
4515         this.badgeEl.dom.innerHTML = str;
4516     }
4517     
4518    
4519      
4520  
4521 });
4522  
4523
4524  /*
4525  * - LGPL
4526  *
4527  * row
4528  * 
4529  */
4530
4531 /**
4532  * @class Roo.bootstrap.Row
4533  * @extends Roo.bootstrap.Component
4534  * Bootstrap Row class (contains columns...)
4535  * 
4536  * @constructor
4537  * Create a new Row
4538  * @param {Object} config The config object
4539  */
4540
4541 Roo.bootstrap.Row = function(config){
4542     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4543 };
4544
4545 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4546     
4547     getAutoCreate : function(){
4548        return {
4549             cls: 'row clearfix'
4550        };
4551     }
4552     
4553     
4554 });
4555
4556  
4557
4558  /*
4559  * - LGPL
4560  *
4561  * element
4562  * 
4563  */
4564
4565 /**
4566  * @class Roo.bootstrap.Element
4567  * @extends Roo.bootstrap.Component
4568  * Bootstrap Element class
4569  * @cfg {String} html contents of the element
4570  * @cfg {String} tag tag of the element
4571  * @cfg {String} cls class of the element
4572  * @cfg {Boolean} preventDefault (true|false) default false
4573  * @cfg {Boolean} clickable (true|false) default false
4574  * 
4575  * @constructor
4576  * Create a new Element
4577  * @param {Object} config The config object
4578  */
4579
4580 Roo.bootstrap.Element = function(config){
4581     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4582     
4583     this.addEvents({
4584         // raw events
4585         /**
4586          * @event click
4587          * When a element is chick
4588          * @param {Roo.bootstrap.Element} this
4589          * @param {Roo.EventObject} e
4590          */
4591         "click" : true
4592     });
4593 };
4594
4595 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4596     
4597     tag: 'div',
4598     cls: '',
4599     html: '',
4600     preventDefault: false, 
4601     clickable: false,
4602     
4603     getAutoCreate : function(){
4604         
4605         var cfg = {
4606             tag: this.tag,
4607             cls: this.cls,
4608             html: this.html
4609         };
4610         
4611         return cfg;
4612     },
4613     
4614     initEvents: function() 
4615     {
4616         Roo.bootstrap.Element.superclass.initEvents.call(this);
4617         
4618         if(this.clickable){
4619             this.el.on('click', this.onClick, this);
4620         }
4621         
4622     },
4623     
4624     onClick : function(e)
4625     {
4626         if(this.preventDefault){
4627             e.preventDefault();
4628         }
4629         
4630         this.fireEvent('click', this, e);
4631     },
4632     
4633     getValue : function()
4634     {
4635         return this.el.dom.innerHTML;
4636     },
4637     
4638     setValue : function(value)
4639     {
4640         this.el.dom.innerHTML = value;
4641     }
4642    
4643 });
4644
4645  
4646
4647  /*
4648  * - LGPL
4649  *
4650  * pagination
4651  * 
4652  */
4653
4654 /**
4655  * @class Roo.bootstrap.Pagination
4656  * @extends Roo.bootstrap.Component
4657  * Bootstrap Pagination class
4658  * @cfg {String} size xs | sm | md | lg
4659  * @cfg {Boolean} inverse false | true
4660  * 
4661  * @constructor
4662  * Create a new Pagination
4663  * @param {Object} config The config object
4664  */
4665
4666 Roo.bootstrap.Pagination = function(config){
4667     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4668 };
4669
4670 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4671     
4672     cls: false,
4673     size: false,
4674     inverse: false,
4675     
4676     getAutoCreate : function(){
4677         var cfg = {
4678             tag: 'ul',
4679                 cls: 'pagination'
4680         };
4681         if (this.inverse) {
4682             cfg.cls += ' inverse';
4683         }
4684         if (this.html) {
4685             cfg.html=this.html;
4686         }
4687         if (this.cls) {
4688             cfg.cls += " " + this.cls;
4689         }
4690         return cfg;
4691     }
4692    
4693 });
4694
4695  
4696
4697  /*
4698  * - LGPL
4699  *
4700  * Pagination item
4701  * 
4702  */
4703
4704
4705 /**
4706  * @class Roo.bootstrap.PaginationItem
4707  * @extends Roo.bootstrap.Component
4708  * Bootstrap PaginationItem class
4709  * @cfg {String} html text
4710  * @cfg {String} href the link
4711  * @cfg {Boolean} preventDefault (true | false) default true
4712  * @cfg {Boolean} active (true | false) default false
4713  * @cfg {Boolean} disabled default false
4714  * 
4715  * 
4716  * @constructor
4717  * Create a new PaginationItem
4718  * @param {Object} config The config object
4719  */
4720
4721
4722 Roo.bootstrap.PaginationItem = function(config){
4723     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4724     this.addEvents({
4725         // raw events
4726         /**
4727          * @event click
4728          * The raw click event for the entire grid.
4729          * @param {Roo.EventObject} e
4730          */
4731         "click" : true
4732     });
4733 };
4734
4735 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4736     
4737     href : false,
4738     html : false,
4739     preventDefault: true,
4740     active : false,
4741     cls : false,
4742     disabled: false,
4743     
4744     getAutoCreate : function(){
4745         var cfg= {
4746             tag: 'li',
4747             cn: [
4748                 {
4749                     tag : 'a',
4750                     href : this.href ? this.href : '#',
4751                     html : this.html ? this.html : ''
4752                 }
4753             ]
4754         };
4755         
4756         if(this.cls){
4757             cfg.cls = this.cls;
4758         }
4759         
4760         if(this.disabled){
4761             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4762         }
4763         
4764         if(this.active){
4765             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4766         }
4767         
4768         return cfg;
4769     },
4770     
4771     initEvents: function() {
4772         
4773         this.el.on('click', this.onClick, this);
4774         
4775     },
4776     onClick : function(e)
4777     {
4778         Roo.log('PaginationItem on click ');
4779         if(this.preventDefault){
4780             e.preventDefault();
4781         }
4782         
4783         if(this.disabled){
4784             return;
4785         }
4786         
4787         this.fireEvent('click', this, e);
4788     }
4789    
4790 });
4791
4792  
4793
4794  /*
4795  * - LGPL
4796  *
4797  * slider
4798  * 
4799  */
4800
4801
4802 /**
4803  * @class Roo.bootstrap.Slider
4804  * @extends Roo.bootstrap.Component
4805  * Bootstrap Slider class
4806  *    
4807  * @constructor
4808  * Create a new Slider
4809  * @param {Object} config The config object
4810  */
4811
4812 Roo.bootstrap.Slider = function(config){
4813     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4814 };
4815
4816 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4817     
4818     getAutoCreate : function(){
4819         
4820         var cfg = {
4821             tag: 'div',
4822             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4823             cn: [
4824                 {
4825                     tag: 'a',
4826                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4827                 }
4828             ]
4829         };
4830         
4831         return cfg;
4832     }
4833    
4834 });
4835
4836  /*
4837  * Based on:
4838  * Ext JS Library 1.1.1
4839  * Copyright(c) 2006-2007, Ext JS, LLC.
4840  *
4841  * Originally Released Under LGPL - original licence link has changed is not relivant.
4842  *
4843  * Fork - LGPL
4844  * <script type="text/javascript">
4845  */
4846  
4847
4848 /**
4849  * @class Roo.grid.ColumnModel
4850  * @extends Roo.util.Observable
4851  * This is the default implementation of a ColumnModel used by the Grid. It defines
4852  * the columns in the grid.
4853  * <br>Usage:<br>
4854  <pre><code>
4855  var colModel = new Roo.grid.ColumnModel([
4856         {header: "Ticker", width: 60, sortable: true, locked: true},
4857         {header: "Company Name", width: 150, sortable: true},
4858         {header: "Market Cap.", width: 100, sortable: true},
4859         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4860         {header: "Employees", width: 100, sortable: true, resizable: false}
4861  ]);
4862  </code></pre>
4863  * <p>
4864  
4865  * The config options listed for this class are options which may appear in each
4866  * individual column definition.
4867  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4868  * @constructor
4869  * @param {Object} config An Array of column config objects. See this class's
4870  * config objects for details.
4871 */
4872 Roo.grid.ColumnModel = function(config){
4873         /**
4874      * The config passed into the constructor
4875      */
4876     this.config = config;
4877     this.lookup = {};
4878
4879     // if no id, create one
4880     // if the column does not have a dataIndex mapping,
4881     // map it to the order it is in the config
4882     for(var i = 0, len = config.length; i < len; i++){
4883         var c = config[i];
4884         if(typeof c.dataIndex == "undefined"){
4885             c.dataIndex = i;
4886         }
4887         if(typeof c.renderer == "string"){
4888             c.renderer = Roo.util.Format[c.renderer];
4889         }
4890         if(typeof c.id == "undefined"){
4891             c.id = Roo.id();
4892         }
4893         if(c.editor && c.editor.xtype){
4894             c.editor  = Roo.factory(c.editor, Roo.grid);
4895         }
4896         if(c.editor && c.editor.isFormField){
4897             c.editor = new Roo.grid.GridEditor(c.editor);
4898         }
4899         this.lookup[c.id] = c;
4900     }
4901
4902     /**
4903      * The width of columns which have no width specified (defaults to 100)
4904      * @type Number
4905      */
4906     this.defaultWidth = 100;
4907
4908     /**
4909      * Default sortable of columns which have no sortable specified (defaults to false)
4910      * @type Boolean
4911      */
4912     this.defaultSortable = false;
4913
4914     this.addEvents({
4915         /**
4916              * @event widthchange
4917              * Fires when the width of a column changes.
4918              * @param {ColumnModel} this
4919              * @param {Number} columnIndex The column index
4920              * @param {Number} newWidth The new width
4921              */
4922             "widthchange": true,
4923         /**
4924              * @event headerchange
4925              * Fires when the text of a header changes.
4926              * @param {ColumnModel} this
4927              * @param {Number} columnIndex The column index
4928              * @param {Number} newText The new header text
4929              */
4930             "headerchange": true,
4931         /**
4932              * @event hiddenchange
4933              * Fires when a column is hidden or "unhidden".
4934              * @param {ColumnModel} this
4935              * @param {Number} columnIndex The column index
4936              * @param {Boolean} hidden true if hidden, false otherwise
4937              */
4938             "hiddenchange": true,
4939             /**
4940          * @event columnmoved
4941          * Fires when a column is moved.
4942          * @param {ColumnModel} this
4943          * @param {Number} oldIndex
4944          * @param {Number} newIndex
4945          */
4946         "columnmoved" : true,
4947         /**
4948          * @event columlockchange
4949          * Fires when a column's locked state is changed
4950          * @param {ColumnModel} this
4951          * @param {Number} colIndex
4952          * @param {Boolean} locked true if locked
4953          */
4954         "columnlockchange" : true
4955     });
4956     Roo.grid.ColumnModel.superclass.constructor.call(this);
4957 };
4958 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4959     /**
4960      * @cfg {String} header The header text to display in the Grid view.
4961      */
4962     /**
4963      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4964      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4965      * specified, the column's index is used as an index into the Record's data Array.
4966      */
4967     /**
4968      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4969      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4970      */
4971     /**
4972      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4973      * Defaults to the value of the {@link #defaultSortable} property.
4974      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4975      */
4976     /**
4977      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4978      */
4979     /**
4980      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4981      */
4982     /**
4983      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4984      */
4985     /**
4986      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4987      */
4988     /**
4989      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4990      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4991      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4992      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4993      */
4994        /**
4995      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4996      */
4997     /**
4998      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4999      */
5000     /**
5001      * @cfg {String} cursor (Optional)
5002      */
5003     /**
5004      * @cfg {String} tooltip (Optional)
5005      */
5006     /**
5007      * @cfg {Number} xs (Optional)
5008      */
5009     /**
5010      * @cfg {Number} sm (Optional)
5011      */
5012     /**
5013      * @cfg {Number} md (Optional)
5014      */
5015     /**
5016      * @cfg {Number} lg (Optional)
5017      */
5018     /**
5019      * Returns the id of the column at the specified index.
5020      * @param {Number} index The column index
5021      * @return {String} the id
5022      */
5023     getColumnId : function(index){
5024         return this.config[index].id;
5025     },
5026
5027     /**
5028      * Returns the column for a specified id.
5029      * @param {String} id The column id
5030      * @return {Object} the column
5031      */
5032     getColumnById : function(id){
5033         return this.lookup[id];
5034     },
5035
5036     
5037     /**
5038      * Returns the column for a specified dataIndex.
5039      * @param {String} dataIndex The column dataIndex
5040      * @return {Object|Boolean} the column or false if not found
5041      */
5042     getColumnByDataIndex: function(dataIndex){
5043         var index = this.findColumnIndex(dataIndex);
5044         return index > -1 ? this.config[index] : false;
5045     },
5046     
5047     /**
5048      * Returns the index for a specified column id.
5049      * @param {String} id The column id
5050      * @return {Number} the index, or -1 if not found
5051      */
5052     getIndexById : function(id){
5053         for(var i = 0, len = this.config.length; i < len; i++){
5054             if(this.config[i].id == id){
5055                 return i;
5056             }
5057         }
5058         return -1;
5059     },
5060     
5061     /**
5062      * Returns the index for a specified column dataIndex.
5063      * @param {String} dataIndex The column dataIndex
5064      * @return {Number} the index, or -1 if not found
5065      */
5066     
5067     findColumnIndex : function(dataIndex){
5068         for(var i = 0, len = this.config.length; i < len; i++){
5069             if(this.config[i].dataIndex == dataIndex){
5070                 return i;
5071             }
5072         }
5073         return -1;
5074     },
5075     
5076     
5077     moveColumn : function(oldIndex, newIndex){
5078         var c = this.config[oldIndex];
5079         this.config.splice(oldIndex, 1);
5080         this.config.splice(newIndex, 0, c);
5081         this.dataMap = null;
5082         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5083     },
5084
5085     isLocked : function(colIndex){
5086         return this.config[colIndex].locked === true;
5087     },
5088
5089     setLocked : function(colIndex, value, suppressEvent){
5090         if(this.isLocked(colIndex) == value){
5091             return;
5092         }
5093         this.config[colIndex].locked = value;
5094         if(!suppressEvent){
5095             this.fireEvent("columnlockchange", this, colIndex, value);
5096         }
5097     },
5098
5099     getTotalLockedWidth : function(){
5100         var totalWidth = 0;
5101         for(var i = 0; i < this.config.length; i++){
5102             if(this.isLocked(i) && !this.isHidden(i)){
5103                 this.totalWidth += this.getColumnWidth(i);
5104             }
5105         }
5106         return totalWidth;
5107     },
5108
5109     getLockedCount : function(){
5110         for(var i = 0, len = this.config.length; i < len; i++){
5111             if(!this.isLocked(i)){
5112                 return i;
5113             }
5114         }
5115     },
5116
5117     /**
5118      * Returns the number of columns.
5119      * @return {Number}
5120      */
5121     getColumnCount : function(visibleOnly){
5122         if(visibleOnly === true){
5123             var c = 0;
5124             for(var i = 0, len = this.config.length; i < len; i++){
5125                 if(!this.isHidden(i)){
5126                     c++;
5127                 }
5128             }
5129             return c;
5130         }
5131         return this.config.length;
5132     },
5133
5134     /**
5135      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5136      * @param {Function} fn
5137      * @param {Object} scope (optional)
5138      * @return {Array} result
5139      */
5140     getColumnsBy : function(fn, scope){
5141         var r = [];
5142         for(var i = 0, len = this.config.length; i < len; i++){
5143             var c = this.config[i];
5144             if(fn.call(scope||this, c, i) === true){
5145                 r[r.length] = c;
5146             }
5147         }
5148         return r;
5149     },
5150
5151     /**
5152      * Returns true if the specified column is sortable.
5153      * @param {Number} col The column index
5154      * @return {Boolean}
5155      */
5156     isSortable : function(col){
5157         if(typeof this.config[col].sortable == "undefined"){
5158             return this.defaultSortable;
5159         }
5160         return this.config[col].sortable;
5161     },
5162
5163     /**
5164      * Returns the rendering (formatting) function defined for the column.
5165      * @param {Number} col The column index.
5166      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5167      */
5168     getRenderer : function(col){
5169         if(!this.config[col].renderer){
5170             return Roo.grid.ColumnModel.defaultRenderer;
5171         }
5172         return this.config[col].renderer;
5173     },
5174
5175     /**
5176      * Sets the rendering (formatting) function for a column.
5177      * @param {Number} col The column index
5178      * @param {Function} fn The function to use to process the cell's raw data
5179      * to return HTML markup for the grid view. The render function is called with
5180      * the following parameters:<ul>
5181      * <li>Data value.</li>
5182      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5183      * <li>css A CSS style string to apply to the table cell.</li>
5184      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5185      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5186      * <li>Row index</li>
5187      * <li>Column index</li>
5188      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5189      */
5190     setRenderer : function(col, fn){
5191         this.config[col].renderer = fn;
5192     },
5193
5194     /**
5195      * Returns the width for the specified column.
5196      * @param {Number} col The column index
5197      * @return {Number}
5198      */
5199     getColumnWidth : function(col){
5200         return this.config[col].width * 1 || this.defaultWidth;
5201     },
5202
5203     /**
5204      * Sets the width for a column.
5205      * @param {Number} col The column index
5206      * @param {Number} width The new width
5207      */
5208     setColumnWidth : function(col, width, suppressEvent){
5209         this.config[col].width = width;
5210         this.totalWidth = null;
5211         if(!suppressEvent){
5212              this.fireEvent("widthchange", this, col, width);
5213         }
5214     },
5215
5216     /**
5217      * Returns the total width of all columns.
5218      * @param {Boolean} includeHidden True to include hidden column widths
5219      * @return {Number}
5220      */
5221     getTotalWidth : function(includeHidden){
5222         if(!this.totalWidth){
5223             this.totalWidth = 0;
5224             for(var i = 0, len = this.config.length; i < len; i++){
5225                 if(includeHidden || !this.isHidden(i)){
5226                     this.totalWidth += this.getColumnWidth(i);
5227                 }
5228             }
5229         }
5230         return this.totalWidth;
5231     },
5232
5233     /**
5234      * Returns the header for the specified column.
5235      * @param {Number} col The column index
5236      * @return {String}
5237      */
5238     getColumnHeader : function(col){
5239         return this.config[col].header;
5240     },
5241
5242     /**
5243      * Sets the header for a column.
5244      * @param {Number} col The column index
5245      * @param {String} header The new header
5246      */
5247     setColumnHeader : function(col, header){
5248         this.config[col].header = header;
5249         this.fireEvent("headerchange", this, col, header);
5250     },
5251
5252     /**
5253      * Returns the tooltip for the specified column.
5254      * @param {Number} col The column index
5255      * @return {String}
5256      */
5257     getColumnTooltip : function(col){
5258             return this.config[col].tooltip;
5259     },
5260     /**
5261      * Sets the tooltip for a column.
5262      * @param {Number} col The column index
5263      * @param {String} tooltip The new tooltip
5264      */
5265     setColumnTooltip : function(col, tooltip){
5266             this.config[col].tooltip = tooltip;
5267     },
5268
5269     /**
5270      * Returns the dataIndex for the specified column.
5271      * @param {Number} col The column index
5272      * @return {Number}
5273      */
5274     getDataIndex : function(col){
5275         return this.config[col].dataIndex;
5276     },
5277
5278     /**
5279      * Sets the dataIndex for a column.
5280      * @param {Number} col The column index
5281      * @param {Number} dataIndex The new dataIndex
5282      */
5283     setDataIndex : function(col, dataIndex){
5284         this.config[col].dataIndex = dataIndex;
5285     },
5286
5287     
5288     
5289     /**
5290      * Returns true if the cell is editable.
5291      * @param {Number} colIndex The column index
5292      * @param {Number} rowIndex The row index
5293      * @return {Boolean}
5294      */
5295     isCellEditable : function(colIndex, rowIndex){
5296         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5297     },
5298
5299     /**
5300      * Returns the editor defined for the cell/column.
5301      * return false or null to disable editing.
5302      * @param {Number} colIndex The column index
5303      * @param {Number} rowIndex The row index
5304      * @return {Object}
5305      */
5306     getCellEditor : function(colIndex, rowIndex){
5307         return this.config[colIndex].editor;
5308     },
5309
5310     /**
5311      * Sets if a column is editable.
5312      * @param {Number} col The column index
5313      * @param {Boolean} editable True if the column is editable
5314      */
5315     setEditable : function(col, editable){
5316         this.config[col].editable = editable;
5317     },
5318
5319
5320     /**
5321      * Returns true if the column is hidden.
5322      * @param {Number} colIndex The column index
5323      * @return {Boolean}
5324      */
5325     isHidden : function(colIndex){
5326         return this.config[colIndex].hidden;
5327     },
5328
5329
5330     /**
5331      * Returns true if the column width cannot be changed
5332      */
5333     isFixed : function(colIndex){
5334         return this.config[colIndex].fixed;
5335     },
5336
5337     /**
5338      * Returns true if the column can be resized
5339      * @return {Boolean}
5340      */
5341     isResizable : function(colIndex){
5342         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5343     },
5344     /**
5345      * Sets if a column is hidden.
5346      * @param {Number} colIndex The column index
5347      * @param {Boolean} hidden True if the column is hidden
5348      */
5349     setHidden : function(colIndex, hidden){
5350         this.config[colIndex].hidden = hidden;
5351         this.totalWidth = null;
5352         this.fireEvent("hiddenchange", this, colIndex, hidden);
5353     },
5354
5355     /**
5356      * Sets the editor for a column.
5357      * @param {Number} col The column index
5358      * @param {Object} editor The editor object
5359      */
5360     setEditor : function(col, editor){
5361         this.config[col].editor = editor;
5362     }
5363 });
5364
5365 Roo.grid.ColumnModel.defaultRenderer = function(value){
5366         if(typeof value == "string" && value.length < 1){
5367             return "&#160;";
5368         }
5369         return value;
5370 };
5371
5372 // Alias for backwards compatibility
5373 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5374 /*
5375  * Based on:
5376  * Ext JS Library 1.1.1
5377  * Copyright(c) 2006-2007, Ext JS, LLC.
5378  *
5379  * Originally Released Under LGPL - original licence link has changed is not relivant.
5380  *
5381  * Fork - LGPL
5382  * <script type="text/javascript">
5383  */
5384  
5385 /**
5386  * @class Roo.LoadMask
5387  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5388  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5389  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5390  * element's UpdateManager load indicator and will be destroyed after the initial load.
5391  * @constructor
5392  * Create a new LoadMask
5393  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5394  * @param {Object} config The config object
5395  */
5396 Roo.LoadMask = function(el, config){
5397     this.el = Roo.get(el);
5398     Roo.apply(this, config);
5399     if(this.store){
5400         this.store.on('beforeload', this.onBeforeLoad, this);
5401         this.store.on('load', this.onLoad, this);
5402         this.store.on('loadexception', this.onLoadException, this);
5403         this.removeMask = false;
5404     }else{
5405         var um = this.el.getUpdateManager();
5406         um.showLoadIndicator = false; // disable the default indicator
5407         um.on('beforeupdate', this.onBeforeLoad, this);
5408         um.on('update', this.onLoad, this);
5409         um.on('failure', this.onLoad, this);
5410         this.removeMask = true;
5411     }
5412 };
5413
5414 Roo.LoadMask.prototype = {
5415     /**
5416      * @cfg {Boolean} removeMask
5417      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5418      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5419      */
5420     /**
5421      * @cfg {String} msg
5422      * The text to display in a centered loading message box (defaults to 'Loading...')
5423      */
5424     msg : 'Loading...',
5425     /**
5426      * @cfg {String} msgCls
5427      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5428      */
5429     msgCls : 'x-mask-loading',
5430
5431     /**
5432      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5433      * @type Boolean
5434      */
5435     disabled: false,
5436
5437     /**
5438      * Disables the mask to prevent it from being displayed
5439      */
5440     disable : function(){
5441        this.disabled = true;
5442     },
5443
5444     /**
5445      * Enables the mask so that it can be displayed
5446      */
5447     enable : function(){
5448         this.disabled = false;
5449     },
5450     
5451     onLoadException : function()
5452     {
5453         Roo.log(arguments);
5454         
5455         if (typeof(arguments[3]) != 'undefined') {
5456             Roo.MessageBox.alert("Error loading",arguments[3]);
5457         } 
5458         /*
5459         try {
5460             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5461                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5462             }   
5463         } catch(e) {
5464             
5465         }
5466         */
5467     
5468         
5469         
5470         this.el.unmask(this.removeMask);
5471     },
5472     // private
5473     onLoad : function()
5474     {
5475         this.el.unmask(this.removeMask);
5476     },
5477
5478     // private
5479     onBeforeLoad : function(){
5480         if(!this.disabled){
5481             this.el.mask(this.msg, this.msgCls);
5482         }
5483     },
5484
5485     // private
5486     destroy : function(){
5487         if(this.store){
5488             this.store.un('beforeload', this.onBeforeLoad, this);
5489             this.store.un('load', this.onLoad, this);
5490             this.store.un('loadexception', this.onLoadException, this);
5491         }else{
5492             var um = this.el.getUpdateManager();
5493             um.un('beforeupdate', this.onBeforeLoad, this);
5494             um.un('update', this.onLoad, this);
5495             um.un('failure', this.onLoad, this);
5496         }
5497     }
5498 };/*
5499  * - LGPL
5500  *
5501  * table
5502  * 
5503  */
5504
5505 /**
5506  * @class Roo.bootstrap.Table
5507  * @extends Roo.bootstrap.Component
5508  * Bootstrap Table class
5509  * @cfg {String} cls table class
5510  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5511  * @cfg {String} bgcolor Specifies the background color for a table
5512  * @cfg {Number} border Specifies whether the table cells should have borders or not
5513  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5514  * @cfg {Number} cellspacing Specifies the space between cells
5515  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5516  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5517  * @cfg {String} sortable Specifies that the table should be sortable
5518  * @cfg {String} summary Specifies a summary of the content of a table
5519  * @cfg {Number} width Specifies the width of a table
5520  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5521  * 
5522  * @cfg {boolean} striped Should the rows be alternative striped
5523  * @cfg {boolean} bordered Add borders to the table
5524  * @cfg {boolean} hover Add hover highlighting
5525  * @cfg {boolean} condensed Format condensed
5526  * @cfg {boolean} responsive Format condensed
5527  * @cfg {Boolean} loadMask (true|false) default false
5528  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5529  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5530  * @cfg {Boolean} rowSelection (true|false) default false
5531  * @cfg {Boolean} cellSelection (true|false) default false
5532  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5533  
5534  * 
5535  * @constructor
5536  * Create a new Table
5537  * @param {Object} config The config object
5538  */
5539
5540 Roo.bootstrap.Table = function(config){
5541     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5542     
5543     // BC...
5544     this.rowSelection = (typeof(config.RowSelection) != 'undefined') ? config.RowSelection : this.rowSelection;
5545     this.cellSelection = (typeof(config.CellSelection) != 'undefined') ? config.CellSelection : this.cellSelection;
5546     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5547     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5548     
5549     
5550     if (this.sm) {
5551         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5552         this.sm = this.selModel;
5553         this.sm.xmodule = this.xmodule || false;
5554     }
5555     if (this.cm && typeof(this.cm.config) == 'undefined') {
5556         this.colModel = new Roo.grid.ColumnModel(this.cm);
5557         this.cm = this.colModel;
5558         this.cm.xmodule = this.xmodule || false;
5559     }
5560     if (this.store) {
5561         this.store= Roo.factory(this.store, Roo.data);
5562         this.ds = this.store;
5563         this.ds.xmodule = this.xmodule || false;
5564          
5565     }
5566     if (this.footer && this.store) {
5567         this.footer.dataSource = this.ds;
5568         this.footer = Roo.factory(this.footer);
5569     }
5570     
5571     /** @private */
5572     this.addEvents({
5573         /**
5574          * @event cellclick
5575          * Fires when a cell is clicked
5576          * @param {Roo.bootstrap.Table} this
5577          * @param {Roo.Element} el
5578          * @param {Number} rowIndex
5579          * @param {Number} columnIndex
5580          * @param {Roo.EventObject} e
5581          */
5582         "cellclick" : true,
5583         /**
5584          * @event celldblclick
5585          * Fires when a cell is double clicked
5586          * @param {Roo.bootstrap.Table} this
5587          * @param {Roo.Element} el
5588          * @param {Number} rowIndex
5589          * @param {Number} columnIndex
5590          * @param {Roo.EventObject} e
5591          */
5592         "celldblclick" : true,
5593         /**
5594          * @event rowclick
5595          * Fires when a row is clicked
5596          * @param {Roo.bootstrap.Table} this
5597          * @param {Roo.Element} el
5598          * @param {Number} rowIndex
5599          * @param {Roo.EventObject} e
5600          */
5601         "rowclick" : true,
5602         /**
5603          * @event rowdblclick
5604          * Fires when a row is double clicked
5605          * @param {Roo.bootstrap.Table} this
5606          * @param {Roo.Element} el
5607          * @param {Number} rowIndex
5608          * @param {Roo.EventObject} e
5609          */
5610         "rowdblclick" : true,
5611         /**
5612          * @event mouseover
5613          * Fires when a mouseover occur
5614          * @param {Roo.bootstrap.Table} this
5615          * @param {Roo.Element} el
5616          * @param {Number} rowIndex
5617          * @param {Number} columnIndex
5618          * @param {Roo.EventObject} e
5619          */
5620         "mouseover" : true,
5621         /**
5622          * @event mouseout
5623          * Fires when a mouseout occur
5624          * @param {Roo.bootstrap.Table} this
5625          * @param {Roo.Element} el
5626          * @param {Number} rowIndex
5627          * @param {Number} columnIndex
5628          * @param {Roo.EventObject} e
5629          */
5630         "mouseout" : true,
5631         /**
5632          * @event rowclass
5633          * Fires when a row is rendered, so you can change add a style to it.
5634          * @param {Roo.bootstrap.Table} this
5635          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5636          */
5637         'rowclass' : true,
5638           /**
5639          * @event rowsrendered
5640          * Fires when all the  rows have been rendered
5641          * @param {Roo.bootstrap.Table} this
5642          */
5643         'rowsrendered' : true
5644         
5645     });
5646 };
5647
5648 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5649     
5650     cls: false,
5651     align: false,
5652     bgcolor: false,
5653     border: false,
5654     cellpadding: false,
5655     cellspacing: false,
5656     frame: false,
5657     rules: false,
5658     sortable: false,
5659     summary: false,
5660     width: false,
5661     striped : false,
5662     bordered: false,
5663     hover:  false,
5664     condensed : false,
5665     responsive : false,
5666     sm : false,
5667     cm : false,
5668     store : false,
5669     loadMask : false,
5670     footerShow : true,
5671     headerShow : true,
5672   
5673     rowSelection : false,
5674     cellSelection : false,
5675     layout : false,
5676     
5677     // Roo.Element - the tbody
5678     mainBody: false, 
5679     
5680     getAutoCreate : function(){
5681         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5682         
5683         cfg = {
5684             tag: 'table',
5685             cls : 'table',
5686             cn : []
5687         };
5688             
5689         if (this.striped) {
5690             cfg.cls += ' table-striped';
5691         }
5692         
5693         if (this.hover) {
5694             cfg.cls += ' table-hover';
5695         }
5696         if (this.bordered) {
5697             cfg.cls += ' table-bordered';
5698         }
5699         if (this.condensed) {
5700             cfg.cls += ' table-condensed';
5701         }
5702         if (this.responsive) {
5703             cfg.cls += ' table-responsive';
5704         }
5705         
5706         if (this.cls) {
5707             cfg.cls+=  ' ' +this.cls;
5708         }
5709         
5710         // this lot should be simplifed...
5711         
5712         if (this.align) {
5713             cfg.align=this.align;
5714         }
5715         if (this.bgcolor) {
5716             cfg.bgcolor=this.bgcolor;
5717         }
5718         if (this.border) {
5719             cfg.border=this.border;
5720         }
5721         if (this.cellpadding) {
5722             cfg.cellpadding=this.cellpadding;
5723         }
5724         if (this.cellspacing) {
5725             cfg.cellspacing=this.cellspacing;
5726         }
5727         if (this.frame) {
5728             cfg.frame=this.frame;
5729         }
5730         if (this.rules) {
5731             cfg.rules=this.rules;
5732         }
5733         if (this.sortable) {
5734             cfg.sortable=this.sortable;
5735         }
5736         if (this.summary) {
5737             cfg.summary=this.summary;
5738         }
5739         if (this.width) {
5740             cfg.width=this.width;
5741         }
5742         if (this.layout) {
5743             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5744         }
5745         
5746         if(this.store || this.cm){
5747             if(this.headerShow){
5748                 cfg.cn.push(this.renderHeader());
5749             }
5750             
5751             cfg.cn.push(this.renderBody());
5752             
5753             if(this.footerShow){
5754                 cfg.cn.push(this.renderFooter());
5755             }
5756             
5757             cfg.cls+=  ' TableGrid';
5758         }
5759         
5760         return { cn : [ cfg ] };
5761     },
5762     
5763     initEvents : function()
5764     {   
5765         if(!this.store || !this.cm){
5766             return;
5767         }
5768         
5769         //Roo.log('initEvents with ds!!!!');
5770         
5771         this.mainBody = this.el.select('tbody', true).first();
5772         
5773         
5774         var _this = this;
5775         
5776         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5777             e.on('click', _this.sort, _this);
5778         });
5779         
5780         this.el.on("click", this.onClick, this);
5781         this.el.on("dblclick", this.onDblClick, this);
5782         
5783         // why is this done????? = it breaks dialogs??
5784         //this.parent().el.setStyle('position', 'relative');
5785         
5786         
5787         if (this.footer) {
5788             this.footer.parentId = this.id;
5789             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5790         }
5791         
5792         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5793         
5794         this.store.on('load', this.onLoad, this);
5795         this.store.on('beforeload', this.onBeforeLoad, this);
5796         this.store.on('update', this.onUpdate, this);
5797         this.store.on('add', this.onAdd, this);
5798         
5799     },
5800     
5801     onMouseover : 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('mouseover', this, cell, rowIndex, cellIndex, e);
5818         
5819     },
5820     
5821     onMouseout : function(e, el)
5822     {
5823         var cell = Roo.get(el);
5824         
5825         if(!cell){
5826             return;
5827         }
5828         
5829         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5830             cell = cell.findParent('td', false, true);
5831         }
5832         
5833         var row = cell.findParent('tr', false, true);
5834         var cellIndex = cell.dom.cellIndex;
5835         var rowIndex = row.dom.rowIndex - 1; // start from 0
5836         
5837         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5838         
5839     },
5840     
5841     onClick : function(e, el)
5842     {
5843         var cell = Roo.get(el);
5844         
5845         if(!cell || (!this.cellSelection && !this.rowSelection)){
5846             return;
5847         }
5848         
5849         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5850             cell = cell.findParent('td', false, true);
5851         }
5852         
5853         if(!cell || typeof(cell) == 'undefined'){
5854             return;
5855         }
5856         
5857         var row = cell.findParent('tr', false, true);
5858         
5859         if(!row || typeof(row) == 'undefined'){
5860             return;
5861         }
5862         
5863         var cellIndex = cell.dom.cellIndex;
5864         var rowIndex = this.getRowIndex(row);
5865         
5866         // why??? - should these not be based on SelectionModel?
5867         if(this.cellSelection){
5868             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5869         }
5870         
5871         if(this.rowSelection){
5872             this.fireEvent('rowclick', this, row, rowIndex, e);
5873         }
5874         
5875         
5876     },
5877     
5878     onDblClick : function(e,el)
5879     {
5880         var cell = Roo.get(el);
5881         
5882         if(!cell || (!this.CellSelection && !this.RowSelection)){
5883             return;
5884         }
5885         
5886         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5887             cell = cell.findParent('td', false, true);
5888         }
5889         
5890         if(!cell || typeof(cell) == 'undefined'){
5891             return;
5892         }
5893         
5894         var row = cell.findParent('tr', false, true);
5895         
5896         if(!row || typeof(row) == 'undefined'){
5897             return;
5898         }
5899         
5900         var cellIndex = cell.dom.cellIndex;
5901         var rowIndex = this.getRowIndex(row);
5902         
5903         if(this.CellSelection){
5904             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5905         }
5906         
5907         if(this.RowSelection){
5908             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5909         }
5910     },
5911     
5912     sort : function(e,el)
5913     {
5914         var col = Roo.get(el);
5915         
5916         if(!col.hasClass('sortable')){
5917             return;
5918         }
5919         
5920         var sort = col.attr('sort');
5921         var dir = 'ASC';
5922         
5923         if(col.hasClass('glyphicon-arrow-up')){
5924             dir = 'DESC';
5925         }
5926         
5927         this.store.sortInfo = {field : sort, direction : dir};
5928         
5929         if (this.footer) {
5930             Roo.log("calling footer first");
5931             this.footer.onClick('first');
5932         } else {
5933         
5934             this.store.load({ params : { start : 0 } });
5935         }
5936     },
5937     
5938     renderHeader : function()
5939     {
5940         var header = {
5941             tag: 'thead',
5942             cn : []
5943         };
5944         
5945         var cm = this.cm;
5946         
5947         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5948             
5949             var config = cm.config[i];
5950             
5951             var c = {
5952                 tag: 'th',
5953                 style : '',
5954                 html: cm.getColumnHeader(i)
5955             };
5956             
5957             var hh = '';
5958             
5959             if(typeof(config.lgHeader) != 'undefined'){
5960                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
5961             }
5962             
5963             if(typeof(config.mdHeader) != 'undefined'){
5964                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
5965             }
5966             
5967             if(typeof(config.smHeader) != 'undefined'){
5968                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
5969             }
5970             
5971             if(typeof(config.xsHeader) != 'undefined'){
5972                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
5973             }
5974             
5975             if(hh.length){
5976                 c.html = hh;
5977             }
5978             
5979             if(typeof(config.tooltip) != 'undefined'){
5980                 c.tooltip = config.tooltip;
5981             }
5982             
5983             if(typeof(config.colspan) != 'undefined'){
5984                 c.colspan = config.colspan;
5985             }
5986             
5987             if(typeof(config.hidden) != 'undefined' && config.hidden){
5988                 c.style += ' display:none;';
5989             }
5990             
5991             if(typeof(config.dataIndex) != 'undefined'){
5992                 c.sort = config.dataIndex;
5993             }
5994             
5995             if(typeof(config.sortable) != 'undefined' && config.sortable){
5996                 c.cls = 'sortable';
5997             }
5998             
5999             if(typeof(config.align) != 'undefined' && config.align.length){
6000                 c.style += ' text-align:' + config.align + ';';
6001             }
6002             
6003             if(typeof(config.width) != 'undefined'){
6004                 c.style += ' width:' + config.width + 'px;';
6005             }
6006             
6007             if(typeof(config.cls) != 'undefined'){
6008                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6009             }
6010             
6011             ['xs','sm','md','lg'].map(function(size){
6012                 
6013                 if(typeof(config[size]) == 'undefined'){
6014                     return;
6015                 }
6016                 
6017                 if (!config[size]) { // 0 = hidden
6018                     c.cls += ' hidden-' + size;
6019                     return;
6020                 }
6021                 
6022                 c.cls += ' col-' + size + '-' + config[size];
6023
6024             });
6025             
6026             header.cn.push(c)
6027         }
6028         
6029         return header;
6030     },
6031     
6032     renderBody : function()
6033     {
6034         var body = {
6035             tag: 'tbody',
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 body;
6050     },
6051     
6052     renderFooter : function()
6053     {
6054         var footer = {
6055             tag: 'tfoot',
6056             cn : [
6057                 {
6058                     tag: 'tr',
6059                     cn : [
6060                         {
6061                             tag : 'td',
6062                             colspan :  this.cm.getColumnCount()
6063                         }
6064                     ]
6065                 }
6066             ]
6067         };
6068         
6069         return footer;
6070     },
6071     
6072     
6073     
6074     onLoad : function()
6075     {
6076         Roo.log('ds onload');
6077         this.clear();
6078         
6079         var _this = this;
6080         var cm = this.cm;
6081         var ds = this.store;
6082         
6083         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6084             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
6085             
6086             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6087                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
6088             }
6089             
6090             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6091                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
6092             }
6093         });
6094         
6095         var tbody =  this.mainBody;
6096               
6097         if(ds.getCount() > 0){
6098             ds.data.each(function(d,rowIndex){
6099                 var row =  this.renderRow(cm, ds, rowIndex);
6100                 
6101                 tbody.createChild(row);
6102                 
6103                 var _this = this;
6104                 
6105                 if(row.cellObjects.length){
6106                     Roo.each(row.cellObjects, function(r){
6107                         _this.renderCellObject(r);
6108                     })
6109                 }
6110                 
6111             }, this);
6112         }
6113         
6114         Roo.each(this.el.select('tbody td', true).elements, function(e){
6115             e.on('mouseover', _this.onMouseover, _this);
6116         });
6117         
6118         Roo.each(this.el.select('tbody td', true).elements, function(e){
6119             e.on('mouseout', _this.onMouseout, _this);
6120         });
6121         this.fireEvent('rowsrendered', this);
6122         //if(this.loadMask){
6123         //    this.maskEl.hide();
6124         //}
6125     },
6126     
6127     
6128     onUpdate : function(ds,record)
6129     {
6130         this.refreshRow(record);
6131     },
6132     
6133     onRemove : function(ds, record, index, isUpdate){
6134         if(isUpdate !== true){
6135             this.fireEvent("beforerowremoved", this, index, record);
6136         }
6137         var bt = this.mainBody.dom;
6138         
6139         var rows = this.el.select('tbody > tr', true).elements;
6140         
6141         if(typeof(rows[index]) != 'undefined'){
6142             bt.removeChild(rows[index].dom);
6143         }
6144         
6145 //        if(bt.rows[index]){
6146 //            bt.removeChild(bt.rows[index]);
6147 //        }
6148         
6149         if(isUpdate !== true){
6150             //this.stripeRows(index);
6151             //this.syncRowHeights(index, index);
6152             //this.layout();
6153             this.fireEvent("rowremoved", this, index, record);
6154         }
6155     },
6156     
6157     onAdd : function(ds, records, rowIndex)
6158     {
6159         //Roo.log('on Add called');
6160         // - note this does not handle multiple adding very well..
6161         var bt = this.mainBody.dom;
6162         for (var i =0 ; i < records.length;i++) {
6163             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6164             //Roo.log(records[i]);
6165             //Roo.log(this.store.getAt(rowIndex+i));
6166             this.insertRow(this.store, rowIndex + i, false);
6167             return;
6168         }
6169         
6170     },
6171     
6172     
6173     refreshRow : function(record){
6174         var ds = this.store, index;
6175         if(typeof record == 'number'){
6176             index = record;
6177             record = ds.getAt(index);
6178         }else{
6179             index = ds.indexOf(record);
6180         }
6181         this.insertRow(ds, index, true);
6182         this.onRemove(ds, record, index+1, true);
6183         //this.syncRowHeights(index, index);
6184         //this.layout();
6185         this.fireEvent("rowupdated", this, index, record);
6186     },
6187     
6188     insertRow : function(dm, rowIndex, isUpdate){
6189         
6190         if(!isUpdate){
6191             this.fireEvent("beforerowsinserted", this, rowIndex);
6192         }
6193             //var s = this.getScrollState();
6194         var row = this.renderRow(this.cm, this.store, rowIndex);
6195         // insert before rowIndex..
6196         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6197         
6198         var _this = this;
6199                 
6200         if(row.cellObjects.length){
6201             Roo.each(row.cellObjects, function(r){
6202                 _this.renderCellObject(r);
6203             })
6204         }
6205             
6206         if(!isUpdate){
6207             this.fireEvent("rowsinserted", this, rowIndex);
6208             //this.syncRowHeights(firstRow, lastRow);
6209             //this.stripeRows(firstRow);
6210             //this.layout();
6211         }
6212         
6213     },
6214     
6215     
6216     getRowDom : function(rowIndex)
6217     {
6218         var rows = this.el.select('tbody > tr', true).elements;
6219         
6220         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6221         
6222     },
6223     // returns the object tree for a tr..
6224   
6225     
6226     renderRow : function(cm, ds, rowIndex) 
6227     {
6228         
6229         var d = ds.getAt(rowIndex);
6230         
6231         var row = {
6232             tag : 'tr',
6233             cn : []
6234         };
6235             
6236         var cellObjects = [];
6237         
6238         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6239             var config = cm.config[i];
6240             
6241             var renderer = cm.getRenderer(i);
6242             var value = '';
6243             var id = false;
6244             
6245             if(typeof(renderer) !== 'undefined'){
6246                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6247             }
6248             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6249             // and are rendered into the cells after the row is rendered - using the id for the element.
6250             
6251             if(typeof(value) === 'object'){
6252                 id = Roo.id();
6253                 cellObjects.push({
6254                     container : id,
6255                     cfg : value 
6256                 })
6257             }
6258             
6259             var rowcfg = {
6260                 record: d,
6261                 rowIndex : rowIndex,
6262                 colIndex : i,
6263                 rowClass : ''
6264             };
6265
6266             this.fireEvent('rowclass', this, rowcfg);
6267             
6268             var td = {
6269                 tag: 'td',
6270                 cls : rowcfg.rowClass,
6271                 style: '',
6272                 html: (typeof(value) === 'object') ? '' : value
6273             };
6274             
6275             if (id) {
6276                 td.id = id;
6277             }
6278             
6279             if(typeof(config.colspan) != 'undefined'){
6280                 td.colspan = config.colspan;
6281             }
6282             
6283             if(typeof(config.hidden) != 'undefined' && config.hidden){
6284                 td.style += ' display:none;';
6285             }
6286             
6287             if(typeof(config.align) != 'undefined' && config.align.length){
6288                 td.style += ' text-align:' + config.align + ';';
6289             }
6290             
6291             if(typeof(config.width) != 'undefined'){
6292                 td.style += ' width:' +  config.width + 'px;';
6293             }
6294             
6295             if(typeof(config.cursor) != 'undefined'){
6296                 td.style += ' cursor:' +  config.cursor + ';';
6297             }
6298             
6299             if(typeof(config.cls) != 'undefined'){
6300                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6301             }
6302             
6303             ['xs','sm','md','lg'].map(function(size){
6304                 
6305                 if(typeof(config[size]) == 'undefined'){
6306                     return;
6307                 }
6308                 
6309                 if (!config[size]) { // 0 = hidden
6310                     td.cls += ' hidden-' + size;
6311                     return;
6312                 }
6313                 
6314                 td.cls += ' col-' + size + '-' + config[size];
6315
6316             });
6317              
6318             row.cn.push(td);
6319            
6320         }
6321         
6322         row.cellObjects = cellObjects;
6323         
6324         return row;
6325           
6326     },
6327     
6328     
6329     
6330     onBeforeLoad : function()
6331     {
6332         //Roo.log('ds onBeforeLoad');
6333         
6334         //this.clear();
6335         
6336         //if(this.loadMask){
6337         //    this.maskEl.show();
6338         //}
6339     },
6340      /**
6341      * Remove all rows
6342      */
6343     clear : function()
6344     {
6345         this.el.select('tbody', true).first().dom.innerHTML = '';
6346     },
6347     /**
6348      * Show or hide a row.
6349      * @param {Number} rowIndex to show or hide
6350      * @param {Boolean} state hide
6351      */
6352     setRowVisibility : function(rowIndex, state)
6353     {
6354         var bt = this.mainBody.dom;
6355         
6356         var rows = this.el.select('tbody > tr', true).elements;
6357         
6358         if(typeof(rows[rowIndex]) == 'undefined'){
6359             return;
6360         }
6361         rows[rowIndex].dom.style.display = state ? '' : 'none';
6362     },
6363     
6364     
6365     getSelectionModel : function(){
6366         if(!this.selModel){
6367             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6368         }
6369         return this.selModel;
6370     },
6371     /*
6372      * Render the Roo.bootstrap object from renderder
6373      */
6374     renderCellObject : function(r)
6375     {
6376         var _this = this;
6377         
6378         var t = r.cfg.render(r.container);
6379         
6380         if(r.cfg.cn){
6381             Roo.each(r.cfg.cn, function(c){
6382                 var child = {
6383                     container: t.getChildContainer(),
6384                     cfg: c
6385                 };
6386                 _this.renderCellObject(child);
6387             })
6388         }
6389     },
6390     
6391     getRowIndex : function(row)
6392     {
6393         var rowIndex = -1;
6394         
6395         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6396             if(el != row){
6397                 return;
6398             }
6399             
6400             rowIndex = index;
6401         });
6402         
6403         return rowIndex;
6404     }
6405    
6406 });
6407
6408  
6409
6410  /*
6411  * - LGPL
6412  *
6413  * table cell
6414  * 
6415  */
6416
6417 /**
6418  * @class Roo.bootstrap.TableCell
6419  * @extends Roo.bootstrap.Component
6420  * Bootstrap TableCell class
6421  * @cfg {String} html cell contain text
6422  * @cfg {String} cls cell class
6423  * @cfg {String} tag cell tag (td|th) default td
6424  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6425  * @cfg {String} align Aligns the content in a cell
6426  * @cfg {String} axis Categorizes cells
6427  * @cfg {String} bgcolor Specifies the background color of a cell
6428  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6429  * @cfg {Number} colspan Specifies the number of columns a cell should span
6430  * @cfg {String} headers Specifies one or more header cells a cell is related to
6431  * @cfg {Number} height Sets the height of a cell
6432  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6433  * @cfg {Number} rowspan Sets the number of rows a cell should span
6434  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6435  * @cfg {String} valign Vertical aligns the content in a cell
6436  * @cfg {Number} width Specifies the width of a cell
6437  * 
6438  * @constructor
6439  * Create a new TableCell
6440  * @param {Object} config The config object
6441  */
6442
6443 Roo.bootstrap.TableCell = function(config){
6444     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6445 };
6446
6447 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6448     
6449     html: false,
6450     cls: false,
6451     tag: false,
6452     abbr: false,
6453     align: false,
6454     axis: false,
6455     bgcolor: false,
6456     charoff: false,
6457     colspan: false,
6458     headers: false,
6459     height: false,
6460     nowrap: false,
6461     rowspan: false,
6462     scope: false,
6463     valign: false,
6464     width: false,
6465     
6466     
6467     getAutoCreate : function(){
6468         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6469         
6470         cfg = {
6471             tag: 'td'
6472         };
6473         
6474         if(this.tag){
6475             cfg.tag = this.tag;
6476         }
6477         
6478         if (this.html) {
6479             cfg.html=this.html
6480         }
6481         if (this.cls) {
6482             cfg.cls=this.cls
6483         }
6484         if (this.abbr) {
6485             cfg.abbr=this.abbr
6486         }
6487         if (this.align) {
6488             cfg.align=this.align
6489         }
6490         if (this.axis) {
6491             cfg.axis=this.axis
6492         }
6493         if (this.bgcolor) {
6494             cfg.bgcolor=this.bgcolor
6495         }
6496         if (this.charoff) {
6497             cfg.charoff=this.charoff
6498         }
6499         if (this.colspan) {
6500             cfg.colspan=this.colspan
6501         }
6502         if (this.headers) {
6503             cfg.headers=this.headers
6504         }
6505         if (this.height) {
6506             cfg.height=this.height
6507         }
6508         if (this.nowrap) {
6509             cfg.nowrap=this.nowrap
6510         }
6511         if (this.rowspan) {
6512             cfg.rowspan=this.rowspan
6513         }
6514         if (this.scope) {
6515             cfg.scope=this.scope
6516         }
6517         if (this.valign) {
6518             cfg.valign=this.valign
6519         }
6520         if (this.width) {
6521             cfg.width=this.width
6522         }
6523         
6524         
6525         return cfg;
6526     }
6527    
6528 });
6529
6530  
6531
6532  /*
6533  * - LGPL
6534  *
6535  * table row
6536  * 
6537  */
6538
6539 /**
6540  * @class Roo.bootstrap.TableRow
6541  * @extends Roo.bootstrap.Component
6542  * Bootstrap TableRow class
6543  * @cfg {String} cls row class
6544  * @cfg {String} align Aligns the content in a table row
6545  * @cfg {String} bgcolor Specifies a background color for a table row
6546  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6547  * @cfg {String} valign Vertical aligns the content in a table row
6548  * 
6549  * @constructor
6550  * Create a new TableRow
6551  * @param {Object} config The config object
6552  */
6553
6554 Roo.bootstrap.TableRow = function(config){
6555     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6556 };
6557
6558 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6559     
6560     cls: false,
6561     align: false,
6562     bgcolor: false,
6563     charoff: false,
6564     valign: false,
6565     
6566     getAutoCreate : function(){
6567         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6568         
6569         cfg = {
6570             tag: 'tr'
6571         };
6572             
6573         if(this.cls){
6574             cfg.cls = this.cls;
6575         }
6576         if(this.align){
6577             cfg.align = this.align;
6578         }
6579         if(this.bgcolor){
6580             cfg.bgcolor = this.bgcolor;
6581         }
6582         if(this.charoff){
6583             cfg.charoff = this.charoff;
6584         }
6585         if(this.valign){
6586             cfg.valign = this.valign;
6587         }
6588         
6589         return cfg;
6590     }
6591    
6592 });
6593
6594  
6595
6596  /*
6597  * - LGPL
6598  *
6599  * table body
6600  * 
6601  */
6602
6603 /**
6604  * @class Roo.bootstrap.TableBody
6605  * @extends Roo.bootstrap.Component
6606  * Bootstrap TableBody class
6607  * @cfg {String} cls element class
6608  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6609  * @cfg {String} align Aligns the content inside the element
6610  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6611  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6612  * 
6613  * @constructor
6614  * Create a new TableBody
6615  * @param {Object} config The config object
6616  */
6617
6618 Roo.bootstrap.TableBody = function(config){
6619     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6620 };
6621
6622 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6623     
6624     cls: false,
6625     tag: false,
6626     align: false,
6627     charoff: false,
6628     valign: false,
6629     
6630     getAutoCreate : function(){
6631         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6632         
6633         cfg = {
6634             tag: 'tbody'
6635         };
6636             
6637         if (this.cls) {
6638             cfg.cls=this.cls
6639         }
6640         if(this.tag){
6641             cfg.tag = this.tag;
6642         }
6643         
6644         if(this.align){
6645             cfg.align = this.align;
6646         }
6647         if(this.charoff){
6648             cfg.charoff = this.charoff;
6649         }
6650         if(this.valign){
6651             cfg.valign = this.valign;
6652         }
6653         
6654         return cfg;
6655     }
6656     
6657     
6658 //    initEvents : function()
6659 //    {
6660 //        
6661 //        if(!this.store){
6662 //            return;
6663 //        }
6664 //        
6665 //        this.store = Roo.factory(this.store, Roo.data);
6666 //        this.store.on('load', this.onLoad, this);
6667 //        
6668 //        this.store.load();
6669 //        
6670 //    },
6671 //    
6672 //    onLoad: function () 
6673 //    {   
6674 //        this.fireEvent('load', this);
6675 //    }
6676 //    
6677 //   
6678 });
6679
6680  
6681
6682  /*
6683  * Based on:
6684  * Ext JS Library 1.1.1
6685  * Copyright(c) 2006-2007, Ext JS, LLC.
6686  *
6687  * Originally Released Under LGPL - original licence link has changed is not relivant.
6688  *
6689  * Fork - LGPL
6690  * <script type="text/javascript">
6691  */
6692
6693 // as we use this in bootstrap.
6694 Roo.namespace('Roo.form');
6695  /**
6696  * @class Roo.form.Action
6697  * Internal Class used to handle form actions
6698  * @constructor
6699  * @param {Roo.form.BasicForm} el The form element or its id
6700  * @param {Object} config Configuration options
6701  */
6702
6703  
6704  
6705 // define the action interface
6706 Roo.form.Action = function(form, options){
6707     this.form = form;
6708     this.options = options || {};
6709 };
6710 /**
6711  * Client Validation Failed
6712  * @const 
6713  */
6714 Roo.form.Action.CLIENT_INVALID = 'client';
6715 /**
6716  * Server Validation Failed
6717  * @const 
6718  */
6719 Roo.form.Action.SERVER_INVALID = 'server';
6720  /**
6721  * Connect to Server Failed
6722  * @const 
6723  */
6724 Roo.form.Action.CONNECT_FAILURE = 'connect';
6725 /**
6726  * Reading Data from Server Failed
6727  * @const 
6728  */
6729 Roo.form.Action.LOAD_FAILURE = 'load';
6730
6731 Roo.form.Action.prototype = {
6732     type : 'default',
6733     failureType : undefined,
6734     response : undefined,
6735     result : undefined,
6736
6737     // interface method
6738     run : function(options){
6739
6740     },
6741
6742     // interface method
6743     success : function(response){
6744
6745     },
6746
6747     // interface method
6748     handleResponse : function(response){
6749
6750     },
6751
6752     // default connection failure
6753     failure : function(response){
6754         
6755         this.response = response;
6756         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6757         this.form.afterAction(this, false);
6758     },
6759
6760     processResponse : function(response){
6761         this.response = response;
6762         if(!response.responseText){
6763             return true;
6764         }
6765         this.result = this.handleResponse(response);
6766         return this.result;
6767     },
6768
6769     // utility functions used internally
6770     getUrl : function(appendParams){
6771         var url = this.options.url || this.form.url || this.form.el.dom.action;
6772         if(appendParams){
6773             var p = this.getParams();
6774             if(p){
6775                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6776             }
6777         }
6778         return url;
6779     },
6780
6781     getMethod : function(){
6782         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6783     },
6784
6785     getParams : function(){
6786         var bp = this.form.baseParams;
6787         var p = this.options.params;
6788         if(p){
6789             if(typeof p == "object"){
6790                 p = Roo.urlEncode(Roo.applyIf(p, bp));
6791             }else if(typeof p == 'string' && bp){
6792                 p += '&' + Roo.urlEncode(bp);
6793             }
6794         }else if(bp){
6795             p = Roo.urlEncode(bp);
6796         }
6797         return p;
6798     },
6799
6800     createCallback : function(){
6801         return {
6802             success: this.success,
6803             failure: this.failure,
6804             scope: this,
6805             timeout: (this.form.timeout*1000),
6806             upload: this.form.fileUpload ? this.success : undefined
6807         };
6808     }
6809 };
6810
6811 Roo.form.Action.Submit = function(form, options){
6812     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6813 };
6814
6815 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6816     type : 'submit',
6817
6818     haveProgress : false,
6819     uploadComplete : false,
6820     
6821     // uploadProgress indicator.
6822     uploadProgress : function()
6823     {
6824         if (!this.form.progressUrl) {
6825             return;
6826         }
6827         
6828         if (!this.haveProgress) {
6829             Roo.MessageBox.progress("Uploading", "Uploading");
6830         }
6831         if (this.uploadComplete) {
6832            Roo.MessageBox.hide();
6833            return;
6834         }
6835         
6836         this.haveProgress = true;
6837    
6838         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6839         
6840         var c = new Roo.data.Connection();
6841         c.request({
6842             url : this.form.progressUrl,
6843             params: {
6844                 id : uid
6845             },
6846             method: 'GET',
6847             success : function(req){
6848                //console.log(data);
6849                 var rdata = false;
6850                 var edata;
6851                 try  {
6852                    rdata = Roo.decode(req.responseText)
6853                 } catch (e) {
6854                     Roo.log("Invalid data from server..");
6855                     Roo.log(edata);
6856                     return;
6857                 }
6858                 if (!rdata || !rdata.success) {
6859                     Roo.log(rdata);
6860                     Roo.MessageBox.alert(Roo.encode(rdata));
6861                     return;
6862                 }
6863                 var data = rdata.data;
6864                 
6865                 if (this.uploadComplete) {
6866                    Roo.MessageBox.hide();
6867                    return;
6868                 }
6869                    
6870                 if (data){
6871                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6872                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6873                     );
6874                 }
6875                 this.uploadProgress.defer(2000,this);
6876             },
6877        
6878             failure: function(data) {
6879                 Roo.log('progress url failed ');
6880                 Roo.log(data);
6881             },
6882             scope : this
6883         });
6884            
6885     },
6886     
6887     
6888     run : function()
6889     {
6890         // run get Values on the form, so it syncs any secondary forms.
6891         this.form.getValues();
6892         
6893         var o = this.options;
6894         var method = this.getMethod();
6895         var isPost = method == 'POST';
6896         if(o.clientValidation === false || this.form.isValid()){
6897             
6898             if (this.form.progressUrl) {
6899                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6900                     (new Date() * 1) + '' + Math.random());
6901                     
6902             } 
6903             
6904             
6905             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6906                 form:this.form.el.dom,
6907                 url:this.getUrl(!isPost),
6908                 method: method,
6909                 params:isPost ? this.getParams() : null,
6910                 isUpload: this.form.fileUpload
6911             }));
6912             
6913             this.uploadProgress();
6914
6915         }else if (o.clientValidation !== false){ // client validation failed
6916             this.failureType = Roo.form.Action.CLIENT_INVALID;
6917             this.form.afterAction(this, false);
6918         }
6919     },
6920
6921     success : function(response)
6922     {
6923         this.uploadComplete= true;
6924         if (this.haveProgress) {
6925             Roo.MessageBox.hide();
6926         }
6927         
6928         
6929         var result = this.processResponse(response);
6930         if(result === true || result.success){
6931             this.form.afterAction(this, true);
6932             return;
6933         }
6934         if(result.errors){
6935             this.form.markInvalid(result.errors);
6936             this.failureType = Roo.form.Action.SERVER_INVALID;
6937         }
6938         this.form.afterAction(this, false);
6939     },
6940     failure : function(response)
6941     {
6942         this.uploadComplete= true;
6943         if (this.haveProgress) {
6944             Roo.MessageBox.hide();
6945         }
6946         
6947         this.response = response;
6948         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6949         this.form.afterAction(this, false);
6950     },
6951     
6952     handleResponse : function(response){
6953         if(this.form.errorReader){
6954             var rs = this.form.errorReader.read(response);
6955             var errors = [];
6956             if(rs.records){
6957                 for(var i = 0, len = rs.records.length; i < len; i++) {
6958                     var r = rs.records[i];
6959                     errors[i] = r.data;
6960                 }
6961             }
6962             if(errors.length < 1){
6963                 errors = null;
6964             }
6965             return {
6966                 success : rs.success,
6967                 errors : errors
6968             };
6969         }
6970         var ret = false;
6971         try {
6972             ret = Roo.decode(response.responseText);
6973         } catch (e) {
6974             ret = {
6975                 success: false,
6976                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6977                 errors : []
6978             };
6979         }
6980         return ret;
6981         
6982     }
6983 });
6984
6985
6986 Roo.form.Action.Load = function(form, options){
6987     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6988     this.reader = this.form.reader;
6989 };
6990
6991 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6992     type : 'load',
6993
6994     run : function(){
6995         
6996         Roo.Ajax.request(Roo.apply(
6997                 this.createCallback(), {
6998                     method:this.getMethod(),
6999                     url:this.getUrl(false),
7000                     params:this.getParams()
7001         }));
7002     },
7003
7004     success : function(response){
7005         
7006         var result = this.processResponse(response);
7007         if(result === true || !result.success || !result.data){
7008             this.failureType = Roo.form.Action.LOAD_FAILURE;
7009             this.form.afterAction(this, false);
7010             return;
7011         }
7012         this.form.clearInvalid();
7013         this.form.setValues(result.data);
7014         this.form.afterAction(this, true);
7015     },
7016
7017     handleResponse : function(response){
7018         if(this.form.reader){
7019             var rs = this.form.reader.read(response);
7020             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7021             return {
7022                 success : rs.success,
7023                 data : data
7024             };
7025         }
7026         return Roo.decode(response.responseText);
7027     }
7028 });
7029
7030 Roo.form.Action.ACTION_TYPES = {
7031     'load' : Roo.form.Action.Load,
7032     'submit' : Roo.form.Action.Submit
7033 };/*
7034  * - LGPL
7035  *
7036  * form
7037  * 
7038  */
7039
7040 /**
7041  * @class Roo.bootstrap.Form
7042  * @extends Roo.bootstrap.Component
7043  * Bootstrap Form class
7044  * @cfg {String} method  GET | POST (default POST)
7045  * @cfg {String} labelAlign top | left (default top)
7046  * @cfg {String} align left  | right - for navbars
7047  * @cfg {Boolean} loadMask load mask when submit (default true)
7048
7049  * 
7050  * @constructor
7051  * Create a new Form
7052  * @param {Object} config The config object
7053  */
7054
7055
7056 Roo.bootstrap.Form = function(config){
7057     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7058     this.addEvents({
7059         /**
7060          * @event clientvalidation
7061          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7062          * @param {Form} this
7063          * @param {Boolean} valid true if the form has passed client-side validation
7064          */
7065         clientvalidation: true,
7066         /**
7067          * @event beforeaction
7068          * Fires before any action is performed. Return false to cancel the action.
7069          * @param {Form} this
7070          * @param {Action} action The action to be performed
7071          */
7072         beforeaction: true,
7073         /**
7074          * @event actionfailed
7075          * Fires when an action fails.
7076          * @param {Form} this
7077          * @param {Action} action The action that failed
7078          */
7079         actionfailed : true,
7080         /**
7081          * @event actioncomplete
7082          * Fires when an action is completed.
7083          * @param {Form} this
7084          * @param {Action} action The action that completed
7085          */
7086         actioncomplete : true
7087     });
7088     
7089 };
7090
7091 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7092       
7093      /**
7094      * @cfg {String} method
7095      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7096      */
7097     method : 'POST',
7098     /**
7099      * @cfg {String} url
7100      * The URL to use for form actions if one isn't supplied in the action options.
7101      */
7102     /**
7103      * @cfg {Boolean} fileUpload
7104      * Set to true if this form is a file upload.
7105      */
7106      
7107     /**
7108      * @cfg {Object} baseParams
7109      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7110      */
7111       
7112     /**
7113      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7114      */
7115     timeout: 30,
7116     /**
7117      * @cfg {Sting} align (left|right) for navbar forms
7118      */
7119     align : 'left',
7120
7121     // private
7122     activeAction : null,
7123  
7124     /**
7125      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7126      * element by passing it or its id or mask the form itself by passing in true.
7127      * @type Mixed
7128      */
7129     waitMsgTarget : false,
7130     
7131     loadMask : true,
7132     
7133     getAutoCreate : function(){
7134         
7135         var cfg = {
7136             tag: 'form',
7137             method : this.method || 'POST',
7138             id : this.id || Roo.id(),
7139             cls : ''
7140         };
7141         if (this.parent().xtype.match(/^Nav/)) {
7142             cfg.cls = 'navbar-form navbar-' + this.align;
7143             
7144         }
7145         
7146         if (this.labelAlign == 'left' ) {
7147             cfg.cls += ' form-horizontal';
7148         }
7149         
7150         
7151         return cfg;
7152     },
7153     initEvents : function()
7154     {
7155         this.el.on('submit', this.onSubmit, this);
7156         // this was added as random key presses on the form where triggering form submit.
7157         this.el.on('keypress', function(e) {
7158             if (e.getCharCode() != 13) {
7159                 return true;
7160             }
7161             // we might need to allow it for textareas.. and some other items.
7162             // check e.getTarget().
7163             
7164             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7165                 return true;
7166             }
7167         
7168             Roo.log("keypress blocked");
7169             
7170             e.preventDefault();
7171             return false;
7172         });
7173         
7174     },
7175     // private
7176     onSubmit : function(e){
7177         e.stopEvent();
7178     },
7179     
7180      /**
7181      * Returns true if client-side validation on the form is successful.
7182      * @return Boolean
7183      */
7184     isValid : function(){
7185         var items = this.getItems();
7186         var valid = true;
7187         items.each(function(f){
7188            if(!f.validate()){
7189                valid = false;
7190                
7191            }
7192         });
7193         return valid;
7194     },
7195     /**
7196      * Returns true if any fields in this form have changed since their original load.
7197      * @return Boolean
7198      */
7199     isDirty : function(){
7200         var dirty = false;
7201         var items = this.getItems();
7202         items.each(function(f){
7203            if(f.isDirty()){
7204                dirty = true;
7205                return false;
7206            }
7207            return true;
7208         });
7209         return dirty;
7210     },
7211      /**
7212      * Performs a predefined action (submit or load) or custom actions you define on this form.
7213      * @param {String} actionName The name of the action type
7214      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7215      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7216      * accept other config options):
7217      * <pre>
7218 Property          Type             Description
7219 ----------------  ---------------  ----------------------------------------------------------------------------------
7220 url               String           The url for the action (defaults to the form's url)
7221 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7222 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7223 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7224                                    validate the form on the client (defaults to false)
7225      * </pre>
7226      * @return {BasicForm} this
7227      */
7228     doAction : function(action, options){
7229         if(typeof action == 'string'){
7230             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7231         }
7232         if(this.fireEvent('beforeaction', this, action) !== false){
7233             this.beforeAction(action);
7234             action.run.defer(100, action);
7235         }
7236         return this;
7237     },
7238     
7239     // private
7240     beforeAction : function(action){
7241         var o = action.options;
7242         
7243         if(this.loadMask){
7244             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7245         }
7246         // not really supported yet.. ??
7247         
7248         //if(this.waitMsgTarget === true){
7249         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7250         //}else if(this.waitMsgTarget){
7251         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7252         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7253         //}else {
7254         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7255        // }
7256          
7257     },
7258
7259     // private
7260     afterAction : function(action, success){
7261         this.activeAction = null;
7262         var o = action.options;
7263         
7264         //if(this.waitMsgTarget === true){
7265             this.el.unmask();
7266         //}else if(this.waitMsgTarget){
7267         //    this.waitMsgTarget.unmask();
7268         //}else{
7269         //    Roo.MessageBox.updateProgress(1);
7270         //    Roo.MessageBox.hide();
7271        // }
7272         // 
7273         if(success){
7274             if(o.reset){
7275                 this.reset();
7276             }
7277             Roo.callback(o.success, o.scope, [this, action]);
7278             this.fireEvent('actioncomplete', this, action);
7279             
7280         }else{
7281             
7282             // failure condition..
7283             // we have a scenario where updates need confirming.
7284             // eg. if a locking scenario exists..
7285             // we look for { errors : { needs_confirm : true }} in the response.
7286             if (
7287                 (typeof(action.result) != 'undefined')  &&
7288                 (typeof(action.result.errors) != 'undefined')  &&
7289                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7290            ){
7291                 var _t = this;
7292                 Roo.log("not supported yet");
7293                  /*
7294                 
7295                 Roo.MessageBox.confirm(
7296                     "Change requires confirmation",
7297                     action.result.errorMsg,
7298                     function(r) {
7299                         if (r != 'yes') {
7300                             return;
7301                         }
7302                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7303                     }
7304                     
7305                 );
7306                 */
7307                 
7308                 
7309                 return;
7310             }
7311             
7312             Roo.callback(o.failure, o.scope, [this, action]);
7313             // show an error message if no failed handler is set..
7314             if (!this.hasListener('actionfailed')) {
7315                 Roo.log("need to add dialog support");
7316                 /*
7317                 Roo.MessageBox.alert("Error",
7318                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7319                         action.result.errorMsg :
7320                         "Saving Failed, please check your entries or try again"
7321                 );
7322                 */
7323             }
7324             
7325             this.fireEvent('actionfailed', this, action);
7326         }
7327         
7328     },
7329     /**
7330      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7331      * @param {String} id The value to search for
7332      * @return Field
7333      */
7334     findField : function(id){
7335         var items = this.getItems();
7336         var field = items.get(id);
7337         if(!field){
7338              items.each(function(f){
7339                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7340                     field = f;
7341                     return false;
7342                 }
7343                 return true;
7344             });
7345         }
7346         return field || null;
7347     },
7348      /**
7349      * Mark fields in this form invalid in bulk.
7350      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7351      * @return {BasicForm} this
7352      */
7353     markInvalid : function(errors){
7354         if(errors instanceof Array){
7355             for(var i = 0, len = errors.length; i < len; i++){
7356                 var fieldError = errors[i];
7357                 var f = this.findField(fieldError.id);
7358                 if(f){
7359                     f.markInvalid(fieldError.msg);
7360                 }
7361             }
7362         }else{
7363             var field, id;
7364             for(id in errors){
7365                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7366                     field.markInvalid(errors[id]);
7367                 }
7368             }
7369         }
7370         //Roo.each(this.childForms || [], function (f) {
7371         //    f.markInvalid(errors);
7372         //});
7373         
7374         return this;
7375     },
7376
7377     /**
7378      * Set values for fields in this form in bulk.
7379      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7380      * @return {BasicForm} this
7381      */
7382     setValues : function(values){
7383         if(values instanceof Array){ // array of objects
7384             for(var i = 0, len = values.length; i < len; i++){
7385                 var v = values[i];
7386                 var f = this.findField(v.id);
7387                 if(f){
7388                     f.setValue(v.value);
7389                     if(this.trackResetOnLoad){
7390                         f.originalValue = f.getValue();
7391                     }
7392                 }
7393             }
7394         }else{ // object hash
7395             var field, id;
7396             for(id in values){
7397                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7398                     
7399                     if (field.setFromData && 
7400                         field.valueField && 
7401                         field.displayField &&
7402                         // combos' with local stores can 
7403                         // be queried via setValue()
7404                         // to set their value..
7405                         (field.store && !field.store.isLocal)
7406                         ) {
7407                         // it's a combo
7408                         var sd = { };
7409                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7410                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7411                         field.setFromData(sd);
7412                         
7413                     } else {
7414                         field.setValue(values[id]);
7415                     }
7416                     
7417                     
7418                     if(this.trackResetOnLoad){
7419                         field.originalValue = field.getValue();
7420                     }
7421                 }
7422             }
7423         }
7424          
7425         //Roo.each(this.childForms || [], function (f) {
7426         //    f.setValues(values);
7427         //});
7428                 
7429         return this;
7430     },
7431
7432     /**
7433      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7434      * they are returned as an array.
7435      * @param {Boolean} asString
7436      * @return {Object}
7437      */
7438     getValues : function(asString){
7439         //if (this.childForms) {
7440             // copy values from the child forms
7441         //    Roo.each(this.childForms, function (f) {
7442         //        this.setValues(f.getValues());
7443         //    }, this);
7444         //}
7445         
7446         
7447         
7448         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7449         if(asString === true){
7450             return fs;
7451         }
7452         return Roo.urlDecode(fs);
7453     },
7454     
7455     /**
7456      * Returns the fields in this form as an object with key/value pairs. 
7457      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7458      * @return {Object}
7459      */
7460     getFieldValues : function(with_hidden)
7461     {
7462         var items = this.getItems();
7463         var ret = {};
7464         items.each(function(f){
7465             if (!f.getName()) {
7466                 return;
7467             }
7468             var v = f.getValue();
7469             if (f.inputType =='radio') {
7470                 if (typeof(ret[f.getName()]) == 'undefined') {
7471                     ret[f.getName()] = ''; // empty..
7472                 }
7473                 
7474                 if (!f.el.dom.checked) {
7475                     return;
7476                     
7477                 }
7478                 v = f.el.dom.value;
7479                 
7480             }
7481             
7482             // not sure if this supported any more..
7483             if ((typeof(v) == 'object') && f.getRawValue) {
7484                 v = f.getRawValue() ; // dates..
7485             }
7486             // combo boxes where name != hiddenName...
7487             if (f.name != f.getName()) {
7488                 ret[f.name] = f.getRawValue();
7489             }
7490             ret[f.getName()] = v;
7491         });
7492         
7493         return ret;
7494     },
7495
7496     /**
7497      * Clears all invalid messages in this form.
7498      * @return {BasicForm} this
7499      */
7500     clearInvalid : function(){
7501         var items = this.getItems();
7502         
7503         items.each(function(f){
7504            f.clearInvalid();
7505         });
7506         
7507         
7508         
7509         return this;
7510     },
7511
7512     /**
7513      * Resets this form.
7514      * @return {BasicForm} this
7515      */
7516     reset : function(){
7517         var items = this.getItems();
7518         items.each(function(f){
7519             f.reset();
7520         });
7521         
7522         Roo.each(this.childForms || [], function (f) {
7523             f.reset();
7524         });
7525        
7526         
7527         return this;
7528     },
7529     getItems : function()
7530     {
7531         var r=new Roo.util.MixedCollection(false, function(o){
7532             return o.id || (o.id = Roo.id());
7533         });
7534         var iter = function(el) {
7535             if (el.inputEl) {
7536                 r.add(el);
7537             }
7538             if (!el.items) {
7539                 return;
7540             }
7541             Roo.each(el.items,function(e) {
7542                 iter(e);
7543             });
7544             
7545             
7546         };
7547         
7548         iter(this);
7549         return r;
7550         
7551         
7552         
7553         
7554     }
7555     
7556 });
7557
7558  
7559 /*
7560  * Based on:
7561  * Ext JS Library 1.1.1
7562  * Copyright(c) 2006-2007, Ext JS, LLC.
7563  *
7564  * Originally Released Under LGPL - original licence link has changed is not relivant.
7565  *
7566  * Fork - LGPL
7567  * <script type="text/javascript">
7568  */
7569 /**
7570  * @class Roo.form.VTypes
7571  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7572  * @singleton
7573  */
7574 Roo.form.VTypes = function(){
7575     // closure these in so they are only created once.
7576     var alpha = /^[a-zA-Z_]+$/;
7577     var alphanum = /^[a-zA-Z0-9_]+$/;
7578     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
7579     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7580
7581     // All these messages and functions are configurable
7582     return {
7583         /**
7584          * The function used to validate email addresses
7585          * @param {String} value The email address
7586          */
7587         'email' : function(v){
7588             return email.test(v);
7589         },
7590         /**
7591          * The error text to display when the email validation function returns false
7592          * @type String
7593          */
7594         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7595         /**
7596          * The keystroke filter mask to be applied on email input
7597          * @type RegExp
7598          */
7599         'emailMask' : /[a-z0-9_\.\-@]/i,
7600
7601         /**
7602          * The function used to validate URLs
7603          * @param {String} value The URL
7604          */
7605         'url' : function(v){
7606             return url.test(v);
7607         },
7608         /**
7609          * The error text to display when the url validation function returns false
7610          * @type String
7611          */
7612         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7613         
7614         /**
7615          * The function used to validate alpha values
7616          * @param {String} value The value
7617          */
7618         'alpha' : function(v){
7619             return alpha.test(v);
7620         },
7621         /**
7622          * The error text to display when the alpha validation function returns false
7623          * @type String
7624          */
7625         'alphaText' : 'This field should only contain letters and _',
7626         /**
7627          * The keystroke filter mask to be applied on alpha input
7628          * @type RegExp
7629          */
7630         'alphaMask' : /[a-z_]/i,
7631
7632         /**
7633          * The function used to validate alphanumeric values
7634          * @param {String} value The value
7635          */
7636         'alphanum' : function(v){
7637             return alphanum.test(v);
7638         },
7639         /**
7640          * The error text to display when the alphanumeric validation function returns false
7641          * @type String
7642          */
7643         'alphanumText' : 'This field should only contain letters, numbers and _',
7644         /**
7645          * The keystroke filter mask to be applied on alphanumeric input
7646          * @type RegExp
7647          */
7648         'alphanumMask' : /[a-z0-9_]/i
7649     };
7650 }();/*
7651  * - LGPL
7652  *
7653  * Input
7654  * 
7655  */
7656
7657 /**
7658  * @class Roo.bootstrap.Input
7659  * @extends Roo.bootstrap.Component
7660  * Bootstrap Input class
7661  * @cfg {Boolean} disabled is it disabled
7662  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7663  * @cfg {String} name name of the input
7664  * @cfg {string} fieldLabel - the label associated
7665  * @cfg {string} placeholder - placeholder to put in text.
7666  * @cfg {string}  before - input group add on before
7667  * @cfg {string} after - input group add on after
7668  * @cfg {string} size - (lg|sm) or leave empty..
7669  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7670  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7671  * @cfg {Number} md colspan out of 12 for computer-sized screens
7672  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7673  * @cfg {string} value default value of the input
7674  * @cfg {Number} labelWidth set the width of label (0-12)
7675  * @cfg {String} labelAlign (top|left)
7676  * @cfg {Boolean} readOnly Specifies that the field should be read-only
7677  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7678
7679  * @cfg {String} align (left|center|right) Default left
7680  * @cfg {Boolean} forceFeedback (true|false) Default false
7681  * 
7682  * 
7683  * 
7684  * 
7685  * @constructor
7686  * Create a new Input
7687  * @param {Object} config The config object
7688  */
7689
7690 Roo.bootstrap.Input = function(config){
7691     Roo.bootstrap.Input.superclass.constructor.call(this, config);
7692    
7693         this.addEvents({
7694             /**
7695              * @event focus
7696              * Fires when this field receives input focus.
7697              * @param {Roo.form.Field} this
7698              */
7699             focus : true,
7700             /**
7701              * @event blur
7702              * Fires when this field loses input focus.
7703              * @param {Roo.form.Field} this
7704              */
7705             blur : true,
7706             /**
7707              * @event specialkey
7708              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
7709              * {@link Roo.EventObject#getKey} to determine which key was pressed.
7710              * @param {Roo.form.Field} this
7711              * @param {Roo.EventObject} e The event object
7712              */
7713             specialkey : true,
7714             /**
7715              * @event change
7716              * Fires just before the field blurs if the field value has changed.
7717              * @param {Roo.form.Field} this
7718              * @param {Mixed} newValue The new value
7719              * @param {Mixed} oldValue The original value
7720              */
7721             change : true,
7722             /**
7723              * @event invalid
7724              * Fires after the field has been marked as invalid.
7725              * @param {Roo.form.Field} this
7726              * @param {String} msg The validation message
7727              */
7728             invalid : true,
7729             /**
7730              * @event valid
7731              * Fires after the field has been validated with no errors.
7732              * @param {Roo.form.Field} this
7733              */
7734             valid : true,
7735              /**
7736              * @event keyup
7737              * Fires after the key up
7738              * @param {Roo.form.Field} this
7739              * @param {Roo.EventObject}  e The event Object
7740              */
7741             keyup : true
7742         });
7743 };
7744
7745 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
7746      /**
7747      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7748       automatic validation (defaults to "keyup").
7749      */
7750     validationEvent : "keyup",
7751      /**
7752      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7753      */
7754     validateOnBlur : true,
7755     /**
7756      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7757      */
7758     validationDelay : 250,
7759      /**
7760      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7761      */
7762     focusClass : "x-form-focus",  // not needed???
7763     
7764        
7765     /**
7766      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7767      */
7768     invalidClass : "has-warning",
7769     
7770     /**
7771      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7772      */
7773     validClass : "has-success",
7774     
7775     /**
7776      * @cfg {Boolean} hasFeedback (true|false) default true
7777      */
7778     hasFeedback : true,
7779     
7780     /**
7781      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7782      */
7783     invalidFeedbackClass : "glyphicon-warning-sign",
7784     
7785     /**
7786      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7787      */
7788     validFeedbackClass : "glyphicon-ok",
7789     
7790     /**
7791      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7792      */
7793     selectOnFocus : false,
7794     
7795      /**
7796      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7797      */
7798     maskRe : null,
7799        /**
7800      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7801      */
7802     vtype : null,
7803     
7804       /**
7805      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7806      */
7807     disableKeyFilter : false,
7808     
7809        /**
7810      * @cfg {Boolean} disabled True to disable the field (defaults to false).
7811      */
7812     disabled : false,
7813      /**
7814      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7815      */
7816     allowBlank : true,
7817     /**
7818      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7819      */
7820     blankText : "This field is required",
7821     
7822      /**
7823      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7824      */
7825     minLength : 0,
7826     /**
7827      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7828      */
7829     maxLength : Number.MAX_VALUE,
7830     /**
7831      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7832      */
7833     minLengthText : "The minimum length for this field is {0}",
7834     /**
7835      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7836      */
7837     maxLengthText : "The maximum length for this field is {0}",
7838   
7839     
7840     /**
7841      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7842      * If available, this function will be called only after the basic validators all return true, and will be passed the
7843      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7844      */
7845     validator : null,
7846     /**
7847      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7848      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7849      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7850      */
7851     regex : null,
7852     /**
7853      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7854      */
7855     regexText : "",
7856     
7857     autocomplete: false,
7858     
7859     
7860     fieldLabel : '',
7861     inputType : 'text',
7862     
7863     name : false,
7864     placeholder: false,
7865     before : false,
7866     after : false,
7867     size : false,
7868     hasFocus : false,
7869     preventMark: false,
7870     isFormField : true,
7871     value : '',
7872     labelWidth : 2,
7873     labelAlign : false,
7874     readOnly : false,
7875     align : false,
7876     formatedValue : false,
7877     forceFeedback : false,
7878     
7879     parentLabelAlign : function()
7880     {
7881         var parent = this;
7882         while (parent.parent()) {
7883             parent = parent.parent();
7884             if (typeof(parent.labelAlign) !='undefined') {
7885                 return parent.labelAlign;
7886             }
7887         }
7888         return 'left';
7889         
7890     },
7891     
7892     getAutoCreate : function(){
7893         
7894         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7895         
7896         var id = Roo.id();
7897         
7898         var cfg = {};
7899         
7900         if(this.inputType != 'hidden'){
7901             cfg.cls = 'form-group' //input-group
7902         }
7903         
7904         var input =  {
7905             tag: 'input',
7906             id : id,
7907             type : this.inputType,
7908             value : this.value,
7909             cls : 'form-control',
7910             placeholder : this.placeholder || '',
7911             autocomplete : this.autocomplete || 'new-password'
7912         };
7913         
7914         
7915         if(this.align){
7916             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7917         }
7918         
7919         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7920             input.maxLength = this.maxLength;
7921         }
7922         
7923         if (this.disabled) {
7924             input.disabled=true;
7925         }
7926         
7927         if (this.readOnly) {
7928             input.readonly=true;
7929         }
7930         
7931         if (this.name) {
7932             input.name = this.name;
7933         }
7934         if (this.size) {
7935             input.cls += ' input-' + this.size;
7936         }
7937         var settings=this;
7938         ['xs','sm','md','lg'].map(function(size){
7939             if (settings[size]) {
7940                 cfg.cls += ' col-' + size + '-' + settings[size];
7941             }
7942         });
7943         
7944         var inputblock = input;
7945         
7946         var feedback = {
7947             tag: 'span',
7948             cls: 'glyphicon form-control-feedback'
7949         };
7950             
7951         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7952             
7953             inputblock = {
7954                 cls : 'has-feedback',
7955                 cn :  [
7956                     input,
7957                     feedback
7958                 ] 
7959             };  
7960         }
7961         
7962         if (this.before || this.after) {
7963             
7964             inputblock = {
7965                 cls : 'input-group',
7966                 cn :  [] 
7967             };
7968             
7969             if (this.before && typeof(this.before) == 'string') {
7970                 
7971                 inputblock.cn.push({
7972                     tag :'span',
7973                     cls : 'roo-input-before input-group-addon',
7974                     html : this.before
7975                 });
7976             }
7977             if (this.before && typeof(this.before) == 'object') {
7978                 this.before = Roo.factory(this.before);
7979                 Roo.log(this.before);
7980                 inputblock.cn.push({
7981                     tag :'span',
7982                     cls : 'roo-input-before input-group-' +
7983                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7984                 });
7985             }
7986             
7987             inputblock.cn.push(input);
7988             
7989             if (this.after && typeof(this.after) == 'string') {
7990                 inputblock.cn.push({
7991                     tag :'span',
7992                     cls : 'roo-input-after input-group-addon',
7993                     html : this.after
7994                 });
7995             }
7996             if (this.after && typeof(this.after) == 'object') {
7997                 this.after = Roo.factory(this.after);
7998                 Roo.log(this.after);
7999                 inputblock.cn.push({
8000                     tag :'span',
8001                     cls : 'roo-input-after input-group-' +
8002                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8003                 });
8004             }
8005             
8006             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8007                 inputblock.cls += ' has-feedback';
8008                 inputblock.cn.push(feedback);
8009             }
8010         };
8011         
8012         if (align ==='left' && this.fieldLabel.length) {
8013                 Roo.log("left and has label");
8014                 cfg.cn = [
8015                     
8016                     {
8017                         tag: 'label',
8018                         'for' :  id,
8019                         cls : 'control-label col-sm-' + this.labelWidth,
8020                         html : this.fieldLabel
8021                         
8022                     },
8023                     {
8024                         cls : "col-sm-" + (12 - this.labelWidth), 
8025                         cn: [
8026                             inputblock
8027                         ]
8028                     }
8029                     
8030                 ];
8031         } else if ( this.fieldLabel.length) {
8032                 Roo.log(" label");
8033                  cfg.cn = [
8034                    
8035                     {
8036                         tag: 'label',
8037                         //cls : 'input-group-addon',
8038                         html : this.fieldLabel
8039                         
8040                     },
8041                     
8042                     inputblock
8043                     
8044                 ];
8045
8046         } else {
8047             
8048                 Roo.log(" no label && no align");
8049                 cfg.cn = [
8050                     
8051                         inputblock
8052                     
8053                 ];
8054                 
8055                 
8056         };
8057         Roo.log('input-parentType: ' + this.parentType);
8058         
8059         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8060            cfg.cls += ' navbar-form';
8061            Roo.log(cfg);
8062         }
8063         
8064         return cfg;
8065         
8066     },
8067     /**
8068      * return the real input element.
8069      */
8070     inputEl: function ()
8071     {
8072         return this.el.select('input.form-control',true).first();
8073     },
8074     
8075     tooltipEl : function()
8076     {
8077         return this.inputEl();
8078     },
8079     
8080     setDisabled : function(v)
8081     {
8082         var i  = this.inputEl().dom;
8083         if (!v) {
8084             i.removeAttribute('disabled');
8085             return;
8086             
8087         }
8088         i.setAttribute('disabled','true');
8089     },
8090     initEvents : function()
8091     {
8092           
8093         this.inputEl().on("keydown" , this.fireKey,  this);
8094         this.inputEl().on("focus", this.onFocus,  this);
8095         this.inputEl().on("blur", this.onBlur,  this);
8096         
8097         this.inputEl().relayEvent('keyup', this);
8098  
8099         // reference to original value for reset
8100         this.originalValue = this.getValue();
8101         //Roo.form.TextField.superclass.initEvents.call(this);
8102         if(this.validationEvent == 'keyup'){
8103             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8104             this.inputEl().on('keyup', this.filterValidation, this);
8105         }
8106         else if(this.validationEvent !== false){
8107             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8108         }
8109         
8110         if(this.selectOnFocus){
8111             this.on("focus", this.preFocus, this);
8112             
8113         }
8114         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8115             this.inputEl().on("keypress", this.filterKeys, this);
8116         }
8117        /* if(this.grow){
8118             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8119             this.el.on("click", this.autoSize,  this);
8120         }
8121         */
8122         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8123             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8124         }
8125         
8126         if (typeof(this.before) == 'object') {
8127             this.before.render(this.el.select('.roo-input-before',true).first());
8128         }
8129         if (typeof(this.after) == 'object') {
8130             this.after.render(this.el.select('.roo-input-after',true).first());
8131         }
8132         
8133         
8134     },
8135     filterValidation : function(e){
8136         if(!e.isNavKeyPress()){
8137             this.validationTask.delay(this.validationDelay);
8138         }
8139     },
8140      /**
8141      * Validates the field value
8142      * @return {Boolean} True if the value is valid, else false
8143      */
8144     validate : function(){
8145         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8146         if(this.disabled || this.validateValue(this.getRawValue())){
8147             this.markValid();
8148             return true;
8149         }
8150         
8151         this.markInvalid();
8152         return false;
8153     },
8154     
8155     
8156     /**
8157      * Validates a value according to the field's validation rules and marks the field as invalid
8158      * if the validation fails
8159      * @param {Mixed} value The value to validate
8160      * @return {Boolean} True if the value is valid, else false
8161      */
8162     validateValue : function(value){
8163         if(value.length < 1)  { // if it's blank
8164             if(this.allowBlank){
8165                 return true;
8166             }
8167             return false;
8168         }
8169         
8170         if(value.length < this.minLength){
8171             return false;
8172         }
8173         if(value.length > this.maxLength){
8174             return false;
8175         }
8176         if(this.vtype){
8177             var vt = Roo.form.VTypes;
8178             if(!vt[this.vtype](value, this)){
8179                 return false;
8180             }
8181         }
8182         if(typeof this.validator == "function"){
8183             var msg = this.validator(value);
8184             if(msg !== true){
8185                 return false;
8186             }
8187         }
8188         
8189         if(this.regex && !this.regex.test(value)){
8190             return false;
8191         }
8192         
8193         return true;
8194     },
8195
8196     
8197     
8198      // private
8199     fireKey : function(e){
8200         //Roo.log('field ' + e.getKey());
8201         if(e.isNavKeyPress()){
8202             this.fireEvent("specialkey", this, e);
8203         }
8204     },
8205     focus : function (selectText){
8206         if(this.rendered){
8207             this.inputEl().focus();
8208             if(selectText === true){
8209                 this.inputEl().dom.select();
8210             }
8211         }
8212         return this;
8213     } ,
8214     
8215     onFocus : function(){
8216         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8217            // this.el.addClass(this.focusClass);
8218         }
8219         if(!this.hasFocus){
8220             this.hasFocus = true;
8221             this.startValue = this.getValue();
8222             this.fireEvent("focus", this);
8223         }
8224     },
8225     
8226     beforeBlur : Roo.emptyFn,
8227
8228     
8229     // private
8230     onBlur : function(){
8231         this.beforeBlur();
8232         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8233             //this.el.removeClass(this.focusClass);
8234         }
8235         this.hasFocus = false;
8236         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8237             this.validate();
8238         }
8239         var v = this.getValue();
8240         if(String(v) !== String(this.startValue)){
8241             this.fireEvent('change', this, v, this.startValue);
8242         }
8243         this.fireEvent("blur", this);
8244     },
8245     
8246     /**
8247      * Resets the current field value to the originally loaded value and clears any validation messages
8248      */
8249     reset : function(){
8250         this.setValue(this.originalValue);
8251         this.validate();
8252     },
8253      /**
8254      * Returns the name of the field
8255      * @return {Mixed} name The name field
8256      */
8257     getName: function(){
8258         return this.name;
8259     },
8260      /**
8261      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8262      * @return {Mixed} value The field value
8263      */
8264     getValue : function(){
8265         
8266         var v = this.inputEl().getValue();
8267         
8268         return v;
8269     },
8270     /**
8271      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8272      * @return {Mixed} value The field value
8273      */
8274     getRawValue : function(){
8275         var v = this.inputEl().getValue();
8276         
8277         return v;
8278     },
8279     
8280     /**
8281      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8282      * @param {Mixed} value The value to set
8283      */
8284     setRawValue : function(v){
8285         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8286     },
8287     
8288     selectText : function(start, end){
8289         var v = this.getRawValue();
8290         if(v.length > 0){
8291             start = start === undefined ? 0 : start;
8292             end = end === undefined ? v.length : end;
8293             var d = this.inputEl().dom;
8294             if(d.setSelectionRange){
8295                 d.setSelectionRange(start, end);
8296             }else if(d.createTextRange){
8297                 var range = d.createTextRange();
8298                 range.moveStart("character", start);
8299                 range.moveEnd("character", v.length-end);
8300                 range.select();
8301             }
8302         }
8303     },
8304     
8305     /**
8306      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8307      * @param {Mixed} value The value to set
8308      */
8309     setValue : function(v){
8310         this.value = v;
8311         if(this.rendered){
8312             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8313             this.validate();
8314         }
8315     },
8316     
8317     /*
8318     processValue : function(value){
8319         if(this.stripCharsRe){
8320             var newValue = value.replace(this.stripCharsRe, '');
8321             if(newValue !== value){
8322                 this.setRawValue(newValue);
8323                 return newValue;
8324             }
8325         }
8326         return value;
8327     },
8328   */
8329     preFocus : function(){
8330         
8331         if(this.selectOnFocus){
8332             this.inputEl().dom.select();
8333         }
8334     },
8335     filterKeys : function(e){
8336         var k = e.getKey();
8337         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8338             return;
8339         }
8340         var c = e.getCharCode(), cc = String.fromCharCode(c);
8341         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8342             return;
8343         }
8344         if(!this.maskRe.test(cc)){
8345             e.stopEvent();
8346         }
8347     },
8348      /**
8349      * Clear any invalid styles/messages for this field
8350      */
8351     clearInvalid : function(){
8352         
8353         if(!this.el || this.preventMark){ // not rendered
8354             return;
8355         }
8356         this.el.removeClass(this.invalidClass);
8357         
8358         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8359             
8360             var feedback = this.el.select('.form-control-feedback', true).first();
8361             
8362             if(feedback){
8363                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8364             }
8365             
8366         }
8367         
8368         this.fireEvent('valid', this);
8369     },
8370     
8371      /**
8372      * Mark this field as valid
8373      */
8374     markValid : function()
8375     {
8376         if(!this.el  || this.preventMark){ // not rendered
8377             return;
8378         }
8379         
8380         this.el.removeClass([this.invalidClass, this.validClass]);
8381         
8382         var feedback = this.el.select('.form-control-feedback', true).first();
8383             
8384         if(feedback){
8385             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8386         }
8387
8388         if(this.disabled || this.allowBlank){
8389             return;
8390         }
8391         
8392         var formGroup = this.el.findParent('.form-group', false, true);
8393         
8394         if(formGroup){
8395             
8396             var label = formGroup.select('label', true).first();
8397             var icon = formGroup.select('i.fa-star', true).first();
8398             
8399             if(label && icon){
8400                 icon.remove();
8401             }
8402         }
8403         
8404         this.el.addClass(this.validClass);
8405         
8406         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8407             
8408             var feedback = this.el.select('.form-control-feedback', true).first();
8409             
8410             if(feedback){
8411                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8412                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8413             }
8414             
8415         }
8416         
8417         this.fireEvent('valid', this);
8418     },
8419     
8420      /**
8421      * Mark this field as invalid
8422      * @param {String} msg The validation message
8423      */
8424     markInvalid : function(msg)
8425     {
8426         if(!this.el  || this.preventMark){ // not rendered
8427             return;
8428         }
8429         
8430         this.el.removeClass([this.invalidClass, this.validClass]);
8431         
8432         var feedback = this.el.select('.form-control-feedback', true).first();
8433             
8434         if(feedback){
8435             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8436         }
8437
8438         if(this.disabled || this.allowBlank){
8439             return;
8440         }
8441         
8442         var formGroup = this.el.findParent('.form-group', false, true);
8443         
8444         if(formGroup){
8445             var label = formGroup.select('label', true).first();
8446             var icon = formGroup.select('i.fa-star', true).first();
8447
8448             if(!this.getValue().length && label && !icon){
8449                 this.el.findParent('.form-group', false, true).createChild({
8450                     tag : 'i',
8451                     cls : 'text-danger fa fa-lg fa-star',
8452                     tooltip : 'This field is required',
8453                     style : 'margin-right:5px;'
8454                 }, label, true);
8455             }
8456         }
8457         
8458         
8459         this.el.addClass(this.invalidClass);
8460         
8461         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8462             
8463             var feedback = this.el.select('.form-control-feedback', true).first();
8464             
8465             if(feedback){
8466                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8467                 
8468                 if(this.getValue().length || this.forceFeedback){
8469                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8470                 }
8471                 
8472             }
8473             
8474         }
8475         
8476         this.fireEvent('invalid', this, msg);
8477     },
8478     // private
8479     SafariOnKeyDown : function(event)
8480     {
8481         // this is a workaround for a password hang bug on chrome/ webkit.
8482         
8483         var isSelectAll = false;
8484         
8485         if(this.inputEl().dom.selectionEnd > 0){
8486             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8487         }
8488         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8489             event.preventDefault();
8490             this.setValue('');
8491             return;
8492         }
8493         
8494         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
8495             
8496             event.preventDefault();
8497             // this is very hacky as keydown always get's upper case.
8498             //
8499             var cc = String.fromCharCode(event.getCharCode());
8500             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8501             
8502         }
8503     },
8504     adjustWidth : function(tag, w){
8505         tag = tag.toLowerCase();
8506         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8507             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8508                 if(tag == 'input'){
8509                     return w + 2;
8510                 }
8511                 if(tag == 'textarea'){
8512                     return w-2;
8513                 }
8514             }else if(Roo.isOpera){
8515                 if(tag == 'input'){
8516                     return w + 2;
8517                 }
8518                 if(tag == 'textarea'){
8519                     return w-2;
8520                 }
8521             }
8522         }
8523         return w;
8524     }
8525     
8526 });
8527
8528  
8529 /*
8530  * - LGPL
8531  *
8532  * Input
8533  * 
8534  */
8535
8536 /**
8537  * @class Roo.bootstrap.TextArea
8538  * @extends Roo.bootstrap.Input
8539  * Bootstrap TextArea class
8540  * @cfg {Number} cols Specifies the visible width of a text area
8541  * @cfg {Number} rows Specifies the visible number of lines in a text area
8542  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8543  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8544  * @cfg {string} html text
8545  * 
8546  * @constructor
8547  * Create a new TextArea
8548  * @param {Object} config The config object
8549  */
8550
8551 Roo.bootstrap.TextArea = function(config){
8552     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8553    
8554 };
8555
8556 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8557      
8558     cols : false,
8559     rows : 5,
8560     readOnly : false,
8561     warp : 'soft',
8562     resize : false,
8563     value: false,
8564     html: false,
8565     
8566     getAutoCreate : function(){
8567         
8568         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8569         
8570         var id = Roo.id();
8571         
8572         var cfg = {};
8573         
8574         var input =  {
8575             tag: 'textarea',
8576             id : id,
8577             warp : this.warp,
8578             rows : this.rows,
8579             value : this.value || '',
8580             html: this.html || '',
8581             cls : 'form-control',
8582             placeholder : this.placeholder || '' 
8583             
8584         };
8585         
8586         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8587             input.maxLength = this.maxLength;
8588         }
8589         
8590         if(this.resize){
8591             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8592         }
8593         
8594         if(this.cols){
8595             input.cols = this.cols;
8596         }
8597         
8598         if (this.readOnly) {
8599             input.readonly = true;
8600         }
8601         
8602         if (this.name) {
8603             input.name = this.name;
8604         }
8605         
8606         if (this.size) {
8607             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8608         }
8609         
8610         var settings=this;
8611         ['xs','sm','md','lg'].map(function(size){
8612             if (settings[size]) {
8613                 cfg.cls += ' col-' + size + '-' + settings[size];
8614             }
8615         });
8616         
8617         var inputblock = input;
8618         
8619         if(this.hasFeedback && !this.allowBlank){
8620             
8621             var feedback = {
8622                 tag: 'span',
8623                 cls: 'glyphicon form-control-feedback'
8624             };
8625
8626             inputblock = {
8627                 cls : 'has-feedback',
8628                 cn :  [
8629                     input,
8630                     feedback
8631                 ] 
8632             };  
8633         }
8634         
8635         
8636         if (this.before || this.after) {
8637             
8638             inputblock = {
8639                 cls : 'input-group',
8640                 cn :  [] 
8641             };
8642             if (this.before) {
8643                 inputblock.cn.push({
8644                     tag :'span',
8645                     cls : 'input-group-addon',
8646                     html : this.before
8647                 });
8648             }
8649             
8650             inputblock.cn.push(input);
8651             
8652             if(this.hasFeedback && !this.allowBlank){
8653                 inputblock.cls += ' has-feedback';
8654                 inputblock.cn.push(feedback);
8655             }
8656             
8657             if (this.after) {
8658                 inputblock.cn.push({
8659                     tag :'span',
8660                     cls : 'input-group-addon',
8661                     html : this.after
8662                 });
8663             }
8664             
8665         }
8666         
8667         if (align ==='left' && this.fieldLabel.length) {
8668                 Roo.log("left and has label");
8669                 cfg.cn = [
8670                     
8671                     {
8672                         tag: 'label',
8673                         'for' :  id,
8674                         cls : 'control-label col-sm-' + this.labelWidth,
8675                         html : this.fieldLabel
8676                         
8677                     },
8678                     {
8679                         cls : "col-sm-" + (12 - this.labelWidth), 
8680                         cn: [
8681                             inputblock
8682                         ]
8683                     }
8684                     
8685                 ];
8686         } else if ( this.fieldLabel.length) {
8687                 Roo.log(" label");
8688                  cfg.cn = [
8689                    
8690                     {
8691                         tag: 'label',
8692                         //cls : 'input-group-addon',
8693                         html : this.fieldLabel
8694                         
8695                     },
8696                     
8697                     inputblock
8698                     
8699                 ];
8700
8701         } else {
8702             
8703                    Roo.log(" no label && no align");
8704                 cfg.cn = [
8705                     
8706                         inputblock
8707                     
8708                 ];
8709                 
8710                 
8711         }
8712         
8713         if (this.disabled) {
8714             input.disabled=true;
8715         }
8716         
8717         return cfg;
8718         
8719     },
8720     /**
8721      * return the real textarea element.
8722      */
8723     inputEl: function ()
8724     {
8725         return this.el.select('textarea.form-control',true).first();
8726     },
8727     
8728     /**
8729      * Clear any invalid styles/messages for this field
8730      */
8731     clearInvalid : function()
8732     {
8733         
8734         if(!this.el || this.preventMark){ // not rendered
8735             return;
8736         }
8737         
8738         var label = this.el.select('label', true).first();
8739         var icon = this.el.select('i.fa-star', true).first();
8740         
8741         if(label && icon){
8742             icon.remove();
8743         }
8744         
8745         this.el.removeClass(this.invalidClass);
8746         
8747         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8748             
8749             var feedback = this.el.select('.form-control-feedback', true).first();
8750             
8751             if(feedback){
8752                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8753             }
8754             
8755         }
8756         
8757         this.fireEvent('valid', this);
8758     },
8759     
8760      /**
8761      * Mark this field as valid
8762      */
8763     markValid : function()
8764     {
8765         if(!this.el  || this.preventMark){ // not rendered
8766             return;
8767         }
8768         
8769         this.el.removeClass([this.invalidClass, this.validClass]);
8770         
8771         var feedback = this.el.select('.form-control-feedback', true).first();
8772             
8773         if(feedback){
8774             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8775         }
8776
8777         if(this.disabled || this.allowBlank){
8778             return;
8779         }
8780         
8781         var label = this.el.select('label', true).first();
8782         var icon = this.el.select('i.fa-star', true).first();
8783         
8784         if(label && icon){
8785             icon.remove();
8786         }
8787         
8788         this.el.addClass(this.validClass);
8789         
8790         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8791             
8792             var feedback = this.el.select('.form-control-feedback', true).first();
8793             
8794             if(feedback){
8795                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8796                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8797             }
8798             
8799         }
8800         
8801         this.fireEvent('valid', this);
8802     },
8803     
8804      /**
8805      * Mark this field as invalid
8806      * @param {String} msg The validation message
8807      */
8808     markInvalid : function(msg)
8809     {
8810         if(!this.el  || this.preventMark){ // not rendered
8811             return;
8812         }
8813         
8814         this.el.removeClass([this.invalidClass, this.validClass]);
8815         
8816         var feedback = this.el.select('.form-control-feedback', true).first();
8817             
8818         if(feedback){
8819             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8820         }
8821
8822         if(this.disabled || this.allowBlank){
8823             return;
8824         }
8825         
8826         var label = this.el.select('label', true).first();
8827         var icon = this.el.select('i.fa-star', true).first();
8828         
8829         if(!this.getValue().length && label && !icon){
8830             this.el.createChild({
8831                 tag : 'i',
8832                 cls : 'text-danger fa fa-lg fa-star',
8833                 tooltip : 'This field is required',
8834                 style : 'margin-right:5px;'
8835             }, label, true);
8836         }
8837
8838         this.el.addClass(this.invalidClass);
8839         
8840         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8841             
8842             var feedback = this.el.select('.form-control-feedback', true).first();
8843             
8844             if(feedback){
8845                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8846                 
8847                 if(this.getValue().length || this.forceFeedback){
8848                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8849                 }
8850                 
8851             }
8852             
8853         }
8854         
8855         this.fireEvent('invalid', this, msg);
8856     }
8857 });
8858
8859  
8860 /*
8861  * - LGPL
8862  *
8863  * trigger field - base class for combo..
8864  * 
8865  */
8866  
8867 /**
8868  * @class Roo.bootstrap.TriggerField
8869  * @extends Roo.bootstrap.Input
8870  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8871  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8872  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8873  * for which you can provide a custom implementation.  For example:
8874  * <pre><code>
8875 var trigger = new Roo.bootstrap.TriggerField();
8876 trigger.onTriggerClick = myTriggerFn;
8877 trigger.applyTo('my-field');
8878 </code></pre>
8879  *
8880  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8881  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8882  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
8883  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8884  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8885
8886  * @constructor
8887  * Create a new TriggerField.
8888  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8889  * to the base TextField)
8890  */
8891 Roo.bootstrap.TriggerField = function(config){
8892     this.mimicing = false;
8893     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8894 };
8895
8896 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
8897     /**
8898      * @cfg {String} triggerClass A CSS class to apply to the trigger
8899      */
8900      /**
8901      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8902      */
8903     hideTrigger:false,
8904
8905     /**
8906      * @cfg {Boolean} removable (true|false) special filter default false
8907      */
8908     removable : false,
8909     
8910     /** @cfg {Boolean} grow @hide */
8911     /** @cfg {Number} growMin @hide */
8912     /** @cfg {Number} growMax @hide */
8913
8914     /**
8915      * @hide 
8916      * @method
8917      */
8918     autoSize: Roo.emptyFn,
8919     // private
8920     monitorTab : true,
8921     // private
8922     deferHeight : true,
8923
8924     
8925     actionMode : 'wrap',
8926     
8927     caret : false,
8928     
8929     
8930     getAutoCreate : function(){
8931        
8932         var align = this.labelAlign || this.parentLabelAlign();
8933         
8934         var id = Roo.id();
8935         
8936         var cfg = {
8937             cls: 'form-group' //input-group
8938         };
8939         
8940         
8941         var input =  {
8942             tag: 'input',
8943             id : id,
8944             type : this.inputType,
8945             cls : 'form-control',
8946             autocomplete: 'new-password',
8947             placeholder : this.placeholder || '' 
8948             
8949         };
8950         if (this.name) {
8951             input.name = this.name;
8952         }
8953         if (this.size) {
8954             input.cls += ' input-' + this.size;
8955         }
8956         
8957         if (this.disabled) {
8958             input.disabled=true;
8959         }
8960         
8961         var inputblock = input;
8962         
8963         if(this.hasFeedback && !this.allowBlank){
8964             
8965             var feedback = {
8966                 tag: 'span',
8967                 cls: 'glyphicon form-control-feedback'
8968             };
8969             
8970             if(this.removable && !this.editable && !this.tickable){
8971                 inputblock = {
8972                     cls : 'has-feedback',
8973                     cn :  [
8974                         inputblock,
8975                         {
8976                             tag: 'button',
8977                             html : 'x',
8978                             cls : 'roo-combo-removable-btn close'
8979                         },
8980                         feedback
8981                     ] 
8982                 };
8983             } else {
8984                 inputblock = {
8985                     cls : 'has-feedback',
8986                     cn :  [
8987                         inputblock,
8988                         feedback
8989                     ] 
8990                 };
8991             }
8992
8993         } else {
8994             if(this.removable && !this.editable && !this.tickable){
8995                 inputblock = {
8996                     cls : 'roo-removable',
8997                     cn :  [
8998                         inputblock,
8999                         {
9000                             tag: 'button',
9001                             html : 'x',
9002                             cls : 'roo-combo-removable-btn close'
9003                         }
9004                     ] 
9005                 };
9006             }
9007         }
9008         
9009         if (this.before || this.after) {
9010             
9011             inputblock = {
9012                 cls : 'input-group',
9013                 cn :  [] 
9014             };
9015             if (this.before) {
9016                 inputblock.cn.push({
9017                     tag :'span',
9018                     cls : 'input-group-addon',
9019                     html : this.before
9020                 });
9021             }
9022             
9023             inputblock.cn.push(input);
9024             
9025             if(this.hasFeedback && !this.allowBlank){
9026                 inputblock.cls += ' has-feedback';
9027                 inputblock.cn.push(feedback);
9028             }
9029             
9030             if (this.after) {
9031                 inputblock.cn.push({
9032                     tag :'span',
9033                     cls : 'input-group-addon',
9034                     html : this.after
9035                 });
9036             }
9037             
9038         };
9039         
9040         var box = {
9041             tag: 'div',
9042             cn: [
9043                 {
9044                     tag: 'input',
9045                     type : 'hidden',
9046                     cls: 'form-hidden-field'
9047                 },
9048                 inputblock
9049             ]
9050             
9051         };
9052         
9053         if(this.multiple){
9054             Roo.log('multiple');
9055             
9056             box = {
9057                 tag: 'div',
9058                 cn: [
9059                     {
9060                         tag: 'input',
9061                         type : 'hidden',
9062                         cls: 'form-hidden-field'
9063                     },
9064                     {
9065                         tag: 'ul',
9066                         cls: 'select2-choices',
9067                         cn:[
9068                             {
9069                                 tag: 'li',
9070                                 cls: 'select2-search-field',
9071                                 cn: [
9072
9073                                     inputblock
9074                                 ]
9075                             }
9076                         ]
9077                     }
9078                 ]
9079             }
9080         };
9081         
9082         var combobox = {
9083             cls: 'select2-container input-group',
9084             cn: [
9085                 box
9086 //                {
9087 //                    tag: 'ul',
9088 //                    cls: 'typeahead typeahead-long dropdown-menu',
9089 //                    style: 'display:none'
9090 //                }
9091             ]
9092         };
9093         
9094         if(!this.multiple && this.showToggleBtn){
9095             
9096             var caret = {
9097                         tag: 'span',
9098                         cls: 'caret'
9099              };
9100             if (this.caret != false) {
9101                 caret = {
9102                      tag: 'i',
9103                      cls: 'fa fa-' + this.caret
9104                 };
9105                 
9106             }
9107             
9108             combobox.cn.push({
9109                 tag :'span',
9110                 cls : 'input-group-addon btn dropdown-toggle',
9111                 cn : [
9112                     caret,
9113                     {
9114                         tag: 'span',
9115                         cls: 'combobox-clear',
9116                         cn  : [
9117                             {
9118                                 tag : 'i',
9119                                 cls: 'icon-remove'
9120                             }
9121                         ]
9122                     }
9123                 ]
9124
9125             })
9126         }
9127         
9128         if(this.multiple){
9129             combobox.cls += ' select2-container-multi';
9130         }
9131         
9132         if (align ==='left' && this.fieldLabel.length) {
9133             
9134                 Roo.log("left and has label");
9135                 cfg.cn = [
9136                     
9137                     {
9138                         tag: 'label',
9139                         'for' :  id,
9140                         cls : 'control-label col-sm-' + this.labelWidth,
9141                         html : this.fieldLabel
9142                         
9143                     },
9144                     {
9145                         cls : "col-sm-" + (12 - this.labelWidth), 
9146                         cn: [
9147                             combobox
9148                         ]
9149                     }
9150                     
9151                 ];
9152         } else if ( this.fieldLabel.length) {
9153                 Roo.log(" label");
9154                  cfg.cn = [
9155                    
9156                     {
9157                         tag: 'label',
9158                         //cls : 'input-group-addon',
9159                         html : this.fieldLabel
9160                         
9161                     },
9162                     
9163                     combobox
9164                     
9165                 ];
9166
9167         } else {
9168             
9169                 Roo.log(" no label && no align");
9170                 cfg = combobox
9171                      
9172                 
9173         }
9174          
9175         var settings=this;
9176         ['xs','sm','md','lg'].map(function(size){
9177             if (settings[size]) {
9178                 cfg.cls += ' col-' + size + '-' + settings[size];
9179             }
9180         });
9181         Roo.log(cfg);
9182         return cfg;
9183         
9184     },
9185     
9186     
9187     
9188     // private
9189     onResize : function(w, h){
9190 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9191 //        if(typeof w == 'number'){
9192 //            var x = w - this.trigger.getWidth();
9193 //            this.inputEl().setWidth(this.adjustWidth('input', x));
9194 //            this.trigger.setStyle('left', x+'px');
9195 //        }
9196     },
9197
9198     // private
9199     adjustSize : Roo.BoxComponent.prototype.adjustSize,
9200
9201     // private
9202     getResizeEl : function(){
9203         return this.inputEl();
9204     },
9205
9206     // private
9207     getPositionEl : function(){
9208         return this.inputEl();
9209     },
9210
9211     // private
9212     alignErrorIcon : function(){
9213         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9214     },
9215
9216     // private
9217     initEvents : function(){
9218         
9219         this.createList();
9220         
9221         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9222         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9223         if(!this.multiple && this.showToggleBtn){
9224             this.trigger = this.el.select('span.dropdown-toggle',true).first();
9225             if(this.hideTrigger){
9226                 this.trigger.setDisplayed(false);
9227             }
9228             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9229         }
9230         
9231         if(this.multiple){
9232             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9233         }
9234         
9235         if(this.removable && !this.editable && !this.tickable){
9236             var close = this.closeTriggerEl();
9237             
9238             if(close){
9239                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9240                 close.on('click', this.removeBtnClick, this, close);
9241             }
9242         }
9243         
9244         //this.trigger.addClassOnOver('x-form-trigger-over');
9245         //this.trigger.addClassOnClick('x-form-trigger-click');
9246         
9247         //if(!this.width){
9248         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9249         //}
9250     },
9251     
9252     closeTriggerEl : function()
9253     {
9254         var close = this.el.select('.roo-combo-removable-btn', true).first();
9255         return close ? close : false;
9256     },
9257     
9258     removeBtnClick : function(e, h, el)
9259     {
9260         e.preventDefault();
9261         
9262         if(this.fireEvent("remove", this) !== false){
9263             this.reset();
9264         }
9265     },
9266     
9267     createList : function()
9268     {
9269         this.list = Roo.get(document.body).createChild({
9270             tag: 'ul',
9271             cls: 'typeahead typeahead-long dropdown-menu',
9272             style: 'display:none'
9273         });
9274         
9275         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9276         
9277     },
9278
9279     // private
9280     initTrigger : function(){
9281        
9282     },
9283
9284     // private
9285     onDestroy : function(){
9286         if(this.trigger){
9287             this.trigger.removeAllListeners();
9288           //  this.trigger.remove();
9289         }
9290         //if(this.wrap){
9291         //    this.wrap.remove();
9292         //}
9293         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9294     },
9295
9296     // private
9297     onFocus : function(){
9298         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9299         /*
9300         if(!this.mimicing){
9301             this.wrap.addClass('x-trigger-wrap-focus');
9302             this.mimicing = true;
9303             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9304             if(this.monitorTab){
9305                 this.el.on("keydown", this.checkTab, this);
9306             }
9307         }
9308         */
9309     },
9310
9311     // private
9312     checkTab : function(e){
9313         if(e.getKey() == e.TAB){
9314             this.triggerBlur();
9315         }
9316     },
9317
9318     // private
9319     onBlur : function(){
9320         // do nothing
9321     },
9322
9323     // private
9324     mimicBlur : function(e, t){
9325         /*
9326         if(!this.wrap.contains(t) && this.validateBlur()){
9327             this.triggerBlur();
9328         }
9329         */
9330     },
9331
9332     // private
9333     triggerBlur : function(){
9334         this.mimicing = false;
9335         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9336         if(this.monitorTab){
9337             this.el.un("keydown", this.checkTab, this);
9338         }
9339         //this.wrap.removeClass('x-trigger-wrap-focus');
9340         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9341     },
9342
9343     // private
9344     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9345     validateBlur : function(e, t){
9346         return true;
9347     },
9348
9349     // private
9350     onDisable : function(){
9351         this.inputEl().dom.disabled = true;
9352         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9353         //if(this.wrap){
9354         //    this.wrap.addClass('x-item-disabled');
9355         //}
9356     },
9357
9358     // private
9359     onEnable : function(){
9360         this.inputEl().dom.disabled = false;
9361         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9362         //if(this.wrap){
9363         //    this.el.removeClass('x-item-disabled');
9364         //}
9365     },
9366
9367     // private
9368     onShow : function(){
9369         var ae = this.getActionEl();
9370         
9371         if(ae){
9372             ae.dom.style.display = '';
9373             ae.dom.style.visibility = 'visible';
9374         }
9375     },
9376
9377     // private
9378     
9379     onHide : function(){
9380         var ae = this.getActionEl();
9381         ae.dom.style.display = 'none';
9382     },
9383
9384     /**
9385      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
9386      * by an implementing function.
9387      * @method
9388      * @param {EventObject} e
9389      */
9390     onTriggerClick : Roo.emptyFn
9391 });
9392  /*
9393  * Based on:
9394  * Ext JS Library 1.1.1
9395  * Copyright(c) 2006-2007, Ext JS, LLC.
9396  *
9397  * Originally Released Under LGPL - original licence link has changed is not relivant.
9398  *
9399  * Fork - LGPL
9400  * <script type="text/javascript">
9401  */
9402
9403
9404 /**
9405  * @class Roo.data.SortTypes
9406  * @singleton
9407  * Defines the default sorting (casting?) comparison functions used when sorting data.
9408  */
9409 Roo.data.SortTypes = {
9410     /**
9411      * Default sort that does nothing
9412      * @param {Mixed} s The value being converted
9413      * @return {Mixed} The comparison value
9414      */
9415     none : function(s){
9416         return s;
9417     },
9418     
9419     /**
9420      * The regular expression used to strip tags
9421      * @type {RegExp}
9422      * @property
9423      */
9424     stripTagsRE : /<\/?[^>]+>/gi,
9425     
9426     /**
9427      * Strips all HTML tags to sort on text only
9428      * @param {Mixed} s The value being converted
9429      * @return {String} The comparison value
9430      */
9431     asText : function(s){
9432         return String(s).replace(this.stripTagsRE, "");
9433     },
9434     
9435     /**
9436      * Strips all HTML tags to sort on text only - Case insensitive
9437      * @param {Mixed} s The value being converted
9438      * @return {String} The comparison value
9439      */
9440     asUCText : function(s){
9441         return String(s).toUpperCase().replace(this.stripTagsRE, "");
9442     },
9443     
9444     /**
9445      * Case insensitive string
9446      * @param {Mixed} s The value being converted
9447      * @return {String} The comparison value
9448      */
9449     asUCString : function(s) {
9450         return String(s).toUpperCase();
9451     },
9452     
9453     /**
9454      * Date sorting
9455      * @param {Mixed} s The value being converted
9456      * @return {Number} The comparison value
9457      */
9458     asDate : function(s) {
9459         if(!s){
9460             return 0;
9461         }
9462         if(s instanceof Date){
9463             return s.getTime();
9464         }
9465         return Date.parse(String(s));
9466     },
9467     
9468     /**
9469      * Float sorting
9470      * @param {Mixed} s The value being converted
9471      * @return {Float} The comparison value
9472      */
9473     asFloat : function(s) {
9474         var val = parseFloat(String(s).replace(/,/g, ""));
9475         if(isNaN(val)) {
9476             val = 0;
9477         }
9478         return val;
9479     },
9480     
9481     /**
9482      * Integer sorting
9483      * @param {Mixed} s The value being converted
9484      * @return {Number} The comparison value
9485      */
9486     asInt : function(s) {
9487         var val = parseInt(String(s).replace(/,/g, ""));
9488         if(isNaN(val)) {
9489             val = 0;
9490         }
9491         return val;
9492     }
9493 };/*
9494  * Based on:
9495  * Ext JS Library 1.1.1
9496  * Copyright(c) 2006-2007, Ext JS, LLC.
9497  *
9498  * Originally Released Under LGPL - original licence link has changed is not relivant.
9499  *
9500  * Fork - LGPL
9501  * <script type="text/javascript">
9502  */
9503
9504 /**
9505 * @class Roo.data.Record
9506  * Instances of this class encapsulate both record <em>definition</em> information, and record
9507  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9508  * to access Records cached in an {@link Roo.data.Store} object.<br>
9509  * <p>
9510  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9511  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9512  * objects.<br>
9513  * <p>
9514  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9515  * @constructor
9516  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9517  * {@link #create}. The parameters are the same.
9518  * @param {Array} data An associative Array of data values keyed by the field name.
9519  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9520  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9521  * not specified an integer id is generated.
9522  */
9523 Roo.data.Record = function(data, id){
9524     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9525     this.data = data;
9526 };
9527
9528 /**
9529  * Generate a constructor for a specific record layout.
9530  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9531  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9532  * Each field definition object may contain the following properties: <ul>
9533  * <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,
9534  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9535  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9536  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9537  * is being used, then this is a string containing the javascript expression to reference the data relative to 
9538  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9539  * to the data item relative to the record element. If the mapping expression is the same as the field name,
9540  * this may be omitted.</p></li>
9541  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9542  * <ul><li>auto (Default, implies no conversion)</li>
9543  * <li>string</li>
9544  * <li>int</li>
9545  * <li>float</li>
9546  * <li>boolean</li>
9547  * <li>date</li></ul></p></li>
9548  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9549  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9550  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9551  * by the Reader into an object that will be stored in the Record. It is passed the
9552  * following parameters:<ul>
9553  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9554  * </ul></p></li>
9555  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9556  * </ul>
9557  * <br>usage:<br><pre><code>
9558 var TopicRecord = Roo.data.Record.create(
9559     {name: 'title', mapping: 'topic_title'},
9560     {name: 'author', mapping: 'username'},
9561     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9562     {name: 'lastPost', mapping: 'post_time', type: 'date'},
9563     {name: 'lastPoster', mapping: 'user2'},
9564     {name: 'excerpt', mapping: 'post_text'}
9565 );
9566
9567 var myNewRecord = new TopicRecord({
9568     title: 'Do my job please',
9569     author: 'noobie',
9570     totalPosts: 1,
9571     lastPost: new Date(),
9572     lastPoster: 'Animal',
9573     excerpt: 'No way dude!'
9574 });
9575 myStore.add(myNewRecord);
9576 </code></pre>
9577  * @method create
9578  * @static
9579  */
9580 Roo.data.Record.create = function(o){
9581     var f = function(){
9582         f.superclass.constructor.apply(this, arguments);
9583     };
9584     Roo.extend(f, Roo.data.Record);
9585     var p = f.prototype;
9586     p.fields = new Roo.util.MixedCollection(false, function(field){
9587         return field.name;
9588     });
9589     for(var i = 0, len = o.length; i < len; i++){
9590         p.fields.add(new Roo.data.Field(o[i]));
9591     }
9592     f.getField = function(name){
9593         return p.fields.get(name);  
9594     };
9595     return f;
9596 };
9597
9598 Roo.data.Record.AUTO_ID = 1000;
9599 Roo.data.Record.EDIT = 'edit';
9600 Roo.data.Record.REJECT = 'reject';
9601 Roo.data.Record.COMMIT = 'commit';
9602
9603 Roo.data.Record.prototype = {
9604     /**
9605      * Readonly flag - true if this record has been modified.
9606      * @type Boolean
9607      */
9608     dirty : false,
9609     editing : false,
9610     error: null,
9611     modified: null,
9612
9613     // private
9614     join : function(store){
9615         this.store = store;
9616     },
9617
9618     /**
9619      * Set the named field to the specified value.
9620      * @param {String} name The name of the field to set.
9621      * @param {Object} value The value to set the field to.
9622      */
9623     set : function(name, value){
9624         if(this.data[name] == value){
9625             return;
9626         }
9627         this.dirty = true;
9628         if(!this.modified){
9629             this.modified = {};
9630         }
9631         if(typeof this.modified[name] == 'undefined'){
9632             this.modified[name] = this.data[name];
9633         }
9634         this.data[name] = value;
9635         if(!this.editing && this.store){
9636             this.store.afterEdit(this);
9637         }       
9638     },
9639
9640     /**
9641      * Get the value of the named field.
9642      * @param {String} name The name of the field to get the value of.
9643      * @return {Object} The value of the field.
9644      */
9645     get : function(name){
9646         return this.data[name]; 
9647     },
9648
9649     // private
9650     beginEdit : function(){
9651         this.editing = true;
9652         this.modified = {}; 
9653     },
9654
9655     // private
9656     cancelEdit : function(){
9657         this.editing = false;
9658         delete this.modified;
9659     },
9660
9661     // private
9662     endEdit : function(){
9663         this.editing = false;
9664         if(this.dirty && this.store){
9665             this.store.afterEdit(this);
9666         }
9667     },
9668
9669     /**
9670      * Usually called by the {@link Roo.data.Store} which owns the Record.
9671      * Rejects all changes made to the Record since either creation, or the last commit operation.
9672      * Modified fields are reverted to their original values.
9673      * <p>
9674      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9675      * of reject operations.
9676      */
9677     reject : function(){
9678         var m = this.modified;
9679         for(var n in m){
9680             if(typeof m[n] != "function"){
9681                 this.data[n] = m[n];
9682             }
9683         }
9684         this.dirty = false;
9685         delete this.modified;
9686         this.editing = false;
9687         if(this.store){
9688             this.store.afterReject(this);
9689         }
9690     },
9691
9692     /**
9693      * Usually called by the {@link Roo.data.Store} which owns the Record.
9694      * Commits all changes made to the Record since either creation, or the last commit operation.
9695      * <p>
9696      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9697      * of commit operations.
9698      */
9699     commit : function(){
9700         this.dirty = false;
9701         delete this.modified;
9702         this.editing = false;
9703         if(this.store){
9704             this.store.afterCommit(this);
9705         }
9706     },
9707
9708     // private
9709     hasError : function(){
9710         return this.error != null;
9711     },
9712
9713     // private
9714     clearError : function(){
9715         this.error = null;
9716     },
9717
9718     /**
9719      * Creates a copy of this record.
9720      * @param {String} id (optional) A new record id if you don't want to use this record's id
9721      * @return {Record}
9722      */
9723     copy : function(newId) {
9724         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9725     }
9726 };/*
9727  * Based on:
9728  * Ext JS Library 1.1.1
9729  * Copyright(c) 2006-2007, Ext JS, LLC.
9730  *
9731  * Originally Released Under LGPL - original licence link has changed is not relivant.
9732  *
9733  * Fork - LGPL
9734  * <script type="text/javascript">
9735  */
9736
9737
9738
9739 /**
9740  * @class Roo.data.Store
9741  * @extends Roo.util.Observable
9742  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9743  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9744  * <p>
9745  * 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
9746  * has no knowledge of the format of the data returned by the Proxy.<br>
9747  * <p>
9748  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9749  * instances from the data object. These records are cached and made available through accessor functions.
9750  * @constructor
9751  * Creates a new Store.
9752  * @param {Object} config A config object containing the objects needed for the Store to access data,
9753  * and read the data into Records.
9754  */
9755 Roo.data.Store = function(config){
9756     this.data = new Roo.util.MixedCollection(false);
9757     this.data.getKey = function(o){
9758         return o.id;
9759     };
9760     this.baseParams = {};
9761     // private
9762     this.paramNames = {
9763         "start" : "start",
9764         "limit" : "limit",
9765         "sort" : "sort",
9766         "dir" : "dir",
9767         "multisort" : "_multisort"
9768     };
9769
9770     if(config && config.data){
9771         this.inlineData = config.data;
9772         delete config.data;
9773     }
9774
9775     Roo.apply(this, config);
9776     
9777     if(this.reader){ // reader passed
9778         this.reader = Roo.factory(this.reader, Roo.data);
9779         this.reader.xmodule = this.xmodule || false;
9780         if(!this.recordType){
9781             this.recordType = this.reader.recordType;
9782         }
9783         if(this.reader.onMetaChange){
9784             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9785         }
9786     }
9787
9788     if(this.recordType){
9789         this.fields = this.recordType.prototype.fields;
9790     }
9791     this.modified = [];
9792
9793     this.addEvents({
9794         /**
9795          * @event datachanged
9796          * Fires when the data cache has changed, and a widget which is using this Store
9797          * as a Record cache should refresh its view.
9798          * @param {Store} this
9799          */
9800         datachanged : true,
9801         /**
9802          * @event metachange
9803          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9804          * @param {Store} this
9805          * @param {Object} meta The JSON metadata
9806          */
9807         metachange : true,
9808         /**
9809          * @event add
9810          * Fires when Records have been added to the Store
9811          * @param {Store} this
9812          * @param {Roo.data.Record[]} records The array of Records added
9813          * @param {Number} index The index at which the record(s) were added
9814          */
9815         add : true,
9816         /**
9817          * @event remove
9818          * Fires when a Record has been removed from the Store
9819          * @param {Store} this
9820          * @param {Roo.data.Record} record The Record that was removed
9821          * @param {Number} index The index at which the record was removed
9822          */
9823         remove : true,
9824         /**
9825          * @event update
9826          * Fires when a Record has been updated
9827          * @param {Store} this
9828          * @param {Roo.data.Record} record The Record that was updated
9829          * @param {String} operation The update operation being performed.  Value may be one of:
9830          * <pre><code>
9831  Roo.data.Record.EDIT
9832  Roo.data.Record.REJECT
9833  Roo.data.Record.COMMIT
9834          * </code></pre>
9835          */
9836         update : true,
9837         /**
9838          * @event clear
9839          * Fires when the data cache has been cleared.
9840          * @param {Store} this
9841          */
9842         clear : true,
9843         /**
9844          * @event beforeload
9845          * Fires before a request is made for a new data object.  If the beforeload handler returns false
9846          * the load action will be canceled.
9847          * @param {Store} this
9848          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9849          */
9850         beforeload : true,
9851         /**
9852          * @event beforeloadadd
9853          * Fires after a new set of Records has been loaded.
9854          * @param {Store} this
9855          * @param {Roo.data.Record[]} records The Records that were loaded
9856          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9857          */
9858         beforeloadadd : true,
9859         /**
9860          * @event load
9861          * Fires after a new set of Records has been loaded, before they are added to the store.
9862          * @param {Store} this
9863          * @param {Roo.data.Record[]} records The Records that were loaded
9864          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9865          * @params {Object} return from reader
9866          */
9867         load : true,
9868         /**
9869          * @event loadexception
9870          * Fires if an exception occurs in the Proxy during loading.
9871          * Called with the signature of the Proxy's "loadexception" event.
9872          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9873          * 
9874          * @param {Proxy} 
9875          * @param {Object} return from JsonData.reader() - success, totalRecords, records
9876          * @param {Object} load options 
9877          * @param {Object} jsonData from your request (normally this contains the Exception)
9878          */
9879         loadexception : true
9880     });
9881     
9882     if(this.proxy){
9883         this.proxy = Roo.factory(this.proxy, Roo.data);
9884         this.proxy.xmodule = this.xmodule || false;
9885         this.relayEvents(this.proxy,  ["loadexception"]);
9886     }
9887     this.sortToggle = {};
9888     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9889
9890     Roo.data.Store.superclass.constructor.call(this);
9891
9892     if(this.inlineData){
9893         this.loadData(this.inlineData);
9894         delete this.inlineData;
9895     }
9896 };
9897
9898 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9899      /**
9900     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
9901     * without a remote query - used by combo/forms at present.
9902     */
9903     
9904     /**
9905     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9906     */
9907     /**
9908     * @cfg {Array} data Inline data to be loaded when the store is initialized.
9909     */
9910     /**
9911     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9912     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9913     */
9914     /**
9915     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9916     * on any HTTP request
9917     */
9918     /**
9919     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9920     */
9921     /**
9922     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9923     */
9924     multiSort: false,
9925     /**
9926     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9927     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9928     */
9929     remoteSort : false,
9930
9931     /**
9932     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9933      * loaded or when a record is removed. (defaults to false).
9934     */
9935     pruneModifiedRecords : false,
9936
9937     // private
9938     lastOptions : null,
9939
9940     /**
9941      * Add Records to the Store and fires the add event.
9942      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9943      */
9944     add : function(records){
9945         records = [].concat(records);
9946         for(var i = 0, len = records.length; i < len; i++){
9947             records[i].join(this);
9948         }
9949         var index = this.data.length;
9950         this.data.addAll(records);
9951         this.fireEvent("add", this, records, index);
9952     },
9953
9954     /**
9955      * Remove a Record from the Store and fires the remove event.
9956      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9957      */
9958     remove : function(record){
9959         var index = this.data.indexOf(record);
9960         this.data.removeAt(index);
9961         if(this.pruneModifiedRecords){
9962             this.modified.remove(record);
9963         }
9964         this.fireEvent("remove", this, record, index);
9965     },
9966
9967     /**
9968      * Remove all Records from the Store and fires the clear event.
9969      */
9970     removeAll : function(){
9971         this.data.clear();
9972         if(this.pruneModifiedRecords){
9973             this.modified = [];
9974         }
9975         this.fireEvent("clear", this);
9976     },
9977
9978     /**
9979      * Inserts Records to the Store at the given index and fires the add event.
9980      * @param {Number} index The start index at which to insert the passed Records.
9981      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9982      */
9983     insert : function(index, records){
9984         records = [].concat(records);
9985         for(var i = 0, len = records.length; i < len; i++){
9986             this.data.insert(index, records[i]);
9987             records[i].join(this);
9988         }
9989         this.fireEvent("add", this, records, index);
9990     },
9991
9992     /**
9993      * Get the index within the cache of the passed Record.
9994      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9995      * @return {Number} The index of the passed Record. Returns -1 if not found.
9996      */
9997     indexOf : function(record){
9998         return this.data.indexOf(record);
9999     },
10000
10001     /**
10002      * Get the index within the cache of the Record with the passed id.
10003      * @param {String} id The id of the Record to find.
10004      * @return {Number} The index of the Record. Returns -1 if not found.
10005      */
10006     indexOfId : function(id){
10007         return this.data.indexOfKey(id);
10008     },
10009
10010     /**
10011      * Get the Record with the specified id.
10012      * @param {String} id The id of the Record to find.
10013      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10014      */
10015     getById : function(id){
10016         return this.data.key(id);
10017     },
10018
10019     /**
10020      * Get the Record at the specified index.
10021      * @param {Number} index The index of the Record to find.
10022      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10023      */
10024     getAt : function(index){
10025         return this.data.itemAt(index);
10026     },
10027
10028     /**
10029      * Returns a range of Records between specified indices.
10030      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10031      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10032      * @return {Roo.data.Record[]} An array of Records
10033      */
10034     getRange : function(start, end){
10035         return this.data.getRange(start, end);
10036     },
10037
10038     // private
10039     storeOptions : function(o){
10040         o = Roo.apply({}, o);
10041         delete o.callback;
10042         delete o.scope;
10043         this.lastOptions = o;
10044     },
10045
10046     /**
10047      * Loads the Record cache from the configured Proxy using the configured Reader.
10048      * <p>
10049      * If using remote paging, then the first load call must specify the <em>start</em>
10050      * and <em>limit</em> properties in the options.params property to establish the initial
10051      * position within the dataset, and the number of Records to cache on each read from the Proxy.
10052      * <p>
10053      * <strong>It is important to note that for remote data sources, loading is asynchronous,
10054      * and this call will return before the new data has been loaded. Perform any post-processing
10055      * in a callback function, or in a "load" event handler.</strong>
10056      * <p>
10057      * @param {Object} options An object containing properties which control loading options:<ul>
10058      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10059      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10060      * passed the following arguments:<ul>
10061      * <li>r : Roo.data.Record[]</li>
10062      * <li>options: Options object from the load call</li>
10063      * <li>success: Boolean success indicator</li></ul></li>
10064      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10065      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10066      * </ul>
10067      */
10068     load : function(options){
10069         options = options || {};
10070         if(this.fireEvent("beforeload", this, options) !== false){
10071             this.storeOptions(options);
10072             var p = Roo.apply(options.params || {}, this.baseParams);
10073             // if meta was not loaded from remote source.. try requesting it.
10074             if (!this.reader.metaFromRemote) {
10075                 p._requestMeta = 1;
10076             }
10077             if(this.sortInfo && this.remoteSort){
10078                 var pn = this.paramNames;
10079                 p[pn["sort"]] = this.sortInfo.field;
10080                 p[pn["dir"]] = this.sortInfo.direction;
10081             }
10082             if (this.multiSort) {
10083                 var pn = this.paramNames;
10084                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10085             }
10086             
10087             this.proxy.load(p, this.reader, this.loadRecords, this, options);
10088         }
10089     },
10090
10091     /**
10092      * Reloads the Record cache from the configured Proxy using the configured Reader and
10093      * the options from the last load operation performed.
10094      * @param {Object} options (optional) An object containing properties which may override the options
10095      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10096      * the most recently used options are reused).
10097      */
10098     reload : function(options){
10099         this.load(Roo.applyIf(options||{}, this.lastOptions));
10100     },
10101
10102     // private
10103     // Called as a callback by the Reader during a load operation.
10104     loadRecords : function(o, options, success){
10105         if(!o || success === false){
10106             if(success !== false){
10107                 this.fireEvent("load", this, [], options, o);
10108             }
10109             if(options.callback){
10110                 options.callback.call(options.scope || this, [], options, false);
10111             }
10112             return;
10113         }
10114         // if data returned failure - throw an exception.
10115         if (o.success === false) {
10116             // show a message if no listener is registered.
10117             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10118                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10119             }
10120             // loadmask wil be hooked into this..
10121             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10122             return;
10123         }
10124         var r = o.records, t = o.totalRecords || r.length;
10125         
10126         this.fireEvent("beforeloadadd", this, r, options, o);
10127         
10128         if(!options || options.add !== true){
10129             if(this.pruneModifiedRecords){
10130                 this.modified = [];
10131             }
10132             for(var i = 0, len = r.length; i < len; i++){
10133                 r[i].join(this);
10134             }
10135             if(this.snapshot){
10136                 this.data = this.snapshot;
10137                 delete this.snapshot;
10138             }
10139             this.data.clear();
10140             this.data.addAll(r);
10141             this.totalLength = t;
10142             this.applySort();
10143             this.fireEvent("datachanged", this);
10144         }else{
10145             this.totalLength = Math.max(t, this.data.length+r.length);
10146             this.add(r);
10147         }
10148         this.fireEvent("load", this, r, options, o);
10149         if(options.callback){
10150             options.callback.call(options.scope || this, r, options, true);
10151         }
10152     },
10153
10154
10155     /**
10156      * Loads data from a passed data block. A Reader which understands the format of the data
10157      * must have been configured in the constructor.
10158      * @param {Object} data The data block from which to read the Records.  The format of the data expected
10159      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10160      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10161      */
10162     loadData : function(o, append){
10163         var r = this.reader.readRecords(o);
10164         this.loadRecords(r, {add: append}, true);
10165     },
10166
10167     /**
10168      * Gets the number of cached records.
10169      * <p>
10170      * <em>If using paging, this may not be the total size of the dataset. If the data object
10171      * used by the Reader contains the dataset size, then the getTotalCount() function returns
10172      * the data set size</em>
10173      */
10174     getCount : function(){
10175         return this.data.length || 0;
10176     },
10177
10178     /**
10179      * Gets the total number of records in the dataset as returned by the server.
10180      * <p>
10181      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10182      * the dataset size</em>
10183      */
10184     getTotalCount : function(){
10185         return this.totalLength || 0;
10186     },
10187
10188     /**
10189      * Returns the sort state of the Store as an object with two properties:
10190      * <pre><code>
10191  field {String} The name of the field by which the Records are sorted
10192  direction {String} The sort order, "ASC" or "DESC"
10193      * </code></pre>
10194      */
10195     getSortState : function(){
10196         return this.sortInfo;
10197     },
10198
10199     // private
10200     applySort : function(){
10201         if(this.sortInfo && !this.remoteSort){
10202             var s = this.sortInfo, f = s.field;
10203             var st = this.fields.get(f).sortType;
10204             var fn = function(r1, r2){
10205                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10206                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10207             };
10208             this.data.sort(s.direction, fn);
10209             if(this.snapshot && this.snapshot != this.data){
10210                 this.snapshot.sort(s.direction, fn);
10211             }
10212         }
10213     },
10214
10215     /**
10216      * Sets the default sort column and order to be used by the next load operation.
10217      * @param {String} fieldName The name of the field to sort by.
10218      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10219      */
10220     setDefaultSort : function(field, dir){
10221         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10222     },
10223
10224     /**
10225      * Sort the Records.
10226      * If remote sorting is used, the sort is performed on the server, and the cache is
10227      * reloaded. If local sorting is used, the cache is sorted internally.
10228      * @param {String} fieldName The name of the field to sort by.
10229      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10230      */
10231     sort : function(fieldName, dir){
10232         var f = this.fields.get(fieldName);
10233         if(!dir){
10234             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10235             
10236             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10237                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10238             }else{
10239                 dir = f.sortDir;
10240             }
10241         }
10242         this.sortToggle[f.name] = dir;
10243         this.sortInfo = {field: f.name, direction: dir};
10244         if(!this.remoteSort){
10245             this.applySort();
10246             this.fireEvent("datachanged", this);
10247         }else{
10248             this.load(this.lastOptions);
10249         }
10250     },
10251
10252     /**
10253      * Calls the specified function for each of the Records in the cache.
10254      * @param {Function} fn The function to call. The Record is passed as the first parameter.
10255      * Returning <em>false</em> aborts and exits the iteration.
10256      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10257      */
10258     each : function(fn, scope){
10259         this.data.each(fn, scope);
10260     },
10261
10262     /**
10263      * Gets all records modified since the last commit.  Modified records are persisted across load operations
10264      * (e.g., during paging).
10265      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10266      */
10267     getModifiedRecords : function(){
10268         return this.modified;
10269     },
10270
10271     // private
10272     createFilterFn : function(property, value, anyMatch){
10273         if(!value.exec){ // not a regex
10274             value = String(value);
10275             if(value.length == 0){
10276                 return false;
10277             }
10278             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10279         }
10280         return function(r){
10281             return value.test(r.data[property]);
10282         };
10283     },
10284
10285     /**
10286      * Sums the value of <i>property</i> for each record between start and end and returns the result.
10287      * @param {String} property A field on your records
10288      * @param {Number} start The record index to start at (defaults to 0)
10289      * @param {Number} end The last record index to include (defaults to length - 1)
10290      * @return {Number} The sum
10291      */
10292     sum : function(property, start, end){
10293         var rs = this.data.items, v = 0;
10294         start = start || 0;
10295         end = (end || end === 0) ? end : rs.length-1;
10296
10297         for(var i = start; i <= end; i++){
10298             v += (rs[i].data[property] || 0);
10299         }
10300         return v;
10301     },
10302
10303     /**
10304      * Filter the records by a specified property.
10305      * @param {String} field A field on your records
10306      * @param {String/RegExp} value Either a string that the field
10307      * should start with or a RegExp to test against the field
10308      * @param {Boolean} anyMatch True to match any part not just the beginning
10309      */
10310     filter : function(property, value, anyMatch){
10311         var fn = this.createFilterFn(property, value, anyMatch);
10312         return fn ? this.filterBy(fn) : this.clearFilter();
10313     },
10314
10315     /**
10316      * Filter by a function. The specified function will be called with each
10317      * record in this data source. If the function returns true the record is included,
10318      * otherwise it is filtered.
10319      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10320      * @param {Object} scope (optional) The scope of the function (defaults to this)
10321      */
10322     filterBy : function(fn, scope){
10323         this.snapshot = this.snapshot || this.data;
10324         this.data = this.queryBy(fn, scope||this);
10325         this.fireEvent("datachanged", this);
10326     },
10327
10328     /**
10329      * Query the records by a specified property.
10330      * @param {String} field A field on your records
10331      * @param {String/RegExp} value Either a string that the field
10332      * should start with or a RegExp to test against the field
10333      * @param {Boolean} anyMatch True to match any part not just the beginning
10334      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10335      */
10336     query : function(property, value, anyMatch){
10337         var fn = this.createFilterFn(property, value, anyMatch);
10338         return fn ? this.queryBy(fn) : this.data.clone();
10339     },
10340
10341     /**
10342      * Query by a function. The specified function will be called with each
10343      * record in this data source. If the function returns true the record is included
10344      * in the results.
10345      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10346      * @param {Object} scope (optional) The scope of the function (defaults to this)
10347       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10348      **/
10349     queryBy : function(fn, scope){
10350         var data = this.snapshot || this.data;
10351         return data.filterBy(fn, scope||this);
10352     },
10353
10354     /**
10355      * Collects unique values for a particular dataIndex from this store.
10356      * @param {String} dataIndex The property to collect
10357      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10358      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10359      * @return {Array} An array of the unique values
10360      **/
10361     collect : function(dataIndex, allowNull, bypassFilter){
10362         var d = (bypassFilter === true && this.snapshot) ?
10363                 this.snapshot.items : this.data.items;
10364         var v, sv, r = [], l = {};
10365         for(var i = 0, len = d.length; i < len; i++){
10366             v = d[i].data[dataIndex];
10367             sv = String(v);
10368             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10369                 l[sv] = true;
10370                 r[r.length] = v;
10371             }
10372         }
10373         return r;
10374     },
10375
10376     /**
10377      * Revert to a view of the Record cache with no filtering applied.
10378      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10379      */
10380     clearFilter : function(suppressEvent){
10381         if(this.snapshot && this.snapshot != this.data){
10382             this.data = this.snapshot;
10383             delete this.snapshot;
10384             if(suppressEvent !== true){
10385                 this.fireEvent("datachanged", this);
10386             }
10387         }
10388     },
10389
10390     // private
10391     afterEdit : function(record){
10392         if(this.modified.indexOf(record) == -1){
10393             this.modified.push(record);
10394         }
10395         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10396     },
10397     
10398     // private
10399     afterReject : function(record){
10400         this.modified.remove(record);
10401         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10402     },
10403
10404     // private
10405     afterCommit : function(record){
10406         this.modified.remove(record);
10407         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10408     },
10409
10410     /**
10411      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10412      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10413      */
10414     commitChanges : function(){
10415         var m = this.modified.slice(0);
10416         this.modified = [];
10417         for(var i = 0, len = m.length; i < len; i++){
10418             m[i].commit();
10419         }
10420     },
10421
10422     /**
10423      * Cancel outstanding changes on all changed records.
10424      */
10425     rejectChanges : function(){
10426         var m = this.modified.slice(0);
10427         this.modified = [];
10428         for(var i = 0, len = m.length; i < len; i++){
10429             m[i].reject();
10430         }
10431     },
10432
10433     onMetaChange : function(meta, rtype, o){
10434         this.recordType = rtype;
10435         this.fields = rtype.prototype.fields;
10436         delete this.snapshot;
10437         this.sortInfo = meta.sortInfo || this.sortInfo;
10438         this.modified = [];
10439         this.fireEvent('metachange', this, this.reader.meta);
10440     },
10441     
10442     moveIndex : function(data, type)
10443     {
10444         var index = this.indexOf(data);
10445         
10446         var newIndex = index + type;
10447         
10448         this.remove(data);
10449         
10450         this.insert(newIndex, data);
10451         
10452     }
10453 });/*
10454  * Based on:
10455  * Ext JS Library 1.1.1
10456  * Copyright(c) 2006-2007, Ext JS, LLC.
10457  *
10458  * Originally Released Under LGPL - original licence link has changed is not relivant.
10459  *
10460  * Fork - LGPL
10461  * <script type="text/javascript">
10462  */
10463
10464 /**
10465  * @class Roo.data.SimpleStore
10466  * @extends Roo.data.Store
10467  * Small helper class to make creating Stores from Array data easier.
10468  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10469  * @cfg {Array} fields An array of field definition objects, or field name strings.
10470  * @cfg {Array} data The multi-dimensional array of data
10471  * @constructor
10472  * @param {Object} config
10473  */
10474 Roo.data.SimpleStore = function(config){
10475     Roo.data.SimpleStore.superclass.constructor.call(this, {
10476         isLocal : true,
10477         reader: new Roo.data.ArrayReader({
10478                 id: config.id
10479             },
10480             Roo.data.Record.create(config.fields)
10481         ),
10482         proxy : new Roo.data.MemoryProxy(config.data)
10483     });
10484     this.load();
10485 };
10486 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10487  * Based on:
10488  * Ext JS Library 1.1.1
10489  * Copyright(c) 2006-2007, Ext JS, LLC.
10490  *
10491  * Originally Released Under LGPL - original licence link has changed is not relivant.
10492  *
10493  * Fork - LGPL
10494  * <script type="text/javascript">
10495  */
10496
10497 /**
10498 /**
10499  * @extends Roo.data.Store
10500  * @class Roo.data.JsonStore
10501  * Small helper class to make creating Stores for JSON data easier. <br/>
10502 <pre><code>
10503 var store = new Roo.data.JsonStore({
10504     url: 'get-images.php',
10505     root: 'images',
10506     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10507 });
10508 </code></pre>
10509  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10510  * JsonReader and HttpProxy (unless inline data is provided).</b>
10511  * @cfg {Array} fields An array of field definition objects, or field name strings.
10512  * @constructor
10513  * @param {Object} config
10514  */
10515 Roo.data.JsonStore = function(c){
10516     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10517         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10518         reader: new Roo.data.JsonReader(c, c.fields)
10519     }));
10520 };
10521 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10522  * Based on:
10523  * Ext JS Library 1.1.1
10524  * Copyright(c) 2006-2007, Ext JS, LLC.
10525  *
10526  * Originally Released Under LGPL - original licence link has changed is not relivant.
10527  *
10528  * Fork - LGPL
10529  * <script type="text/javascript">
10530  */
10531
10532  
10533 Roo.data.Field = function(config){
10534     if(typeof config == "string"){
10535         config = {name: config};
10536     }
10537     Roo.apply(this, config);
10538     
10539     if(!this.type){
10540         this.type = "auto";
10541     }
10542     
10543     var st = Roo.data.SortTypes;
10544     // named sortTypes are supported, here we look them up
10545     if(typeof this.sortType == "string"){
10546         this.sortType = st[this.sortType];
10547     }
10548     
10549     // set default sortType for strings and dates
10550     if(!this.sortType){
10551         switch(this.type){
10552             case "string":
10553                 this.sortType = st.asUCString;
10554                 break;
10555             case "date":
10556                 this.sortType = st.asDate;
10557                 break;
10558             default:
10559                 this.sortType = st.none;
10560         }
10561     }
10562
10563     // define once
10564     var stripRe = /[\$,%]/g;
10565
10566     // prebuilt conversion function for this field, instead of
10567     // switching every time we're reading a value
10568     if(!this.convert){
10569         var cv, dateFormat = this.dateFormat;
10570         switch(this.type){
10571             case "":
10572             case "auto":
10573             case undefined:
10574                 cv = function(v){ return v; };
10575                 break;
10576             case "string":
10577                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10578                 break;
10579             case "int":
10580                 cv = function(v){
10581                     return v !== undefined && v !== null && v !== '' ?
10582                            parseInt(String(v).replace(stripRe, ""), 10) : '';
10583                     };
10584                 break;
10585             case "float":
10586                 cv = function(v){
10587                     return v !== undefined && v !== null && v !== '' ?
10588                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
10589                     };
10590                 break;
10591             case "bool":
10592             case "boolean":
10593                 cv = function(v){ return v === true || v === "true" || v == 1; };
10594                 break;
10595             case "date":
10596                 cv = function(v){
10597                     if(!v){
10598                         return '';
10599                     }
10600                     if(v instanceof Date){
10601                         return v;
10602                     }
10603                     if(dateFormat){
10604                         if(dateFormat == "timestamp"){
10605                             return new Date(v*1000);
10606                         }
10607                         return Date.parseDate(v, dateFormat);
10608                     }
10609                     var parsed = Date.parse(v);
10610                     return parsed ? new Date(parsed) : null;
10611                 };
10612              break;
10613             
10614         }
10615         this.convert = cv;
10616     }
10617 };
10618
10619 Roo.data.Field.prototype = {
10620     dateFormat: null,
10621     defaultValue: "",
10622     mapping: null,
10623     sortType : null,
10624     sortDir : "ASC"
10625 };/*
10626  * Based on:
10627  * Ext JS Library 1.1.1
10628  * Copyright(c) 2006-2007, Ext JS, LLC.
10629  *
10630  * Originally Released Under LGPL - original licence link has changed is not relivant.
10631  *
10632  * Fork - LGPL
10633  * <script type="text/javascript">
10634  */
10635  
10636 // Base class for reading structured data from a data source.  This class is intended to be
10637 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10638
10639 /**
10640  * @class Roo.data.DataReader
10641  * Base class for reading structured data from a data source.  This class is intended to be
10642  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10643  */
10644
10645 Roo.data.DataReader = function(meta, recordType){
10646     
10647     this.meta = meta;
10648     
10649     this.recordType = recordType instanceof Array ? 
10650         Roo.data.Record.create(recordType) : recordType;
10651 };
10652
10653 Roo.data.DataReader.prototype = {
10654      /**
10655      * Create an empty record
10656      * @param {Object} data (optional) - overlay some values
10657      * @return {Roo.data.Record} record created.
10658      */
10659     newRow :  function(d) {
10660         var da =  {};
10661         this.recordType.prototype.fields.each(function(c) {
10662             switch( c.type) {
10663                 case 'int' : da[c.name] = 0; break;
10664                 case 'date' : da[c.name] = new Date(); break;
10665                 case 'float' : da[c.name] = 0.0; break;
10666                 case 'boolean' : da[c.name] = false; break;
10667                 default : da[c.name] = ""; break;
10668             }
10669             
10670         });
10671         return new this.recordType(Roo.apply(da, d));
10672     }
10673     
10674 };/*
10675  * Based on:
10676  * Ext JS Library 1.1.1
10677  * Copyright(c) 2006-2007, Ext JS, LLC.
10678  *
10679  * Originally Released Under LGPL - original licence link has changed is not relivant.
10680  *
10681  * Fork - LGPL
10682  * <script type="text/javascript">
10683  */
10684
10685 /**
10686  * @class Roo.data.DataProxy
10687  * @extends Roo.data.Observable
10688  * This class is an abstract base class for implementations which provide retrieval of
10689  * unformatted data objects.<br>
10690  * <p>
10691  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10692  * (of the appropriate type which knows how to parse the data object) to provide a block of
10693  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10694  * <p>
10695  * Custom implementations must implement the load method as described in
10696  * {@link Roo.data.HttpProxy#load}.
10697  */
10698 Roo.data.DataProxy = function(){
10699     this.addEvents({
10700         /**
10701          * @event beforeload
10702          * Fires before a network request is made to retrieve a data object.
10703          * @param {Object} This DataProxy object.
10704          * @param {Object} params The params parameter to the load function.
10705          */
10706         beforeload : true,
10707         /**
10708          * @event load
10709          * Fires before the load method's callback is called.
10710          * @param {Object} This DataProxy object.
10711          * @param {Object} o The data object.
10712          * @param {Object} arg The callback argument object passed to the load function.
10713          */
10714         load : true,
10715         /**
10716          * @event loadexception
10717          * Fires if an Exception occurs during data retrieval.
10718          * @param {Object} This DataProxy object.
10719          * @param {Object} o The data object.
10720          * @param {Object} arg The callback argument object passed to the load function.
10721          * @param {Object} e The Exception.
10722          */
10723         loadexception : true
10724     });
10725     Roo.data.DataProxy.superclass.constructor.call(this);
10726 };
10727
10728 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10729
10730     /**
10731      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10732      */
10733 /*
10734  * Based on:
10735  * Ext JS Library 1.1.1
10736  * Copyright(c) 2006-2007, Ext JS, LLC.
10737  *
10738  * Originally Released Under LGPL - original licence link has changed is not relivant.
10739  *
10740  * Fork - LGPL
10741  * <script type="text/javascript">
10742  */
10743 /**
10744  * @class Roo.data.MemoryProxy
10745  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10746  * to the Reader when its load method is called.
10747  * @constructor
10748  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10749  */
10750 Roo.data.MemoryProxy = function(data){
10751     if (data.data) {
10752         data = data.data;
10753     }
10754     Roo.data.MemoryProxy.superclass.constructor.call(this);
10755     this.data = data;
10756 };
10757
10758 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10759     /**
10760      * Load data from the requested source (in this case an in-memory
10761      * data object passed to the constructor), read the data object into
10762      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10763      * process that block using the passed callback.
10764      * @param {Object} params This parameter is not used by the MemoryProxy class.
10765      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10766      * object into a block of Roo.data.Records.
10767      * @param {Function} callback The function into which to pass the block of Roo.data.records.
10768      * The function must be passed <ul>
10769      * <li>The Record block object</li>
10770      * <li>The "arg" argument from the load function</li>
10771      * <li>A boolean success indicator</li>
10772      * </ul>
10773      * @param {Object} scope The scope in which to call the callback
10774      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10775      */
10776     load : function(params, reader, callback, scope, arg){
10777         params = params || {};
10778         var result;
10779         try {
10780             result = reader.readRecords(this.data);
10781         }catch(e){
10782             this.fireEvent("loadexception", this, arg, null, e);
10783             callback.call(scope, null, arg, false);
10784             return;
10785         }
10786         callback.call(scope, result, arg, true);
10787     },
10788     
10789     // private
10790     update : function(params, records){
10791         
10792     }
10793 });/*
10794  * Based on:
10795  * Ext JS Library 1.1.1
10796  * Copyright(c) 2006-2007, Ext JS, LLC.
10797  *
10798  * Originally Released Under LGPL - original licence link has changed is not relivant.
10799  *
10800  * Fork - LGPL
10801  * <script type="text/javascript">
10802  */
10803 /**
10804  * @class Roo.data.HttpProxy
10805  * @extends Roo.data.DataProxy
10806  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10807  * configured to reference a certain URL.<br><br>
10808  * <p>
10809  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10810  * from which the running page was served.<br><br>
10811  * <p>
10812  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10813  * <p>
10814  * Be aware that to enable the browser to parse an XML document, the server must set
10815  * the Content-Type header in the HTTP response to "text/xml".
10816  * @constructor
10817  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10818  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
10819  * will be used to make the request.
10820  */
10821 Roo.data.HttpProxy = function(conn){
10822     Roo.data.HttpProxy.superclass.constructor.call(this);
10823     // is conn a conn config or a real conn?
10824     this.conn = conn;
10825     this.useAjax = !conn || !conn.events;
10826   
10827 };
10828
10829 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10830     // thse are take from connection...
10831     
10832     /**
10833      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10834      */
10835     /**
10836      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10837      * extra parameters to each request made by this object. (defaults to undefined)
10838      */
10839     /**
10840      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10841      *  to each request made by this object. (defaults to undefined)
10842      */
10843     /**
10844      * @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)
10845      */
10846     /**
10847      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10848      */
10849      /**
10850      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10851      * @type Boolean
10852      */
10853   
10854
10855     /**
10856      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10857      * @type Boolean
10858      */
10859     /**
10860      * Return the {@link Roo.data.Connection} object being used by this Proxy.
10861      * @return {Connection} The Connection object. This object may be used to subscribe to events on
10862      * a finer-grained basis than the DataProxy events.
10863      */
10864     getConnection : function(){
10865         return this.useAjax ? Roo.Ajax : this.conn;
10866     },
10867
10868     /**
10869      * Load data from the configured {@link Roo.data.Connection}, read the data object into
10870      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10871      * process that block using the passed callback.
10872      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10873      * for the request to the remote server.
10874      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10875      * object into a block of Roo.data.Records.
10876      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10877      * The function must be passed <ul>
10878      * <li>The Record block object</li>
10879      * <li>The "arg" argument from the load function</li>
10880      * <li>A boolean success indicator</li>
10881      * </ul>
10882      * @param {Object} scope The scope in which to call the callback
10883      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10884      */
10885     load : function(params, reader, callback, scope, arg){
10886         if(this.fireEvent("beforeload", this, params) !== false){
10887             var  o = {
10888                 params : params || {},
10889                 request: {
10890                     callback : callback,
10891                     scope : scope,
10892                     arg : arg
10893                 },
10894                 reader: reader,
10895                 callback : this.loadResponse,
10896                 scope: this
10897             };
10898             if(this.useAjax){
10899                 Roo.applyIf(o, this.conn);
10900                 if(this.activeRequest){
10901                     Roo.Ajax.abort(this.activeRequest);
10902                 }
10903                 this.activeRequest = Roo.Ajax.request(o);
10904             }else{
10905                 this.conn.request(o);
10906             }
10907         }else{
10908             callback.call(scope||this, null, arg, false);
10909         }
10910     },
10911
10912     // private
10913     loadResponse : function(o, success, response){
10914         delete this.activeRequest;
10915         if(!success){
10916             this.fireEvent("loadexception", this, o, response);
10917             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10918             return;
10919         }
10920         var result;
10921         try {
10922             result = o.reader.read(response);
10923         }catch(e){
10924             this.fireEvent("loadexception", this, o, response, e);
10925             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10926             return;
10927         }
10928         
10929         this.fireEvent("load", this, o, o.request.arg);
10930         o.request.callback.call(o.request.scope, result, o.request.arg, true);
10931     },
10932
10933     // private
10934     update : function(dataSet){
10935
10936     },
10937
10938     // private
10939     updateResponse : function(dataSet){
10940
10941     }
10942 });/*
10943  * Based on:
10944  * Ext JS Library 1.1.1
10945  * Copyright(c) 2006-2007, Ext JS, LLC.
10946  *
10947  * Originally Released Under LGPL - original licence link has changed is not relivant.
10948  *
10949  * Fork - LGPL
10950  * <script type="text/javascript">
10951  */
10952
10953 /**
10954  * @class Roo.data.ScriptTagProxy
10955  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10956  * other than the originating domain of the running page.<br><br>
10957  * <p>
10958  * <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
10959  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10960  * <p>
10961  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10962  * source code that is used as the source inside a &lt;script> tag.<br><br>
10963  * <p>
10964  * In order for the browser to process the returned data, the server must wrap the data object
10965  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10966  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10967  * depending on whether the callback name was passed:
10968  * <p>
10969  * <pre><code>
10970 boolean scriptTag = false;
10971 String cb = request.getParameter("callback");
10972 if (cb != null) {
10973     scriptTag = true;
10974     response.setContentType("text/javascript");
10975 } else {
10976     response.setContentType("application/x-json");
10977 }
10978 Writer out = response.getWriter();
10979 if (scriptTag) {
10980     out.write(cb + "(");
10981 }
10982 out.print(dataBlock.toJsonString());
10983 if (scriptTag) {
10984     out.write(");");
10985 }
10986 </pre></code>
10987  *
10988  * @constructor
10989  * @param {Object} config A configuration object.
10990  */
10991 Roo.data.ScriptTagProxy = function(config){
10992     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10993     Roo.apply(this, config);
10994     this.head = document.getElementsByTagName("head")[0];
10995 };
10996
10997 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10998
10999 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11000     /**
11001      * @cfg {String} url The URL from which to request the data object.
11002      */
11003     /**
11004      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11005      */
11006     timeout : 30000,
11007     /**
11008      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11009      * the server the name of the callback function set up by the load call to process the returned data object.
11010      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11011      * javascript output which calls this named function passing the data object as its only parameter.
11012      */
11013     callbackParam : "callback",
11014     /**
11015      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11016      * name to the request.
11017      */
11018     nocache : true,
11019
11020     /**
11021      * Load data from the configured URL, read the data object into
11022      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11023      * process that block using the passed callback.
11024      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11025      * for the request to the remote server.
11026      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11027      * object into a block of Roo.data.Records.
11028      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11029      * The function must be passed <ul>
11030      * <li>The Record block object</li>
11031      * <li>The "arg" argument from the load function</li>
11032      * <li>A boolean success indicator</li>
11033      * </ul>
11034      * @param {Object} scope The scope in which to call the callback
11035      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11036      */
11037     load : function(params, reader, callback, scope, arg){
11038         if(this.fireEvent("beforeload", this, params) !== false){
11039
11040             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11041
11042             var url = this.url;
11043             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11044             if(this.nocache){
11045                 url += "&_dc=" + (new Date().getTime());
11046             }
11047             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11048             var trans = {
11049                 id : transId,
11050                 cb : "stcCallback"+transId,
11051                 scriptId : "stcScript"+transId,
11052                 params : params,
11053                 arg : arg,
11054                 url : url,
11055                 callback : callback,
11056                 scope : scope,
11057                 reader : reader
11058             };
11059             var conn = this;
11060
11061             window[trans.cb] = function(o){
11062                 conn.handleResponse(o, trans);
11063             };
11064
11065             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11066
11067             if(this.autoAbort !== false){
11068                 this.abort();
11069             }
11070
11071             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11072
11073             var script = document.createElement("script");
11074             script.setAttribute("src", url);
11075             script.setAttribute("type", "text/javascript");
11076             script.setAttribute("id", trans.scriptId);
11077             this.head.appendChild(script);
11078
11079             this.trans = trans;
11080         }else{
11081             callback.call(scope||this, null, arg, false);
11082         }
11083     },
11084
11085     // private
11086     isLoading : function(){
11087         return this.trans ? true : false;
11088     },
11089
11090     /**
11091      * Abort the current server request.
11092      */
11093     abort : function(){
11094         if(this.isLoading()){
11095             this.destroyTrans(this.trans);
11096         }
11097     },
11098
11099     // private
11100     destroyTrans : function(trans, isLoaded){
11101         this.head.removeChild(document.getElementById(trans.scriptId));
11102         clearTimeout(trans.timeoutId);
11103         if(isLoaded){
11104             window[trans.cb] = undefined;
11105             try{
11106                 delete window[trans.cb];
11107             }catch(e){}
11108         }else{
11109             // if hasn't been loaded, wait for load to remove it to prevent script error
11110             window[trans.cb] = function(){
11111                 window[trans.cb] = undefined;
11112                 try{
11113                     delete window[trans.cb];
11114                 }catch(e){}
11115             };
11116         }
11117     },
11118
11119     // private
11120     handleResponse : function(o, trans){
11121         this.trans = false;
11122         this.destroyTrans(trans, true);
11123         var result;
11124         try {
11125             result = trans.reader.readRecords(o);
11126         }catch(e){
11127             this.fireEvent("loadexception", this, o, trans.arg, e);
11128             trans.callback.call(trans.scope||window, null, trans.arg, false);
11129             return;
11130         }
11131         this.fireEvent("load", this, o, trans.arg);
11132         trans.callback.call(trans.scope||window, result, trans.arg, true);
11133     },
11134
11135     // private
11136     handleFailure : function(trans){
11137         this.trans = false;
11138         this.destroyTrans(trans, false);
11139         this.fireEvent("loadexception", this, null, trans.arg);
11140         trans.callback.call(trans.scope||window, null, trans.arg, false);
11141     }
11142 });/*
11143  * Based on:
11144  * Ext JS Library 1.1.1
11145  * Copyright(c) 2006-2007, Ext JS, LLC.
11146  *
11147  * Originally Released Under LGPL - original licence link has changed is not relivant.
11148  *
11149  * Fork - LGPL
11150  * <script type="text/javascript">
11151  */
11152
11153 /**
11154  * @class Roo.data.JsonReader
11155  * @extends Roo.data.DataReader
11156  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11157  * based on mappings in a provided Roo.data.Record constructor.
11158  * 
11159  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11160  * in the reply previously. 
11161  * 
11162  * <p>
11163  * Example code:
11164  * <pre><code>
11165 var RecordDef = Roo.data.Record.create([
11166     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
11167     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
11168 ]);
11169 var myReader = new Roo.data.JsonReader({
11170     totalProperty: "results",    // The property which contains the total dataset size (optional)
11171     root: "rows",                // The property which contains an Array of row objects
11172     id: "id"                     // The property within each row object that provides an ID for the record (optional)
11173 }, RecordDef);
11174 </code></pre>
11175  * <p>
11176  * This would consume a JSON file like this:
11177  * <pre><code>
11178 { 'results': 2, 'rows': [
11179     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11180     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11181 }
11182 </code></pre>
11183  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11184  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11185  * paged from the remote server.
11186  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11187  * @cfg {String} root name of the property which contains the Array of row objects.
11188  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11189  * @cfg {Array} fields Array of field definition objects
11190  * @constructor
11191  * Create a new JsonReader
11192  * @param {Object} meta Metadata configuration options
11193  * @param {Object} recordType Either an Array of field definition objects,
11194  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11195  */
11196 Roo.data.JsonReader = function(meta, recordType){
11197     
11198     meta = meta || {};
11199     // set some defaults:
11200     Roo.applyIf(meta, {
11201         totalProperty: 'total',
11202         successProperty : 'success',
11203         root : 'data',
11204         id : 'id'
11205     });
11206     
11207     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11208 };
11209 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11210     
11211     /**
11212      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
11213      * Used by Store query builder to append _requestMeta to params.
11214      * 
11215      */
11216     metaFromRemote : false,
11217     /**
11218      * This method is only used by a DataProxy which has retrieved data from a remote server.
11219      * @param {Object} response The XHR object which contains the JSON data in its responseText.
11220      * @return {Object} data A data block which is used by an Roo.data.Store object as
11221      * a cache of Roo.data.Records.
11222      */
11223     read : function(response){
11224         var json = response.responseText;
11225        
11226         var o = /* eval:var:o */ eval("("+json+")");
11227         if(!o) {
11228             throw {message: "JsonReader.read: Json object not found"};
11229         }
11230         
11231         if(o.metaData){
11232             
11233             delete this.ef;
11234             this.metaFromRemote = true;
11235             this.meta = o.metaData;
11236             this.recordType = Roo.data.Record.create(o.metaData.fields);
11237             this.onMetaChange(this.meta, this.recordType, o);
11238         }
11239         return this.readRecords(o);
11240     },
11241
11242     // private function a store will implement
11243     onMetaChange : function(meta, recordType, o){
11244
11245     },
11246
11247     /**
11248          * @ignore
11249          */
11250     simpleAccess: function(obj, subsc) {
11251         return obj[subsc];
11252     },
11253
11254         /**
11255          * @ignore
11256          */
11257     getJsonAccessor: function(){
11258         var re = /[\[\.]/;
11259         return function(expr) {
11260             try {
11261                 return(re.test(expr))
11262                     ? new Function("obj", "return obj." + expr)
11263                     : function(obj){
11264                         return obj[expr];
11265                     };
11266             } catch(e){}
11267             return Roo.emptyFn;
11268         };
11269     }(),
11270
11271     /**
11272      * Create a data block containing Roo.data.Records from an XML document.
11273      * @param {Object} o An object which contains an Array of row objects in the property specified
11274      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11275      * which contains the total size of the dataset.
11276      * @return {Object} data A data block which is used by an Roo.data.Store object as
11277      * a cache of Roo.data.Records.
11278      */
11279     readRecords : function(o){
11280         /**
11281          * After any data loads, the raw JSON data is available for further custom processing.
11282          * @type Object
11283          */
11284         this.o = o;
11285         var s = this.meta, Record = this.recordType,
11286             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11287
11288 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
11289         if (!this.ef) {
11290             if(s.totalProperty) {
11291                     this.getTotal = this.getJsonAccessor(s.totalProperty);
11292                 }
11293                 if(s.successProperty) {
11294                     this.getSuccess = this.getJsonAccessor(s.successProperty);
11295                 }
11296                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11297                 if (s.id) {
11298                         var g = this.getJsonAccessor(s.id);
11299                         this.getId = function(rec) {
11300                                 var r = g(rec);  
11301                                 return (r === undefined || r === "") ? null : r;
11302                         };
11303                 } else {
11304                         this.getId = function(){return null;};
11305                 }
11306             this.ef = [];
11307             for(var jj = 0; jj < fl; jj++){
11308                 f = fi[jj];
11309                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11310                 this.ef[jj] = this.getJsonAccessor(map);
11311             }
11312         }
11313
11314         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11315         if(s.totalProperty){
11316             var vt = parseInt(this.getTotal(o), 10);
11317             if(!isNaN(vt)){
11318                 totalRecords = vt;
11319             }
11320         }
11321         if(s.successProperty){
11322             var vs = this.getSuccess(o);
11323             if(vs === false || vs === 'false'){
11324                 success = false;
11325             }
11326         }
11327         var records = [];
11328         for(var i = 0; i < c; i++){
11329                 var n = root[i];
11330             var values = {};
11331             var id = this.getId(n);
11332             for(var j = 0; j < fl; j++){
11333                 f = fi[j];
11334             var v = this.ef[j](n);
11335             if (!f.convert) {
11336                 Roo.log('missing convert for ' + f.name);
11337                 Roo.log(f);
11338                 continue;
11339             }
11340             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11341             }
11342             var record = new Record(values, id);
11343             record.json = n;
11344             records[i] = record;
11345         }
11346         return {
11347             raw : o,
11348             success : success,
11349             records : records,
11350             totalRecords : totalRecords
11351         };
11352     }
11353 });/*
11354  * Based on:
11355  * Ext JS Library 1.1.1
11356  * Copyright(c) 2006-2007, Ext JS, LLC.
11357  *
11358  * Originally Released Under LGPL - original licence link has changed is not relivant.
11359  *
11360  * Fork - LGPL
11361  * <script type="text/javascript">
11362  */
11363
11364 /**
11365  * @class Roo.data.ArrayReader
11366  * @extends Roo.data.DataReader
11367  * Data reader class to create an Array of Roo.data.Record objects from an Array.
11368  * Each element of that Array represents a row of data fields. The
11369  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11370  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11371  * <p>
11372  * Example code:.
11373  * <pre><code>
11374 var RecordDef = Roo.data.Record.create([
11375     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
11376     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
11377 ]);
11378 var myReader = new Roo.data.ArrayReader({
11379     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
11380 }, RecordDef);
11381 </code></pre>
11382  * <p>
11383  * This would consume an Array like this:
11384  * <pre><code>
11385 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11386   </code></pre>
11387  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11388  * @constructor
11389  * Create a new JsonReader
11390  * @param {Object} meta Metadata configuration options.
11391  * @param {Object} recordType Either an Array of field definition objects
11392  * as specified to {@link Roo.data.Record#create},
11393  * or an {@link Roo.data.Record} object
11394  * created using {@link Roo.data.Record#create}.
11395  */
11396 Roo.data.ArrayReader = function(meta, recordType){
11397     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11398 };
11399
11400 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11401     /**
11402      * Create a data block containing Roo.data.Records from an XML document.
11403      * @param {Object} o An Array of row objects which represents the dataset.
11404      * @return {Object} data A data block which is used by an Roo.data.Store object as
11405      * a cache of Roo.data.Records.
11406      */
11407     readRecords : function(o){
11408         var sid = this.meta ? this.meta.id : null;
11409         var recordType = this.recordType, fields = recordType.prototype.fields;
11410         var records = [];
11411         var root = o;
11412             for(var i = 0; i < root.length; i++){
11413                     var n = root[i];
11414                 var values = {};
11415                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11416                 for(var j = 0, jlen = fields.length; j < jlen; j++){
11417                 var f = fields.items[j];
11418                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11419                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11420                 v = f.convert(v);
11421                 values[f.name] = v;
11422             }
11423                 var record = new recordType(values, id);
11424                 record.json = n;
11425                 records[records.length] = record;
11426             }
11427             return {
11428                 records : records,
11429                 totalRecords : records.length
11430             };
11431     }
11432 });/*
11433  * - LGPL
11434  * * 
11435  */
11436
11437 /**
11438  * @class Roo.bootstrap.ComboBox
11439  * @extends Roo.bootstrap.TriggerField
11440  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11441  * @cfg {Boolean} append (true|false) default false
11442  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11443  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11444  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11445  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11446  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11447  * @cfg {Boolean} animate default true
11448  * @cfg {Boolean} emptyResultText only for touch device
11449  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11450  * @constructor
11451  * Create a new ComboBox.
11452  * @param {Object} config Configuration options
11453  */
11454 Roo.bootstrap.ComboBox = function(config){
11455     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11456     this.addEvents({
11457         /**
11458          * @event expand
11459          * Fires when the dropdown list is expanded
11460              * @param {Roo.bootstrap.ComboBox} combo This combo box
11461              */
11462         'expand' : true,
11463         /**
11464          * @event collapse
11465          * Fires when the dropdown list is collapsed
11466              * @param {Roo.bootstrap.ComboBox} combo This combo box
11467              */
11468         'collapse' : true,
11469         /**
11470          * @event beforeselect
11471          * Fires before a list item is selected. Return false to cancel the selection.
11472              * @param {Roo.bootstrap.ComboBox} combo This combo box
11473              * @param {Roo.data.Record} record The data record returned from the underlying store
11474              * @param {Number} index The index of the selected item in the dropdown list
11475              */
11476         'beforeselect' : true,
11477         /**
11478          * @event select
11479          * Fires when a list item is selected
11480              * @param {Roo.bootstrap.ComboBox} combo This combo box
11481              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11482              * @param {Number} index The index of the selected item in the dropdown list
11483              */
11484         'select' : true,
11485         /**
11486          * @event beforequery
11487          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11488          * The event object passed has these properties:
11489              * @param {Roo.bootstrap.ComboBox} combo This combo box
11490              * @param {String} query The query
11491              * @param {Boolean} forceAll true to force "all" query
11492              * @param {Boolean} cancel true to cancel the query
11493              * @param {Object} e The query event object
11494              */
11495         'beforequery': true,
11496          /**
11497          * @event add
11498          * Fires when the 'add' icon is pressed (add a listener to enable add button)
11499              * @param {Roo.bootstrap.ComboBox} combo This combo box
11500              */
11501         'add' : true,
11502         /**
11503          * @event edit
11504          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11505              * @param {Roo.bootstrap.ComboBox} combo This combo box
11506              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11507              */
11508         'edit' : true,
11509         /**
11510          * @event remove
11511          * Fires when the remove value from the combobox array
11512              * @param {Roo.bootstrap.ComboBox} combo This combo box
11513              */
11514         'remove' : true,
11515         /**
11516          * @event specialfilter
11517          * Fires when specialfilter
11518             * @param {Roo.bootstrap.ComboBox} combo This combo box
11519             */
11520         'specialfilter' : true,
11521         /**
11522          * @event tick
11523          * Fires when tick the element
11524             * @param {Roo.bootstrap.ComboBox} combo This combo box
11525             */
11526         'tick' : true,
11527         /**
11528          * @event touchviewdisplay
11529          * Fires when touch view require special display (default is using displayField)
11530             * @param {Roo.bootstrap.ComboBox} combo This combo box
11531             * @param {Object} cfg set html .
11532             */
11533         'touchviewdisplay' : true
11534         
11535     });
11536     
11537     this.item = [];
11538     this.tickItems = [];
11539     
11540     this.selectedIndex = -1;
11541     if(this.mode == 'local'){
11542         if(config.queryDelay === undefined){
11543             this.queryDelay = 10;
11544         }
11545         if(config.minChars === undefined){
11546             this.minChars = 0;
11547         }
11548     }
11549 };
11550
11551 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11552      
11553     /**
11554      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11555      * rendering into an Roo.Editor, defaults to false)
11556      */
11557     /**
11558      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11559      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11560      */
11561     /**
11562      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11563      */
11564     /**
11565      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11566      * the dropdown list (defaults to undefined, with no header element)
11567      */
11568
11569      /**
11570      * @cfg {String/Roo.Template} tpl The template to use to render the output
11571      */
11572      
11573      /**
11574      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11575      */
11576     listWidth: undefined,
11577     /**
11578      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11579      * mode = 'remote' or 'text' if mode = 'local')
11580      */
11581     displayField: undefined,
11582     
11583     /**
11584      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11585      * mode = 'remote' or 'value' if mode = 'local'). 
11586      * Note: use of a valueField requires the user make a selection
11587      * in order for a value to be mapped.
11588      */
11589     valueField: undefined,
11590     
11591     
11592     /**
11593      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11594      * field's data value (defaults to the underlying DOM element's name)
11595      */
11596     hiddenName: undefined,
11597     /**
11598      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11599      */
11600     listClass: '',
11601     /**
11602      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11603      */
11604     selectedClass: 'active',
11605     
11606     /**
11607      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11608      */
11609     shadow:'sides',
11610     /**
11611      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11612      * anchor positions (defaults to 'tl-bl')
11613      */
11614     listAlign: 'tl-bl?',
11615     /**
11616      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11617      */
11618     maxHeight: 300,
11619     /**
11620      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
11621      * query specified by the allQuery config option (defaults to 'query')
11622      */
11623     triggerAction: 'query',
11624     /**
11625      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11626      * (defaults to 4, does not apply if editable = false)
11627      */
11628     minChars : 4,
11629     /**
11630      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11631      * delay (typeAheadDelay) if it matches a known value (defaults to false)
11632      */
11633     typeAhead: false,
11634     /**
11635      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11636      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11637      */
11638     queryDelay: 500,
11639     /**
11640      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11641      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
11642      */
11643     pageSize: 0,
11644     /**
11645      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
11646      * when editable = true (defaults to false)
11647      */
11648     selectOnFocus:false,
11649     /**
11650      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11651      */
11652     queryParam: 'query',
11653     /**
11654      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
11655      * when mode = 'remote' (defaults to 'Loading...')
11656      */
11657     loadingText: 'Loading...',
11658     /**
11659      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11660      */
11661     resizable: false,
11662     /**
11663      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11664      */
11665     handleHeight : 8,
11666     /**
11667      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11668      * traditional select (defaults to true)
11669      */
11670     editable: true,
11671     /**
11672      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11673      */
11674     allQuery: '',
11675     /**
11676      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11677      */
11678     mode: 'remote',
11679     /**
11680      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11681      * listWidth has a higher value)
11682      */
11683     minListWidth : 70,
11684     /**
11685      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11686      * allow the user to set arbitrary text into the field (defaults to false)
11687      */
11688     forceSelection:false,
11689     /**
11690      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11691      * if typeAhead = true (defaults to 250)
11692      */
11693     typeAheadDelay : 250,
11694     /**
11695      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11696      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11697      */
11698     valueNotFoundText : undefined,
11699     /**
11700      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11701      */
11702     blockFocus : false,
11703     
11704     /**
11705      * @cfg {Boolean} disableClear Disable showing of clear button.
11706      */
11707     disableClear : false,
11708     /**
11709      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
11710      */
11711     alwaysQuery : false,
11712     
11713     /**
11714      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
11715      */
11716     multiple : false,
11717     
11718     /**
11719      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11720      */
11721     invalidClass : "has-warning",
11722     
11723     /**
11724      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11725      */
11726     validClass : "has-success",
11727     
11728     /**
11729      * @cfg {Boolean} specialFilter (true|false) special filter default false
11730      */
11731     specialFilter : false,
11732     
11733     /**
11734      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
11735      */
11736     mobileTouchView : true,
11737     
11738     //private
11739     addicon : false,
11740     editicon: false,
11741     
11742     page: 0,
11743     hasQuery: false,
11744     append: false,
11745     loadNext: false,
11746     autoFocus : true,
11747     tickable : false,
11748     btnPosition : 'right',
11749     triggerList : true,
11750     showToggleBtn : true,
11751     animate : true,
11752     emptyResultText: 'Empty',
11753     triggerText : 'Select',
11754     
11755     // element that contains real text value.. (when hidden is used..)
11756     
11757     getAutoCreate : function()
11758     {
11759         var cfg = false;
11760         
11761         /*
11762          * Touch Devices
11763          */
11764         
11765         if(Roo.isTouch && this.mobileTouchView){
11766             cfg = this.getAutoCreateTouchView();
11767             return cfg;;
11768         }
11769         
11770         /*
11771          *  Normal ComboBox
11772          */
11773         if(!this.tickable){
11774             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11775             return cfg;
11776         }
11777         
11778         /*
11779          *  ComboBox with tickable selections
11780          */
11781              
11782         var align = this.labelAlign || this.parentLabelAlign();
11783         
11784         cfg = {
11785             cls : 'form-group roo-combobox-tickable' //input-group
11786         };
11787         
11788         var buttons = {
11789             tag : 'div',
11790             cls : 'tickable-buttons',
11791             cn : [
11792                 {
11793                     tag : 'button',
11794                     type : 'button',
11795                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11796                     html : this.triggerText
11797                 },
11798                 {
11799                     tag : 'button',
11800                     type : 'button',
11801                     name : 'ok',
11802                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11803                     html : 'Done'
11804                 },
11805                 {
11806                     tag : 'button',
11807                     type : 'button',
11808                     name : 'cancel',
11809                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11810                     html : 'Cancel'
11811                 }
11812             ]
11813         };
11814         
11815         if(this.editable){
11816             buttons.cn.unshift({
11817                 tag: 'input',
11818                 cls: 'select2-search-field-input'
11819             });
11820         }
11821         
11822         var _this = this;
11823         
11824         Roo.each(buttons.cn, function(c){
11825             if (_this.size) {
11826                 c.cls += ' btn-' + _this.size;
11827             }
11828
11829             if (_this.disabled) {
11830                 c.disabled = true;
11831             }
11832         });
11833         
11834         var box = {
11835             tag: 'div',
11836             cn: [
11837                 {
11838                     tag: 'input',
11839                     type : 'hidden',
11840                     cls: 'form-hidden-field'
11841                 },
11842                 {
11843                     tag: 'ul',
11844                     cls: 'select2-choices',
11845                     cn:[
11846                         {
11847                             tag: 'li',
11848                             cls: 'select2-search-field',
11849                             cn: [
11850
11851                                 buttons
11852                             ]
11853                         }
11854                     ]
11855                 }
11856             ]
11857         };
11858         
11859         var combobox = {
11860             cls: 'select2-container input-group select2-container-multi',
11861             cn: [
11862                 box
11863 //                {
11864 //                    tag: 'ul',
11865 //                    cls: 'typeahead typeahead-long dropdown-menu',
11866 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
11867 //                }
11868             ]
11869         };
11870         
11871         if(this.hasFeedback && !this.allowBlank){
11872             
11873             var feedback = {
11874                 tag: 'span',
11875                 cls: 'glyphicon form-control-feedback'
11876             };
11877
11878             combobox.cn.push(feedback);
11879         }
11880         
11881         if (align ==='left' && this.fieldLabel.length) {
11882             
11883                 Roo.log("left and has label");
11884                 cfg.cn = [
11885                     
11886                     {
11887                         tag: 'label',
11888                         'for' :  id,
11889                         cls : 'control-label col-sm-' + this.labelWidth,
11890                         html : this.fieldLabel
11891                         
11892                     },
11893                     {
11894                         cls : "col-sm-" + (12 - this.labelWidth), 
11895                         cn: [
11896                             combobox
11897                         ]
11898                     }
11899                     
11900                 ];
11901         } else if ( this.fieldLabel.length) {
11902                 Roo.log(" label");
11903                  cfg.cn = [
11904                    
11905                     {
11906                         tag: 'label',
11907                         //cls : 'input-group-addon',
11908                         html : this.fieldLabel
11909                         
11910                     },
11911                     
11912                     combobox
11913                     
11914                 ];
11915
11916         } else {
11917             
11918                 Roo.log(" no label && no align");
11919                 cfg = combobox
11920                      
11921                 
11922         }
11923          
11924         var settings=this;
11925         ['xs','sm','md','lg'].map(function(size){
11926             if (settings[size]) {
11927                 cfg.cls += ' col-' + size + '-' + settings[size];
11928             }
11929         });
11930         
11931         return cfg;
11932         
11933     },
11934     
11935     _initEventsCalled : false,
11936     
11937     // private
11938     initEvents: function()
11939     {
11940         
11941         if (this._initEventsCalled) { // as we call render... prevent looping...
11942             return;
11943         }
11944         this._initEventsCalled = true;
11945         
11946         if (!this.store) {
11947             throw "can not find store for combo";
11948         }
11949         
11950         this.store = Roo.factory(this.store, Roo.data);
11951         
11952         // if we are building from html. then this element is so complex, that we can not really
11953         // use the rendered HTML.
11954         // so we have to trash and replace the previous code.
11955         if (Roo.XComponent.build_from_html) {
11956             
11957             // remove this element....
11958             var e = this.el.dom, k=0;
11959             while (e ) { e = e.previousSibling;  ++k;}
11960
11961             this.el.remove();
11962             
11963             this.el=false;
11964             this.rendered = false;
11965             
11966             this.render(this.parent().getChildContainer(true), k);
11967             
11968             
11969             
11970         }
11971         
11972         
11973         /*
11974          * Touch Devices
11975          */
11976         
11977         if(Roo.isTouch && this.mobileTouchView){
11978             this.initTouchView();
11979             return;
11980         }
11981         
11982         if(this.tickable){
11983             this.initTickableEvents();
11984             return;
11985         }
11986         
11987         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11988         
11989         if(this.hiddenName){
11990             
11991             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11992             
11993             this.hiddenField.dom.value =
11994                 this.hiddenValue !== undefined ? this.hiddenValue :
11995                 this.value !== undefined ? this.value : '';
11996
11997             // prevent input submission
11998             this.el.dom.removeAttribute('name');
11999             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12000              
12001              
12002         }
12003         //if(Roo.isGecko){
12004         //    this.el.dom.setAttribute('autocomplete', 'off');
12005         //}
12006         
12007         var cls = 'x-combo-list';
12008         
12009         //this.list = new Roo.Layer({
12010         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12011         //});
12012         
12013         var _this = this;
12014         
12015         (function(){
12016             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12017             _this.list.setWidth(lw);
12018         }).defer(100);
12019         
12020         this.list.on('mouseover', this.onViewOver, this);
12021         this.list.on('mousemove', this.onViewMove, this);
12022         
12023         this.list.on('scroll', this.onViewScroll, this);
12024         
12025         /*
12026         this.list.swallowEvent('mousewheel');
12027         this.assetHeight = 0;
12028
12029         if(this.title){
12030             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12031             this.assetHeight += this.header.getHeight();
12032         }
12033
12034         this.innerList = this.list.createChild({cls:cls+'-inner'});
12035         this.innerList.on('mouseover', this.onViewOver, this);
12036         this.innerList.on('mousemove', this.onViewMove, this);
12037         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12038         
12039         if(this.allowBlank && !this.pageSize && !this.disableClear){
12040             this.footer = this.list.createChild({cls:cls+'-ft'});
12041             this.pageTb = new Roo.Toolbar(this.footer);
12042            
12043         }
12044         if(this.pageSize){
12045             this.footer = this.list.createChild({cls:cls+'-ft'});
12046             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12047                     {pageSize: this.pageSize});
12048             
12049         }
12050         
12051         if (this.pageTb && this.allowBlank && !this.disableClear) {
12052             var _this = this;
12053             this.pageTb.add(new Roo.Toolbar.Fill(), {
12054                 cls: 'x-btn-icon x-btn-clear',
12055                 text: '&#160;',
12056                 handler: function()
12057                 {
12058                     _this.collapse();
12059                     _this.clearValue();
12060                     _this.onSelect(false, -1);
12061                 }
12062             });
12063         }
12064         if (this.footer) {
12065             this.assetHeight += this.footer.getHeight();
12066         }
12067         */
12068             
12069         if(!this.tpl){
12070             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12071         }
12072
12073         this.view = new Roo.View(this.list, this.tpl, {
12074             singleSelect:true, store: this.store, selectedClass: this.selectedClass
12075         });
12076         //this.view.wrapEl.setDisplayed(false);
12077         this.view.on('click', this.onViewClick, this);
12078         
12079         
12080         
12081         this.store.on('beforeload', this.onBeforeLoad, this);
12082         this.store.on('load', this.onLoad, this);
12083         this.store.on('loadexception', this.onLoadException, this);
12084         /*
12085         if(this.resizable){
12086             this.resizer = new Roo.Resizable(this.list,  {
12087                pinned:true, handles:'se'
12088             });
12089             this.resizer.on('resize', function(r, w, h){
12090                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12091                 this.listWidth = w;
12092                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12093                 this.restrictHeight();
12094             }, this);
12095             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12096         }
12097         */
12098         if(!this.editable){
12099             this.editable = true;
12100             this.setEditable(false);
12101         }
12102         
12103         /*
12104         
12105         if (typeof(this.events.add.listeners) != 'undefined') {
12106             
12107             this.addicon = this.wrap.createChild(
12108                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
12109        
12110             this.addicon.on('click', function(e) {
12111                 this.fireEvent('add', this);
12112             }, this);
12113         }
12114         if (typeof(this.events.edit.listeners) != 'undefined') {
12115             
12116             this.editicon = this.wrap.createChild(
12117                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
12118             if (this.addicon) {
12119                 this.editicon.setStyle('margin-left', '40px');
12120             }
12121             this.editicon.on('click', function(e) {
12122                 
12123                 // we fire even  if inothing is selected..
12124                 this.fireEvent('edit', this, this.lastData );
12125                 
12126             }, this);
12127         }
12128         */
12129         
12130         this.keyNav = new Roo.KeyNav(this.inputEl(), {
12131             "up" : function(e){
12132                 this.inKeyMode = true;
12133                 this.selectPrev();
12134             },
12135
12136             "down" : function(e){
12137                 if(!this.isExpanded()){
12138                     this.onTriggerClick();
12139                 }else{
12140                     this.inKeyMode = true;
12141                     this.selectNext();
12142                 }
12143             },
12144
12145             "enter" : function(e){
12146 //                this.onViewClick();
12147                 //return true;
12148                 this.collapse();
12149                 
12150                 if(this.fireEvent("specialkey", this, e)){
12151                     this.onViewClick(false);
12152                 }
12153                 
12154                 return true;
12155             },
12156
12157             "esc" : function(e){
12158                 this.collapse();
12159             },
12160
12161             "tab" : function(e){
12162                 this.collapse();
12163                 
12164                 if(this.fireEvent("specialkey", this, e)){
12165                     this.onViewClick(false);
12166                 }
12167                 
12168                 return true;
12169             },
12170
12171             scope : this,
12172
12173             doRelay : function(foo, bar, hname){
12174                 if(hname == 'down' || this.scope.isExpanded()){
12175                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12176                 }
12177                 return true;
12178             },
12179
12180             forceKeyDown: true
12181         });
12182         
12183         
12184         this.queryDelay = Math.max(this.queryDelay || 10,
12185                 this.mode == 'local' ? 10 : 250);
12186         
12187         
12188         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12189         
12190         if(this.typeAhead){
12191             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12192         }
12193         if(this.editable !== false){
12194             this.inputEl().on("keyup", this.onKeyUp, this);
12195         }
12196         if(this.forceSelection){
12197             this.inputEl().on('blur', this.doForce, this);
12198         }
12199         
12200         if(this.multiple){
12201             this.choices = this.el.select('ul.select2-choices', true).first();
12202             this.searchField = this.el.select('ul li.select2-search-field', true).first();
12203         }
12204     },
12205     
12206     initTickableEvents: function()
12207     {   
12208         this.createList();
12209         
12210         if(this.hiddenName){
12211             
12212             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12213             
12214             this.hiddenField.dom.value =
12215                 this.hiddenValue !== undefined ? this.hiddenValue :
12216                 this.value !== undefined ? this.value : '';
12217
12218             // prevent input submission
12219             this.el.dom.removeAttribute('name');
12220             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12221              
12222              
12223         }
12224         
12225 //        this.list = this.el.select('ul.dropdown-menu',true).first();
12226         
12227         this.choices = this.el.select('ul.select2-choices', true).first();
12228         this.searchField = this.el.select('ul li.select2-search-field', true).first();
12229         if(this.triggerList){
12230             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12231         }
12232          
12233         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12234         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12235         
12236         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12237         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12238         
12239         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12240         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12241         
12242         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12243         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12244         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12245         
12246         this.okBtn.hide();
12247         this.cancelBtn.hide();
12248         
12249         var _this = this;
12250         
12251         (function(){
12252             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12253             _this.list.setWidth(lw);
12254         }).defer(100);
12255         
12256         this.list.on('mouseover', this.onViewOver, this);
12257         this.list.on('mousemove', this.onViewMove, this);
12258         
12259         this.list.on('scroll', this.onViewScroll, this);
12260         
12261         if(!this.tpl){
12262             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>';
12263         }
12264
12265         this.view = new Roo.View(this.list, this.tpl, {
12266             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12267         });
12268         
12269         //this.view.wrapEl.setDisplayed(false);
12270         this.view.on('click', this.onViewClick, this);
12271         
12272         
12273         
12274         this.store.on('beforeload', this.onBeforeLoad, this);
12275         this.store.on('load', this.onLoad, this);
12276         this.store.on('loadexception', this.onLoadException, this);
12277         
12278         if(this.editable){
12279             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12280                 "up" : function(e){
12281                     this.inKeyMode = true;
12282                     this.selectPrev();
12283                 },
12284
12285                 "down" : function(e){
12286                     this.inKeyMode = true;
12287                     this.selectNext();
12288                 },
12289
12290                 "enter" : function(e){
12291                     if(this.fireEvent("specialkey", this, e)){
12292                         this.onViewClick(false);
12293                     }
12294                     
12295                     return true;
12296                 },
12297
12298                 "esc" : function(e){
12299                     this.onTickableFooterButtonClick(e, false, false);
12300                 },
12301
12302                 "tab" : function(e){
12303                     this.fireEvent("specialkey", this, e);
12304                     
12305                     this.onTickableFooterButtonClick(e, false, false);
12306                     
12307                     return true;
12308                 },
12309
12310                 scope : this,
12311
12312                 doRelay : function(e, fn, key){
12313                     if(this.scope.isExpanded()){
12314                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12315                     }
12316                     return true;
12317                 },
12318
12319                 forceKeyDown: true
12320             });
12321         }
12322         
12323         this.queryDelay = Math.max(this.queryDelay || 10,
12324                 this.mode == 'local' ? 10 : 250);
12325         
12326         
12327         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12328         
12329         if(this.typeAhead){
12330             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12331         }
12332         
12333         if(this.editable !== false){
12334             this.tickableInputEl().on("keyup", this.onKeyUp, this);
12335         }
12336         
12337     },
12338
12339     onDestroy : function(){
12340         if(this.view){
12341             this.view.setStore(null);
12342             this.view.el.removeAllListeners();
12343             this.view.el.remove();
12344             this.view.purgeListeners();
12345         }
12346         if(this.list){
12347             this.list.dom.innerHTML  = '';
12348         }
12349         
12350         if(this.store){
12351             this.store.un('beforeload', this.onBeforeLoad, this);
12352             this.store.un('load', this.onLoad, this);
12353             this.store.un('loadexception', this.onLoadException, this);
12354         }
12355         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12356     },
12357
12358     // private
12359     fireKey : function(e){
12360         if(e.isNavKeyPress() && !this.list.isVisible()){
12361             this.fireEvent("specialkey", this, e);
12362         }
12363     },
12364
12365     // private
12366     onResize: function(w, h){
12367 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12368 //        
12369 //        if(typeof w != 'number'){
12370 //            // we do not handle it!?!?
12371 //            return;
12372 //        }
12373 //        var tw = this.trigger.getWidth();
12374 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
12375 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
12376 //        var x = w - tw;
12377 //        this.inputEl().setWidth( this.adjustWidth('input', x));
12378 //            
12379 //        //this.trigger.setStyle('left', x+'px');
12380 //        
12381 //        if(this.list && this.listWidth === undefined){
12382 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12383 //            this.list.setWidth(lw);
12384 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12385 //        }
12386         
12387     
12388         
12389     },
12390
12391     /**
12392      * Allow or prevent the user from directly editing the field text.  If false is passed,
12393      * the user will only be able to select from the items defined in the dropdown list.  This method
12394      * is the runtime equivalent of setting the 'editable' config option at config time.
12395      * @param {Boolean} value True to allow the user to directly edit the field text
12396      */
12397     setEditable : function(value){
12398         if(value == this.editable){
12399             return;
12400         }
12401         this.editable = value;
12402         if(!value){
12403             this.inputEl().dom.setAttribute('readOnly', true);
12404             this.inputEl().on('mousedown', this.onTriggerClick,  this);
12405             this.inputEl().addClass('x-combo-noedit');
12406         }else{
12407             this.inputEl().dom.setAttribute('readOnly', false);
12408             this.inputEl().un('mousedown', this.onTriggerClick,  this);
12409             this.inputEl().removeClass('x-combo-noedit');
12410         }
12411     },
12412
12413     // private
12414     
12415     onBeforeLoad : function(combo,opts){
12416         if(!this.hasFocus){
12417             return;
12418         }
12419          if (!opts.add) {
12420             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12421          }
12422         this.restrictHeight();
12423         this.selectedIndex = -1;
12424     },
12425
12426     // private
12427     onLoad : function(){
12428         
12429         this.hasQuery = false;
12430         
12431         if(!this.hasFocus){
12432             return;
12433         }
12434         
12435         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12436             this.loading.hide();
12437         }
12438              
12439         if(this.store.getCount() > 0){
12440             this.expand();
12441             this.restrictHeight();
12442             if(this.lastQuery == this.allQuery){
12443                 if(this.editable && !this.tickable){
12444                     this.inputEl().dom.select();
12445                 }
12446                 
12447                 if(
12448                     !this.selectByValue(this.value, true) &&
12449                     this.autoFocus && 
12450                     (
12451                         !this.store.lastOptions ||
12452                         typeof(this.store.lastOptions.add) == 'undefined' || 
12453                         this.store.lastOptions.add != true
12454                     )
12455                 ){
12456                     this.select(0, true);
12457                 }
12458             }else{
12459                 if(this.autoFocus){
12460                     this.selectNext();
12461                 }
12462                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12463                     this.taTask.delay(this.typeAheadDelay);
12464                 }
12465             }
12466         }else{
12467             this.onEmptyResults();
12468         }
12469         
12470         //this.el.focus();
12471     },
12472     // private
12473     onLoadException : function()
12474     {
12475         this.hasQuery = false;
12476         
12477         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12478             this.loading.hide();
12479         }
12480         
12481         if(this.tickable && this.editable){
12482             return;
12483         }
12484         
12485         this.collapse();
12486         // only causes errors at present
12487         //Roo.log(this.store.reader.jsonData);
12488         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12489             // fixme
12490             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12491         //}
12492         
12493         
12494     },
12495     // private
12496     onTypeAhead : function(){
12497         if(this.store.getCount() > 0){
12498             var r = this.store.getAt(0);
12499             var newValue = r.data[this.displayField];
12500             var len = newValue.length;
12501             var selStart = this.getRawValue().length;
12502             
12503             if(selStart != len){
12504                 this.setRawValue(newValue);
12505                 this.selectText(selStart, newValue.length);
12506             }
12507         }
12508     },
12509
12510     // private
12511     onSelect : function(record, index){
12512         
12513         if(this.fireEvent('beforeselect', this, record, index) !== false){
12514         
12515             this.setFromData(index > -1 ? record.data : false);
12516             
12517             this.collapse();
12518             this.fireEvent('select', this, record, index);
12519         }
12520     },
12521
12522     /**
12523      * Returns the currently selected field value or empty string if no value is set.
12524      * @return {String} value The selected value
12525      */
12526     getValue : function(){
12527         
12528         if(this.multiple){
12529             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12530         }
12531         
12532         if(this.valueField){
12533             return typeof this.value != 'undefined' ? this.value : '';
12534         }else{
12535             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12536         }
12537     },
12538
12539     /**
12540      * Clears any text/value currently set in the field
12541      */
12542     clearValue : function(){
12543         if(this.hiddenField){
12544             this.hiddenField.dom.value = '';
12545         }
12546         this.value = '';
12547         this.setRawValue('');
12548         this.lastSelectionText = '';
12549         this.lastData = false;
12550         
12551         var close = this.closeTriggerEl();
12552         
12553         if(close){
12554             close.hide();
12555         }
12556         
12557     },
12558
12559     /**
12560      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
12561      * will be displayed in the field.  If the value does not match the data value of an existing item,
12562      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12563      * Otherwise the field will be blank (although the value will still be set).
12564      * @param {String} value The value to match
12565      */
12566     setValue : function(v){
12567         if(this.multiple){
12568             this.syncValue();
12569             return;
12570         }
12571         
12572         var text = v;
12573         if(this.valueField){
12574             var r = this.findRecord(this.valueField, v);
12575             if(r){
12576                 text = r.data[this.displayField];
12577             }else if(this.valueNotFoundText !== undefined){
12578                 text = this.valueNotFoundText;
12579             }
12580         }
12581         this.lastSelectionText = text;
12582         if(this.hiddenField){
12583             this.hiddenField.dom.value = v;
12584         }
12585         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12586         this.value = v;
12587         
12588         var close = this.closeTriggerEl();
12589         
12590         if(close){
12591             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12592         }
12593     },
12594     /**
12595      * @property {Object} the last set data for the element
12596      */
12597     
12598     lastData : false,
12599     /**
12600      * Sets the value of the field based on a object which is related to the record format for the store.
12601      * @param {Object} value the value to set as. or false on reset?
12602      */
12603     setFromData : function(o){
12604         
12605         if(this.multiple){
12606             this.addItem(o);
12607             return;
12608         }
12609             
12610         var dv = ''; // display value
12611         var vv = ''; // value value..
12612         this.lastData = o;
12613         if (this.displayField) {
12614             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12615         } else {
12616             // this is an error condition!!!
12617             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12618         }
12619         
12620         if(this.valueField){
12621             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12622         }
12623         
12624         var close = this.closeTriggerEl();
12625         
12626         if(close){
12627             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12628         }
12629         
12630         if(this.hiddenField){
12631             this.hiddenField.dom.value = vv;
12632             
12633             this.lastSelectionText = dv;
12634             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12635             this.value = vv;
12636             return;
12637         }
12638         // no hidden field.. - we store the value in 'value', but still display
12639         // display field!!!!
12640         this.lastSelectionText = dv;
12641         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12642         this.value = vv;
12643         
12644         
12645         
12646     },
12647     // private
12648     reset : function(){
12649         // overridden so that last data is reset..
12650         
12651         if(this.multiple){
12652             this.clearItem();
12653             return;
12654         }
12655         
12656         this.setValue(this.originalValue);
12657         this.clearInvalid();
12658         this.lastData = false;
12659         if (this.view) {
12660             this.view.clearSelections();
12661         }
12662     },
12663     // private
12664     findRecord : function(prop, value){
12665         var record;
12666         if(this.store.getCount() > 0){
12667             this.store.each(function(r){
12668                 if(r.data[prop] == value){
12669                     record = r;
12670                     return false;
12671                 }
12672                 return true;
12673             });
12674         }
12675         return record;
12676     },
12677     
12678     getName: function()
12679     {
12680         // returns hidden if it's set..
12681         if (!this.rendered) {return ''};
12682         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
12683         
12684     },
12685     // private
12686     onViewMove : function(e, t){
12687         this.inKeyMode = false;
12688     },
12689
12690     // private
12691     onViewOver : function(e, t){
12692         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12693             return;
12694         }
12695         var item = this.view.findItemFromChild(t);
12696         
12697         if(item){
12698             var index = this.view.indexOf(item);
12699             this.select(index, false);
12700         }
12701     },
12702
12703     // private
12704     onViewClick : function(view, doFocus, el, e)
12705     {
12706         var index = this.view.getSelectedIndexes()[0];
12707         
12708         var r = this.store.getAt(index);
12709         
12710         if(this.tickable){
12711             
12712             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12713                 return;
12714             }
12715             
12716             var rm = false;
12717             var _this = this;
12718             
12719             Roo.each(this.tickItems, function(v,k){
12720                 
12721                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12722                     Roo.log(v);
12723                     _this.tickItems.splice(k, 1);
12724                     
12725                     if(typeof(e) == 'undefined' && view == false){
12726                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12727                     }
12728                     
12729                     rm = true;
12730                     return;
12731                 }
12732             });
12733             
12734             if(rm){
12735                 return;
12736             }
12737             
12738             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
12739                 this.tickItems.push(r.data);
12740             }
12741             
12742             if(typeof(e) == 'undefined' && view == false){
12743                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12744             }
12745                     
12746             return;
12747         }
12748         
12749         if(r){
12750             this.onSelect(r, index);
12751         }
12752         if(doFocus !== false && !this.blockFocus){
12753             this.inputEl().focus();
12754         }
12755     },
12756
12757     // private
12758     restrictHeight : function(){
12759         //this.innerList.dom.style.height = '';
12760         //var inner = this.innerList.dom;
12761         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12762         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12763         //this.list.beginUpdate();
12764         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12765         this.list.alignTo(this.inputEl(), this.listAlign);
12766         this.list.alignTo(this.inputEl(), this.listAlign);
12767         //this.list.endUpdate();
12768     },
12769
12770     // private
12771     onEmptyResults : function(){
12772         
12773         if(this.tickable && this.editable){
12774             this.restrictHeight();
12775             return;
12776         }
12777         
12778         this.collapse();
12779     },
12780
12781     /**
12782      * Returns true if the dropdown list is expanded, else false.
12783      */
12784     isExpanded : function(){
12785         return this.list.isVisible();
12786     },
12787
12788     /**
12789      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12790      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12791      * @param {String} value The data value of the item to select
12792      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12793      * selected item if it is not currently in view (defaults to true)
12794      * @return {Boolean} True if the value matched an item in the list, else false
12795      */
12796     selectByValue : function(v, scrollIntoView){
12797         if(v !== undefined && v !== null){
12798             var r = this.findRecord(this.valueField || this.displayField, v);
12799             if(r){
12800                 this.select(this.store.indexOf(r), scrollIntoView);
12801                 return true;
12802             }
12803         }
12804         return false;
12805     },
12806
12807     /**
12808      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12809      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12810      * @param {Number} index The zero-based index of the list item to select
12811      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12812      * selected item if it is not currently in view (defaults to true)
12813      */
12814     select : function(index, scrollIntoView){
12815         this.selectedIndex = index;
12816         this.view.select(index);
12817         if(scrollIntoView !== false){
12818             var el = this.view.getNode(index);
12819             /*
12820              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12821              */
12822             if(el){
12823                 this.list.scrollChildIntoView(el, false);
12824             }
12825         }
12826     },
12827
12828     // private
12829     selectNext : function(){
12830         var ct = this.store.getCount();
12831         if(ct > 0){
12832             if(this.selectedIndex == -1){
12833                 this.select(0);
12834             }else if(this.selectedIndex < ct-1){
12835                 this.select(this.selectedIndex+1);
12836             }
12837         }
12838     },
12839
12840     // private
12841     selectPrev : function(){
12842         var ct = this.store.getCount();
12843         if(ct > 0){
12844             if(this.selectedIndex == -1){
12845                 this.select(0);
12846             }else if(this.selectedIndex != 0){
12847                 this.select(this.selectedIndex-1);
12848             }
12849         }
12850     },
12851
12852     // private
12853     onKeyUp : function(e){
12854         if(this.editable !== false && !e.isSpecialKey()){
12855             this.lastKey = e.getKey();
12856             this.dqTask.delay(this.queryDelay);
12857         }
12858     },
12859
12860     // private
12861     validateBlur : function(){
12862         return !this.list || !this.list.isVisible();   
12863     },
12864
12865     // private
12866     initQuery : function(){
12867         
12868         var v = this.getRawValue();
12869         
12870         if(this.tickable && this.editable){
12871             v = this.tickableInputEl().getValue();
12872         }
12873         
12874         this.doQuery(v);
12875     },
12876
12877     // private
12878     doForce : function(){
12879         if(this.inputEl().dom.value.length > 0){
12880             this.inputEl().dom.value =
12881                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12882              
12883         }
12884     },
12885
12886     /**
12887      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
12888      * query allowing the query action to be canceled if needed.
12889      * @param {String} query The SQL query to execute
12890      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12891      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
12892      * saved in the current store (defaults to false)
12893      */
12894     doQuery : function(q, forceAll){
12895         
12896         if(q === undefined || q === null){
12897             q = '';
12898         }
12899         var qe = {
12900             query: q,
12901             forceAll: forceAll,
12902             combo: this,
12903             cancel:false
12904         };
12905         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12906             return false;
12907         }
12908         q = qe.query;
12909         
12910         forceAll = qe.forceAll;
12911         if(forceAll === true || (q.length >= this.minChars)){
12912             
12913             this.hasQuery = true;
12914             
12915             if(this.lastQuery != q || this.alwaysQuery){
12916                 this.lastQuery = q;
12917                 if(this.mode == 'local'){
12918                     this.selectedIndex = -1;
12919                     if(forceAll){
12920                         this.store.clearFilter();
12921                     }else{
12922                         
12923                         if(this.specialFilter){
12924                             this.fireEvent('specialfilter', this);
12925                             this.onLoad();
12926                             return;
12927                         }
12928                         
12929                         this.store.filter(this.displayField, q);
12930                     }
12931                     
12932                     this.store.fireEvent("datachanged", this.store);
12933                     
12934                     this.onLoad();
12935                     
12936                     
12937                 }else{
12938                     
12939                     this.store.baseParams[this.queryParam] = q;
12940                     
12941                     var options = {params : this.getParams(q)};
12942                     
12943                     if(this.loadNext){
12944                         options.add = true;
12945                         options.params.start = this.page * this.pageSize;
12946                     }
12947                     
12948                     this.store.load(options);
12949                     
12950                     /*
12951                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
12952                      *  we should expand the list on onLoad
12953                      *  so command out it
12954                      */
12955 //                    this.expand();
12956                 }
12957             }else{
12958                 this.selectedIndex = -1;
12959                 this.onLoad();   
12960             }
12961         }
12962         
12963         this.loadNext = false;
12964     },
12965     
12966     // private
12967     getParams : function(q){
12968         var p = {};
12969         //p[this.queryParam] = q;
12970         
12971         if(this.pageSize){
12972             p.start = 0;
12973             p.limit = this.pageSize;
12974         }
12975         return p;
12976     },
12977
12978     /**
12979      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12980      */
12981     collapse : function(){
12982         if(!this.isExpanded()){
12983             return;
12984         }
12985         
12986         this.list.hide();
12987         
12988         if(this.tickable){
12989             this.hasFocus = false;
12990             this.okBtn.hide();
12991             this.cancelBtn.hide();
12992             this.trigger.show();
12993             
12994             if(this.editable){
12995                 this.tickableInputEl().dom.value = '';
12996                 this.tickableInputEl().blur();
12997             }
12998             
12999         }
13000         
13001         Roo.get(document).un('mousedown', this.collapseIf, this);
13002         Roo.get(document).un('mousewheel', this.collapseIf, this);
13003         if (!this.editable) {
13004             Roo.get(document).un('keydown', this.listKeyPress, this);
13005         }
13006         this.fireEvent('collapse', this);
13007     },
13008
13009     // private
13010     collapseIf : function(e){
13011         var in_combo  = e.within(this.el);
13012         var in_list =  e.within(this.list);
13013         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13014         
13015         if (in_combo || in_list || is_list) {
13016             //e.stopPropagation();
13017             return;
13018         }
13019         
13020         if(this.tickable){
13021             this.onTickableFooterButtonClick(e, false, false);
13022         }
13023
13024         this.collapse();
13025         
13026     },
13027
13028     /**
13029      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13030      */
13031     expand : function(){
13032        
13033         if(this.isExpanded() || !this.hasFocus){
13034             return;
13035         }
13036         
13037         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13038         this.list.setWidth(lw);
13039         
13040         
13041          Roo.log('expand');
13042         
13043         this.list.show();
13044         
13045         this.restrictHeight();
13046         
13047         if(this.tickable){
13048             
13049             this.tickItems = Roo.apply([], this.item);
13050             
13051             this.okBtn.show();
13052             this.cancelBtn.show();
13053             this.trigger.hide();
13054             
13055             if(this.editable){
13056                 this.tickableInputEl().focus();
13057             }
13058             
13059         }
13060         
13061         Roo.get(document).on('mousedown', this.collapseIf, this);
13062         Roo.get(document).on('mousewheel', this.collapseIf, this);
13063         if (!this.editable) {
13064             Roo.get(document).on('keydown', this.listKeyPress, this);
13065         }
13066         
13067         this.fireEvent('expand', this);
13068     },
13069
13070     // private
13071     // Implements the default empty TriggerField.onTriggerClick function
13072     onTriggerClick : function(e)
13073     {
13074         Roo.log('trigger click');
13075         
13076         if(this.disabled || !this.triggerList){
13077             return;
13078         }
13079         
13080         this.page = 0;
13081         this.loadNext = false;
13082         
13083         if(this.isExpanded()){
13084             this.collapse();
13085             if (!this.blockFocus) {
13086                 this.inputEl().focus();
13087             }
13088             
13089         }else {
13090             this.hasFocus = true;
13091             if(this.triggerAction == 'all') {
13092                 this.doQuery(this.allQuery, true);
13093             } else {
13094                 this.doQuery(this.getRawValue());
13095             }
13096             if (!this.blockFocus) {
13097                 this.inputEl().focus();
13098             }
13099         }
13100     },
13101     
13102     onTickableTriggerClick : function(e)
13103     {
13104         if(this.disabled){
13105             return;
13106         }
13107         
13108         this.page = 0;
13109         this.loadNext = false;
13110         this.hasFocus = true;
13111         
13112         if(this.triggerAction == 'all') {
13113             this.doQuery(this.allQuery, true);
13114         } else {
13115             this.doQuery(this.getRawValue());
13116         }
13117     },
13118     
13119     onSearchFieldClick : function(e)
13120     {
13121         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13122             this.onTickableFooterButtonClick(e, false, false);
13123             return;
13124         }
13125         
13126         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13127             return;
13128         }
13129         
13130         this.page = 0;
13131         this.loadNext = false;
13132         this.hasFocus = true;
13133         
13134         if(this.triggerAction == 'all') {
13135             this.doQuery(this.allQuery, true);
13136         } else {
13137             this.doQuery(this.getRawValue());
13138         }
13139     },
13140     
13141     listKeyPress : function(e)
13142     {
13143         //Roo.log('listkeypress');
13144         // scroll to first matching element based on key pres..
13145         if (e.isSpecialKey()) {
13146             return false;
13147         }
13148         var k = String.fromCharCode(e.getKey()).toUpperCase();
13149         //Roo.log(k);
13150         var match  = false;
13151         var csel = this.view.getSelectedNodes();
13152         var cselitem = false;
13153         if (csel.length) {
13154             var ix = this.view.indexOf(csel[0]);
13155             cselitem  = this.store.getAt(ix);
13156             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13157                 cselitem = false;
13158             }
13159             
13160         }
13161         
13162         this.store.each(function(v) { 
13163             if (cselitem) {
13164                 // start at existing selection.
13165                 if (cselitem.id == v.id) {
13166                     cselitem = false;
13167                 }
13168                 return true;
13169             }
13170                 
13171             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13172                 match = this.store.indexOf(v);
13173                 return false;
13174             }
13175             return true;
13176         }, this);
13177         
13178         if (match === false) {
13179             return true; // no more action?
13180         }
13181         // scroll to?
13182         this.view.select(match);
13183         var sn = Roo.get(this.view.getSelectedNodes()[0]);
13184         sn.scrollIntoView(sn.dom.parentNode, false);
13185     },
13186     
13187     onViewScroll : function(e, t){
13188         
13189         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){
13190             return;
13191         }
13192         
13193         this.hasQuery = true;
13194         
13195         this.loading = this.list.select('.loading', true).first();
13196         
13197         if(this.loading === null){
13198             this.list.createChild({
13199                 tag: 'div',
13200                 cls: 'loading select2-more-results select2-active',
13201                 html: 'Loading more results...'
13202             });
13203             
13204             this.loading = this.list.select('.loading', true).first();
13205             
13206             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13207             
13208             this.loading.hide();
13209         }
13210         
13211         this.loading.show();
13212         
13213         var _combo = this;
13214         
13215         this.page++;
13216         this.loadNext = true;
13217         
13218         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13219         
13220         return;
13221     },
13222     
13223     addItem : function(o)
13224     {   
13225         var dv = ''; // display value
13226         
13227         if (this.displayField) {
13228             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13229         } else {
13230             // this is an error condition!!!
13231             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13232         }
13233         
13234         if(!dv.length){
13235             return;
13236         }
13237         
13238         var choice = this.choices.createChild({
13239             tag: 'li',
13240             cls: 'select2-search-choice',
13241             cn: [
13242                 {
13243                     tag: 'div',
13244                     html: dv
13245                 },
13246                 {
13247                     tag: 'a',
13248                     href: '#',
13249                     cls: 'select2-search-choice-close',
13250                     tabindex: '-1'
13251                 }
13252             ]
13253             
13254         }, this.searchField);
13255         
13256         var close = choice.select('a.select2-search-choice-close', true).first();
13257         
13258         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13259         
13260         this.item.push(o);
13261         
13262         this.lastData = o;
13263         
13264         this.syncValue();
13265         
13266         this.inputEl().dom.value = '';
13267         
13268         this.validate();
13269     },
13270     
13271     onRemoveItem : function(e, _self, o)
13272     {
13273         e.preventDefault();
13274         
13275         this.lastItem = Roo.apply([], this.item);
13276         
13277         var index = this.item.indexOf(o.data) * 1;
13278         
13279         if( index < 0){
13280             Roo.log('not this item?!');
13281             return;
13282         }
13283         
13284         this.item.splice(index, 1);
13285         o.item.remove();
13286         
13287         this.syncValue();
13288         
13289         this.fireEvent('remove', this, e);
13290         
13291         this.validate();
13292         
13293     },
13294     
13295     syncValue : function()
13296     {
13297         if(!this.item.length){
13298             this.clearValue();
13299             return;
13300         }
13301             
13302         var value = [];
13303         var _this = this;
13304         Roo.each(this.item, function(i){
13305             if(_this.valueField){
13306                 value.push(i[_this.valueField]);
13307                 return;
13308             }
13309
13310             value.push(i);
13311         });
13312
13313         this.value = value.join(',');
13314
13315         if(this.hiddenField){
13316             this.hiddenField.dom.value = this.value;
13317         }
13318         
13319         this.store.fireEvent("datachanged", this.store);
13320     },
13321     
13322     clearItem : function()
13323     {
13324         if(!this.multiple){
13325             return;
13326         }
13327         
13328         this.item = [];
13329         
13330         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
13331            c.remove();
13332         });
13333         
13334         this.syncValue();
13335         
13336         this.validate();
13337         
13338         if(this.tickable && !Roo.isTouch){
13339             this.view.refresh();
13340         }
13341     },
13342     
13343     inputEl: function ()
13344     {
13345         if(Roo.isTouch && this.mobileTouchView){
13346             return this.el.select('input.form-control',true).first();
13347         }
13348         
13349         if(this.tickable){
13350             return this.searchField;
13351         }
13352         
13353         return this.el.select('input.form-control',true).first();
13354     },
13355     
13356     
13357     onTickableFooterButtonClick : function(e, btn, el)
13358     {
13359         e.preventDefault();
13360         
13361         this.lastItem = Roo.apply([], this.item);
13362         
13363         if(btn && btn.name == 'cancel'){
13364             this.tickItems = Roo.apply([], this.item);
13365             this.collapse();
13366             return;
13367         }
13368         
13369         this.clearItem();
13370         
13371         var _this = this;
13372         
13373         Roo.each(this.tickItems, function(o){
13374             _this.addItem(o);
13375         });
13376         
13377         this.collapse();
13378         
13379     },
13380     
13381     validate : function()
13382     {
13383         var v = this.getRawValue();
13384         
13385         if(this.multiple){
13386             v = this.getValue();
13387         }
13388         
13389         if(this.disabled || this.allowBlank || v.length){
13390             this.markValid();
13391             return true;
13392         }
13393         
13394         this.markInvalid();
13395         return false;
13396     },
13397     
13398     tickableInputEl : function()
13399     {
13400         if(!this.tickable || !this.editable){
13401             return this.inputEl();
13402         }
13403         
13404         return this.inputEl().select('.select2-search-field-input', true).first();
13405     },
13406     
13407     
13408     getAutoCreateTouchView : function()
13409     {
13410         var id = Roo.id();
13411         
13412         var cfg = {
13413             cls: 'form-group' //input-group
13414         };
13415         
13416         var input =  {
13417             tag: 'input',
13418             id : id,
13419             type : this.inputType,
13420             cls : 'form-control x-combo-noedit',
13421             autocomplete: 'new-password',
13422             placeholder : this.placeholder || '',
13423             readonly : true
13424         };
13425         
13426         if (this.name) {
13427             input.name = this.name;
13428         }
13429         
13430         if (this.size) {
13431             input.cls += ' input-' + this.size;
13432         }
13433         
13434         if (this.disabled) {
13435             input.disabled = true;
13436         }
13437         
13438         var inputblock = {
13439             cls : '',
13440             cn : [
13441                 input
13442             ]
13443         };
13444         
13445         if(this.before){
13446             inputblock.cls += ' input-group';
13447             
13448             inputblock.cn.unshift({
13449                 tag :'span',
13450                 cls : 'input-group-addon',
13451                 html : this.before
13452             });
13453         }
13454         
13455         if(this.removable && !this.multiple){
13456             inputblock.cls += ' roo-removable';
13457             
13458             inputblock.cn.push({
13459                 tag: 'button',
13460                 html : 'x',
13461                 cls : 'roo-combo-removable-btn close'
13462             });
13463         }
13464
13465         if(this.hasFeedback && !this.allowBlank){
13466             
13467             inputblock.cls += ' has-feedback';
13468             
13469             inputblock.cn.push({
13470                 tag: 'span',
13471                 cls: 'glyphicon form-control-feedback'
13472             });
13473             
13474         }
13475         
13476         if (this.after) {
13477             
13478             inputblock.cls += (this.before) ? '' : ' input-group';
13479             
13480             inputblock.cn.push({
13481                 tag :'span',
13482                 cls : 'input-group-addon',
13483                 html : this.after
13484             });
13485         }
13486
13487         var box = {
13488             tag: 'div',
13489             cn: [
13490                 {
13491                     tag: 'input',
13492                     type : 'hidden',
13493                     cls: 'form-hidden-field'
13494                 },
13495                 inputblock
13496             ]
13497             
13498         };
13499         
13500         if(this.multiple){
13501             box = {
13502                 tag: 'div',
13503                 cn: [
13504                     {
13505                         tag: 'input',
13506                         type : 'hidden',
13507                         cls: 'form-hidden-field'
13508                     },
13509                     {
13510                         tag: 'ul',
13511                         cls: 'select2-choices',
13512                         cn:[
13513                             {
13514                                 tag: 'li',
13515                                 cls: 'select2-search-field',
13516                                 cn: [
13517
13518                                     inputblock
13519                                 ]
13520                             }
13521                         ]
13522                     }
13523                 ]
13524             }
13525         };
13526         
13527         var combobox = {
13528             cls: 'select2-container input-group',
13529             cn: [
13530                 box
13531             ]
13532         };
13533         
13534         if(this.multiple){
13535             combobox.cls += ' select2-container-multi';
13536         }
13537         
13538         var align = this.labelAlign || this.parentLabelAlign();
13539         
13540         cfg.cn = combobox;
13541         
13542         if(this.fieldLabel.length){
13543             
13544             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13545             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13546             
13547             cfg.cn = [
13548                 {
13549                     tag: 'label',
13550                     cls : 'control-label ' + lw,
13551                     html : this.fieldLabel
13552
13553                 },
13554                 {
13555                     cls : cw, 
13556                     cn: [
13557                         combobox
13558                     ]
13559                 }
13560             ];
13561         }
13562         
13563         var settings = this;
13564         
13565         ['xs','sm','md','lg'].map(function(size){
13566             if (settings[size]) {
13567                 cfg.cls += ' col-' + size + '-' + settings[size];
13568             }
13569         });
13570         
13571         return cfg;
13572     },
13573     
13574     initTouchView : function()
13575     {
13576         this.renderTouchView();
13577         
13578         this.touchViewEl.on('scroll', function(){
13579             this.el.dom.scrollTop = 0;
13580         }, this);
13581         
13582         this.inputEl().on("click", this.showTouchView, this);
13583         
13584         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13585         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13586         
13587         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13588         
13589         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13590         this.store.on('load', this.onTouchViewLoad, this);
13591         this.store.on('loadexception', this.onTouchViewLoadException, this);
13592         
13593         if(this.hiddenName){
13594             
13595             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13596             
13597             this.hiddenField.dom.value =
13598                 this.hiddenValue !== undefined ? this.hiddenValue :
13599                 this.value !== undefined ? this.value : '';
13600         
13601             this.el.dom.removeAttribute('name');
13602             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13603         }
13604         
13605         if(this.multiple){
13606             this.choices = this.el.select('ul.select2-choices', true).first();
13607             this.searchField = this.el.select('ul li.select2-search-field', true).first();
13608         }
13609         
13610         if(this.removable && !this.multiple){
13611             var close = this.closeTriggerEl();
13612             if(close){
13613                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13614                 close.on('click', this.removeBtnClick, this, close);
13615             }
13616         }
13617         /*
13618          * fix the bug in Safari iOS8
13619          */
13620         _this = this;
13621         
13622         this.inputEl().on("focus", function(e){
13623             if(_this.disabled){
13624                 return;
13625             }
13626             document.activeElement.blur();
13627         }, this);
13628         
13629         return;
13630         
13631         
13632     },
13633     
13634     renderTouchView : function()
13635     {
13636         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13637         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13638         
13639         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13640         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13641         
13642         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13643         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13644         this.touchViewBodyEl.setStyle('overflow', 'auto');
13645         
13646         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13647         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13648         
13649         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13650         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13651         
13652     },
13653     
13654     showTouchView : function()
13655     {
13656         if(this.disabled){
13657             return;
13658         }
13659         
13660         this.touchViewHeaderEl.hide();
13661
13662         if(this.fieldLabel.length){
13663             this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13664             this.touchViewHeaderEl.show();
13665         }
13666
13667         this.touchViewEl.show();
13668
13669         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13670         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13671
13672         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13673
13674         if(this.fieldLabel.length){
13675             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13676         }
13677         
13678         this.touchViewBodyEl.setHeight(bodyHeight);
13679
13680         if(this.animate){
13681             var _this = this;
13682             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13683         }else{
13684             this.touchViewEl.addClass('in');
13685         }
13686
13687         this.doTouchViewQuery();
13688         
13689     },
13690     
13691     hideTouchView : function()
13692     {
13693         this.touchViewEl.removeClass('in');
13694
13695         if(this.animate){
13696             var _this = this;
13697             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13698         }else{
13699             this.touchViewEl.setStyle('display', 'none');
13700         }
13701         
13702     },
13703     
13704     setTouchViewValue : function()
13705     {
13706         if(this.multiple){
13707             this.clearItem();
13708         
13709             var _this = this;
13710
13711             Roo.each(this.tickItems, function(o){
13712                 this.addItem(o);
13713             }, this);
13714         }
13715         
13716         this.hideTouchView();
13717     },
13718     
13719     doTouchViewQuery : function()
13720     {
13721         var qe = {
13722             query: '',
13723             forceAll: true,
13724             combo: this,
13725             cancel:false
13726         };
13727         
13728         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13729             return false;
13730         }
13731         
13732         if(!this.alwaysQuery || this.mode == 'local'){
13733             this.onTouchViewLoad();
13734             return;
13735         }
13736         
13737         this.store.load();
13738     },
13739     
13740     onTouchViewBeforeLoad : function(combo,opts)
13741     {
13742         return;
13743     },
13744
13745     // private
13746     onTouchViewLoad : function()
13747     {
13748         if(this.store.getCount() < 1){
13749             this.onTouchViewEmptyResults();
13750             return;
13751         }
13752         
13753         this.clearTouchView();
13754         
13755         var rawValue = this.getRawValue();
13756         
13757         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13758         
13759         this.tickItems = [];
13760         
13761         this.store.data.each(function(d, rowIndex){
13762             var row = this.touchViewListGroup.createChild(template);
13763             
13764             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13765                 var cfg = {
13766                     data : d.data,
13767                     html : d.data[this.displayField]
13768                 };
13769                 
13770                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
13771                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
13772                 }
13773             }
13774             
13775             if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
13776                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13777             }
13778             
13779             if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
13780                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13781                 this.tickItems.push(d.data);
13782             }
13783             
13784             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
13785             
13786         }, this);
13787         
13788         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
13789         
13790         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13791
13792         if(this.fieldLabel.length){
13793             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13794         }
13795
13796         var listHeight = this.touchViewListGroup.getHeight();
13797         
13798         var _this = this;
13799         
13800         if(firstChecked && listHeight > bodyHeight){
13801             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
13802         }
13803         
13804     },
13805     
13806     onTouchViewLoadException : function()
13807     {
13808         this.hideTouchView();
13809     },
13810     
13811     onTouchViewEmptyResults : function()
13812     {
13813         this.clearTouchView();
13814         
13815         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
13816         
13817         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
13818         
13819     },
13820     
13821     clearTouchView : function()
13822     {
13823         this.touchViewListGroup.dom.innerHTML = '';
13824     },
13825     
13826     onTouchViewClick : function(e, el, o)
13827     {
13828         e.preventDefault();
13829         
13830         var row = o.row;
13831         var rowIndex = o.rowIndex;
13832         
13833         var r = this.store.getAt(rowIndex);
13834         
13835         if(!this.multiple){
13836             Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
13837                 c.dom.removeAttribute('checked');
13838             }, this);
13839             
13840             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13841         
13842             this.setFromData(r.data);
13843             
13844             var close = this.closeTriggerEl();
13845         
13846             if(close){
13847                 close.show();
13848             }
13849
13850             this.hideTouchView();
13851             
13852             this.fireEvent('select', this, r, rowIndex);
13853             
13854             return;
13855         }
13856         
13857         if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
13858             row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
13859             this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
13860             return;
13861         }
13862         
13863         row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13864         this.addItem(r.data);
13865         this.tickItems.push(r.data);
13866         
13867     }
13868     
13869
13870     /** 
13871     * @cfg {Boolean} grow 
13872     * @hide 
13873     */
13874     /** 
13875     * @cfg {Number} growMin 
13876     * @hide 
13877     */
13878     /** 
13879     * @cfg {Number} growMax 
13880     * @hide 
13881     */
13882     /**
13883      * @hide
13884      * @method autoSize
13885      */
13886 });
13887
13888 Roo.apply(Roo.bootstrap.ComboBox,  {
13889     
13890     header : {
13891         tag: 'div',
13892         cls: 'modal-header',
13893         cn: [
13894             {
13895                 tag: 'h4',
13896                 cls: 'modal-title'
13897             }
13898         ]
13899     },
13900     
13901     body : {
13902         tag: 'div',
13903         cls: 'modal-body',
13904         cn: [
13905             {
13906                 tag: 'ul',
13907                 cls: 'list-group'
13908             }
13909         ]
13910     },
13911     
13912     listItemRadio : {
13913         tag: 'li',
13914         cls: 'list-group-item',
13915         cn: [
13916             {
13917                 tag: 'span',
13918                 cls: 'roo-combobox-list-group-item-value'
13919             },
13920             {
13921                 tag: 'div',
13922                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
13923                 cn: [
13924                     {
13925                         tag: 'input',
13926                         type: 'radio'
13927                     },
13928                     {
13929                         tag: 'label'
13930                     }
13931                 ]
13932             }
13933         ]
13934     },
13935     
13936     listItemCheckbox : {
13937         tag: 'li',
13938         cls: 'list-group-item',
13939         cn: [
13940             {
13941                 tag: 'span',
13942                 cls: 'roo-combobox-list-group-item-value'
13943             },
13944             {
13945                 tag: 'div',
13946                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
13947                 cn: [
13948                     {
13949                         tag: 'input',
13950                         type: 'checkbox'
13951                     },
13952                     {
13953                         tag: 'label'
13954                     }
13955                 ]
13956             }
13957         ]
13958     },
13959     
13960     emptyResult : {
13961         tag: 'div',
13962         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
13963     },
13964     
13965     footer : {
13966         tag: 'div',
13967         cls: 'modal-footer',
13968         cn: [
13969             {
13970                 tag: 'div',
13971                 cls: 'row',
13972                 cn: [
13973                     {
13974                         tag: 'div',
13975                         cls: 'col-xs-6 text-left',
13976                         cn: {
13977                             tag: 'button',
13978                             cls: 'btn btn-danger roo-touch-view-cancel',
13979                             html: 'Cancel'
13980                         }
13981                     },
13982                     {
13983                         tag: 'div',
13984                         cls: 'col-xs-6 text-right',
13985                         cn: {
13986                             tag: 'button',
13987                             cls: 'btn btn-success roo-touch-view-ok',
13988                             html: 'OK'
13989                         }
13990                     }
13991                 ]
13992             }
13993         ]
13994         
13995     }
13996 });
13997
13998 Roo.apply(Roo.bootstrap.ComboBox,  {
13999     
14000     touchViewTemplate : {
14001         tag: 'div',
14002         cls: 'modal fade roo-combobox-touch-view',
14003         cn: [
14004             {
14005                 tag: 'div',
14006                 cls: 'modal-dialog',
14007                 style : 'position:fixed', // we have to fix position....
14008                 cn: [
14009                     {
14010                         tag: 'div',
14011                         cls: 'modal-content',
14012                         cn: [
14013                             Roo.bootstrap.ComboBox.header,
14014                             Roo.bootstrap.ComboBox.body,
14015                             Roo.bootstrap.ComboBox.footer
14016                         ]
14017                     }
14018                 ]
14019             }
14020         ]
14021     }
14022 });/*
14023  * Based on:
14024  * Ext JS Library 1.1.1
14025  * Copyright(c) 2006-2007, Ext JS, LLC.
14026  *
14027  * Originally Released Under LGPL - original licence link has changed is not relivant.
14028  *
14029  * Fork - LGPL
14030  * <script type="text/javascript">
14031  */
14032
14033 /**
14034  * @class Roo.View
14035  * @extends Roo.util.Observable
14036  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
14037  * This class also supports single and multi selection modes. <br>
14038  * Create a data model bound view:
14039  <pre><code>
14040  var store = new Roo.data.Store(...);
14041
14042  var view = new Roo.View({
14043     el : "my-element",
14044     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
14045  
14046     singleSelect: true,
14047     selectedClass: "ydataview-selected",
14048     store: store
14049  });
14050
14051  // listen for node click?
14052  view.on("click", function(vw, index, node, e){
14053  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14054  });
14055
14056  // load XML data
14057  dataModel.load("foobar.xml");
14058  </code></pre>
14059  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14060  * <br><br>
14061  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14062  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14063  * 
14064  * Note: old style constructor is still suported (container, template, config)
14065  * 
14066  * @constructor
14067  * Create a new View
14068  * @param {Object} config The config object
14069  * 
14070  */
14071 Roo.View = function(config, depreciated_tpl, depreciated_config){
14072     
14073     this.parent = false;
14074     
14075     if (typeof(depreciated_tpl) == 'undefined') {
14076         // new way.. - universal constructor.
14077         Roo.apply(this, config);
14078         this.el  = Roo.get(this.el);
14079     } else {
14080         // old format..
14081         this.el  = Roo.get(config);
14082         this.tpl = depreciated_tpl;
14083         Roo.apply(this, depreciated_config);
14084     }
14085     this.wrapEl  = this.el.wrap().wrap();
14086     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14087     
14088     
14089     if(typeof(this.tpl) == "string"){
14090         this.tpl = new Roo.Template(this.tpl);
14091     } else {
14092         // support xtype ctors..
14093         this.tpl = new Roo.factory(this.tpl, Roo);
14094     }
14095     
14096     
14097     this.tpl.compile();
14098     
14099     /** @private */
14100     this.addEvents({
14101         /**
14102          * @event beforeclick
14103          * Fires before a click is processed. Returns false to cancel the default action.
14104          * @param {Roo.View} this
14105          * @param {Number} index The index of the target node
14106          * @param {HTMLElement} node The target node
14107          * @param {Roo.EventObject} e The raw event object
14108          */
14109             "beforeclick" : true,
14110         /**
14111          * @event click
14112          * Fires when a template node is clicked.
14113          * @param {Roo.View} this
14114          * @param {Number} index The index of the target node
14115          * @param {HTMLElement} node The target node
14116          * @param {Roo.EventObject} e The raw event object
14117          */
14118             "click" : true,
14119         /**
14120          * @event dblclick
14121          * Fires when a template node is double clicked.
14122          * @param {Roo.View} this
14123          * @param {Number} index The index of the target node
14124          * @param {HTMLElement} node The target node
14125          * @param {Roo.EventObject} e The raw event object
14126          */
14127             "dblclick" : true,
14128         /**
14129          * @event contextmenu
14130          * Fires when a template node is right clicked.
14131          * @param {Roo.View} this
14132          * @param {Number} index The index of the target node
14133          * @param {HTMLElement} node The target node
14134          * @param {Roo.EventObject} e The raw event object
14135          */
14136             "contextmenu" : true,
14137         /**
14138          * @event selectionchange
14139          * Fires when the selected nodes change.
14140          * @param {Roo.View} this
14141          * @param {Array} selections Array of the selected nodes
14142          */
14143             "selectionchange" : true,
14144     
14145         /**
14146          * @event beforeselect
14147          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14148          * @param {Roo.View} this
14149          * @param {HTMLElement} node The node to be selected
14150          * @param {Array} selections Array of currently selected nodes
14151          */
14152             "beforeselect" : true,
14153         /**
14154          * @event preparedata
14155          * Fires on every row to render, to allow you to change the data.
14156          * @param {Roo.View} this
14157          * @param {Object} data to be rendered (change this)
14158          */
14159           "preparedata" : true
14160           
14161           
14162         });
14163
14164
14165
14166     this.el.on({
14167         "click": this.onClick,
14168         "dblclick": this.onDblClick,
14169         "contextmenu": this.onContextMenu,
14170         scope:this
14171     });
14172
14173     this.selections = [];
14174     this.nodes = [];
14175     this.cmp = new Roo.CompositeElementLite([]);
14176     if(this.store){
14177         this.store = Roo.factory(this.store, Roo.data);
14178         this.setStore(this.store, true);
14179     }
14180     
14181     if ( this.footer && this.footer.xtype) {
14182            
14183          var fctr = this.wrapEl.appendChild(document.createElement("div"));
14184         
14185         this.footer.dataSource = this.store;
14186         this.footer.container = fctr;
14187         this.footer = Roo.factory(this.footer, Roo);
14188         fctr.insertFirst(this.el);
14189         
14190         // this is a bit insane - as the paging toolbar seems to detach the el..
14191 //        dom.parentNode.parentNode.parentNode
14192          // they get detached?
14193     }
14194     
14195     
14196     Roo.View.superclass.constructor.call(this);
14197     
14198     
14199 };
14200
14201 Roo.extend(Roo.View, Roo.util.Observable, {
14202     
14203      /**
14204      * @cfg {Roo.data.Store} store Data store to load data from.
14205      */
14206     store : false,
14207     
14208     /**
14209      * @cfg {String|Roo.Element} el The container element.
14210      */
14211     el : '',
14212     
14213     /**
14214      * @cfg {String|Roo.Template} tpl The template used by this View 
14215      */
14216     tpl : false,
14217     /**
14218      * @cfg {String} dataName the named area of the template to use as the data area
14219      *                          Works with domtemplates roo-name="name"
14220      */
14221     dataName: false,
14222     /**
14223      * @cfg {String} selectedClass The css class to add to selected nodes
14224      */
14225     selectedClass : "x-view-selected",
14226      /**
14227      * @cfg {String} emptyText The empty text to show when nothing is loaded.
14228      */
14229     emptyText : "",
14230     
14231     /**
14232      * @cfg {String} text to display on mask (default Loading)
14233      */
14234     mask : false,
14235     /**
14236      * @cfg {Boolean} multiSelect Allow multiple selection
14237      */
14238     multiSelect : false,
14239     /**
14240      * @cfg {Boolean} singleSelect Allow single selection
14241      */
14242     singleSelect:  false,
14243     
14244     /**
14245      * @cfg {Boolean} toggleSelect - selecting 
14246      */
14247     toggleSelect : false,
14248     
14249     /**
14250      * @cfg {Boolean} tickable - selecting 
14251      */
14252     tickable : false,
14253     
14254     /**
14255      * Returns the element this view is bound to.
14256      * @return {Roo.Element}
14257      */
14258     getEl : function(){
14259         return this.wrapEl;
14260     },
14261     
14262     
14263
14264     /**
14265      * Refreshes the view. - called by datachanged on the store. - do not call directly.
14266      */
14267     refresh : function(){
14268         //Roo.log('refresh');
14269         var t = this.tpl;
14270         
14271         // if we are using something like 'domtemplate', then
14272         // the what gets used is:
14273         // t.applySubtemplate(NAME, data, wrapping data..)
14274         // the outer template then get' applied with
14275         //     the store 'extra data'
14276         // and the body get's added to the
14277         //      roo-name="data" node?
14278         //      <span class='roo-tpl-{name}'></span> ?????
14279         
14280         
14281         
14282         this.clearSelections();
14283         this.el.update("");
14284         var html = [];
14285         var records = this.store.getRange();
14286         if(records.length < 1) {
14287             
14288             // is this valid??  = should it render a template??
14289             
14290             this.el.update(this.emptyText);
14291             return;
14292         }
14293         var el = this.el;
14294         if (this.dataName) {
14295             this.el.update(t.apply(this.store.meta)); //????
14296             el = this.el.child('.roo-tpl-' + this.dataName);
14297         }
14298         
14299         for(var i = 0, len = records.length; i < len; i++){
14300             var data = this.prepareData(records[i].data, i, records[i]);
14301             this.fireEvent("preparedata", this, data, i, records[i]);
14302             
14303             var d = Roo.apply({}, data);
14304             
14305             if(this.tickable){
14306                 Roo.apply(d, {'roo-id' : Roo.id()});
14307                 
14308                 var _this = this;
14309             
14310                 Roo.each(this.parent.item, function(item){
14311                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14312                         return;
14313                     }
14314                     Roo.apply(d, {'roo-data-checked' : 'checked'});
14315                 });
14316             }
14317             
14318             html[html.length] = Roo.util.Format.trim(
14319                 this.dataName ?
14320                     t.applySubtemplate(this.dataName, d, this.store.meta) :
14321                     t.apply(d)
14322             );
14323         }
14324         
14325         
14326         
14327         el.update(html.join(""));
14328         this.nodes = el.dom.childNodes;
14329         this.updateIndexes(0);
14330     },
14331     
14332
14333     /**
14334      * Function to override to reformat the data that is sent to
14335      * the template for each node.
14336      * DEPRICATED - use the preparedata event handler.
14337      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14338      * a JSON object for an UpdateManager bound view).
14339      */
14340     prepareData : function(data, index, record)
14341     {
14342         this.fireEvent("preparedata", this, data, index, record);
14343         return data;
14344     },
14345
14346     onUpdate : function(ds, record){
14347         // Roo.log('on update');   
14348         this.clearSelections();
14349         var index = this.store.indexOf(record);
14350         var n = this.nodes[index];
14351         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14352         n.parentNode.removeChild(n);
14353         this.updateIndexes(index, index);
14354     },
14355
14356     
14357     
14358 // --------- FIXME     
14359     onAdd : function(ds, records, index)
14360     {
14361         //Roo.log(['on Add', ds, records, index] );        
14362         this.clearSelections();
14363         if(this.nodes.length == 0){
14364             this.refresh();
14365             return;
14366         }
14367         var n = this.nodes[index];
14368         for(var i = 0, len = records.length; i < len; i++){
14369             var d = this.prepareData(records[i].data, i, records[i]);
14370             if(n){
14371                 this.tpl.insertBefore(n, d);
14372             }else{
14373                 
14374                 this.tpl.append(this.el, d);
14375             }
14376         }
14377         this.updateIndexes(index);
14378     },
14379
14380     onRemove : function(ds, record, index){
14381        // Roo.log('onRemove');
14382         this.clearSelections();
14383         var el = this.dataName  ?
14384             this.el.child('.roo-tpl-' + this.dataName) :
14385             this.el; 
14386         
14387         el.dom.removeChild(this.nodes[index]);
14388         this.updateIndexes(index);
14389     },
14390
14391     /**
14392      * Refresh an individual node.
14393      * @param {Number} index
14394      */
14395     refreshNode : function(index){
14396         this.onUpdate(this.store, this.store.getAt(index));
14397     },
14398
14399     updateIndexes : function(startIndex, endIndex){
14400         var ns = this.nodes;
14401         startIndex = startIndex || 0;
14402         endIndex = endIndex || ns.length - 1;
14403         for(var i = startIndex; i <= endIndex; i++){
14404             ns[i].nodeIndex = i;
14405         }
14406     },
14407
14408     /**
14409      * Changes the data store this view uses and refresh the view.
14410      * @param {Store} store
14411      */
14412     setStore : function(store, initial){
14413         if(!initial && this.store){
14414             this.store.un("datachanged", this.refresh);
14415             this.store.un("add", this.onAdd);
14416             this.store.un("remove", this.onRemove);
14417             this.store.un("update", this.onUpdate);
14418             this.store.un("clear", this.refresh);
14419             this.store.un("beforeload", this.onBeforeLoad);
14420             this.store.un("load", this.onLoad);
14421             this.store.un("loadexception", this.onLoad);
14422         }
14423         if(store){
14424           
14425             store.on("datachanged", this.refresh, this);
14426             store.on("add", this.onAdd, this);
14427             store.on("remove", this.onRemove, this);
14428             store.on("update", this.onUpdate, this);
14429             store.on("clear", this.refresh, this);
14430             store.on("beforeload", this.onBeforeLoad, this);
14431             store.on("load", this.onLoad, this);
14432             store.on("loadexception", this.onLoad, this);
14433         }
14434         
14435         if(store){
14436             this.refresh();
14437         }
14438     },
14439     /**
14440      * onbeforeLoad - masks the loading area.
14441      *
14442      */
14443     onBeforeLoad : function(store,opts)
14444     {
14445          //Roo.log('onBeforeLoad');   
14446         if (!opts.add) {
14447             this.el.update("");
14448         }
14449         this.el.mask(this.mask ? this.mask : "Loading" ); 
14450     },
14451     onLoad : function ()
14452     {
14453         this.el.unmask();
14454     },
14455     
14456
14457     /**
14458      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14459      * @param {HTMLElement} node
14460      * @return {HTMLElement} The template node
14461      */
14462     findItemFromChild : function(node){
14463         var el = this.dataName  ?
14464             this.el.child('.roo-tpl-' + this.dataName,true) :
14465             this.el.dom; 
14466         
14467         if(!node || node.parentNode == el){
14468                     return node;
14469             }
14470             var p = node.parentNode;
14471             while(p && p != el){
14472             if(p.parentNode == el){
14473                 return p;
14474             }
14475             p = p.parentNode;
14476         }
14477             return null;
14478     },
14479
14480     /** @ignore */
14481     onClick : function(e){
14482         var item = this.findItemFromChild(e.getTarget());
14483         if(item){
14484             var index = this.indexOf(item);
14485             if(this.onItemClick(item, index, e) !== false){
14486                 this.fireEvent("click", this, index, item, e);
14487             }
14488         }else{
14489             this.clearSelections();
14490         }
14491     },
14492
14493     /** @ignore */
14494     onContextMenu : function(e){
14495         var item = this.findItemFromChild(e.getTarget());
14496         if(item){
14497             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14498         }
14499     },
14500
14501     /** @ignore */
14502     onDblClick : function(e){
14503         var item = this.findItemFromChild(e.getTarget());
14504         if(item){
14505             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14506         }
14507     },
14508
14509     onItemClick : function(item, index, e)
14510     {
14511         if(this.fireEvent("beforeclick", this, index, item, e) === false){
14512             return false;
14513         }
14514         if (this.toggleSelect) {
14515             var m = this.isSelected(item) ? 'unselect' : 'select';
14516             //Roo.log(m);
14517             var _t = this;
14518             _t[m](item, true, false);
14519             return true;
14520         }
14521         if(this.multiSelect || this.singleSelect){
14522             if(this.multiSelect && e.shiftKey && this.lastSelection){
14523                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14524             }else{
14525                 this.select(item, this.multiSelect && e.ctrlKey);
14526                 this.lastSelection = item;
14527             }
14528             
14529             if(!this.tickable){
14530                 e.preventDefault();
14531             }
14532             
14533         }
14534         return true;
14535     },
14536
14537     /**
14538      * Get the number of selected nodes.
14539      * @return {Number}
14540      */
14541     getSelectionCount : function(){
14542         return this.selections.length;
14543     },
14544
14545     /**
14546      * Get the currently selected nodes.
14547      * @return {Array} An array of HTMLElements
14548      */
14549     getSelectedNodes : function(){
14550         return this.selections;
14551     },
14552
14553     /**
14554      * Get the indexes of the selected nodes.
14555      * @return {Array}
14556      */
14557     getSelectedIndexes : function(){
14558         var indexes = [], s = this.selections;
14559         for(var i = 0, len = s.length; i < len; i++){
14560             indexes.push(s[i].nodeIndex);
14561         }
14562         return indexes;
14563     },
14564
14565     /**
14566      * Clear all selections
14567      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14568      */
14569     clearSelections : function(suppressEvent){
14570         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14571             this.cmp.elements = this.selections;
14572             this.cmp.removeClass(this.selectedClass);
14573             this.selections = [];
14574             if(!suppressEvent){
14575                 this.fireEvent("selectionchange", this, this.selections);
14576             }
14577         }
14578     },
14579
14580     /**
14581      * Returns true if the passed node is selected
14582      * @param {HTMLElement/Number} node The node or node index
14583      * @return {Boolean}
14584      */
14585     isSelected : function(node){
14586         var s = this.selections;
14587         if(s.length < 1){
14588             return false;
14589         }
14590         node = this.getNode(node);
14591         return s.indexOf(node) !== -1;
14592     },
14593
14594     /**
14595      * Selects nodes.
14596      * @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
14597      * @param {Boolean} keepExisting (optional) true to keep existing selections
14598      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14599      */
14600     select : function(nodeInfo, keepExisting, suppressEvent){
14601         if(nodeInfo instanceof Array){
14602             if(!keepExisting){
14603                 this.clearSelections(true);
14604             }
14605             for(var i = 0, len = nodeInfo.length; i < len; i++){
14606                 this.select(nodeInfo[i], true, true);
14607             }
14608             return;
14609         } 
14610         var node = this.getNode(nodeInfo);
14611         if(!node || this.isSelected(node)){
14612             return; // already selected.
14613         }
14614         if(!keepExisting){
14615             this.clearSelections(true);
14616         }
14617         
14618         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14619             Roo.fly(node).addClass(this.selectedClass);
14620             this.selections.push(node);
14621             if(!suppressEvent){
14622                 this.fireEvent("selectionchange", this, this.selections);
14623             }
14624         }
14625         
14626         
14627     },
14628       /**
14629      * Unselects nodes.
14630      * @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
14631      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14632      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14633      */
14634     unselect : function(nodeInfo, keepExisting, suppressEvent)
14635     {
14636         if(nodeInfo instanceof Array){
14637             Roo.each(this.selections, function(s) {
14638                 this.unselect(s, nodeInfo);
14639             }, this);
14640             return;
14641         }
14642         var node = this.getNode(nodeInfo);
14643         if(!node || !this.isSelected(node)){
14644             //Roo.log("not selected");
14645             return; // not selected.
14646         }
14647         // fireevent???
14648         var ns = [];
14649         Roo.each(this.selections, function(s) {
14650             if (s == node ) {
14651                 Roo.fly(node).removeClass(this.selectedClass);
14652
14653                 return;
14654             }
14655             ns.push(s);
14656         },this);
14657         
14658         this.selections= ns;
14659         this.fireEvent("selectionchange", this, this.selections);
14660     },
14661
14662     /**
14663      * Gets a template node.
14664      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14665      * @return {HTMLElement} The node or null if it wasn't found
14666      */
14667     getNode : function(nodeInfo){
14668         if(typeof nodeInfo == "string"){
14669             return document.getElementById(nodeInfo);
14670         }else if(typeof nodeInfo == "number"){
14671             return this.nodes[nodeInfo];
14672         }
14673         return nodeInfo;
14674     },
14675
14676     /**
14677      * Gets a range template nodes.
14678      * @param {Number} startIndex
14679      * @param {Number} endIndex
14680      * @return {Array} An array of nodes
14681      */
14682     getNodes : function(start, end){
14683         var ns = this.nodes;
14684         start = start || 0;
14685         end = typeof end == "undefined" ? ns.length - 1 : end;
14686         var nodes = [];
14687         if(start <= end){
14688             for(var i = start; i <= end; i++){
14689                 nodes.push(ns[i]);
14690             }
14691         } else{
14692             for(var i = start; i >= end; i--){
14693                 nodes.push(ns[i]);
14694             }
14695         }
14696         return nodes;
14697     },
14698
14699     /**
14700      * Finds the index of the passed node
14701      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14702      * @return {Number} The index of the node or -1
14703      */
14704     indexOf : function(node){
14705         node = this.getNode(node);
14706         if(typeof node.nodeIndex == "number"){
14707             return node.nodeIndex;
14708         }
14709         var ns = this.nodes;
14710         for(var i = 0, len = ns.length; i < len; i++){
14711             if(ns[i] == node){
14712                 return i;
14713             }
14714         }
14715         return -1;
14716     }
14717 });
14718 /*
14719  * - LGPL
14720  *
14721  * based on jquery fullcalendar
14722  * 
14723  */
14724
14725 Roo.bootstrap = Roo.bootstrap || {};
14726 /**
14727  * @class Roo.bootstrap.Calendar
14728  * @extends Roo.bootstrap.Component
14729  * Bootstrap Calendar class
14730  * @cfg {Boolean} loadMask (true|false) default false
14731  * @cfg {Object} header generate the user specific header of the calendar, default false
14732
14733  * @constructor
14734  * Create a new Container
14735  * @param {Object} config The config object
14736  */
14737
14738
14739
14740 Roo.bootstrap.Calendar = function(config){
14741     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14742      this.addEvents({
14743         /**
14744              * @event select
14745              * Fires when a date is selected
14746              * @param {DatePicker} this
14747              * @param {Date} date The selected date
14748              */
14749         'select': true,
14750         /**
14751              * @event monthchange
14752              * Fires when the displayed month changes 
14753              * @param {DatePicker} this
14754              * @param {Date} date The selected month
14755              */
14756         'monthchange': true,
14757         /**
14758              * @event evententer
14759              * Fires when mouse over an event
14760              * @param {Calendar} this
14761              * @param {event} Event
14762              */
14763         'evententer': true,
14764         /**
14765              * @event eventleave
14766              * Fires when the mouse leaves an
14767              * @param {Calendar} this
14768              * @param {event}
14769              */
14770         'eventleave': true,
14771         /**
14772              * @event eventclick
14773              * Fires when the mouse click an
14774              * @param {Calendar} this
14775              * @param {event}
14776              */
14777         'eventclick': true
14778         
14779     });
14780
14781 };
14782
14783 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
14784     
14785      /**
14786      * @cfg {Number} startDay
14787      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
14788      */
14789     startDay : 0,
14790     
14791     loadMask : false,
14792     
14793     header : false,
14794       
14795     getAutoCreate : function(){
14796         
14797         
14798         var fc_button = function(name, corner, style, content ) {
14799             return Roo.apply({},{
14800                 tag : 'span',
14801                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
14802                          (corner.length ?
14803                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
14804                             ''
14805                         ),
14806                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
14807                 unselectable: 'on'
14808             });
14809         };
14810         
14811         var header = {};
14812         
14813         if(!this.header){
14814             header = {
14815                 tag : 'table',
14816                 cls : 'fc-header',
14817                 style : 'width:100%',
14818                 cn : [
14819                     {
14820                         tag: 'tr',
14821                         cn : [
14822                             {
14823                                 tag : 'td',
14824                                 cls : 'fc-header-left',
14825                                 cn : [
14826                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
14827                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
14828                                     { tag: 'span', cls: 'fc-header-space' },
14829                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
14830
14831
14832                                 ]
14833                             },
14834
14835                             {
14836                                 tag : 'td',
14837                                 cls : 'fc-header-center',
14838                                 cn : [
14839                                     {
14840                                         tag: 'span',
14841                                         cls: 'fc-header-title',
14842                                         cn : {
14843                                             tag: 'H2',
14844                                             html : 'month / year'
14845                                         }
14846                                     }
14847
14848                                 ]
14849                             },
14850                             {
14851                                 tag : 'td',
14852                                 cls : 'fc-header-right',
14853                                 cn : [
14854                               /*      fc_button('month', 'left', '', 'month' ),
14855                                     fc_button('week', '', '', 'week' ),
14856                                     fc_button('day', 'right', '', 'day' )
14857                                 */    
14858
14859                                 ]
14860                             }
14861
14862                         ]
14863                     }
14864                 ]
14865             };
14866         }
14867         
14868         header = this.header;
14869         
14870        
14871         var cal_heads = function() {
14872             var ret = [];
14873             // fixme - handle this.
14874             
14875             for (var i =0; i < Date.dayNames.length; i++) {
14876                 var d = Date.dayNames[i];
14877                 ret.push({
14878                     tag: 'th',
14879                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
14880                     html : d.substring(0,3)
14881                 });
14882                 
14883             }
14884             ret[0].cls += ' fc-first';
14885             ret[6].cls += ' fc-last';
14886             return ret;
14887         };
14888         var cal_cell = function(n) {
14889             return  {
14890                 tag: 'td',
14891                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
14892                 cn : [
14893                     {
14894                         cn : [
14895                             {
14896                                 cls: 'fc-day-number',
14897                                 html: 'D'
14898                             },
14899                             {
14900                                 cls: 'fc-day-content',
14901                              
14902                                 cn : [
14903                                      {
14904                                         style: 'position: relative;' // height: 17px;
14905                                     }
14906                                 ]
14907                             }
14908                             
14909                             
14910                         ]
14911                     }
14912                 ]
14913                 
14914             }
14915         };
14916         var cal_rows = function() {
14917             
14918             var ret = [];
14919             for (var r = 0; r < 6; r++) {
14920                 var row= {
14921                     tag : 'tr',
14922                     cls : 'fc-week',
14923                     cn : []
14924                 };
14925                 
14926                 for (var i =0; i < Date.dayNames.length; i++) {
14927                     var d = Date.dayNames[i];
14928                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
14929
14930                 }
14931                 row.cn[0].cls+=' fc-first';
14932                 row.cn[0].cn[0].style = 'min-height:90px';
14933                 row.cn[6].cls+=' fc-last';
14934                 ret.push(row);
14935                 
14936             }
14937             ret[0].cls += ' fc-first';
14938             ret[4].cls += ' fc-prev-last';
14939             ret[5].cls += ' fc-last';
14940             return ret;
14941             
14942         };
14943         
14944         var cal_table = {
14945             tag: 'table',
14946             cls: 'fc-border-separate',
14947             style : 'width:100%',
14948             cellspacing  : 0,
14949             cn : [
14950                 { 
14951                     tag: 'thead',
14952                     cn : [
14953                         { 
14954                             tag: 'tr',
14955                             cls : 'fc-first fc-last',
14956                             cn : cal_heads()
14957                         }
14958                     ]
14959                 },
14960                 { 
14961                     tag: 'tbody',
14962                     cn : cal_rows()
14963                 }
14964                   
14965             ]
14966         };
14967          
14968          var cfg = {
14969             cls : 'fc fc-ltr',
14970             cn : [
14971                 header,
14972                 {
14973                     cls : 'fc-content',
14974                     style : "position: relative;",
14975                     cn : [
14976                         {
14977                             cls : 'fc-view fc-view-month fc-grid',
14978                             style : 'position: relative',
14979                             unselectable : 'on',
14980                             cn : [
14981                                 {
14982                                     cls : 'fc-event-container',
14983                                     style : 'position:absolute;z-index:8;top:0;left:0;'
14984                                 },
14985                                 cal_table
14986                             ]
14987                         }
14988                     ]
14989     
14990                 }
14991            ] 
14992             
14993         };
14994         
14995          
14996         
14997         return cfg;
14998     },
14999     
15000     
15001     initEvents : function()
15002     {
15003         if(!this.store){
15004             throw "can not find store for calendar";
15005         }
15006         
15007         var mark = {
15008             tag: "div",
15009             cls:"x-dlg-mask",
15010             style: "text-align:center",
15011             cn: [
15012                 {
15013                     tag: "div",
15014                     style: "background-color:white;width:50%;margin:250 auto",
15015                     cn: [
15016                         {
15017                             tag: "img",
15018                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
15019                         },
15020                         {
15021                             tag: "span",
15022                             html: "Loading"
15023                         }
15024                         
15025                     ]
15026                 }
15027             ]
15028         };
15029         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15030         
15031         var size = this.el.select('.fc-content', true).first().getSize();
15032         this.maskEl.setSize(size.width, size.height);
15033         this.maskEl.enableDisplayMode("block");
15034         if(!this.loadMask){
15035             this.maskEl.hide();
15036         }
15037         
15038         this.store = Roo.factory(this.store, Roo.data);
15039         this.store.on('load', this.onLoad, this);
15040         this.store.on('beforeload', this.onBeforeLoad, this);
15041         
15042         this.resize();
15043         
15044         this.cells = this.el.select('.fc-day',true);
15045         //Roo.log(this.cells);
15046         this.textNodes = this.el.query('.fc-day-number');
15047         this.cells.addClassOnOver('fc-state-hover');
15048         
15049         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15050         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15051         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15052         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15053         
15054         this.on('monthchange', this.onMonthChange, this);
15055         
15056         this.update(new Date().clearTime());
15057     },
15058     
15059     resize : function() {
15060         var sz  = this.el.getSize();
15061         
15062         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15063         this.el.select('.fc-day-content div',true).setHeight(34);
15064     },
15065     
15066     
15067     // private
15068     showPrevMonth : function(e){
15069         this.update(this.activeDate.add("mo", -1));
15070     },
15071     showToday : function(e){
15072         this.update(new Date().clearTime());
15073     },
15074     // private
15075     showNextMonth : function(e){
15076         this.update(this.activeDate.add("mo", 1));
15077     },
15078
15079     // private
15080     showPrevYear : function(){
15081         this.update(this.activeDate.add("y", -1));
15082     },
15083
15084     // private
15085     showNextYear : function(){
15086         this.update(this.activeDate.add("y", 1));
15087     },
15088
15089     
15090    // private
15091     update : function(date)
15092     {
15093         var vd = this.activeDate;
15094         this.activeDate = date;
15095 //        if(vd && this.el){
15096 //            var t = date.getTime();
15097 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15098 //                Roo.log('using add remove');
15099 //                
15100 //                this.fireEvent('monthchange', this, date);
15101 //                
15102 //                this.cells.removeClass("fc-state-highlight");
15103 //                this.cells.each(function(c){
15104 //                   if(c.dateValue == t){
15105 //                       c.addClass("fc-state-highlight");
15106 //                       setTimeout(function(){
15107 //                            try{c.dom.firstChild.focus();}catch(e){}
15108 //                       }, 50);
15109 //                       return false;
15110 //                   }
15111 //                   return true;
15112 //                });
15113 //                return;
15114 //            }
15115 //        }
15116         
15117         var days = date.getDaysInMonth();
15118         
15119         var firstOfMonth = date.getFirstDateOfMonth();
15120         var startingPos = firstOfMonth.getDay()-this.startDay;
15121         
15122         if(startingPos < this.startDay){
15123             startingPos += 7;
15124         }
15125         
15126         var pm = date.add(Date.MONTH, -1);
15127         var prevStart = pm.getDaysInMonth()-startingPos;
15128 //        
15129         this.cells = this.el.select('.fc-day',true);
15130         this.textNodes = this.el.query('.fc-day-number');
15131         this.cells.addClassOnOver('fc-state-hover');
15132         
15133         var cells = this.cells.elements;
15134         var textEls = this.textNodes;
15135         
15136         Roo.each(cells, function(cell){
15137             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15138         });
15139         
15140         days += startingPos;
15141
15142         // convert everything to numbers so it's fast
15143         var day = 86400000;
15144         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15145         //Roo.log(d);
15146         //Roo.log(pm);
15147         //Roo.log(prevStart);
15148         
15149         var today = new Date().clearTime().getTime();
15150         var sel = date.clearTime().getTime();
15151         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15152         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15153         var ddMatch = this.disabledDatesRE;
15154         var ddText = this.disabledDatesText;
15155         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15156         var ddaysText = this.disabledDaysText;
15157         var format = this.format;
15158         
15159         var setCellClass = function(cal, cell){
15160             cell.row = 0;
15161             cell.events = [];
15162             cell.more = [];
15163             //Roo.log('set Cell Class');
15164             cell.title = "";
15165             var t = d.getTime();
15166             
15167             //Roo.log(d);
15168             
15169             cell.dateValue = t;
15170             if(t == today){
15171                 cell.className += " fc-today";
15172                 cell.className += " fc-state-highlight";
15173                 cell.title = cal.todayText;
15174             }
15175             if(t == sel){
15176                 // disable highlight in other month..
15177                 //cell.className += " fc-state-highlight";
15178                 
15179             }
15180             // disabling
15181             if(t < min) {
15182                 cell.className = " fc-state-disabled";
15183                 cell.title = cal.minText;
15184                 return;
15185             }
15186             if(t > max) {
15187                 cell.className = " fc-state-disabled";
15188                 cell.title = cal.maxText;
15189                 return;
15190             }
15191             if(ddays){
15192                 if(ddays.indexOf(d.getDay()) != -1){
15193                     cell.title = ddaysText;
15194                     cell.className = " fc-state-disabled";
15195                 }
15196             }
15197             if(ddMatch && format){
15198                 var fvalue = d.dateFormat(format);
15199                 if(ddMatch.test(fvalue)){
15200                     cell.title = ddText.replace("%0", fvalue);
15201                     cell.className = " fc-state-disabled";
15202                 }
15203             }
15204             
15205             if (!cell.initialClassName) {
15206                 cell.initialClassName = cell.dom.className;
15207             }
15208             
15209             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
15210         };
15211
15212         var i = 0;
15213         
15214         for(; i < startingPos; i++) {
15215             textEls[i].innerHTML = (++prevStart);
15216             d.setDate(d.getDate()+1);
15217             
15218             cells[i].className = "fc-past fc-other-month";
15219             setCellClass(this, cells[i]);
15220         }
15221         
15222         var intDay = 0;
15223         
15224         for(; i < days; i++){
15225             intDay = i - startingPos + 1;
15226             textEls[i].innerHTML = (intDay);
15227             d.setDate(d.getDate()+1);
15228             
15229             cells[i].className = ''; // "x-date-active";
15230             setCellClass(this, cells[i]);
15231         }
15232         var extraDays = 0;
15233         
15234         for(; i < 42; i++) {
15235             textEls[i].innerHTML = (++extraDays);
15236             d.setDate(d.getDate()+1);
15237             
15238             cells[i].className = "fc-future fc-other-month";
15239             setCellClass(this, cells[i]);
15240         }
15241         
15242         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15243         
15244         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15245         
15246         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15247         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15248         
15249         if(totalRows != 6){
15250             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15251             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15252         }
15253         
15254         this.fireEvent('monthchange', this, date);
15255         
15256         
15257         /*
15258         if(!this.internalRender){
15259             var main = this.el.dom.firstChild;
15260             var w = main.offsetWidth;
15261             this.el.setWidth(w + this.el.getBorderWidth("lr"));
15262             Roo.fly(main).setWidth(w);
15263             this.internalRender = true;
15264             // opera does not respect the auto grow header center column
15265             // then, after it gets a width opera refuses to recalculate
15266             // without a second pass
15267             if(Roo.isOpera && !this.secondPass){
15268                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15269                 this.secondPass = true;
15270                 this.update.defer(10, this, [date]);
15271             }
15272         }
15273         */
15274         
15275     },
15276     
15277     findCell : function(dt) {
15278         dt = dt.clearTime().getTime();
15279         var ret = false;
15280         this.cells.each(function(c){
15281             //Roo.log("check " +c.dateValue + '?=' + dt);
15282             if(c.dateValue == dt){
15283                 ret = c;
15284                 return false;
15285             }
15286             return true;
15287         });
15288         
15289         return ret;
15290     },
15291     
15292     findCells : function(ev) {
15293         var s = ev.start.clone().clearTime().getTime();
15294        // Roo.log(s);
15295         var e= ev.end.clone().clearTime().getTime();
15296        // Roo.log(e);
15297         var ret = [];
15298         this.cells.each(function(c){
15299              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15300             
15301             if(c.dateValue > e){
15302                 return ;
15303             }
15304             if(c.dateValue < s){
15305                 return ;
15306             }
15307             ret.push(c);
15308         });
15309         
15310         return ret;    
15311     },
15312     
15313 //    findBestRow: function(cells)
15314 //    {
15315 //        var ret = 0;
15316 //        
15317 //        for (var i =0 ; i < cells.length;i++) {
15318 //            ret  = Math.max(cells[i].rows || 0,ret);
15319 //        }
15320 //        return ret;
15321 //        
15322 //    },
15323     
15324     
15325     addItem : function(ev)
15326     {
15327         // look for vertical location slot in
15328         var cells = this.findCells(ev);
15329         
15330 //        ev.row = this.findBestRow(cells);
15331         
15332         // work out the location.
15333         
15334         var crow = false;
15335         var rows = [];
15336         for(var i =0; i < cells.length; i++) {
15337             
15338             cells[i].row = cells[0].row;
15339             
15340             if(i == 0){
15341                 cells[i].row = cells[i].row + 1;
15342             }
15343             
15344             if (!crow) {
15345                 crow = {
15346                     start : cells[i],
15347                     end :  cells[i]
15348                 };
15349                 continue;
15350             }
15351             if (crow.start.getY() == cells[i].getY()) {
15352                 // on same row.
15353                 crow.end = cells[i];
15354                 continue;
15355             }
15356             // different row.
15357             rows.push(crow);
15358             crow = {
15359                 start: cells[i],
15360                 end : cells[i]
15361             };
15362             
15363         }
15364         
15365         rows.push(crow);
15366         ev.els = [];
15367         ev.rows = rows;
15368         ev.cells = cells;
15369         
15370         cells[0].events.push(ev);
15371         
15372         this.calevents.push(ev);
15373     },
15374     
15375     clearEvents: function() {
15376         
15377         if(!this.calevents){
15378             return;
15379         }
15380         
15381         Roo.each(this.cells.elements, function(c){
15382             c.row = 0;
15383             c.events = [];
15384             c.more = [];
15385         });
15386         
15387         Roo.each(this.calevents, function(e) {
15388             Roo.each(e.els, function(el) {
15389                 el.un('mouseenter' ,this.onEventEnter, this);
15390                 el.un('mouseleave' ,this.onEventLeave, this);
15391                 el.remove();
15392             },this);
15393         },this);
15394         
15395         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
15396             e.remove();
15397         });
15398         
15399     },
15400     
15401     renderEvents: function()
15402     {   
15403         var _this = this;
15404         
15405         this.cells.each(function(c) {
15406             
15407             if(c.row < 5){
15408                 return;
15409             }
15410             
15411             var ev = c.events;
15412             
15413             var r = 4;
15414             if(c.row != c.events.length){
15415                 r = 4 - (4 - (c.row - c.events.length));
15416             }
15417             
15418             c.events = ev.slice(0, r);
15419             c.more = ev.slice(r);
15420             
15421             if(c.more.length && c.more.length == 1){
15422                 c.events.push(c.more.pop());
15423             }
15424             
15425             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15426             
15427         });
15428             
15429         this.cells.each(function(c) {
15430             
15431             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15432             
15433             
15434             for (var e = 0; e < c.events.length; e++){
15435                 var ev = c.events[e];
15436                 var rows = ev.rows;
15437                 
15438                 for(var i = 0; i < rows.length; i++) {
15439                 
15440                     // how many rows should it span..
15441
15442                     var  cfg = {
15443                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15444                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15445
15446                         unselectable : "on",
15447                         cn : [
15448                             {
15449                                 cls: 'fc-event-inner',
15450                                 cn : [
15451     //                                {
15452     //                                  tag:'span',
15453     //                                  cls: 'fc-event-time',
15454     //                                  html : cells.length > 1 ? '' : ev.time
15455     //                                },
15456                                     {
15457                                       tag:'span',
15458                                       cls: 'fc-event-title',
15459                                       html : String.format('{0}', ev.title)
15460                                     }
15461
15462
15463                                 ]
15464                             },
15465                             {
15466                                 cls: 'ui-resizable-handle ui-resizable-e',
15467                                 html : '&nbsp;&nbsp;&nbsp'
15468                             }
15469
15470                         ]
15471                     };
15472
15473                     if (i == 0) {
15474                         cfg.cls += ' fc-event-start';
15475                     }
15476                     if ((i+1) == rows.length) {
15477                         cfg.cls += ' fc-event-end';
15478                     }
15479
15480                     var ctr = _this.el.select('.fc-event-container',true).first();
15481                     var cg = ctr.createChild(cfg);
15482
15483                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15484                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15485
15486                     var r = (c.more.length) ? 1 : 0;
15487                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
15488                     cg.setWidth(ebox.right - sbox.x -2);
15489
15490                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15491                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15492                     cg.on('click', _this.onEventClick, _this, ev);
15493
15494                     ev.els.push(cg);
15495                     
15496                 }
15497                 
15498             }
15499             
15500             
15501             if(c.more.length){
15502                 var  cfg = {
15503                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15504                     style : 'position: absolute',
15505                     unselectable : "on",
15506                     cn : [
15507                         {
15508                             cls: 'fc-event-inner',
15509                             cn : [
15510                                 {
15511                                   tag:'span',
15512                                   cls: 'fc-event-title',
15513                                   html : 'More'
15514                                 }
15515
15516
15517                             ]
15518                         },
15519                         {
15520                             cls: 'ui-resizable-handle ui-resizable-e',
15521                             html : '&nbsp;&nbsp;&nbsp'
15522                         }
15523
15524                     ]
15525                 };
15526
15527                 var ctr = _this.el.select('.fc-event-container',true).first();
15528                 var cg = ctr.createChild(cfg);
15529
15530                 var sbox = c.select('.fc-day-content',true).first().getBox();
15531                 var ebox = c.select('.fc-day-content',true).first().getBox();
15532                 //Roo.log(cg);
15533                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
15534                 cg.setWidth(ebox.right - sbox.x -2);
15535
15536                 cg.on('click', _this.onMoreEventClick, _this, c.more);
15537                 
15538             }
15539             
15540         });
15541         
15542         
15543         
15544     },
15545     
15546     onEventEnter: function (e, el,event,d) {
15547         this.fireEvent('evententer', this, el, event);
15548     },
15549     
15550     onEventLeave: function (e, el,event,d) {
15551         this.fireEvent('eventleave', this, el, event);
15552     },
15553     
15554     onEventClick: function (e, el,event,d) {
15555         this.fireEvent('eventclick', this, el, event);
15556     },
15557     
15558     onMonthChange: function () {
15559         this.store.load();
15560     },
15561     
15562     onMoreEventClick: function(e, el, more)
15563     {
15564         var _this = this;
15565         
15566         this.calpopover.placement = 'right';
15567         this.calpopover.setTitle('More');
15568         
15569         this.calpopover.setContent('');
15570         
15571         var ctr = this.calpopover.el.select('.popover-content', true).first();
15572         
15573         Roo.each(more, function(m){
15574             var cfg = {
15575                 cls : 'fc-event-hori fc-event-draggable',
15576                 html : m.title
15577             };
15578             var cg = ctr.createChild(cfg);
15579             
15580             cg.on('click', _this.onEventClick, _this, m);
15581         });
15582         
15583         this.calpopover.show(el);
15584         
15585         
15586     },
15587     
15588     onLoad: function () 
15589     {   
15590         this.calevents = [];
15591         var cal = this;
15592         
15593         if(this.store.getCount() > 0){
15594             this.store.data.each(function(d){
15595                cal.addItem({
15596                     id : d.data.id,
15597                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15598                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15599                     time : d.data.start_time,
15600                     title : d.data.title,
15601                     description : d.data.description,
15602                     venue : d.data.venue
15603                 });
15604             });
15605         }
15606         
15607         this.renderEvents();
15608         
15609         if(this.calevents.length && this.loadMask){
15610             this.maskEl.hide();
15611         }
15612     },
15613     
15614     onBeforeLoad: function()
15615     {
15616         this.clearEvents();
15617         if(this.loadMask){
15618             this.maskEl.show();
15619         }
15620     }
15621 });
15622
15623  
15624  /*
15625  * - LGPL
15626  *
15627  * element
15628  * 
15629  */
15630
15631 /**
15632  * @class Roo.bootstrap.Popover
15633  * @extends Roo.bootstrap.Component
15634  * Bootstrap Popover class
15635  * @cfg {String} html contents of the popover   (or false to use children..)
15636  * @cfg {String} title of popover (or false to hide)
15637  * @cfg {String} placement how it is placed
15638  * @cfg {String} trigger click || hover (or false to trigger manually)
15639  * @cfg {String} over what (parent or false to trigger manually.)
15640  * @cfg {Number} delay - delay before showing
15641  
15642  * @constructor
15643  * Create a new Popover
15644  * @param {Object} config The config object
15645  */
15646
15647 Roo.bootstrap.Popover = function(config){
15648     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15649 };
15650
15651 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
15652     
15653     title: 'Fill in a title',
15654     html: false,
15655     
15656     placement : 'right',
15657     trigger : 'hover', // hover
15658     
15659     delay : 0,
15660     
15661     over: 'parent',
15662     
15663     can_build_overlaid : false,
15664     
15665     getChildContainer : function()
15666     {
15667         return this.el.select('.popover-content',true).first();
15668     },
15669     
15670     getAutoCreate : function(){
15671          Roo.log('make popover?');
15672         var cfg = {
15673            cls : 'popover roo-dynamic',
15674            style: 'display:block',
15675            cn : [
15676                 {
15677                     cls : 'arrow'
15678                 },
15679                 {
15680                     cls : 'popover-inner',
15681                     cn : [
15682                         {
15683                             tag: 'h3',
15684                             cls: 'popover-title',
15685                             html : this.title
15686                         },
15687                         {
15688                             cls : 'popover-content',
15689                             html : this.html
15690                         }
15691                     ]
15692                     
15693                 }
15694            ]
15695         };
15696         
15697         return cfg;
15698     },
15699     setTitle: function(str)
15700     {
15701         this.title = str;
15702         this.el.select('.popover-title',true).first().dom.innerHTML = str;
15703     },
15704     setContent: function(str)
15705     {
15706         this.html = str;
15707         this.el.select('.popover-content',true).first().dom.innerHTML = str;
15708     },
15709     // as it get's added to the bottom of the page.
15710     onRender : function(ct, position)
15711     {
15712         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15713         if(!this.el){
15714             var cfg = Roo.apply({},  this.getAutoCreate());
15715             cfg.id = Roo.id();
15716             
15717             if (this.cls) {
15718                 cfg.cls += ' ' + this.cls;
15719             }
15720             if (this.style) {
15721                 cfg.style = this.style;
15722             }
15723             //Roo.log("adding to ");
15724             this.el = Roo.get(document.body).createChild(cfg, position);
15725             Roo.log(this.el);
15726         }
15727         this.initEvents();
15728     },
15729     
15730     initEvents : function()
15731     {
15732         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15733         this.el.enableDisplayMode('block');
15734         this.el.hide();
15735         if (this.over === false) {
15736             return; 
15737         }
15738         if (this.triggers === false) {
15739             return;
15740         }
15741         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15742         var triggers = this.trigger ? this.trigger.split(' ') : [];
15743         Roo.each(triggers, function(trigger) {
15744         
15745             if (trigger == 'click') {
15746                 on_el.on('click', this.toggle, this);
15747             } else if (trigger != 'manual') {
15748                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
15749                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
15750       
15751                 on_el.on(eventIn  ,this.enter, this);
15752                 on_el.on(eventOut, this.leave, this);
15753             }
15754         }, this);
15755         
15756     },
15757     
15758     
15759     // private
15760     timeout : null,
15761     hoverState : null,
15762     
15763     toggle : function () {
15764         this.hoverState == 'in' ? this.leave() : this.enter();
15765     },
15766     
15767     enter : function () {
15768        
15769     
15770         clearTimeout(this.timeout);
15771     
15772         this.hoverState = 'in';
15773     
15774         if (!this.delay || !this.delay.show) {
15775             this.show();
15776             return;
15777         }
15778         var _t = this;
15779         this.timeout = setTimeout(function () {
15780             if (_t.hoverState == 'in') {
15781                 _t.show();
15782             }
15783         }, this.delay.show)
15784     },
15785     leave : function() {
15786         clearTimeout(this.timeout);
15787     
15788         this.hoverState = 'out';
15789     
15790         if (!this.delay || !this.delay.hide) {
15791             this.hide();
15792             return;
15793         }
15794         var _t = this;
15795         this.timeout = setTimeout(function () {
15796             if (_t.hoverState == 'out') {
15797                 _t.hide();
15798             }
15799         }, this.delay.hide)
15800     },
15801     
15802     show : function (on_el)
15803     {
15804         if (!on_el) {
15805             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15806         }
15807         // set content.
15808         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
15809         if (this.html !== false) {
15810             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
15811         }
15812         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
15813         if (!this.title.length) {
15814             this.el.select('.popover-title',true).hide();
15815         }
15816         
15817         var placement = typeof this.placement == 'function' ?
15818             this.placement.call(this, this.el, on_el) :
15819             this.placement;
15820             
15821         var autoToken = /\s?auto?\s?/i;
15822         var autoPlace = autoToken.test(placement);
15823         if (autoPlace) {
15824             placement = placement.replace(autoToken, '') || 'top';
15825         }
15826         
15827         //this.el.detach()
15828         //this.el.setXY([0,0]);
15829         this.el.show();
15830         this.el.dom.style.display='block';
15831         this.el.addClass(placement);
15832         
15833         //this.el.appendTo(on_el);
15834         
15835         var p = this.getPosition();
15836         var box = this.el.getBox();
15837         
15838         if (autoPlace) {
15839             // fixme..
15840         }
15841         var align = Roo.bootstrap.Popover.alignment[placement];
15842         this.el.alignTo(on_el, align[0],align[1]);
15843         //var arrow = this.el.select('.arrow',true).first();
15844         //arrow.set(align[2], 
15845         
15846         this.el.addClass('in');
15847         
15848         
15849         if (this.el.hasClass('fade')) {
15850             // fade it?
15851         }
15852         
15853     },
15854     hide : function()
15855     {
15856         this.el.setXY([0,0]);
15857         this.el.removeClass('in');
15858         this.el.hide();
15859         this.hoverState = null;
15860         
15861     }
15862     
15863 });
15864
15865 Roo.bootstrap.Popover.alignment = {
15866     'left' : ['r-l', [-10,0], 'right'],
15867     'right' : ['l-r', [10,0], 'left'],
15868     'bottom' : ['t-b', [0,10], 'top'],
15869     'top' : [ 'b-t', [0,-10], 'bottom']
15870 };
15871
15872  /*
15873  * - LGPL
15874  *
15875  * Progress
15876  * 
15877  */
15878
15879 /**
15880  * @class Roo.bootstrap.Progress
15881  * @extends Roo.bootstrap.Component
15882  * Bootstrap Progress class
15883  * @cfg {Boolean} striped striped of the progress bar
15884  * @cfg {Boolean} active animated of the progress bar
15885  * 
15886  * 
15887  * @constructor
15888  * Create a new Progress
15889  * @param {Object} config The config object
15890  */
15891
15892 Roo.bootstrap.Progress = function(config){
15893     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
15894 };
15895
15896 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
15897     
15898     striped : false,
15899     active: false,
15900     
15901     getAutoCreate : function(){
15902         var cfg = {
15903             tag: 'div',
15904             cls: 'progress'
15905         };
15906         
15907         
15908         if(this.striped){
15909             cfg.cls += ' progress-striped';
15910         }
15911       
15912         if(this.active){
15913             cfg.cls += ' active';
15914         }
15915         
15916         
15917         return cfg;
15918     }
15919    
15920 });
15921
15922  
15923
15924  /*
15925  * - LGPL
15926  *
15927  * ProgressBar
15928  * 
15929  */
15930
15931 /**
15932  * @class Roo.bootstrap.ProgressBar
15933  * @extends Roo.bootstrap.Component
15934  * Bootstrap ProgressBar class
15935  * @cfg {Number} aria_valuenow aria-value now
15936  * @cfg {Number} aria_valuemin aria-value min
15937  * @cfg {Number} aria_valuemax aria-value max
15938  * @cfg {String} label label for the progress bar
15939  * @cfg {String} panel (success | info | warning | danger )
15940  * @cfg {String} role role of the progress bar
15941  * @cfg {String} sr_only text
15942  * 
15943  * 
15944  * @constructor
15945  * Create a new ProgressBar
15946  * @param {Object} config The config object
15947  */
15948
15949 Roo.bootstrap.ProgressBar = function(config){
15950     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
15951 };
15952
15953 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
15954     
15955     aria_valuenow : 0,
15956     aria_valuemin : 0,
15957     aria_valuemax : 100,
15958     label : false,
15959     panel : false,
15960     role : false,
15961     sr_only: false,
15962     
15963     getAutoCreate : function()
15964     {
15965         
15966         var cfg = {
15967             tag: 'div',
15968             cls: 'progress-bar',
15969             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
15970         };
15971         
15972         if(this.sr_only){
15973             cfg.cn = {
15974                 tag: 'span',
15975                 cls: 'sr-only',
15976                 html: this.sr_only
15977             }
15978         }
15979         
15980         if(this.role){
15981             cfg.role = this.role;
15982         }
15983         
15984         if(this.aria_valuenow){
15985             cfg['aria-valuenow'] = this.aria_valuenow;
15986         }
15987         
15988         if(this.aria_valuemin){
15989             cfg['aria-valuemin'] = this.aria_valuemin;
15990         }
15991         
15992         if(this.aria_valuemax){
15993             cfg['aria-valuemax'] = this.aria_valuemax;
15994         }
15995         
15996         if(this.label && !this.sr_only){
15997             cfg.html = this.label;
15998         }
15999         
16000         if(this.panel){
16001             cfg.cls += ' progress-bar-' + this.panel;
16002         }
16003         
16004         return cfg;
16005     },
16006     
16007     update : function(aria_valuenow)
16008     {
16009         this.aria_valuenow = aria_valuenow;
16010         
16011         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16012     }
16013    
16014 });
16015
16016  
16017
16018  /*
16019  * - LGPL
16020  *
16021  * column
16022  * 
16023  */
16024
16025 /**
16026  * @class Roo.bootstrap.TabGroup
16027  * @extends Roo.bootstrap.Column
16028  * Bootstrap Column class
16029  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16030  * @cfg {Boolean} carousel true to make the group behave like a carousel
16031  * @cfg {Boolean} bullets show bullets for the panels
16032  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16033  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
16034  * @cfg {Number} timer auto slide timer .. default 0 millisecond
16035  * 
16036  * @constructor
16037  * Create a new TabGroup
16038  * @param {Object} config The config object
16039  */
16040
16041 Roo.bootstrap.TabGroup = function(config){
16042     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16043     if (!this.navId) {
16044         this.navId = Roo.id();
16045     }
16046     this.tabs = [];
16047     Roo.bootstrap.TabGroup.register(this);
16048     
16049 };
16050
16051 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
16052     
16053     carousel : false,
16054     transition : false,
16055     bullets : 0,
16056     timer : 0,
16057     autoslide : false,
16058     slideFn : false,
16059     slideOnTouch : false,
16060     
16061     getAutoCreate : function()
16062     {
16063         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16064         
16065         cfg.cls += ' tab-content';
16066         
16067         Roo.log('get auto create...............');
16068         
16069         if (this.carousel) {
16070             cfg.cls += ' carousel slide';
16071             
16072             cfg.cn = [{
16073                cls : 'carousel-inner'
16074             }];
16075         
16076             if(this.bullets  && !Roo.isTouch){
16077                 
16078                 var bullets = {
16079                     cls : 'carousel-bullets',
16080                     cn : []
16081                 };
16082                
16083                 if(this.bullets_cls){
16084                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16085                 }
16086                  /*
16087                 for (var i = 0; i < this.bullets; i++){
16088                     bullets.cn.push({
16089                         cls : 'bullet bullet-' + i
16090                     });
16091                 }
16092                 */
16093                 bullets.cn.push({
16094                     cls : 'clear'
16095                 });
16096                 
16097                 cfg.cn[0].cn = bullets;
16098             }
16099         }
16100         
16101         return cfg;
16102     },
16103     
16104     initEvents:  function()
16105     {
16106         Roo.log('-------- init events on tab group ---------');
16107         
16108         
16109         
16110         Roo.log(this);
16111         
16112         if(Roo.isTouch && this.slideOnTouch){
16113             this.el.on("touchstart", this.onTouchStart, this);
16114         }
16115         
16116         if(this.autoslide){
16117             var _this = this;
16118             
16119             this.slideFn = window.setInterval(function() {
16120                 _this.showPanelNext();
16121             }, this.timer);
16122         }
16123         
16124     },
16125     
16126     onTouchStart : function(e, el, o)
16127     {
16128         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
16129             return;
16130         }
16131         
16132         this.showPanelNext();
16133     },
16134     
16135     getChildContainer : function()
16136     {
16137         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
16138     },
16139     
16140     /**
16141     * register a Navigation item
16142     * @param {Roo.bootstrap.NavItem} the navitem to add
16143     */
16144     register : function(item)
16145     {
16146         this.tabs.push( item);
16147         item.navId = this.navId; // not really needed..
16148         this.addBullet();
16149     
16150     },
16151     
16152     getActivePanel : function()
16153     {
16154         var r = false;
16155         Roo.each(this.tabs, function(t) {
16156             if (t.active) {
16157                 r = t;
16158                 return false;
16159             }
16160             return null;
16161         });
16162         return r;
16163         
16164     },
16165     getPanelByName : function(n)
16166     {
16167         var r = false;
16168         Roo.each(this.tabs, function(t) {
16169             if (t.tabId == n) {
16170                 r = t;
16171                 return false;
16172             }
16173             return null;
16174         });
16175         return r;
16176     },
16177     indexOfPanel : function(p)
16178     {
16179         var r = false;
16180         Roo.each(this.tabs, function(t,i) {
16181             if (t.tabId == p.tabId) {
16182                 r = i;
16183                 return false;
16184             }
16185             return null;
16186         });
16187         return r;
16188     },
16189     /**
16190      * show a specific panel
16191      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16192      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16193      */
16194     showPanel : function (pan)
16195     {
16196         if(this.transition || typeof(pan) == 'undefined'){
16197             Roo.log("waiting for the transitionend");
16198             return;
16199         }
16200         
16201         if (typeof(pan) == 'number') {
16202             pan = this.tabs[pan];
16203         }
16204         
16205         if (typeof(pan) == 'string') {
16206             pan = this.getPanelByName(pan);
16207         }
16208         
16209         var cur = this.getActivePanel();
16210         
16211         if(!pan || !cur){
16212             Roo.log('pan or acitve pan is undefined');
16213             return false;
16214         }
16215         
16216         if (pan.tabId == this.getActivePanel().tabId) {
16217             return true;
16218         }
16219         
16220         if (false === cur.fireEvent('beforedeactivate')) {
16221             return false;
16222         }
16223         
16224         if(this.bullets > 0 && !Roo.isTouch){
16225             this.setActiveBullet(this.indexOfPanel(pan));
16226         }
16227         
16228         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16229             
16230             this.transition = true;
16231             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
16232             var lr = dir == 'next' ? 'left' : 'right';
16233             pan.el.addClass(dir); // or prev
16234             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16235             cur.el.addClass(lr); // or right
16236             pan.el.addClass(lr);
16237             
16238             var _this = this;
16239             cur.el.on('transitionend', function() {
16240                 Roo.log("trans end?");
16241                 
16242                 pan.el.removeClass([lr,dir]);
16243                 pan.setActive(true);
16244                 
16245                 cur.el.removeClass([lr]);
16246                 cur.setActive(false);
16247                 
16248                 _this.transition = false;
16249                 
16250             }, this, { single:  true } );
16251             
16252             return true;
16253         }
16254         
16255         cur.setActive(false);
16256         pan.setActive(true);
16257         
16258         return true;
16259         
16260     },
16261     showPanelNext : function()
16262     {
16263         var i = this.indexOfPanel(this.getActivePanel());
16264         
16265         if (i >= this.tabs.length - 1 && !this.autoslide) {
16266             return;
16267         }
16268         
16269         if (i >= this.tabs.length - 1 && this.autoslide) {
16270             i = -1;
16271         }
16272         
16273         this.showPanel(this.tabs[i+1]);
16274     },
16275     
16276     showPanelPrev : function()
16277     {
16278         var i = this.indexOfPanel(this.getActivePanel());
16279         
16280         if (i  < 1 && !this.autoslide) {
16281             return;
16282         }
16283         
16284         if (i < 1 && this.autoslide) {
16285             i = this.tabs.length;
16286         }
16287         
16288         this.showPanel(this.tabs[i-1]);
16289     },
16290     
16291     
16292     addBullet: function()
16293     {
16294         if(!this.bullets || Roo.isTouch){
16295             return;
16296         }
16297         var ctr = this.el.select('.carousel-bullets',true).first();
16298         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16299         var bullet = ctr.createChild({
16300             cls : 'bullet bullet-' + i
16301         },ctr.dom.lastChild);
16302         
16303         
16304         var _this = this;
16305         
16306         bullet.on('click', (function(e, el, o, ii, t){
16307
16308             e.preventDefault();
16309
16310             this.showPanel(ii);
16311
16312             if(this.autoslide && this.slideFn){
16313                 clearInterval(this.slideFn);
16314                 this.slideFn = window.setInterval(function() {
16315                     _this.showPanelNext();
16316                 }, this.timer);
16317             }
16318
16319         }).createDelegate(this, [i, bullet], true));
16320                 
16321         
16322     },
16323      
16324     setActiveBullet : function(i)
16325     {
16326         if(Roo.isTouch){
16327             return;
16328         }
16329         
16330         Roo.each(this.el.select('.bullet', true).elements, function(el){
16331             el.removeClass('selected');
16332         });
16333
16334         var bullet = this.el.select('.bullet-' + i, true).first();
16335         
16336         if(!bullet){
16337             return;
16338         }
16339         
16340         bullet.addClass('selected');
16341     }
16342     
16343     
16344   
16345 });
16346
16347  
16348
16349  
16350  
16351 Roo.apply(Roo.bootstrap.TabGroup, {
16352     
16353     groups: {},
16354      /**
16355     * register a Navigation Group
16356     * @param {Roo.bootstrap.NavGroup} the navgroup to add
16357     */
16358     register : function(navgrp)
16359     {
16360         this.groups[navgrp.navId] = navgrp;
16361         
16362     },
16363     /**
16364     * fetch a Navigation Group based on the navigation ID
16365     * if one does not exist , it will get created.
16366     * @param {string} the navgroup to add
16367     * @returns {Roo.bootstrap.NavGroup} the navgroup 
16368     */
16369     get: function(navId) {
16370         if (typeof(this.groups[navId]) == 'undefined') {
16371             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
16372         }
16373         return this.groups[navId] ;
16374     }
16375     
16376     
16377     
16378 });
16379
16380  /*
16381  * - LGPL
16382  *
16383  * TabPanel
16384  * 
16385  */
16386
16387 /**
16388  * @class Roo.bootstrap.TabPanel
16389  * @extends Roo.bootstrap.Component
16390  * Bootstrap TabPanel class
16391  * @cfg {Boolean} active panel active
16392  * @cfg {String} html panel content
16393  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16394  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16395  * 
16396  * 
16397  * @constructor
16398  * Create a new TabPanel
16399  * @param {Object} config The config object
16400  */
16401
16402 Roo.bootstrap.TabPanel = function(config){
16403     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16404     this.addEvents({
16405         /**
16406              * @event changed
16407              * Fires when the active status changes
16408              * @param {Roo.bootstrap.TabPanel} this
16409              * @param {Boolean} state the new state
16410             
16411          */
16412         'changed': true,
16413         /**
16414              * @event beforedeactivate
16415              * Fires before a tab is de-activated - can be used to do validation on a form.
16416              * @param {Roo.bootstrap.TabPanel} this
16417              * @return {Boolean} false if there is an error
16418             
16419          */
16420         'beforedeactivate': true
16421      });
16422     
16423     this.tabId = this.tabId || Roo.id();
16424   
16425 };
16426
16427 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
16428     
16429     active: false,
16430     html: false,
16431     tabId: false,
16432     navId : false,
16433     
16434     getAutoCreate : function(){
16435         var cfg = {
16436             tag: 'div',
16437             // item is needed for carousel - not sure if it has any effect otherwise
16438             cls: 'tab-pane item',
16439             html: this.html || ''
16440         };
16441         
16442         if(this.active){
16443             cfg.cls += ' active';
16444         }
16445         
16446         if(this.tabId){
16447             cfg.tabId = this.tabId;
16448         }
16449         
16450         
16451         return cfg;
16452     },
16453     
16454     initEvents:  function()
16455     {
16456         Roo.log('-------- init events on tab panel ---------');
16457         
16458         var p = this.parent();
16459         this.navId = this.navId || p.navId;
16460         
16461         if (typeof(this.navId) != 'undefined') {
16462             // not really needed.. but just in case.. parent should be a NavGroup.
16463             var tg = Roo.bootstrap.TabGroup.get(this.navId);
16464             Roo.log(['register', tg, this]);
16465             tg.register(this);
16466             
16467             var i = tg.tabs.length - 1;
16468             
16469             if(this.active && tg.bullets > 0 && i < tg.bullets){
16470                 tg.setActiveBullet(i);
16471             }
16472         }
16473         
16474     },
16475     
16476     
16477     onRender : function(ct, position)
16478     {
16479        // Roo.log("Call onRender: " + this.xtype);
16480         
16481         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16482         
16483         
16484         
16485         
16486         
16487     },
16488     
16489     setActive: function(state)
16490     {
16491         Roo.log("panel - set active " + this.tabId + "=" + state);
16492         
16493         this.active = state;
16494         if (!state) {
16495             this.el.removeClass('active');
16496             
16497         } else  if (!this.el.hasClass('active')) {
16498             this.el.addClass('active');
16499         }
16500         
16501         this.fireEvent('changed', this, state);
16502     }
16503     
16504     
16505 });
16506  
16507
16508  
16509
16510  /*
16511  * - LGPL
16512  *
16513  * DateField
16514  * 
16515  */
16516
16517 /**
16518  * @class Roo.bootstrap.DateField
16519  * @extends Roo.bootstrap.Input
16520  * Bootstrap DateField class
16521  * @cfg {Number} weekStart default 0
16522  * @cfg {String} viewMode default empty, (months|years)
16523  * @cfg {String} minViewMode default empty, (months|years)
16524  * @cfg {Number} startDate default -Infinity
16525  * @cfg {Number} endDate default Infinity
16526  * @cfg {Boolean} todayHighlight default false
16527  * @cfg {Boolean} todayBtn default false
16528  * @cfg {Boolean} calendarWeeks default false
16529  * @cfg {Object} daysOfWeekDisabled default empty
16530  * @cfg {Boolean} singleMode default false (true | false)
16531  * 
16532  * @cfg {Boolean} keyboardNavigation default true
16533  * @cfg {String} language default en
16534  * 
16535  * @constructor
16536  * Create a new DateField
16537  * @param {Object} config The config object
16538  */
16539
16540 Roo.bootstrap.DateField = function(config){
16541     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16542      this.addEvents({
16543             /**
16544              * @event show
16545              * Fires when this field show.
16546              * @param {Roo.bootstrap.DateField} this
16547              * @param {Mixed} date The date value
16548              */
16549             show : true,
16550             /**
16551              * @event show
16552              * Fires when this field hide.
16553              * @param {Roo.bootstrap.DateField} this
16554              * @param {Mixed} date The date value
16555              */
16556             hide : true,
16557             /**
16558              * @event select
16559              * Fires when select a date.
16560              * @param {Roo.bootstrap.DateField} this
16561              * @param {Mixed} date The date value
16562              */
16563             select : true,
16564             /**
16565              * @event beforeselect
16566              * Fires when before select a date.
16567              * @param {Roo.bootstrap.DateField} this
16568              * @param {Mixed} date The date value
16569              */
16570             beforeselect : true
16571         });
16572 };
16573
16574 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
16575     
16576     /**
16577      * @cfg {String} format
16578      * The default date format string which can be overriden for localization support.  The format must be
16579      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16580      */
16581     format : "m/d/y",
16582     /**
16583      * @cfg {String} altFormats
16584      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16585      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16586      */
16587     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16588     
16589     weekStart : 0,
16590     
16591     viewMode : '',
16592     
16593     minViewMode : '',
16594     
16595     todayHighlight : false,
16596     
16597     todayBtn: false,
16598     
16599     language: 'en',
16600     
16601     keyboardNavigation: true,
16602     
16603     calendarWeeks: false,
16604     
16605     startDate: -Infinity,
16606     
16607     endDate: Infinity,
16608     
16609     daysOfWeekDisabled: [],
16610     
16611     _events: [],
16612     
16613     singleMode : false,
16614     
16615     UTCDate: function()
16616     {
16617         return new Date(Date.UTC.apply(Date, arguments));
16618     },
16619     
16620     UTCToday: function()
16621     {
16622         var today = new Date();
16623         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16624     },
16625     
16626     getDate: function() {
16627             var d = this.getUTCDate();
16628             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16629     },
16630     
16631     getUTCDate: function() {
16632             return this.date;
16633     },
16634     
16635     setDate: function(d) {
16636             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16637     },
16638     
16639     setUTCDate: function(d) {
16640             this.date = d;
16641             this.setValue(this.formatDate(this.date));
16642     },
16643         
16644     onRender: function(ct, position)
16645     {
16646         
16647         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16648         
16649         this.language = this.language || 'en';
16650         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16651         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16652         
16653         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16654         this.format = this.format || 'm/d/y';
16655         this.isInline = false;
16656         this.isInput = true;
16657         this.component = this.el.select('.add-on', true).first() || false;
16658         this.component = (this.component && this.component.length === 0) ? false : this.component;
16659         this.hasInput = this.component && this.inputEL().length;
16660         
16661         if (typeof(this.minViewMode === 'string')) {
16662             switch (this.minViewMode) {
16663                 case 'months':
16664                     this.minViewMode = 1;
16665                     break;
16666                 case 'years':
16667                     this.minViewMode = 2;
16668                     break;
16669                 default:
16670                     this.minViewMode = 0;
16671                     break;
16672             }
16673         }
16674         
16675         if (typeof(this.viewMode === 'string')) {
16676             switch (this.viewMode) {
16677                 case 'months':
16678                     this.viewMode = 1;
16679                     break;
16680                 case 'years':
16681                     this.viewMode = 2;
16682                     break;
16683                 default:
16684                     this.viewMode = 0;
16685                     break;
16686             }
16687         }
16688                 
16689         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16690         
16691 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16692         
16693         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16694         
16695         this.picker().on('mousedown', this.onMousedown, this);
16696         this.picker().on('click', this.onClick, this);
16697         
16698         this.picker().addClass('datepicker-dropdown');
16699         
16700         this.startViewMode = this.viewMode;
16701         
16702         if(this.singleMode){
16703             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16704                 v.setVisibilityMode(Roo.Element.DISPLAY);
16705                 v.hide();
16706             });
16707             
16708             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16709                 v.setStyle('width', '189px');
16710             });
16711         }
16712         
16713         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16714             if(!this.calendarWeeks){
16715                 v.remove();
16716                 return;
16717             }
16718             
16719             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16720             v.attr('colspan', function(i, val){
16721                 return parseInt(val) + 1;
16722             });
16723         });
16724                         
16725         
16726         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16727         
16728         this.setStartDate(this.startDate);
16729         this.setEndDate(this.endDate);
16730         
16731         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16732         
16733         this.fillDow();
16734         this.fillMonths();
16735         this.update();
16736         this.showMode();
16737         
16738         if(this.isInline) {
16739             this.show();
16740         }
16741     },
16742     
16743     picker : function()
16744     {
16745         return this.pickerEl;
16746 //        return this.el.select('.datepicker', true).first();
16747     },
16748     
16749     fillDow: function()
16750     {
16751         var dowCnt = this.weekStart;
16752         
16753         var dow = {
16754             tag: 'tr',
16755             cn: [
16756                 
16757             ]
16758         };
16759         
16760         if(this.calendarWeeks){
16761             dow.cn.push({
16762                 tag: 'th',
16763                 cls: 'cw',
16764                 html: '&nbsp;'
16765             })
16766         }
16767         
16768         while (dowCnt < this.weekStart + 7) {
16769             dow.cn.push({
16770                 tag: 'th',
16771                 cls: 'dow',
16772                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16773             });
16774         }
16775         
16776         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16777     },
16778     
16779     fillMonths: function()
16780     {    
16781         var i = 0;
16782         var months = this.picker().select('>.datepicker-months td', true).first();
16783         
16784         months.dom.innerHTML = '';
16785         
16786         while (i < 12) {
16787             var month = {
16788                 tag: 'span',
16789                 cls: 'month',
16790                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16791             };
16792             
16793             months.createChild(month);
16794         }
16795         
16796     },
16797     
16798     update: function()
16799     {
16800         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;
16801         
16802         if (this.date < this.startDate) {
16803             this.viewDate = new Date(this.startDate);
16804         } else if (this.date > this.endDate) {
16805             this.viewDate = new Date(this.endDate);
16806         } else {
16807             this.viewDate = new Date(this.date);
16808         }
16809         
16810         this.fill();
16811     },
16812     
16813     fill: function() 
16814     {
16815         var d = new Date(this.viewDate),
16816                 year = d.getUTCFullYear(),
16817                 month = d.getUTCMonth(),
16818                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16819                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16820                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16821                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16822                 currentDate = this.date && this.date.valueOf(),
16823                 today = this.UTCToday();
16824         
16825         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16826         
16827 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16828         
16829 //        this.picker.select('>tfoot th.today').
16830 //                                              .text(dates[this.language].today)
16831 //                                              .toggle(this.todayBtn !== false);
16832     
16833         this.updateNavArrows();
16834         this.fillMonths();
16835                                                 
16836         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16837         
16838         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16839          
16840         prevMonth.setUTCDate(day);
16841         
16842         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16843         
16844         var nextMonth = new Date(prevMonth);
16845         
16846         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16847         
16848         nextMonth = nextMonth.valueOf();
16849         
16850         var fillMonths = false;
16851         
16852         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16853         
16854         while(prevMonth.valueOf() < nextMonth) {
16855             var clsName = '';
16856             
16857             if (prevMonth.getUTCDay() === this.weekStart) {
16858                 if(fillMonths){
16859                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16860                 }
16861                     
16862                 fillMonths = {
16863                     tag: 'tr',
16864                     cn: []
16865                 };
16866                 
16867                 if(this.calendarWeeks){
16868                     // ISO 8601: First week contains first thursday.
16869                     // ISO also states week starts on Monday, but we can be more abstract here.
16870                     var
16871                     // Start of current week: based on weekstart/current date
16872                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16873                     // Thursday of this week
16874                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16875                     // First Thursday of year, year from thursday
16876                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16877                     // Calendar week: ms between thursdays, div ms per day, div 7 days
16878                     calWeek =  (th - yth) / 864e5 / 7 + 1;
16879                     
16880                     fillMonths.cn.push({
16881                         tag: 'td',
16882                         cls: 'cw',
16883                         html: calWeek
16884                     });
16885                 }
16886             }
16887             
16888             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16889                 clsName += ' old';
16890             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16891                 clsName += ' new';
16892             }
16893             if (this.todayHighlight &&
16894                 prevMonth.getUTCFullYear() == today.getFullYear() &&
16895                 prevMonth.getUTCMonth() == today.getMonth() &&
16896                 prevMonth.getUTCDate() == today.getDate()) {
16897                 clsName += ' today';
16898             }
16899             
16900             if (currentDate && prevMonth.valueOf() === currentDate) {
16901                 clsName += ' active';
16902             }
16903             
16904             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
16905                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
16906                     clsName += ' disabled';
16907             }
16908             
16909             fillMonths.cn.push({
16910                 tag: 'td',
16911                 cls: 'day ' + clsName,
16912                 html: prevMonth.getDate()
16913             });
16914             
16915             prevMonth.setDate(prevMonth.getDate()+1);
16916         }
16917           
16918         var currentYear = this.date && this.date.getUTCFullYear();
16919         var currentMonth = this.date && this.date.getUTCMonth();
16920         
16921         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
16922         
16923         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
16924             v.removeClass('active');
16925             
16926             if(currentYear === year && k === currentMonth){
16927                 v.addClass('active');
16928             }
16929             
16930             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
16931                 v.addClass('disabled');
16932             }
16933             
16934         });
16935         
16936         
16937         year = parseInt(year/10, 10) * 10;
16938         
16939         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
16940         
16941         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
16942         
16943         year -= 1;
16944         for (var i = -1; i < 11; i++) {
16945             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
16946                 tag: 'span',
16947                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
16948                 html: year
16949             });
16950             
16951             year += 1;
16952         }
16953     },
16954     
16955     showMode: function(dir) 
16956     {
16957         if (dir) {
16958             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
16959         }
16960         
16961         Roo.each(this.picker().select('>div',true).elements, function(v){
16962             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16963             v.hide();
16964         });
16965         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
16966     },
16967     
16968     place: function()
16969     {
16970         if(this.isInline) {
16971             return;
16972         }
16973         
16974         this.picker().removeClass(['bottom', 'top']);
16975         
16976         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16977             /*
16978              * place to the top of element!
16979              *
16980              */
16981             
16982             this.picker().addClass('top');
16983             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16984             
16985             return;
16986         }
16987         
16988         this.picker().addClass('bottom');
16989         
16990         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16991     },
16992     
16993     parseDate : function(value)
16994     {
16995         if(!value || value instanceof Date){
16996             return value;
16997         }
16998         var v = Date.parseDate(value, this.format);
16999         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
17000             v = Date.parseDate(value, 'Y-m-d');
17001         }
17002         if(!v && this.altFormats){
17003             if(!this.altFormatsArray){
17004                 this.altFormatsArray = this.altFormats.split("|");
17005             }
17006             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17007                 v = Date.parseDate(value, this.altFormatsArray[i]);
17008             }
17009         }
17010         return v;
17011     },
17012     
17013     formatDate : function(date, fmt)
17014     {   
17015         return (!date || !(date instanceof Date)) ?
17016         date : date.dateFormat(fmt || this.format);
17017     },
17018     
17019     onFocus : function()
17020     {
17021         Roo.bootstrap.DateField.superclass.onFocus.call(this);
17022         this.show();
17023     },
17024     
17025     onBlur : function()
17026     {
17027         Roo.bootstrap.DateField.superclass.onBlur.call(this);
17028         
17029         var d = this.inputEl().getValue();
17030         
17031         this.setValue(d);
17032                 
17033         this.hide();
17034     },
17035     
17036     show : function()
17037     {
17038         this.picker().show();
17039         this.update();
17040         this.place();
17041         
17042         this.fireEvent('show', this, this.date);
17043     },
17044     
17045     hide : function()
17046     {
17047         if(this.isInline) {
17048             return;
17049         }
17050         this.picker().hide();
17051         this.viewMode = this.startViewMode;
17052         this.showMode();
17053         
17054         this.fireEvent('hide', this, this.date);
17055         
17056     },
17057     
17058     onMousedown: function(e)
17059     {
17060         e.stopPropagation();
17061         e.preventDefault();
17062     },
17063     
17064     keyup: function(e)
17065     {
17066         Roo.bootstrap.DateField.superclass.keyup.call(this);
17067         this.update();
17068     },
17069
17070     setValue: function(v)
17071     {
17072         if(this.fireEvent('beforeselect', this, v) !== false){
17073             var d = new Date(this.parseDate(v) ).clearTime();
17074         
17075             if(isNaN(d.getTime())){
17076                 this.date = this.viewDate = '';
17077                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17078                 return;
17079             }
17080
17081             v = this.formatDate(d);
17082
17083             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17084
17085             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17086
17087             this.update();
17088
17089             this.fireEvent('select', this, this.date);
17090         }
17091     },
17092     
17093     getValue: function()
17094     {
17095         return this.formatDate(this.date);
17096     },
17097     
17098     fireKey: function(e)
17099     {
17100         if (!this.picker().isVisible()){
17101             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17102                 this.show();
17103             }
17104             return;
17105         }
17106         
17107         var dateChanged = false,
17108         dir, day, month,
17109         newDate, newViewDate;
17110         
17111         switch(e.keyCode){
17112             case 27: // escape
17113                 this.hide();
17114                 e.preventDefault();
17115                 break;
17116             case 37: // left
17117             case 39: // right
17118                 if (!this.keyboardNavigation) {
17119                     break;
17120                 }
17121                 dir = e.keyCode == 37 ? -1 : 1;
17122                 
17123                 if (e.ctrlKey){
17124                     newDate = this.moveYear(this.date, dir);
17125                     newViewDate = this.moveYear(this.viewDate, dir);
17126                 } else if (e.shiftKey){
17127                     newDate = this.moveMonth(this.date, dir);
17128                     newViewDate = this.moveMonth(this.viewDate, dir);
17129                 } else {
17130                     newDate = new Date(this.date);
17131                     newDate.setUTCDate(this.date.getUTCDate() + dir);
17132                     newViewDate = new Date(this.viewDate);
17133                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
17134                 }
17135                 if (this.dateWithinRange(newDate)){
17136                     this.date = newDate;
17137                     this.viewDate = newViewDate;
17138                     this.setValue(this.formatDate(this.date));
17139 //                    this.update();
17140                     e.preventDefault();
17141                     dateChanged = true;
17142                 }
17143                 break;
17144             case 38: // up
17145             case 40: // down
17146                 if (!this.keyboardNavigation) {
17147                     break;
17148                 }
17149                 dir = e.keyCode == 38 ? -1 : 1;
17150                 if (e.ctrlKey){
17151                     newDate = this.moveYear(this.date, dir);
17152                     newViewDate = this.moveYear(this.viewDate, dir);
17153                 } else if (e.shiftKey){
17154                     newDate = this.moveMonth(this.date, dir);
17155                     newViewDate = this.moveMonth(this.viewDate, dir);
17156                 } else {
17157                     newDate = new Date(this.date);
17158                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
17159                     newViewDate = new Date(this.viewDate);
17160                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
17161                 }
17162                 if (this.dateWithinRange(newDate)){
17163                     this.date = newDate;
17164                     this.viewDate = newViewDate;
17165                     this.setValue(this.formatDate(this.date));
17166 //                    this.update();
17167                     e.preventDefault();
17168                     dateChanged = true;
17169                 }
17170                 break;
17171             case 13: // enter
17172                 this.setValue(this.formatDate(this.date));
17173                 this.hide();
17174                 e.preventDefault();
17175                 break;
17176             case 9: // tab
17177                 this.setValue(this.formatDate(this.date));
17178                 this.hide();
17179                 break;
17180             case 16: // shift
17181             case 17: // ctrl
17182             case 18: // alt
17183                 break;
17184             default :
17185                 this.hide();
17186                 
17187         }
17188     },
17189     
17190     
17191     onClick: function(e) 
17192     {
17193         e.stopPropagation();
17194         e.preventDefault();
17195         
17196         var target = e.getTarget();
17197         
17198         if(target.nodeName.toLowerCase() === 'i'){
17199             target = Roo.get(target).dom.parentNode;
17200         }
17201         
17202         var nodeName = target.nodeName;
17203         var className = target.className;
17204         var html = target.innerHTML;
17205         //Roo.log(nodeName);
17206         
17207         switch(nodeName.toLowerCase()) {
17208             case 'th':
17209                 switch(className) {
17210                     case 'switch':
17211                         this.showMode(1);
17212                         break;
17213                     case 'prev':
17214                     case 'next':
17215                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17216                         switch(this.viewMode){
17217                                 case 0:
17218                                         this.viewDate = this.moveMonth(this.viewDate, dir);
17219                                         break;
17220                                 case 1:
17221                                 case 2:
17222                                         this.viewDate = this.moveYear(this.viewDate, dir);
17223                                         break;
17224                         }
17225                         this.fill();
17226                         break;
17227                     case 'today':
17228                         var date = new Date();
17229                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17230 //                        this.fill()
17231                         this.setValue(this.formatDate(this.date));
17232                         
17233                         this.hide();
17234                         break;
17235                 }
17236                 break;
17237             case 'span':
17238                 if (className.indexOf('disabled') < 0) {
17239                     this.viewDate.setUTCDate(1);
17240                     if (className.indexOf('month') > -1) {
17241                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17242                     } else {
17243                         var year = parseInt(html, 10) || 0;
17244                         this.viewDate.setUTCFullYear(year);
17245                         
17246                     }
17247                     
17248                     if(this.singleMode){
17249                         this.setValue(this.formatDate(this.viewDate));
17250                         this.hide();
17251                         return;
17252                     }
17253                     
17254                     this.showMode(-1);
17255                     this.fill();
17256                 }
17257                 break;
17258                 
17259             case 'td':
17260                 //Roo.log(className);
17261                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17262                     var day = parseInt(html, 10) || 1;
17263                     var year = this.viewDate.getUTCFullYear(),
17264                         month = this.viewDate.getUTCMonth();
17265
17266                     if (className.indexOf('old') > -1) {
17267                         if(month === 0 ){
17268                             month = 11;
17269                             year -= 1;
17270                         }else{
17271                             month -= 1;
17272                         }
17273                     } else if (className.indexOf('new') > -1) {
17274                         if (month == 11) {
17275                             month = 0;
17276                             year += 1;
17277                         } else {
17278                             month += 1;
17279                         }
17280                     }
17281                     //Roo.log([year,month,day]);
17282                     this.date = this.UTCDate(year, month, day,0,0,0,0);
17283                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17284 //                    this.fill();
17285                     //Roo.log(this.formatDate(this.date));
17286                     this.setValue(this.formatDate(this.date));
17287                     this.hide();
17288                 }
17289                 break;
17290         }
17291     },
17292     
17293     setStartDate: function(startDate)
17294     {
17295         this.startDate = startDate || -Infinity;
17296         if (this.startDate !== -Infinity) {
17297             this.startDate = this.parseDate(this.startDate);
17298         }
17299         this.update();
17300         this.updateNavArrows();
17301     },
17302
17303     setEndDate: function(endDate)
17304     {
17305         this.endDate = endDate || Infinity;
17306         if (this.endDate !== Infinity) {
17307             this.endDate = this.parseDate(this.endDate);
17308         }
17309         this.update();
17310         this.updateNavArrows();
17311     },
17312     
17313     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17314     {
17315         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17316         if (typeof(this.daysOfWeekDisabled) !== 'object') {
17317             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17318         }
17319         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17320             return parseInt(d, 10);
17321         });
17322         this.update();
17323         this.updateNavArrows();
17324     },
17325     
17326     updateNavArrows: function() 
17327     {
17328         if(this.singleMode){
17329             return;
17330         }
17331         
17332         var d = new Date(this.viewDate),
17333         year = d.getUTCFullYear(),
17334         month = d.getUTCMonth();
17335         
17336         Roo.each(this.picker().select('.prev', true).elements, function(v){
17337             v.show();
17338             switch (this.viewMode) {
17339                 case 0:
17340
17341                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
17342                         v.hide();
17343                     }
17344                     break;
17345                 case 1:
17346                 case 2:
17347                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
17348                         v.hide();
17349                     }
17350                     break;
17351             }
17352         });
17353         
17354         Roo.each(this.picker().select('.next', true).elements, function(v){
17355             v.show();
17356             switch (this.viewMode) {
17357                 case 0:
17358
17359                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
17360                         v.hide();
17361                     }
17362                     break;
17363                 case 1:
17364                 case 2:
17365                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
17366                         v.hide();
17367                     }
17368                     break;
17369             }
17370         })
17371     },
17372     
17373     moveMonth: function(date, dir)
17374     {
17375         if (!dir) {
17376             return date;
17377         }
17378         var new_date = new Date(date.valueOf()),
17379         day = new_date.getUTCDate(),
17380         month = new_date.getUTCMonth(),
17381         mag = Math.abs(dir),
17382         new_month, test;
17383         dir = dir > 0 ? 1 : -1;
17384         if (mag == 1){
17385             test = dir == -1
17386             // If going back one month, make sure month is not current month
17387             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
17388             ? function(){
17389                 return new_date.getUTCMonth() == month;
17390             }
17391             // If going forward one month, make sure month is as expected
17392             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
17393             : function(){
17394                 return new_date.getUTCMonth() != new_month;
17395             };
17396             new_month = month + dir;
17397             new_date.setUTCMonth(new_month);
17398             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
17399             if (new_month < 0 || new_month > 11) {
17400                 new_month = (new_month + 12) % 12;
17401             }
17402         } else {
17403             // For magnitudes >1, move one month at a time...
17404             for (var i=0; i<mag; i++) {
17405                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
17406                 new_date = this.moveMonth(new_date, dir);
17407             }
17408             // ...then reset the day, keeping it in the new month
17409             new_month = new_date.getUTCMonth();
17410             new_date.setUTCDate(day);
17411             test = function(){
17412                 return new_month != new_date.getUTCMonth();
17413             };
17414         }
17415         // Common date-resetting loop -- if date is beyond end of month, make it
17416         // end of month
17417         while (test()){
17418             new_date.setUTCDate(--day);
17419             new_date.setUTCMonth(new_month);
17420         }
17421         return new_date;
17422     },
17423
17424     moveYear: function(date, dir)
17425     {
17426         return this.moveMonth(date, dir*12);
17427     },
17428
17429     dateWithinRange: function(date)
17430     {
17431         return date >= this.startDate && date <= this.endDate;
17432     },
17433
17434     
17435     remove: function() 
17436     {
17437         this.picker().remove();
17438     }
17439    
17440 });
17441
17442 Roo.apply(Roo.bootstrap.DateField,  {
17443     
17444     head : {
17445         tag: 'thead',
17446         cn: [
17447         {
17448             tag: 'tr',
17449             cn: [
17450             {
17451                 tag: 'th',
17452                 cls: 'prev',
17453                 html: '<i class="fa fa-arrow-left"/>'
17454             },
17455             {
17456                 tag: 'th',
17457                 cls: 'switch',
17458                 colspan: '5'
17459             },
17460             {
17461                 tag: 'th',
17462                 cls: 'next',
17463                 html: '<i class="fa fa-arrow-right"/>'
17464             }
17465
17466             ]
17467         }
17468         ]
17469     },
17470     
17471     content : {
17472         tag: 'tbody',
17473         cn: [
17474         {
17475             tag: 'tr',
17476             cn: [
17477             {
17478                 tag: 'td',
17479                 colspan: '7'
17480             }
17481             ]
17482         }
17483         ]
17484     },
17485     
17486     footer : {
17487         tag: 'tfoot',
17488         cn: [
17489         {
17490             tag: 'tr',
17491             cn: [
17492             {
17493                 tag: 'th',
17494                 colspan: '7',
17495                 cls: 'today'
17496             }
17497                     
17498             ]
17499         }
17500         ]
17501     },
17502     
17503     dates:{
17504         en: {
17505             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17506             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17507             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17508             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17509             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17510             today: "Today"
17511         }
17512     },
17513     
17514     modes: [
17515     {
17516         clsName: 'days',
17517         navFnc: 'Month',
17518         navStep: 1
17519     },
17520     {
17521         clsName: 'months',
17522         navFnc: 'FullYear',
17523         navStep: 1
17524     },
17525     {
17526         clsName: 'years',
17527         navFnc: 'FullYear',
17528         navStep: 10
17529     }]
17530 });
17531
17532 Roo.apply(Roo.bootstrap.DateField,  {
17533   
17534     template : {
17535         tag: 'div',
17536         cls: 'datepicker dropdown-menu roo-dynamic',
17537         cn: [
17538         {
17539             tag: 'div',
17540             cls: 'datepicker-days',
17541             cn: [
17542             {
17543                 tag: 'table',
17544                 cls: 'table-condensed',
17545                 cn:[
17546                 Roo.bootstrap.DateField.head,
17547                 {
17548                     tag: 'tbody'
17549                 },
17550                 Roo.bootstrap.DateField.footer
17551                 ]
17552             }
17553             ]
17554         },
17555         {
17556             tag: 'div',
17557             cls: 'datepicker-months',
17558             cn: [
17559             {
17560                 tag: 'table',
17561                 cls: 'table-condensed',
17562                 cn:[
17563                 Roo.bootstrap.DateField.head,
17564                 Roo.bootstrap.DateField.content,
17565                 Roo.bootstrap.DateField.footer
17566                 ]
17567             }
17568             ]
17569         },
17570         {
17571             tag: 'div',
17572             cls: 'datepicker-years',
17573             cn: [
17574             {
17575                 tag: 'table',
17576                 cls: 'table-condensed',
17577                 cn:[
17578                 Roo.bootstrap.DateField.head,
17579                 Roo.bootstrap.DateField.content,
17580                 Roo.bootstrap.DateField.footer
17581                 ]
17582             }
17583             ]
17584         }
17585         ]
17586     }
17587 });
17588
17589  
17590
17591  /*
17592  * - LGPL
17593  *
17594  * TimeField
17595  * 
17596  */
17597
17598 /**
17599  * @class Roo.bootstrap.TimeField
17600  * @extends Roo.bootstrap.Input
17601  * Bootstrap DateField class
17602  * 
17603  * 
17604  * @constructor
17605  * Create a new TimeField
17606  * @param {Object} config The config object
17607  */
17608
17609 Roo.bootstrap.TimeField = function(config){
17610     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17611     this.addEvents({
17612             /**
17613              * @event show
17614              * Fires when this field show.
17615              * @param {Roo.bootstrap.DateField} thisthis
17616              * @param {Mixed} date The date value
17617              */
17618             show : true,
17619             /**
17620              * @event show
17621              * Fires when this field hide.
17622              * @param {Roo.bootstrap.DateField} this
17623              * @param {Mixed} date The date value
17624              */
17625             hide : true,
17626             /**
17627              * @event select
17628              * Fires when select a date.
17629              * @param {Roo.bootstrap.DateField} this
17630              * @param {Mixed} date The date value
17631              */
17632             select : true
17633         });
17634 };
17635
17636 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
17637     
17638     /**
17639      * @cfg {String} format
17640      * The default time format string which can be overriden for localization support.  The format must be
17641      * valid according to {@link Date#parseDate} (defaults to 'H:i').
17642      */
17643     format : "H:i",
17644        
17645     onRender: function(ct, position)
17646     {
17647         
17648         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17649                 
17650         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17651         
17652         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17653         
17654         this.pop = this.picker().select('>.datepicker-time',true).first();
17655         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17656         
17657         this.picker().on('mousedown', this.onMousedown, this);
17658         this.picker().on('click', this.onClick, this);
17659         
17660         this.picker().addClass('datepicker-dropdown');
17661     
17662         this.fillTime();
17663         this.update();
17664             
17665         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17666         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17667         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17668         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17669         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17670         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17671
17672     },
17673     
17674     fireKey: function(e){
17675         if (!this.picker().isVisible()){
17676             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17677                 this.show();
17678             }
17679             return;
17680         }
17681
17682         e.preventDefault();
17683         
17684         switch(e.keyCode){
17685             case 27: // escape
17686                 this.hide();
17687                 break;
17688             case 37: // left
17689             case 39: // right
17690                 this.onTogglePeriod();
17691                 break;
17692             case 38: // up
17693                 this.onIncrementMinutes();
17694                 break;
17695             case 40: // down
17696                 this.onDecrementMinutes();
17697                 break;
17698             case 13: // enter
17699             case 9: // tab
17700                 this.setTime();
17701                 break;
17702         }
17703     },
17704     
17705     onClick: function(e) {
17706         e.stopPropagation();
17707         e.preventDefault();
17708     },
17709     
17710     picker : function()
17711     {
17712         return this.el.select('.datepicker', true).first();
17713     },
17714     
17715     fillTime: function()
17716     {    
17717         var time = this.pop.select('tbody', true).first();
17718         
17719         time.dom.innerHTML = '';
17720         
17721         time.createChild({
17722             tag: 'tr',
17723             cn: [
17724                 {
17725                     tag: 'td',
17726                     cn: [
17727                         {
17728                             tag: 'a',
17729                             href: '#',
17730                             cls: 'btn',
17731                             cn: [
17732                                 {
17733                                     tag: 'span',
17734                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
17735                                 }
17736                             ]
17737                         } 
17738                     ]
17739                 },
17740                 {
17741                     tag: 'td',
17742                     cls: 'separator'
17743                 },
17744                 {
17745                     tag: 'td',
17746                     cn: [
17747                         {
17748                             tag: 'a',
17749                             href: '#',
17750                             cls: 'btn',
17751                             cn: [
17752                                 {
17753                                     tag: 'span',
17754                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
17755                                 }
17756                             ]
17757                         }
17758                     ]
17759                 },
17760                 {
17761                     tag: 'td',
17762                     cls: 'separator'
17763                 }
17764             ]
17765         });
17766         
17767         time.createChild({
17768             tag: 'tr',
17769             cn: [
17770                 {
17771                     tag: 'td',
17772                     cn: [
17773                         {
17774                             tag: 'span',
17775                             cls: 'timepicker-hour',
17776                             html: '00'
17777                         }  
17778                     ]
17779                 },
17780                 {
17781                     tag: 'td',
17782                     cls: 'separator',
17783                     html: ':'
17784                 },
17785                 {
17786                     tag: 'td',
17787                     cn: [
17788                         {
17789                             tag: 'span',
17790                             cls: 'timepicker-minute',
17791                             html: '00'
17792                         }  
17793                     ]
17794                 },
17795                 {
17796                     tag: 'td',
17797                     cls: 'separator'
17798                 },
17799                 {
17800                     tag: 'td',
17801                     cn: [
17802                         {
17803                             tag: 'button',
17804                             type: 'button',
17805                             cls: 'btn btn-primary period',
17806                             html: 'AM'
17807                             
17808                         }
17809                     ]
17810                 }
17811             ]
17812         });
17813         
17814         time.createChild({
17815             tag: 'tr',
17816             cn: [
17817                 {
17818                     tag: 'td',
17819                     cn: [
17820                         {
17821                             tag: 'a',
17822                             href: '#',
17823                             cls: 'btn',
17824                             cn: [
17825                                 {
17826                                     tag: 'span',
17827                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
17828                                 }
17829                             ]
17830                         }
17831                     ]
17832                 },
17833                 {
17834                     tag: 'td',
17835                     cls: 'separator'
17836                 },
17837                 {
17838                     tag: 'td',
17839                     cn: [
17840                         {
17841                             tag: 'a',
17842                             href: '#',
17843                             cls: 'btn',
17844                             cn: [
17845                                 {
17846                                     tag: 'span',
17847                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
17848                                 }
17849                             ]
17850                         }
17851                     ]
17852                 },
17853                 {
17854                     tag: 'td',
17855                     cls: 'separator'
17856                 }
17857             ]
17858         });
17859         
17860     },
17861     
17862     update: function()
17863     {
17864         
17865         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17866         
17867         this.fill();
17868     },
17869     
17870     fill: function() 
17871     {
17872         var hours = this.time.getHours();
17873         var minutes = this.time.getMinutes();
17874         var period = 'AM';
17875         
17876         if(hours > 11){
17877             period = 'PM';
17878         }
17879         
17880         if(hours == 0){
17881             hours = 12;
17882         }
17883         
17884         
17885         if(hours > 12){
17886             hours = hours - 12;
17887         }
17888         
17889         if(hours < 10){
17890             hours = '0' + hours;
17891         }
17892         
17893         if(minutes < 10){
17894             minutes = '0' + minutes;
17895         }
17896         
17897         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
17898         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
17899         this.pop.select('button', true).first().dom.innerHTML = period;
17900         
17901     },
17902     
17903     place: function()
17904     {   
17905         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
17906         
17907         var cls = ['bottom'];
17908         
17909         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
17910             cls.pop();
17911             cls.push('top');
17912         }
17913         
17914         cls.push('right');
17915         
17916         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
17917             cls.pop();
17918             cls.push('left');
17919         }
17920         
17921         this.picker().addClass(cls.join('-'));
17922         
17923         var _this = this;
17924         
17925         Roo.each(cls, function(c){
17926             if(c == 'bottom'){
17927                 _this.picker().setTop(_this.inputEl().getHeight());
17928                 return;
17929             }
17930             if(c == 'top'){
17931                 _this.picker().setTop(0 - _this.picker().getHeight());
17932                 return;
17933             }
17934             
17935             if(c == 'left'){
17936                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
17937                 return;
17938             }
17939             if(c == 'right'){
17940                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
17941                 return;
17942             }
17943         });
17944         
17945     },
17946   
17947     onFocus : function()
17948     {
17949         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
17950         this.show();
17951     },
17952     
17953     onBlur : function()
17954     {
17955         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
17956         this.hide();
17957     },
17958     
17959     show : function()
17960     {
17961         this.picker().show();
17962         this.pop.show();
17963         this.update();
17964         this.place();
17965         
17966         this.fireEvent('show', this, this.date);
17967     },
17968     
17969     hide : function()
17970     {
17971         this.picker().hide();
17972         this.pop.hide();
17973         
17974         this.fireEvent('hide', this, this.date);
17975     },
17976     
17977     setTime : function()
17978     {
17979         this.hide();
17980         this.setValue(this.time.format(this.format));
17981         
17982         this.fireEvent('select', this, this.date);
17983         
17984         
17985     },
17986     
17987     onMousedown: function(e){
17988         e.stopPropagation();
17989         e.preventDefault();
17990     },
17991     
17992     onIncrementHours: function()
17993     {
17994         Roo.log('onIncrementHours');
17995         this.time = this.time.add(Date.HOUR, 1);
17996         this.update();
17997         
17998     },
17999     
18000     onDecrementHours: function()
18001     {
18002         Roo.log('onDecrementHours');
18003         this.time = this.time.add(Date.HOUR, -1);
18004         this.update();
18005     },
18006     
18007     onIncrementMinutes: function()
18008     {
18009         Roo.log('onIncrementMinutes');
18010         this.time = this.time.add(Date.MINUTE, 1);
18011         this.update();
18012     },
18013     
18014     onDecrementMinutes: function()
18015     {
18016         Roo.log('onDecrementMinutes');
18017         this.time = this.time.add(Date.MINUTE, -1);
18018         this.update();
18019     },
18020     
18021     onTogglePeriod: function()
18022     {
18023         Roo.log('onTogglePeriod');
18024         this.time = this.time.add(Date.HOUR, 12);
18025         this.update();
18026     }
18027     
18028    
18029 });
18030
18031 Roo.apply(Roo.bootstrap.TimeField,  {
18032     
18033     content : {
18034         tag: 'tbody',
18035         cn: [
18036             {
18037                 tag: 'tr',
18038                 cn: [
18039                 {
18040                     tag: 'td',
18041                     colspan: '7'
18042                 }
18043                 ]
18044             }
18045         ]
18046     },
18047     
18048     footer : {
18049         tag: 'tfoot',
18050         cn: [
18051             {
18052                 tag: 'tr',
18053                 cn: [
18054                 {
18055                     tag: 'th',
18056                     colspan: '7',
18057                     cls: '',
18058                     cn: [
18059                         {
18060                             tag: 'button',
18061                             cls: 'btn btn-info ok',
18062                             html: 'OK'
18063                         }
18064                     ]
18065                 }
18066
18067                 ]
18068             }
18069         ]
18070     }
18071 });
18072
18073 Roo.apply(Roo.bootstrap.TimeField,  {
18074   
18075     template : {
18076         tag: 'div',
18077         cls: 'datepicker dropdown-menu',
18078         cn: [
18079             {
18080                 tag: 'div',
18081                 cls: 'datepicker-time',
18082                 cn: [
18083                 {
18084                     tag: 'table',
18085                     cls: 'table-condensed',
18086                     cn:[
18087                     Roo.bootstrap.TimeField.content,
18088                     Roo.bootstrap.TimeField.footer
18089                     ]
18090                 }
18091                 ]
18092             }
18093         ]
18094     }
18095 });
18096
18097  
18098
18099  /*
18100  * - LGPL
18101  *
18102  * MonthField
18103  * 
18104  */
18105
18106 /**
18107  * @class Roo.bootstrap.MonthField
18108  * @extends Roo.bootstrap.Input
18109  * Bootstrap MonthField class
18110  * 
18111  * @cfg {String} language default en
18112  * 
18113  * @constructor
18114  * Create a new MonthField
18115  * @param {Object} config The config object
18116  */
18117
18118 Roo.bootstrap.MonthField = function(config){
18119     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
18120     
18121     this.addEvents({
18122         /**
18123          * @event show
18124          * Fires when this field show.
18125          * @param {Roo.bootstrap.MonthField} this
18126          * @param {Mixed} date The date value
18127          */
18128         show : true,
18129         /**
18130          * @event show
18131          * Fires when this field hide.
18132          * @param {Roo.bootstrap.MonthField} this
18133          * @param {Mixed} date The date value
18134          */
18135         hide : true,
18136         /**
18137          * @event select
18138          * Fires when select a date.
18139          * @param {Roo.bootstrap.MonthField} this
18140          * @param {String} oldvalue The old value
18141          * @param {String} newvalue The new value
18142          */
18143         select : true
18144     });
18145 };
18146
18147 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
18148     
18149     onRender: function(ct, position)
18150     {
18151         
18152         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
18153         
18154         this.language = this.language || 'en';
18155         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
18156         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
18157         
18158         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
18159         this.isInline = false;
18160         this.isInput = true;
18161         this.component = this.el.select('.add-on', true).first() || false;
18162         this.component = (this.component && this.component.length === 0) ? false : this.component;
18163         this.hasInput = this.component && this.inputEL().length;
18164         
18165         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
18166         
18167         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18168         
18169         this.picker().on('mousedown', this.onMousedown, this);
18170         this.picker().on('click', this.onClick, this);
18171         
18172         this.picker().addClass('datepicker-dropdown');
18173         
18174         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18175             v.setStyle('width', '189px');
18176         });
18177         
18178         this.fillMonths();
18179         
18180         this.update();
18181         
18182         if(this.isInline) {
18183             this.show();
18184         }
18185         
18186     },
18187     
18188     setValue: function(v, suppressEvent)
18189     {   
18190         var o = this.getValue();
18191         
18192         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
18193         
18194         this.update();
18195
18196         if(suppressEvent !== true){
18197             this.fireEvent('select', this, o, v);
18198         }
18199         
18200     },
18201     
18202     getValue: function()
18203     {
18204         return this.value;
18205     },
18206     
18207     onClick: function(e) 
18208     {
18209         e.stopPropagation();
18210         e.preventDefault();
18211         
18212         var target = e.getTarget();
18213         
18214         if(target.nodeName.toLowerCase() === 'i'){
18215             target = Roo.get(target).dom.parentNode;
18216         }
18217         
18218         var nodeName = target.nodeName;
18219         var className = target.className;
18220         var html = target.innerHTML;
18221         
18222         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18223             return;
18224         }
18225         
18226         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18227         
18228         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18229         
18230         this.hide();
18231                         
18232     },
18233     
18234     picker : function()
18235     {
18236         return this.pickerEl;
18237     },
18238     
18239     fillMonths: function()
18240     {    
18241         var i = 0;
18242         var months = this.picker().select('>.datepicker-months td', true).first();
18243         
18244         months.dom.innerHTML = '';
18245         
18246         while (i < 12) {
18247             var month = {
18248                 tag: 'span',
18249                 cls: 'month',
18250                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18251             };
18252             
18253             months.createChild(month);
18254         }
18255         
18256     },
18257     
18258     update: function()
18259     {
18260         var _this = this;
18261         
18262         if(typeof(this.vIndex) == 'undefined' && this.value.length){
18263             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18264         }
18265         
18266         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18267             e.removeClass('active');
18268             
18269             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18270                 e.addClass('active');
18271             }
18272         })
18273     },
18274     
18275     place: function()
18276     {
18277         if(this.isInline) {
18278             return;
18279         }
18280         
18281         this.picker().removeClass(['bottom', 'top']);
18282         
18283         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18284             /*
18285              * place to the top of element!
18286              *
18287              */
18288             
18289             this.picker().addClass('top');
18290             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18291             
18292             return;
18293         }
18294         
18295         this.picker().addClass('bottom');
18296         
18297         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18298     },
18299     
18300     onFocus : function()
18301     {
18302         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18303         this.show();
18304     },
18305     
18306     onBlur : function()
18307     {
18308         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18309         
18310         var d = this.inputEl().getValue();
18311         
18312         this.setValue(d);
18313                 
18314         this.hide();
18315     },
18316     
18317     show : function()
18318     {
18319         this.picker().show();
18320         this.picker().select('>.datepicker-months', true).first().show();
18321         this.update();
18322         this.place();
18323         
18324         this.fireEvent('show', this, this.date);
18325     },
18326     
18327     hide : function()
18328     {
18329         if(this.isInline) {
18330             return;
18331         }
18332         this.picker().hide();
18333         this.fireEvent('hide', this, this.date);
18334         
18335     },
18336     
18337     onMousedown: function(e)
18338     {
18339         e.stopPropagation();
18340         e.preventDefault();
18341     },
18342     
18343     keyup: function(e)
18344     {
18345         Roo.bootstrap.MonthField.superclass.keyup.call(this);
18346         this.update();
18347     },
18348
18349     fireKey: function(e)
18350     {
18351         if (!this.picker().isVisible()){
18352             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
18353                 this.show();
18354             }
18355             return;
18356         }
18357         
18358         var dir;
18359         
18360         switch(e.keyCode){
18361             case 27: // escape
18362                 this.hide();
18363                 e.preventDefault();
18364                 break;
18365             case 37: // left
18366             case 39: // right
18367                 dir = e.keyCode == 37 ? -1 : 1;
18368                 
18369                 this.vIndex = this.vIndex + dir;
18370                 
18371                 if(this.vIndex < 0){
18372                     this.vIndex = 0;
18373                 }
18374                 
18375                 if(this.vIndex > 11){
18376                     this.vIndex = 11;
18377                 }
18378                 
18379                 if(isNaN(this.vIndex)){
18380                     this.vIndex = 0;
18381                 }
18382                 
18383                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18384                 
18385                 break;
18386             case 38: // up
18387             case 40: // down
18388                 
18389                 dir = e.keyCode == 38 ? -1 : 1;
18390                 
18391                 this.vIndex = this.vIndex + dir * 4;
18392                 
18393                 if(this.vIndex < 0){
18394                     this.vIndex = 0;
18395                 }
18396                 
18397                 if(this.vIndex > 11){
18398                     this.vIndex = 11;
18399                 }
18400                 
18401                 if(isNaN(this.vIndex)){
18402                     this.vIndex = 0;
18403                 }
18404                 
18405                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18406                 break;
18407                 
18408             case 13: // enter
18409                 
18410                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18411                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18412                 }
18413                 
18414                 this.hide();
18415                 e.preventDefault();
18416                 break;
18417             case 9: // tab
18418                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18419                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18420                 }
18421                 this.hide();
18422                 break;
18423             case 16: // shift
18424             case 17: // ctrl
18425             case 18: // alt
18426                 break;
18427             default :
18428                 this.hide();
18429                 
18430         }
18431     },
18432     
18433     remove: function() 
18434     {
18435         this.picker().remove();
18436     }
18437    
18438 });
18439
18440 Roo.apply(Roo.bootstrap.MonthField,  {
18441     
18442     content : {
18443         tag: 'tbody',
18444         cn: [
18445         {
18446             tag: 'tr',
18447             cn: [
18448             {
18449                 tag: 'td',
18450                 colspan: '7'
18451             }
18452             ]
18453         }
18454         ]
18455     },
18456     
18457     dates:{
18458         en: {
18459             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18460             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18461         }
18462     }
18463 });
18464
18465 Roo.apply(Roo.bootstrap.MonthField,  {
18466   
18467     template : {
18468         tag: 'div',
18469         cls: 'datepicker dropdown-menu roo-dynamic',
18470         cn: [
18471             {
18472                 tag: 'div',
18473                 cls: 'datepicker-months',
18474                 cn: [
18475                 {
18476                     tag: 'table',
18477                     cls: 'table-condensed',
18478                     cn:[
18479                         Roo.bootstrap.DateField.content
18480                     ]
18481                 }
18482                 ]
18483             }
18484         ]
18485     }
18486 });
18487
18488  
18489
18490  
18491  /*
18492  * - LGPL
18493  *
18494  * CheckBox
18495  * 
18496  */
18497
18498 /**
18499  * @class Roo.bootstrap.CheckBox
18500  * @extends Roo.bootstrap.Input
18501  * Bootstrap CheckBox class
18502  * 
18503  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18504  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18505  * @cfg {String} boxLabel The text that appears beside the checkbox
18506  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18507  * @cfg {Boolean} checked initnal the element
18508  * @cfg {Boolean} inline inline the element (default false)
18509  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18510  * 
18511  * @constructor
18512  * Create a new CheckBox
18513  * @param {Object} config The config object
18514  */
18515
18516 Roo.bootstrap.CheckBox = function(config){
18517     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18518    
18519     this.addEvents({
18520         /**
18521         * @event check
18522         * Fires when the element is checked or unchecked.
18523         * @param {Roo.bootstrap.CheckBox} this This input
18524         * @param {Boolean} checked The new checked value
18525         */
18526        check : true
18527     });
18528     
18529 };
18530
18531 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
18532   
18533     inputType: 'checkbox',
18534     inputValue: 1,
18535     valueOff: 0,
18536     boxLabel: false,
18537     checked: false,
18538     weight : false,
18539     inline: false,
18540     
18541     getAutoCreate : function()
18542     {
18543         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18544         
18545         var id = Roo.id();
18546         
18547         var cfg = {};
18548         
18549         cfg.cls = 'form-group ' + this.inputType; //input-group
18550         
18551         if(this.inline){
18552             cfg.cls += ' ' + this.inputType + '-inline';
18553         }
18554         
18555         var input =  {
18556             tag: 'input',
18557             id : id,
18558             type : this.inputType,
18559             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18560             cls : 'roo-' + this.inputType, //'form-box',
18561             placeholder : this.placeholder || ''
18562             
18563         };
18564         
18565         if (this.weight) { // Validity check?
18566             cfg.cls += " " + this.inputType + "-" + this.weight;
18567         }
18568         
18569         if (this.disabled) {
18570             input.disabled=true;
18571         }
18572         
18573         if(this.checked){
18574             input.checked = this.checked;
18575         }
18576         
18577         if (this.name) {
18578             input.name = this.name;
18579         }
18580         
18581         if (this.size) {
18582             input.cls += ' input-' + this.size;
18583         }
18584         
18585         var settings=this;
18586         
18587         ['xs','sm','md','lg'].map(function(size){
18588             if (settings[size]) {
18589                 cfg.cls += ' col-' + size + '-' + settings[size];
18590             }
18591         });
18592         
18593         var inputblock = input;
18594          
18595         if (this.before || this.after) {
18596             
18597             inputblock = {
18598                 cls : 'input-group',
18599                 cn :  [] 
18600             };
18601             
18602             if (this.before) {
18603                 inputblock.cn.push({
18604                     tag :'span',
18605                     cls : 'input-group-addon',
18606                     html : this.before
18607                 });
18608             }
18609             
18610             inputblock.cn.push(input);
18611             
18612             if (this.after) {
18613                 inputblock.cn.push({
18614                     tag :'span',
18615                     cls : 'input-group-addon',
18616                     html : this.after
18617                 });
18618             }
18619             
18620         }
18621         
18622         if (align ==='left' && this.fieldLabel.length) {
18623                 Roo.log("left and has label");
18624                 cfg.cn = [
18625                     
18626                     {
18627                         tag: 'label',
18628                         'for' :  id,
18629                         cls : 'control-label col-md-' + this.labelWidth,
18630                         html : this.fieldLabel
18631                         
18632                     },
18633                     {
18634                         cls : "col-md-" + (12 - this.labelWidth), 
18635                         cn: [
18636                             inputblock
18637                         ]
18638                     }
18639                     
18640                 ];
18641         } else if ( this.fieldLabel.length) {
18642                 Roo.log(" label");
18643                 cfg.cn = [
18644                    
18645                     {
18646                         tag: this.boxLabel ? 'span' : 'label',
18647                         'for': id,
18648                         cls: 'control-label box-input-label',
18649                         //cls : 'input-group-addon',
18650                         html : this.fieldLabel
18651                         
18652                     },
18653                     
18654                     inputblock
18655                     
18656                 ];
18657
18658         } else {
18659             
18660                 Roo.log(" no label && no align");
18661                 cfg.cn = [  inputblock ] ;
18662                 
18663                 
18664         }
18665         if(this.boxLabel){
18666              var boxLabelCfg = {
18667                 tag: 'label',
18668                 //'for': id, // box label is handled by onclick - so no for...
18669                 cls: 'box-label',
18670                 html: this.boxLabel
18671             };
18672             
18673             if(this.tooltip){
18674                 boxLabelCfg.tooltip = this.tooltip;
18675             }
18676              
18677             cfg.cn.push(boxLabelCfg);
18678         }
18679         
18680         
18681        
18682         return cfg;
18683         
18684     },
18685     
18686     /**
18687      * return the real input element.
18688      */
18689     inputEl: function ()
18690     {
18691         return this.el.select('input.roo-' + this.inputType,true).first();
18692     },
18693     
18694     labelEl: function()
18695     {
18696         return this.el.select('label.control-label',true).first();
18697     },
18698     /* depricated... */
18699     
18700     label: function()
18701     {
18702         return this.labelEl();
18703     },
18704     
18705     initEvents : function()
18706     {
18707 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18708         
18709         this.inputEl().on('click', this.onClick,  this);
18710         
18711         if (this.boxLabel) { 
18712             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
18713         }
18714         
18715         this.startValue = this.getValue();
18716         
18717         if(this.groupId){
18718             Roo.bootstrap.CheckBox.register(this);
18719         }
18720     },
18721     
18722     onClick : function()
18723     {   
18724         this.setChecked(!this.checked);
18725     },
18726     
18727     setChecked : function(state,suppressEvent)
18728     {
18729         this.startValue = this.getValue();
18730         
18731         if(this.inputType == 'radio'){
18732             
18733             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18734                 e.dom.checked = false;
18735             });
18736             
18737             this.inputEl().dom.checked = true;
18738             
18739             this.inputEl().dom.value = this.inputValue;
18740             
18741             if(suppressEvent !== true){
18742                 this.fireEvent('check', this, true);
18743             }
18744             
18745             this.validate();
18746             
18747             return;
18748         }
18749         
18750         this.checked = state;
18751         
18752         this.inputEl().dom.checked = state;
18753         
18754         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18755         
18756         if(suppressEvent !== true){
18757             this.fireEvent('check', this, state);
18758         }
18759         
18760         this.validate();
18761     },
18762     
18763     getValue : function()
18764     {
18765         if(this.inputType == 'radio'){
18766             return this.getGroupValue();
18767         }
18768         
18769         return this.inputEl().getValue();
18770         
18771     },
18772     
18773     getGroupValue : function()
18774     {
18775         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18776             return '';
18777         }
18778         
18779         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18780     },
18781     
18782     setValue : function(v,suppressEvent)
18783     {
18784         if(this.inputType == 'radio'){
18785             this.setGroupValue(v, suppressEvent);
18786             return;
18787         }
18788         
18789         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18790         
18791         this.validate();
18792     },
18793     
18794     setGroupValue : function(v, suppressEvent)
18795     {
18796         this.startValue = this.getValue();
18797         
18798         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18799             e.dom.checked = false;
18800             
18801             if(e.dom.value == v){
18802                 e.dom.checked = true;
18803             }
18804         });
18805         
18806         if(suppressEvent !== true){
18807             this.fireEvent('check', this, true);
18808         }
18809
18810         this.validate();
18811         
18812         return;
18813     },
18814     
18815     validate : function()
18816     {
18817         if(
18818                 this.disabled || 
18819                 (this.inputType == 'radio' && this.validateRadio()) ||
18820                 (this.inputType == 'checkbox' && this.validateCheckbox())
18821         ){
18822             this.markValid();
18823             return true;
18824         }
18825         
18826         this.markInvalid();
18827         return false;
18828     },
18829     
18830     validateRadio : function()
18831     {
18832         var valid = false;
18833         
18834         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18835             if(!e.dom.checked){
18836                 return;
18837             }
18838             
18839             valid = true;
18840             
18841             return false;
18842         });
18843         
18844         return valid;
18845     },
18846     
18847     validateCheckbox : function()
18848     {
18849         if(!this.groupId){
18850             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18851         }
18852         
18853         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18854         
18855         if(!group){
18856             return false;
18857         }
18858         
18859         var r = false;
18860         
18861         for(var i in group){
18862             if(r){
18863                 break;
18864             }
18865             
18866             r = (group[i].getValue() == group[i].inputValue) ? true : false;
18867         }
18868         
18869         return r;
18870     },
18871     
18872     /**
18873      * Mark this field as valid
18874      */
18875     markValid : function()
18876     {
18877         if(this.allowBlank){
18878             return;
18879         }
18880         
18881         var _this = this;
18882         
18883         this.fireEvent('valid', this);
18884         
18885         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18886         
18887         if(this.groupId){
18888             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18889         }
18890         
18891         if(label){
18892             label.markValid();
18893         }
18894         
18895         if(this.inputType == 'radio'){
18896             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18897                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18898                 e.findParent('.form-group', false, true).addClass(_this.validClass);
18899             });
18900             
18901             return;
18902         }
18903         
18904         if(!this.groupId){
18905             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18906             this.el.findParent('.form-group', false, true).addClass(this.validClass);
18907             return;
18908         }
18909         
18910         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18911             
18912         if(!group){
18913             return;
18914         }
18915         
18916         for(var i in group){
18917             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18918             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
18919         }
18920     },
18921     
18922      /**
18923      * Mark this field as invalid
18924      * @param {String} msg The validation message
18925      */
18926     markInvalid : function(msg)
18927     {
18928         if(this.allowBlank){
18929             return;
18930         }
18931         
18932         var _this = this;
18933         
18934         this.fireEvent('invalid', this, msg);
18935         
18936         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18937         
18938         if(this.groupId){
18939             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18940         }
18941         
18942         if(label){
18943             label.markInvalid();
18944         }
18945             
18946         if(this.inputType == 'radio'){
18947             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18948                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18949                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
18950             });
18951             
18952             return;
18953         }
18954         
18955         if(!this.groupId){
18956             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18957             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
18958             return;
18959         }
18960         
18961         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18962         
18963         if(!group){
18964             return;
18965         }
18966         
18967         for(var i in group){
18968             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18969             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
18970         }
18971         
18972     }
18973     
18974 });
18975
18976 Roo.apply(Roo.bootstrap.CheckBox, {
18977     
18978     groups: {},
18979     
18980      /**
18981     * register a CheckBox Group
18982     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
18983     */
18984     register : function(checkbox)
18985     {
18986         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
18987             this.groups[checkbox.groupId] = {};
18988         }
18989         
18990         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
18991             return;
18992         }
18993         
18994         this.groups[checkbox.groupId][checkbox.name] = checkbox;
18995         
18996     },
18997     /**
18998     * fetch a CheckBox Group based on the group ID
18999     * @param {string} the group ID
19000     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
19001     */
19002     get: function(groupId) {
19003         if (typeof(this.groups[groupId]) == 'undefined') {
19004             return false;
19005         }
19006         
19007         return this.groups[groupId] ;
19008     }
19009     
19010     
19011 });
19012 /*
19013  * - LGPL
19014  *
19015  * Radio
19016  *
19017  *
19018  * not inline
19019  *<div class="radio">
19020   <label>
19021     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
19022     Option one is this and that&mdash;be sure to include why it's great
19023   </label>
19024 </div>
19025  *
19026  *
19027  *inline
19028  *<span>
19029  *<label class="radio-inline">fieldLabel</label>
19030  *<label class="radio-inline">
19031   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
19032 </label>
19033 <span>
19034  * 
19035  * 
19036  */
19037
19038 /**
19039  * @class Roo.bootstrap.Radio
19040  * @extends Roo.bootstrap.CheckBox
19041  * Bootstrap Radio class
19042
19043  * @constructor
19044  * Create a new Radio
19045  * @param {Object} config The config object
19046  */
19047
19048 Roo.bootstrap.Radio = function(config){
19049     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
19050    
19051 };
19052
19053 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
19054     
19055     inputType: 'radio',
19056     inputValue: '',
19057     valueOff: '',
19058     
19059     getAutoCreate : function()
19060     {
19061         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19062         align = align || 'left'; // default...
19063         
19064         
19065         
19066         var id = Roo.id();
19067         
19068         var cfg = {
19069                 tag : this.inline ? 'span' : 'div',
19070                 cls : '',
19071                 cn : []
19072         };
19073         
19074         var inline = this.inline ? ' radio-inline' : '';
19075         
19076         var lbl = {
19077                 tag: 'label' ,
19078                 // does not need for, as we wrap the input with it..
19079                 'for' : id,
19080                 cls : 'control-label box-label' + inline,
19081                 cn : []
19082         };
19083         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
19084         
19085         var fieldLabel = {
19086             tag: 'label' ,
19087             //cls : 'control-label' + inline,
19088             html : this.fieldLabel,
19089             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
19090         };
19091         
19092  
19093         
19094         
19095         var input =  {
19096             tag: 'input',
19097             id : id,
19098             type : this.inputType,
19099             //value : (!this.checked) ? this.valueOff : this.inputValue,
19100             value : this.inputValue,
19101             cls : 'roo-radio',
19102             placeholder : this.placeholder || '' // ?? needed????
19103             
19104         };
19105         if (this.weight) { // Validity check?
19106             input.cls += " radio-" + this.weight;
19107         }
19108         if (this.disabled) {
19109             input.disabled=true;
19110         }
19111         
19112         if(this.checked){
19113             input.checked = this.checked;
19114         }
19115         
19116         if (this.name) {
19117             input.name = this.name;
19118         }
19119         
19120         if (this.size) {
19121             input.cls += ' input-' + this.size;
19122         }
19123         
19124         //?? can span's inline have a width??
19125         
19126         var settings=this;
19127         ['xs','sm','md','lg'].map(function(size){
19128             if (settings[size]) {
19129                 cfg.cls += ' col-' + size + '-' + settings[size];
19130             }
19131         });
19132         
19133         var inputblock = input;
19134         
19135         if (this.before || this.after) {
19136             
19137             inputblock = {
19138                 cls : 'input-group',
19139                 tag : 'span',
19140                 cn :  [] 
19141             };
19142             if (this.before) {
19143                 inputblock.cn.push({
19144                     tag :'span',
19145                     cls : 'input-group-addon',
19146                     html : this.before
19147                 });
19148             }
19149             inputblock.cn.push(input);
19150             if (this.after) {
19151                 inputblock.cn.push({
19152                     tag :'span',
19153                     cls : 'input-group-addon',
19154                     html : this.after
19155                 });
19156             }
19157             
19158         };
19159         
19160         
19161         if (this.fieldLabel && this.fieldLabel.length) {
19162             cfg.cn.push(fieldLabel);
19163         }
19164        
19165         // normal bootstrap puts the input inside the label.
19166         // however with our styled version - it has to go after the input.
19167        
19168         //lbl.cn.push(inputblock);
19169         
19170         var lblwrap =  {
19171             tag: 'span',
19172             cls: 'radio' + inline,
19173             cn: [
19174                 inputblock,
19175                 lbl
19176             ]
19177         };
19178         
19179         cfg.cn.push( lblwrap);
19180         
19181         if(this.boxLabel){
19182             lbl.cn.push({
19183                 tag: 'span',
19184                 html: this.boxLabel
19185             })
19186         }
19187          
19188         
19189         return cfg;
19190         
19191     },
19192     
19193     initEvents : function()
19194     {
19195 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19196         
19197         this.inputEl().on('click', this.onClick,  this);
19198         if (this.boxLabel) {
19199             //Roo.log('find label');
19200             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
19201         }
19202         
19203     },
19204     
19205     inputEl: function ()
19206     {
19207         return this.el.select('input.roo-radio',true).first();
19208     },
19209     onClick : function()
19210     {   
19211         Roo.log("click");
19212         this.setChecked(true);
19213     },
19214     
19215     setChecked : function(state,suppressEvent)
19216     {
19217         if(state){
19218             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19219                 v.dom.checked = false;
19220             });
19221         }
19222         Roo.log(this.inputEl().dom);
19223         this.checked = state;
19224         this.inputEl().dom.checked = state;
19225         
19226         if(suppressEvent !== true){
19227             this.fireEvent('check', this, state);
19228         }
19229         
19230         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19231         
19232     },
19233     
19234     getGroupValue : function()
19235     {
19236         var value = '';
19237         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19238             if(v.dom.checked == true){
19239                 value = v.dom.value;
19240             }
19241         });
19242         
19243         return value;
19244     },
19245     
19246     /**
19247      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
19248      * @return {Mixed} value The field value
19249      */
19250     getValue : function(){
19251         return this.getGroupValue();
19252     }
19253     
19254 });
19255
19256  
19257 //<script type="text/javascript">
19258
19259 /*
19260  * Based  Ext JS Library 1.1.1
19261  * Copyright(c) 2006-2007, Ext JS, LLC.
19262  * LGPL
19263  *
19264  */
19265  
19266 /**
19267  * @class Roo.HtmlEditorCore
19268  * @extends Roo.Component
19269  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
19270  *
19271  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
19272  */
19273
19274 Roo.HtmlEditorCore = function(config){
19275     
19276     
19277     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
19278     
19279     
19280     this.addEvents({
19281         /**
19282          * @event initialize
19283          * Fires when the editor is fully initialized (including the iframe)
19284          * @param {Roo.HtmlEditorCore} this
19285          */
19286         initialize: true,
19287         /**
19288          * @event activate
19289          * Fires when the editor is first receives the focus. Any insertion must wait
19290          * until after this event.
19291          * @param {Roo.HtmlEditorCore} this
19292          */
19293         activate: true,
19294          /**
19295          * @event beforesync
19296          * Fires before the textarea is updated with content from the editor iframe. Return false
19297          * to cancel the sync.
19298          * @param {Roo.HtmlEditorCore} this
19299          * @param {String} html
19300          */
19301         beforesync: true,
19302          /**
19303          * @event beforepush
19304          * Fires before the iframe editor is updated with content from the textarea. Return false
19305          * to cancel the push.
19306          * @param {Roo.HtmlEditorCore} this
19307          * @param {String} html
19308          */
19309         beforepush: true,
19310          /**
19311          * @event sync
19312          * Fires when the textarea is updated with content from the editor iframe.
19313          * @param {Roo.HtmlEditorCore} this
19314          * @param {String} html
19315          */
19316         sync: true,
19317          /**
19318          * @event push
19319          * Fires when the iframe editor is updated with content from the textarea.
19320          * @param {Roo.HtmlEditorCore} this
19321          * @param {String} html
19322          */
19323         push: true,
19324         
19325         /**
19326          * @event editorevent
19327          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19328          * @param {Roo.HtmlEditorCore} this
19329          */
19330         editorevent: true
19331         
19332     });
19333     
19334     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
19335     
19336     // defaults : white / black...
19337     this.applyBlacklists();
19338     
19339     
19340     
19341 };
19342
19343
19344 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
19345
19346
19347      /**
19348      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
19349      */
19350     
19351     owner : false,
19352     
19353      /**
19354      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
19355      *                        Roo.resizable.
19356      */
19357     resizable : false,
19358      /**
19359      * @cfg {Number} height (in pixels)
19360      */   
19361     height: 300,
19362    /**
19363      * @cfg {Number} width (in pixels)
19364      */   
19365     width: 500,
19366     
19367     /**
19368      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19369      * 
19370      */
19371     stylesheets: false,
19372     
19373     // id of frame..
19374     frameId: false,
19375     
19376     // private properties
19377     validationEvent : false,
19378     deferHeight: true,
19379     initialized : false,
19380     activated : false,
19381     sourceEditMode : false,
19382     onFocus : Roo.emptyFn,
19383     iframePad:3,
19384     hideMode:'offsets',
19385     
19386     clearUp: true,
19387     
19388     // blacklist + whitelisted elements..
19389     black: false,
19390     white: false,
19391      
19392     
19393
19394     /**
19395      * Protected method that will not generally be called directly. It
19396      * is called when the editor initializes the iframe with HTML contents. Override this method if you
19397      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
19398      */
19399     getDocMarkup : function(){
19400         // body styles..
19401         var st = '';
19402         
19403         // inherit styels from page...?? 
19404         if (this.stylesheets === false) {
19405             
19406             Roo.get(document.head).select('style').each(function(node) {
19407                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19408             });
19409             
19410             Roo.get(document.head).select('link').each(function(node) { 
19411                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19412             });
19413             
19414         } else if (!this.stylesheets.length) {
19415                 // simple..
19416                 st = '<style type="text/css">' +
19417                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19418                    '</style>';
19419         } else { 
19420             
19421         }
19422         
19423         st +=  '<style type="text/css">' +
19424             'IMG { cursor: pointer } ' +
19425         '</style>';
19426
19427         
19428         return '<html><head>' + st  +
19429             //<style type="text/css">' +
19430             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19431             //'</style>' +
19432             ' </head><body class="roo-htmleditor-body"></body></html>';
19433     },
19434
19435     // private
19436     onRender : function(ct, position)
19437     {
19438         var _t = this;
19439         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19440         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19441         
19442         
19443         this.el.dom.style.border = '0 none';
19444         this.el.dom.setAttribute('tabIndex', -1);
19445         this.el.addClass('x-hidden hide');
19446         
19447         
19448         
19449         if(Roo.isIE){ // fix IE 1px bogus margin
19450             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19451         }
19452        
19453         
19454         this.frameId = Roo.id();
19455         
19456          
19457         
19458         var iframe = this.owner.wrap.createChild({
19459             tag: 'iframe',
19460             cls: 'form-control', // bootstrap..
19461             id: this.frameId,
19462             name: this.frameId,
19463             frameBorder : 'no',
19464             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
19465         }, this.el
19466         );
19467         
19468         
19469         this.iframe = iframe.dom;
19470
19471          this.assignDocWin();
19472         
19473         this.doc.designMode = 'on';
19474        
19475         this.doc.open();
19476         this.doc.write(this.getDocMarkup());
19477         this.doc.close();
19478
19479         
19480         var task = { // must defer to wait for browser to be ready
19481             run : function(){
19482                 //console.log("run task?" + this.doc.readyState);
19483                 this.assignDocWin();
19484                 if(this.doc.body || this.doc.readyState == 'complete'){
19485                     try {
19486                         this.doc.designMode="on";
19487                     } catch (e) {
19488                         return;
19489                     }
19490                     Roo.TaskMgr.stop(task);
19491                     this.initEditor.defer(10, this);
19492                 }
19493             },
19494             interval : 10,
19495             duration: 10000,
19496             scope: this
19497         };
19498         Roo.TaskMgr.start(task);
19499
19500     },
19501
19502     // private
19503     onResize : function(w, h)
19504     {
19505          Roo.log('resize: ' +w + ',' + h );
19506         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19507         if(!this.iframe){
19508             return;
19509         }
19510         if(typeof w == 'number'){
19511             
19512             this.iframe.style.width = w + 'px';
19513         }
19514         if(typeof h == 'number'){
19515             
19516             this.iframe.style.height = h + 'px';
19517             if(this.doc){
19518                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19519             }
19520         }
19521         
19522     },
19523
19524     /**
19525      * Toggles the editor between standard and source edit mode.
19526      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19527      */
19528     toggleSourceEdit : function(sourceEditMode){
19529         
19530         this.sourceEditMode = sourceEditMode === true;
19531         
19532         if(this.sourceEditMode){
19533  
19534             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
19535             
19536         }else{
19537             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19538             //this.iframe.className = '';
19539             this.deferFocus();
19540         }
19541         //this.setSize(this.owner.wrap.getSize());
19542         //this.fireEvent('editmodechange', this, this.sourceEditMode);
19543     },
19544
19545     
19546   
19547
19548     /**
19549      * Protected method that will not generally be called directly. If you need/want
19550      * custom HTML cleanup, this is the method you should override.
19551      * @param {String} html The HTML to be cleaned
19552      * return {String} The cleaned HTML
19553      */
19554     cleanHtml : function(html){
19555         html = String(html);
19556         if(html.length > 5){
19557             if(Roo.isSafari){ // strip safari nonsense
19558                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19559             }
19560         }
19561         if(html == '&nbsp;'){
19562             html = '';
19563         }
19564         return html;
19565     },
19566
19567     /**
19568      * HTML Editor -> Textarea
19569      * Protected method that will not generally be called directly. Syncs the contents
19570      * of the editor iframe with the textarea.
19571      */
19572     syncValue : function(){
19573         if(this.initialized){
19574             var bd = (this.doc.body || this.doc.documentElement);
19575             //this.cleanUpPaste(); -- this is done else where and causes havoc..
19576             var html = bd.innerHTML;
19577             if(Roo.isSafari){
19578                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19579                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19580                 if(m && m[1]){
19581                     html = '<div style="'+m[0]+'">' + html + '</div>';
19582                 }
19583             }
19584             html = this.cleanHtml(html);
19585             // fix up the special chars.. normaly like back quotes in word...
19586             // however we do not want to do this with chinese..
19587             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19588                 var cc = b.charCodeAt();
19589                 if (
19590                     (cc >= 0x4E00 && cc < 0xA000 ) ||
19591                     (cc >= 0x3400 && cc < 0x4E00 ) ||
19592                     (cc >= 0xf900 && cc < 0xfb00 )
19593                 ) {
19594                         return b;
19595                 }
19596                 return "&#"+cc+";" 
19597             });
19598             if(this.owner.fireEvent('beforesync', this, html) !== false){
19599                 this.el.dom.value = html;
19600                 this.owner.fireEvent('sync', this, html);
19601             }
19602         }
19603     },
19604
19605     /**
19606      * Protected method that will not generally be called directly. Pushes the value of the textarea
19607      * into the iframe editor.
19608      */
19609     pushValue : function(){
19610         if(this.initialized){
19611             var v = this.el.dom.value.trim();
19612             
19613 //            if(v.length < 1){
19614 //                v = '&#160;';
19615 //            }
19616             
19617             if(this.owner.fireEvent('beforepush', this, v) !== false){
19618                 var d = (this.doc.body || this.doc.documentElement);
19619                 d.innerHTML = v;
19620                 this.cleanUpPaste();
19621                 this.el.dom.value = d.innerHTML;
19622                 this.owner.fireEvent('push', this, v);
19623             }
19624         }
19625     },
19626
19627     // private
19628     deferFocus : function(){
19629         this.focus.defer(10, this);
19630     },
19631
19632     // doc'ed in Field
19633     focus : function(){
19634         if(this.win && !this.sourceEditMode){
19635             this.win.focus();
19636         }else{
19637             this.el.focus();
19638         }
19639     },
19640     
19641     assignDocWin: function()
19642     {
19643         var iframe = this.iframe;
19644         
19645          if(Roo.isIE){
19646             this.doc = iframe.contentWindow.document;
19647             this.win = iframe.contentWindow;
19648         } else {
19649 //            if (!Roo.get(this.frameId)) {
19650 //                return;
19651 //            }
19652 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19653 //            this.win = Roo.get(this.frameId).dom.contentWindow;
19654             
19655             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19656                 return;
19657             }
19658             
19659             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19660             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19661         }
19662     },
19663     
19664     // private
19665     initEditor : function(){
19666         //console.log("INIT EDITOR");
19667         this.assignDocWin();
19668         
19669         
19670         
19671         this.doc.designMode="on";
19672         this.doc.open();
19673         this.doc.write(this.getDocMarkup());
19674         this.doc.close();
19675         
19676         var dbody = (this.doc.body || this.doc.documentElement);
19677         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19678         // this copies styles from the containing element into thsi one..
19679         // not sure why we need all of this..
19680         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19681         
19682         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19683         //ss['background-attachment'] = 'fixed'; // w3c
19684         dbody.bgProperties = 'fixed'; // ie
19685         //Roo.DomHelper.applyStyles(dbody, ss);
19686         Roo.EventManager.on(this.doc, {
19687             //'mousedown': this.onEditorEvent,
19688             'mouseup': this.onEditorEvent,
19689             'dblclick': this.onEditorEvent,
19690             'click': this.onEditorEvent,
19691             'keyup': this.onEditorEvent,
19692             buffer:100,
19693             scope: this
19694         });
19695         if(Roo.isGecko){
19696             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19697         }
19698         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19699             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19700         }
19701         this.initialized = true;
19702
19703         this.owner.fireEvent('initialize', this);
19704         this.pushValue();
19705     },
19706
19707     // private
19708     onDestroy : function(){
19709         
19710         
19711         
19712         if(this.rendered){
19713             
19714             //for (var i =0; i < this.toolbars.length;i++) {
19715             //    // fixme - ask toolbars for heights?
19716             //    this.toolbars[i].onDestroy();
19717            // }
19718             
19719             //this.wrap.dom.innerHTML = '';
19720             //this.wrap.remove();
19721         }
19722     },
19723
19724     // private
19725     onFirstFocus : function(){
19726         
19727         this.assignDocWin();
19728         
19729         
19730         this.activated = true;
19731          
19732     
19733         if(Roo.isGecko){ // prevent silly gecko errors
19734             this.win.focus();
19735             var s = this.win.getSelection();
19736             if(!s.focusNode || s.focusNode.nodeType != 3){
19737                 var r = s.getRangeAt(0);
19738                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19739                 r.collapse(true);
19740                 this.deferFocus();
19741             }
19742             try{
19743                 this.execCmd('useCSS', true);
19744                 this.execCmd('styleWithCSS', false);
19745             }catch(e){}
19746         }
19747         this.owner.fireEvent('activate', this);
19748     },
19749
19750     // private
19751     adjustFont: function(btn){
19752         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19753         //if(Roo.isSafari){ // safari
19754         //    adjust *= 2;
19755        // }
19756         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19757         if(Roo.isSafari){ // safari
19758             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19759             v =  (v < 10) ? 10 : v;
19760             v =  (v > 48) ? 48 : v;
19761             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19762             
19763         }
19764         
19765         
19766         v = Math.max(1, v+adjust);
19767         
19768         this.execCmd('FontSize', v  );
19769     },
19770
19771     onEditorEvent : function(e)
19772     {
19773         this.owner.fireEvent('editorevent', this, e);
19774       //  this.updateToolbar();
19775         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19776     },
19777
19778     insertTag : function(tg)
19779     {
19780         // could be a bit smarter... -> wrap the current selected tRoo..
19781         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19782             
19783             range = this.createRange(this.getSelection());
19784             var wrappingNode = this.doc.createElement(tg.toLowerCase());
19785             wrappingNode.appendChild(range.extractContents());
19786             range.insertNode(wrappingNode);
19787
19788             return;
19789             
19790             
19791             
19792         }
19793         this.execCmd("formatblock",   tg);
19794         
19795     },
19796     
19797     insertText : function(txt)
19798     {
19799         
19800         
19801         var range = this.createRange();
19802         range.deleteContents();
19803                //alert(Sender.getAttribute('label'));
19804                
19805         range.insertNode(this.doc.createTextNode(txt));
19806     } ,
19807     
19808      
19809
19810     /**
19811      * Executes a Midas editor command on the editor document and performs necessary focus and
19812      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19813      * @param {String} cmd The Midas command
19814      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19815      */
19816     relayCmd : function(cmd, value){
19817         this.win.focus();
19818         this.execCmd(cmd, value);
19819         this.owner.fireEvent('editorevent', this);
19820         //this.updateToolbar();
19821         this.owner.deferFocus();
19822     },
19823
19824     /**
19825      * Executes a Midas editor command directly on the editor document.
19826      * For visual commands, you should use {@link #relayCmd} instead.
19827      * <b>This should only be called after the editor is initialized.</b>
19828      * @param {String} cmd The Midas command
19829      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19830      */
19831     execCmd : function(cmd, value){
19832         this.doc.execCommand(cmd, false, value === undefined ? null : value);
19833         this.syncValue();
19834     },
19835  
19836  
19837    
19838     /**
19839      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19840      * to insert tRoo.
19841      * @param {String} text | dom node.. 
19842      */
19843     insertAtCursor : function(text)
19844     {
19845         
19846         
19847         
19848         if(!this.activated){
19849             return;
19850         }
19851         /*
19852         if(Roo.isIE){
19853             this.win.focus();
19854             var r = this.doc.selection.createRange();
19855             if(r){
19856                 r.collapse(true);
19857                 r.pasteHTML(text);
19858                 this.syncValue();
19859                 this.deferFocus();
19860             
19861             }
19862             return;
19863         }
19864         */
19865         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19866             this.win.focus();
19867             
19868             
19869             // from jquery ui (MIT licenced)
19870             var range, node;
19871             var win = this.win;
19872             
19873             if (win.getSelection && win.getSelection().getRangeAt) {
19874                 range = win.getSelection().getRangeAt(0);
19875                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19876                 range.insertNode(node);
19877             } else if (win.document.selection && win.document.selection.createRange) {
19878                 // no firefox support
19879                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19880                 win.document.selection.createRange().pasteHTML(txt);
19881             } else {
19882                 // no firefox support
19883                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19884                 this.execCmd('InsertHTML', txt);
19885             } 
19886             
19887             this.syncValue();
19888             
19889             this.deferFocus();
19890         }
19891     },
19892  // private
19893     mozKeyPress : function(e){
19894         if(e.ctrlKey){
19895             var c = e.getCharCode(), cmd;
19896           
19897             if(c > 0){
19898                 c = String.fromCharCode(c).toLowerCase();
19899                 switch(c){
19900                     case 'b':
19901                         cmd = 'bold';
19902                         break;
19903                     case 'i':
19904                         cmd = 'italic';
19905                         break;
19906                     
19907                     case 'u':
19908                         cmd = 'underline';
19909                         break;
19910                     
19911                     case 'v':
19912                         this.cleanUpPaste.defer(100, this);
19913                         return;
19914                         
19915                 }
19916                 if(cmd){
19917                     this.win.focus();
19918                     this.execCmd(cmd);
19919                     this.deferFocus();
19920                     e.preventDefault();
19921                 }
19922                 
19923             }
19924         }
19925     },
19926
19927     // private
19928     fixKeys : function(){ // load time branching for fastest keydown performance
19929         if(Roo.isIE){
19930             return function(e){
19931                 var k = e.getKey(), r;
19932                 if(k == e.TAB){
19933                     e.stopEvent();
19934                     r = this.doc.selection.createRange();
19935                     if(r){
19936                         r.collapse(true);
19937                         r.pasteHTML('&#160;&#160;&#160;&#160;');
19938                         this.deferFocus();
19939                     }
19940                     return;
19941                 }
19942                 
19943                 if(k == e.ENTER){
19944                     r = this.doc.selection.createRange();
19945                     if(r){
19946                         var target = r.parentElement();
19947                         if(!target || target.tagName.toLowerCase() != 'li'){
19948                             e.stopEvent();
19949                             r.pasteHTML('<br />');
19950                             r.collapse(false);
19951                             r.select();
19952                         }
19953                     }
19954                 }
19955                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19956                     this.cleanUpPaste.defer(100, this);
19957                     return;
19958                 }
19959                 
19960                 
19961             };
19962         }else if(Roo.isOpera){
19963             return function(e){
19964                 var k = e.getKey();
19965                 if(k == e.TAB){
19966                     e.stopEvent();
19967                     this.win.focus();
19968                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
19969                     this.deferFocus();
19970                 }
19971                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19972                     this.cleanUpPaste.defer(100, this);
19973                     return;
19974                 }
19975                 
19976             };
19977         }else if(Roo.isSafari){
19978             return function(e){
19979                 var k = e.getKey();
19980                 
19981                 if(k == e.TAB){
19982                     e.stopEvent();
19983                     this.execCmd('InsertText','\t');
19984                     this.deferFocus();
19985                     return;
19986                 }
19987                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19988                     this.cleanUpPaste.defer(100, this);
19989                     return;
19990                 }
19991                 
19992              };
19993         }
19994     }(),
19995     
19996     getAllAncestors: function()
19997     {
19998         var p = this.getSelectedNode();
19999         var a = [];
20000         if (!p) {
20001             a.push(p); // push blank onto stack..
20002             p = this.getParentElement();
20003         }
20004         
20005         
20006         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
20007             a.push(p);
20008             p = p.parentNode;
20009         }
20010         a.push(this.doc.body);
20011         return a;
20012     },
20013     lastSel : false,
20014     lastSelNode : false,
20015     
20016     
20017     getSelection : function() 
20018     {
20019         this.assignDocWin();
20020         return Roo.isIE ? this.doc.selection : this.win.getSelection();
20021     },
20022     
20023     getSelectedNode: function() 
20024     {
20025         // this may only work on Gecko!!!
20026         
20027         // should we cache this!!!!
20028         
20029         
20030         
20031          
20032         var range = this.createRange(this.getSelection()).cloneRange();
20033         
20034         if (Roo.isIE) {
20035             var parent = range.parentElement();
20036             while (true) {
20037                 var testRange = range.duplicate();
20038                 testRange.moveToElementText(parent);
20039                 if (testRange.inRange(range)) {
20040                     break;
20041                 }
20042                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
20043                     break;
20044                 }
20045                 parent = parent.parentElement;
20046             }
20047             return parent;
20048         }
20049         
20050         // is ancestor a text element.
20051         var ac =  range.commonAncestorContainer;
20052         if (ac.nodeType == 3) {
20053             ac = ac.parentNode;
20054         }
20055         
20056         var ar = ac.childNodes;
20057          
20058         var nodes = [];
20059         var other_nodes = [];
20060         var has_other_nodes = false;
20061         for (var i=0;i<ar.length;i++) {
20062             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
20063                 continue;
20064             }
20065             // fullly contained node.
20066             
20067             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
20068                 nodes.push(ar[i]);
20069                 continue;
20070             }
20071             
20072             // probably selected..
20073             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
20074                 other_nodes.push(ar[i]);
20075                 continue;
20076             }
20077             // outer..
20078             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
20079                 continue;
20080             }
20081             
20082             
20083             has_other_nodes = true;
20084         }
20085         if (!nodes.length && other_nodes.length) {
20086             nodes= other_nodes;
20087         }
20088         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
20089             return false;
20090         }
20091         
20092         return nodes[0];
20093     },
20094     createRange: function(sel)
20095     {
20096         // this has strange effects when using with 
20097         // top toolbar - not sure if it's a great idea.
20098         //this.editor.contentWindow.focus();
20099         if (typeof sel != "undefined") {
20100             try {
20101                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
20102             } catch(e) {
20103                 return this.doc.createRange();
20104             }
20105         } else {
20106             return this.doc.createRange();
20107         }
20108     },
20109     getParentElement: function()
20110     {
20111         
20112         this.assignDocWin();
20113         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
20114         
20115         var range = this.createRange(sel);
20116          
20117         try {
20118             var p = range.commonAncestorContainer;
20119             while (p.nodeType == 3) { // text node
20120                 p = p.parentNode;
20121             }
20122             return p;
20123         } catch (e) {
20124             return null;
20125         }
20126     
20127     },
20128     /***
20129      *
20130      * Range intersection.. the hard stuff...
20131      *  '-1' = before
20132      *  '0' = hits..
20133      *  '1' = after.
20134      *         [ -- selected range --- ]
20135      *   [fail]                        [fail]
20136      *
20137      *    basically..
20138      *      if end is before start or  hits it. fail.
20139      *      if start is after end or hits it fail.
20140      *
20141      *   if either hits (but other is outside. - then it's not 
20142      *   
20143      *    
20144      **/
20145     
20146     
20147     // @see http://www.thismuchiknow.co.uk/?p=64.
20148     rangeIntersectsNode : function(range, node)
20149     {
20150         var nodeRange = node.ownerDocument.createRange();
20151         try {
20152             nodeRange.selectNode(node);
20153         } catch (e) {
20154             nodeRange.selectNodeContents(node);
20155         }
20156     
20157         var rangeStartRange = range.cloneRange();
20158         rangeStartRange.collapse(true);
20159     
20160         var rangeEndRange = range.cloneRange();
20161         rangeEndRange.collapse(false);
20162     
20163         var nodeStartRange = nodeRange.cloneRange();
20164         nodeStartRange.collapse(true);
20165     
20166         var nodeEndRange = nodeRange.cloneRange();
20167         nodeEndRange.collapse(false);
20168     
20169         return rangeStartRange.compareBoundaryPoints(
20170                  Range.START_TO_START, nodeEndRange) == -1 &&
20171                rangeEndRange.compareBoundaryPoints(
20172                  Range.START_TO_START, nodeStartRange) == 1;
20173         
20174          
20175     },
20176     rangeCompareNode : function(range, node)
20177     {
20178         var nodeRange = node.ownerDocument.createRange();
20179         try {
20180             nodeRange.selectNode(node);
20181         } catch (e) {
20182             nodeRange.selectNodeContents(node);
20183         }
20184         
20185         
20186         range.collapse(true);
20187     
20188         nodeRange.collapse(true);
20189      
20190         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
20191         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
20192          
20193         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
20194         
20195         var nodeIsBefore   =  ss == 1;
20196         var nodeIsAfter    = ee == -1;
20197         
20198         if (nodeIsBefore && nodeIsAfter) {
20199             return 0; // outer
20200         }
20201         if (!nodeIsBefore && nodeIsAfter) {
20202             return 1; //right trailed.
20203         }
20204         
20205         if (nodeIsBefore && !nodeIsAfter) {
20206             return 2;  // left trailed.
20207         }
20208         // fully contined.
20209         return 3;
20210     },
20211
20212     // private? - in a new class?
20213     cleanUpPaste :  function()
20214     {
20215         // cleans up the whole document..
20216         Roo.log('cleanuppaste');
20217         
20218         this.cleanUpChildren(this.doc.body);
20219         var clean = this.cleanWordChars(this.doc.body.innerHTML);
20220         if (clean != this.doc.body.innerHTML) {
20221             this.doc.body.innerHTML = clean;
20222         }
20223         
20224     },
20225     
20226     cleanWordChars : function(input) {// change the chars to hex code
20227         var he = Roo.HtmlEditorCore;
20228         
20229         var output = input;
20230         Roo.each(he.swapCodes, function(sw) { 
20231             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
20232             
20233             output = output.replace(swapper, sw[1]);
20234         });
20235         
20236         return output;
20237     },
20238     
20239     
20240     cleanUpChildren : function (n)
20241     {
20242         if (!n.childNodes.length) {
20243             return;
20244         }
20245         for (var i = n.childNodes.length-1; i > -1 ; i--) {
20246            this.cleanUpChild(n.childNodes[i]);
20247         }
20248     },
20249     
20250     
20251         
20252     
20253     cleanUpChild : function (node)
20254     {
20255         var ed = this;
20256         //console.log(node);
20257         if (node.nodeName == "#text") {
20258             // clean up silly Windows -- stuff?
20259             return; 
20260         }
20261         if (node.nodeName == "#comment") {
20262             node.parentNode.removeChild(node);
20263             // clean up silly Windows -- stuff?
20264             return; 
20265         }
20266         var lcname = node.tagName.toLowerCase();
20267         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
20268         // whitelist of tags..
20269         
20270         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
20271             // remove node.
20272             node.parentNode.removeChild(node);
20273             return;
20274             
20275         }
20276         
20277         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
20278         
20279         // remove <a name=....> as rendering on yahoo mailer is borked with this.
20280         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
20281         
20282         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
20283         //    remove_keep_children = true;
20284         //}
20285         
20286         if (remove_keep_children) {
20287             this.cleanUpChildren(node);
20288             // inserts everything just before this node...
20289             while (node.childNodes.length) {
20290                 var cn = node.childNodes[0];
20291                 node.removeChild(cn);
20292                 node.parentNode.insertBefore(cn, node);
20293             }
20294             node.parentNode.removeChild(node);
20295             return;
20296         }
20297         
20298         if (!node.attributes || !node.attributes.length) {
20299             this.cleanUpChildren(node);
20300             return;
20301         }
20302         
20303         function cleanAttr(n,v)
20304         {
20305             
20306             if (v.match(/^\./) || v.match(/^\//)) {
20307                 return;
20308             }
20309             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
20310                 return;
20311             }
20312             if (v.match(/^#/)) {
20313                 return;
20314             }
20315 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
20316             node.removeAttribute(n);
20317             
20318         }
20319         
20320         var cwhite = this.cwhite;
20321         var cblack = this.cblack;
20322             
20323         function cleanStyle(n,v)
20324         {
20325             if (v.match(/expression/)) { //XSS?? should we even bother..
20326                 node.removeAttribute(n);
20327                 return;
20328             }
20329             
20330             var parts = v.split(/;/);
20331             var clean = [];
20332             
20333             Roo.each(parts, function(p) {
20334                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
20335                 if (!p.length) {
20336                     return true;
20337                 }
20338                 var l = p.split(':').shift().replace(/\s+/g,'');
20339                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
20340                 
20341                 if ( cwhite.length && cblack.indexOf(l) > -1) {
20342 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20343                     //node.removeAttribute(n);
20344                     return true;
20345                 }
20346                 //Roo.log()
20347                 // only allow 'c whitelisted system attributes'
20348                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
20349 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20350                     //node.removeAttribute(n);
20351                     return true;
20352                 }
20353                 
20354                 
20355                  
20356                 
20357                 clean.push(p);
20358                 return true;
20359             });
20360             if (clean.length) { 
20361                 node.setAttribute(n, clean.join(';'));
20362             } else {
20363                 node.removeAttribute(n);
20364             }
20365             
20366         }
20367         
20368         
20369         for (var i = node.attributes.length-1; i > -1 ; i--) {
20370             var a = node.attributes[i];
20371             //console.log(a);
20372             
20373             if (a.name.toLowerCase().substr(0,2)=='on')  {
20374                 node.removeAttribute(a.name);
20375                 continue;
20376             }
20377             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
20378                 node.removeAttribute(a.name);
20379                 continue;
20380             }
20381             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
20382                 cleanAttr(a.name,a.value); // fixme..
20383                 continue;
20384             }
20385             if (a.name == 'style') {
20386                 cleanStyle(a.name,a.value);
20387                 continue;
20388             }
20389             /// clean up MS crap..
20390             // tecnically this should be a list of valid class'es..
20391             
20392             
20393             if (a.name == 'class') {
20394                 if (a.value.match(/^Mso/)) {
20395                     node.className = '';
20396                 }
20397                 
20398                 if (a.value.match(/body/)) {
20399                     node.className = '';
20400                 }
20401                 continue;
20402             }
20403             
20404             // style cleanup!?
20405             // class cleanup?
20406             
20407         }
20408         
20409         
20410         this.cleanUpChildren(node);
20411         
20412         
20413     },
20414     
20415     /**
20416      * Clean up MS wordisms...
20417      */
20418     cleanWord : function(node)
20419     {
20420         
20421         
20422         if (!node) {
20423             this.cleanWord(this.doc.body);
20424             return;
20425         }
20426         if (node.nodeName == "#text") {
20427             // clean up silly Windows -- stuff?
20428             return; 
20429         }
20430         if (node.nodeName == "#comment") {
20431             node.parentNode.removeChild(node);
20432             // clean up silly Windows -- stuff?
20433             return; 
20434         }
20435         
20436         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
20437             node.parentNode.removeChild(node);
20438             return;
20439         }
20440         
20441         // remove - but keep children..
20442         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20443             while (node.childNodes.length) {
20444                 var cn = node.childNodes[0];
20445                 node.removeChild(cn);
20446                 node.parentNode.insertBefore(cn, node);
20447             }
20448             node.parentNode.removeChild(node);
20449             this.iterateChildren(node, this.cleanWord);
20450             return;
20451         }
20452         // clean styles
20453         if (node.className.length) {
20454             
20455             var cn = node.className.split(/\W+/);
20456             var cna = [];
20457             Roo.each(cn, function(cls) {
20458                 if (cls.match(/Mso[a-zA-Z]+/)) {
20459                     return;
20460                 }
20461                 cna.push(cls);
20462             });
20463             node.className = cna.length ? cna.join(' ') : '';
20464             if (!cna.length) {
20465                 node.removeAttribute("class");
20466             }
20467         }
20468         
20469         if (node.hasAttribute("lang")) {
20470             node.removeAttribute("lang");
20471         }
20472         
20473         if (node.hasAttribute("style")) {
20474             
20475             var styles = node.getAttribute("style").split(";");
20476             var nstyle = [];
20477             Roo.each(styles, function(s) {
20478                 if (!s.match(/:/)) {
20479                     return;
20480                 }
20481                 var kv = s.split(":");
20482                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20483                     return;
20484                 }
20485                 // what ever is left... we allow.
20486                 nstyle.push(s);
20487             });
20488             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20489             if (!nstyle.length) {
20490                 node.removeAttribute('style');
20491             }
20492         }
20493         this.iterateChildren(node, this.cleanWord);
20494         
20495         
20496         
20497     },
20498     /**
20499      * iterateChildren of a Node, calling fn each time, using this as the scole..
20500      * @param {DomNode} node node to iterate children of.
20501      * @param {Function} fn method of this class to call on each item.
20502      */
20503     iterateChildren : function(node, fn)
20504     {
20505         if (!node.childNodes.length) {
20506                 return;
20507         }
20508         for (var i = node.childNodes.length-1; i > -1 ; i--) {
20509            fn.call(this, node.childNodes[i])
20510         }
20511     },
20512     
20513     
20514     /**
20515      * cleanTableWidths.
20516      *
20517      * Quite often pasting from word etc.. results in tables with column and widths.
20518      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20519      *
20520      */
20521     cleanTableWidths : function(node)
20522     {
20523          
20524          
20525         if (!node) {
20526             this.cleanTableWidths(this.doc.body);
20527             return;
20528         }
20529         
20530         // ignore list...
20531         if (node.nodeName == "#text" || node.nodeName == "#comment") {
20532             return; 
20533         }
20534         Roo.log(node.tagName);
20535         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20536             this.iterateChildren(node, this.cleanTableWidths);
20537             return;
20538         }
20539         if (node.hasAttribute('width')) {
20540             node.removeAttribute('width');
20541         }
20542         
20543          
20544         if (node.hasAttribute("style")) {
20545             // pretty basic...
20546             
20547             var styles = node.getAttribute("style").split(";");
20548             var nstyle = [];
20549             Roo.each(styles, function(s) {
20550                 if (!s.match(/:/)) {
20551                     return;
20552                 }
20553                 var kv = s.split(":");
20554                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20555                     return;
20556                 }
20557                 // what ever is left... we allow.
20558                 nstyle.push(s);
20559             });
20560             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20561             if (!nstyle.length) {
20562                 node.removeAttribute('style');
20563             }
20564         }
20565         
20566         this.iterateChildren(node, this.cleanTableWidths);
20567         
20568         
20569     },
20570     
20571     
20572     
20573     
20574     domToHTML : function(currentElement, depth, nopadtext) {
20575         
20576         depth = depth || 0;
20577         nopadtext = nopadtext || false;
20578     
20579         if (!currentElement) {
20580             return this.domToHTML(this.doc.body);
20581         }
20582         
20583         //Roo.log(currentElement);
20584         var j;
20585         var allText = false;
20586         var nodeName = currentElement.nodeName;
20587         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20588         
20589         if  (nodeName == '#text') {
20590             
20591             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20592         }
20593         
20594         
20595         var ret = '';
20596         if (nodeName != 'BODY') {
20597              
20598             var i = 0;
20599             // Prints the node tagName, such as <A>, <IMG>, etc
20600             if (tagName) {
20601                 var attr = [];
20602                 for(i = 0; i < currentElement.attributes.length;i++) {
20603                     // quoting?
20604                     var aname = currentElement.attributes.item(i).name;
20605                     if (!currentElement.attributes.item(i).value.length) {
20606                         continue;
20607                     }
20608                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20609                 }
20610                 
20611                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20612             } 
20613             else {
20614                 
20615                 // eack
20616             }
20617         } else {
20618             tagName = false;
20619         }
20620         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20621             return ret;
20622         }
20623         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20624             nopadtext = true;
20625         }
20626         
20627         
20628         // Traverse the tree
20629         i = 0;
20630         var currentElementChild = currentElement.childNodes.item(i);
20631         var allText = true;
20632         var innerHTML  = '';
20633         lastnode = '';
20634         while (currentElementChild) {
20635             // Formatting code (indent the tree so it looks nice on the screen)
20636             var nopad = nopadtext;
20637             if (lastnode == 'SPAN') {
20638                 nopad  = true;
20639             }
20640             // text
20641             if  (currentElementChild.nodeName == '#text') {
20642                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20643                 toadd = nopadtext ? toadd : toadd.trim();
20644                 if (!nopad && toadd.length > 80) {
20645                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
20646                 }
20647                 innerHTML  += toadd;
20648                 
20649                 i++;
20650                 currentElementChild = currentElement.childNodes.item(i);
20651                 lastNode = '';
20652                 continue;
20653             }
20654             allText = false;
20655             
20656             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
20657                 
20658             // Recursively traverse the tree structure of the child node
20659             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
20660             lastnode = currentElementChild.nodeName;
20661             i++;
20662             currentElementChild=currentElement.childNodes.item(i);
20663         }
20664         
20665         ret += innerHTML;
20666         
20667         if (!allText) {
20668                 // The remaining code is mostly for formatting the tree
20669             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
20670         }
20671         
20672         
20673         if (tagName) {
20674             ret+= "</"+tagName+">";
20675         }
20676         return ret;
20677         
20678     },
20679         
20680     applyBlacklists : function()
20681     {
20682         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
20683         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
20684         
20685         this.white = [];
20686         this.black = [];
20687         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20688             if (b.indexOf(tag) > -1) {
20689                 return;
20690             }
20691             this.white.push(tag);
20692             
20693         }, this);
20694         
20695         Roo.each(w, function(tag) {
20696             if (b.indexOf(tag) > -1) {
20697                 return;
20698             }
20699             if (this.white.indexOf(tag) > -1) {
20700                 return;
20701             }
20702             this.white.push(tag);
20703             
20704         }, this);
20705         
20706         
20707         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20708             if (w.indexOf(tag) > -1) {
20709                 return;
20710             }
20711             this.black.push(tag);
20712             
20713         }, this);
20714         
20715         Roo.each(b, function(tag) {
20716             if (w.indexOf(tag) > -1) {
20717                 return;
20718             }
20719             if (this.black.indexOf(tag) > -1) {
20720                 return;
20721             }
20722             this.black.push(tag);
20723             
20724         }, this);
20725         
20726         
20727         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
20728         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
20729         
20730         this.cwhite = [];
20731         this.cblack = [];
20732         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20733             if (b.indexOf(tag) > -1) {
20734                 return;
20735             }
20736             this.cwhite.push(tag);
20737             
20738         }, this);
20739         
20740         Roo.each(w, function(tag) {
20741             if (b.indexOf(tag) > -1) {
20742                 return;
20743             }
20744             if (this.cwhite.indexOf(tag) > -1) {
20745                 return;
20746             }
20747             this.cwhite.push(tag);
20748             
20749         }, this);
20750         
20751         
20752         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20753             if (w.indexOf(tag) > -1) {
20754                 return;
20755             }
20756             this.cblack.push(tag);
20757             
20758         }, this);
20759         
20760         Roo.each(b, function(tag) {
20761             if (w.indexOf(tag) > -1) {
20762                 return;
20763             }
20764             if (this.cblack.indexOf(tag) > -1) {
20765                 return;
20766             }
20767             this.cblack.push(tag);
20768             
20769         }, this);
20770     },
20771     
20772     setStylesheets : function(stylesheets)
20773     {
20774         if(typeof(stylesheets) == 'string'){
20775             Roo.get(this.iframe.contentDocument.head).createChild({
20776                 tag : 'link',
20777                 rel : 'stylesheet',
20778                 type : 'text/css',
20779                 href : stylesheets
20780             });
20781             
20782             return;
20783         }
20784         var _this = this;
20785      
20786         Roo.each(stylesheets, function(s) {
20787             if(!s.length){
20788                 return;
20789             }
20790             
20791             Roo.get(_this.iframe.contentDocument.head).createChild({
20792                 tag : 'link',
20793                 rel : 'stylesheet',
20794                 type : 'text/css',
20795                 href : s
20796             });
20797         });
20798
20799         
20800     },
20801     
20802     removeStylesheets : function()
20803     {
20804         var _this = this;
20805         
20806         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20807             s.remove();
20808         });
20809     }
20810     
20811     // hide stuff that is not compatible
20812     /**
20813      * @event blur
20814      * @hide
20815      */
20816     /**
20817      * @event change
20818      * @hide
20819      */
20820     /**
20821      * @event focus
20822      * @hide
20823      */
20824     /**
20825      * @event specialkey
20826      * @hide
20827      */
20828     /**
20829      * @cfg {String} fieldClass @hide
20830      */
20831     /**
20832      * @cfg {String} focusClass @hide
20833      */
20834     /**
20835      * @cfg {String} autoCreate @hide
20836      */
20837     /**
20838      * @cfg {String} inputType @hide
20839      */
20840     /**
20841      * @cfg {String} invalidClass @hide
20842      */
20843     /**
20844      * @cfg {String} invalidText @hide
20845      */
20846     /**
20847      * @cfg {String} msgFx @hide
20848      */
20849     /**
20850      * @cfg {String} validateOnBlur @hide
20851      */
20852 });
20853
20854 Roo.HtmlEditorCore.white = [
20855         'area', 'br', 'img', 'input', 'hr', 'wbr',
20856         
20857        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
20858        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
20859        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
20860        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
20861        'table',   'ul',         'xmp', 
20862        
20863        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
20864       'thead',   'tr', 
20865      
20866       'dir', 'menu', 'ol', 'ul', 'dl',
20867        
20868       'embed',  'object'
20869 ];
20870
20871
20872 Roo.HtmlEditorCore.black = [
20873     //    'embed',  'object', // enable - backend responsiblity to clean thiese
20874         'applet', // 
20875         'base',   'basefont', 'bgsound', 'blink',  'body', 
20876         'frame',  'frameset', 'head',    'html',   'ilayer', 
20877         'iframe', 'layer',  'link',     'meta',    'object',   
20878         'script', 'style' ,'title',  'xml' // clean later..
20879 ];
20880 Roo.HtmlEditorCore.clean = [
20881     'script', 'style', 'title', 'xml'
20882 ];
20883 Roo.HtmlEditorCore.remove = [
20884     'font'
20885 ];
20886 // attributes..
20887
20888 Roo.HtmlEditorCore.ablack = [
20889     'on'
20890 ];
20891     
20892 Roo.HtmlEditorCore.aclean = [ 
20893     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
20894 ];
20895
20896 // protocols..
20897 Roo.HtmlEditorCore.pwhite= [
20898         'http',  'https',  'mailto'
20899 ];
20900
20901 // white listed style attributes.
20902 Roo.HtmlEditorCore.cwhite= [
20903       //  'text-align', /// default is to allow most things..
20904       
20905          
20906 //        'font-size'//??
20907 ];
20908
20909 // black listed style attributes.
20910 Roo.HtmlEditorCore.cblack= [
20911       //  'font-size' -- this can be set by the project 
20912 ];
20913
20914
20915 Roo.HtmlEditorCore.swapCodes   =[ 
20916     [    8211, "--" ], 
20917     [    8212, "--" ], 
20918     [    8216,  "'" ],  
20919     [    8217, "'" ],  
20920     [    8220, '"' ],  
20921     [    8221, '"' ],  
20922     [    8226, "*" ],  
20923     [    8230, "..." ]
20924 ]; 
20925
20926     /*
20927  * - LGPL
20928  *
20929  * HtmlEditor
20930  * 
20931  */
20932
20933 /**
20934  * @class Roo.bootstrap.HtmlEditor
20935  * @extends Roo.bootstrap.TextArea
20936  * Bootstrap HtmlEditor class
20937
20938  * @constructor
20939  * Create a new HtmlEditor
20940  * @param {Object} config The config object
20941  */
20942
20943 Roo.bootstrap.HtmlEditor = function(config){
20944     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
20945     if (!this.toolbars) {
20946         this.toolbars = [];
20947     }
20948     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
20949     this.addEvents({
20950             /**
20951              * @event initialize
20952              * Fires when the editor is fully initialized (including the iframe)
20953              * @param {HtmlEditor} this
20954              */
20955             initialize: true,
20956             /**
20957              * @event activate
20958              * Fires when the editor is first receives the focus. Any insertion must wait
20959              * until after this event.
20960              * @param {HtmlEditor} this
20961              */
20962             activate: true,
20963              /**
20964              * @event beforesync
20965              * Fires before the textarea is updated with content from the editor iframe. Return false
20966              * to cancel the sync.
20967              * @param {HtmlEditor} this
20968              * @param {String} html
20969              */
20970             beforesync: true,
20971              /**
20972              * @event beforepush
20973              * Fires before the iframe editor is updated with content from the textarea. Return false
20974              * to cancel the push.
20975              * @param {HtmlEditor} this
20976              * @param {String} html
20977              */
20978             beforepush: true,
20979              /**
20980              * @event sync
20981              * Fires when the textarea is updated with content from the editor iframe.
20982              * @param {HtmlEditor} this
20983              * @param {String} html
20984              */
20985             sync: true,
20986              /**
20987              * @event push
20988              * Fires when the iframe editor is updated with content from the textarea.
20989              * @param {HtmlEditor} this
20990              * @param {String} html
20991              */
20992             push: true,
20993              /**
20994              * @event editmodechange
20995              * Fires when the editor switches edit modes
20996              * @param {HtmlEditor} this
20997              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
20998              */
20999             editmodechange: true,
21000             /**
21001              * @event editorevent
21002              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21003              * @param {HtmlEditor} this
21004              */
21005             editorevent: true,
21006             /**
21007              * @event firstfocus
21008              * Fires when on first focus - needed by toolbars..
21009              * @param {HtmlEditor} this
21010              */
21011             firstfocus: true,
21012             /**
21013              * @event autosave
21014              * Auto save the htmlEditor value as a file into Events
21015              * @param {HtmlEditor} this
21016              */
21017             autosave: true,
21018             /**
21019              * @event savedpreview
21020              * preview the saved version of htmlEditor
21021              * @param {HtmlEditor} this
21022              */
21023             savedpreview: true
21024         });
21025 };
21026
21027
21028 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
21029     
21030     
21031       /**
21032      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
21033      */
21034     toolbars : false,
21035    
21036      /**
21037      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21038      *                        Roo.resizable.
21039      */
21040     resizable : false,
21041      /**
21042      * @cfg {Number} height (in pixels)
21043      */   
21044     height: 300,
21045    /**
21046      * @cfg {Number} width (in pixels)
21047      */   
21048     width: false,
21049     
21050     /**
21051      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21052      * 
21053      */
21054     stylesheets: false,
21055     
21056     // id of frame..
21057     frameId: false,
21058     
21059     // private properties
21060     validationEvent : false,
21061     deferHeight: true,
21062     initialized : false,
21063     activated : false,
21064     
21065     onFocus : Roo.emptyFn,
21066     iframePad:3,
21067     hideMode:'offsets',
21068     
21069     
21070     tbContainer : false,
21071     
21072     toolbarContainer :function() {
21073         return this.wrap.select('.x-html-editor-tb',true).first();
21074     },
21075
21076     /**
21077      * Protected method that will not generally be called directly. It
21078      * is called when the editor creates its toolbar. Override this method if you need to
21079      * add custom toolbar buttons.
21080      * @param {HtmlEditor} editor
21081      */
21082     createToolbar : function(){
21083         
21084         Roo.log("create toolbars");
21085         
21086         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
21087         this.toolbars[0].render(this.toolbarContainer());
21088         
21089         return;
21090         
21091 //        if (!editor.toolbars || !editor.toolbars.length) {
21092 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
21093 //        }
21094 //        
21095 //        for (var i =0 ; i < editor.toolbars.length;i++) {
21096 //            editor.toolbars[i] = Roo.factory(
21097 //                    typeof(editor.toolbars[i]) == 'string' ?
21098 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
21099 //                Roo.bootstrap.HtmlEditor);
21100 //            editor.toolbars[i].init(editor);
21101 //        }
21102     },
21103
21104      
21105     // private
21106     onRender : function(ct, position)
21107     {
21108        // Roo.log("Call onRender: " + this.xtype);
21109         var _t = this;
21110         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
21111       
21112         this.wrap = this.inputEl().wrap({
21113             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
21114         });
21115         
21116         this.editorcore.onRender(ct, position);
21117          
21118         if (this.resizable) {
21119             this.resizeEl = new Roo.Resizable(this.wrap, {
21120                 pinned : true,
21121                 wrap: true,
21122                 dynamic : true,
21123                 minHeight : this.height,
21124                 height: this.height,
21125                 handles : this.resizable,
21126                 width: this.width,
21127                 listeners : {
21128                     resize : function(r, w, h) {
21129                         _t.onResize(w,h); // -something
21130                     }
21131                 }
21132             });
21133             
21134         }
21135         this.createToolbar(this);
21136        
21137         
21138         if(!this.width && this.resizable){
21139             this.setSize(this.wrap.getSize());
21140         }
21141         if (this.resizeEl) {
21142             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
21143             // should trigger onReize..
21144         }
21145         
21146     },
21147
21148     // private
21149     onResize : function(w, h)
21150     {
21151         Roo.log('resize: ' +w + ',' + h );
21152         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
21153         var ew = false;
21154         var eh = false;
21155         
21156         if(this.inputEl() ){
21157             if(typeof w == 'number'){
21158                 var aw = w - this.wrap.getFrameWidth('lr');
21159                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
21160                 ew = aw;
21161             }
21162             if(typeof h == 'number'){
21163                  var tbh = -11;  // fixme it needs to tool bar size!
21164                 for (var i =0; i < this.toolbars.length;i++) {
21165                     // fixme - ask toolbars for heights?
21166                     tbh += this.toolbars[i].el.getHeight();
21167                     //if (this.toolbars[i].footer) {
21168                     //    tbh += this.toolbars[i].footer.el.getHeight();
21169                     //}
21170                 }
21171               
21172                 
21173                 
21174                 
21175                 
21176                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
21177                 ah -= 5; // knock a few pixes off for look..
21178                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
21179                 var eh = ah;
21180             }
21181         }
21182         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
21183         this.editorcore.onResize(ew,eh);
21184         
21185     },
21186
21187     /**
21188      * Toggles the editor between standard and source edit mode.
21189      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21190      */
21191     toggleSourceEdit : function(sourceEditMode)
21192     {
21193         this.editorcore.toggleSourceEdit(sourceEditMode);
21194         
21195         if(this.editorcore.sourceEditMode){
21196             Roo.log('editor - showing textarea');
21197             
21198 //            Roo.log('in');
21199 //            Roo.log(this.syncValue());
21200             this.syncValue();
21201             this.inputEl().removeClass(['hide', 'x-hidden']);
21202             this.inputEl().dom.removeAttribute('tabIndex');
21203             this.inputEl().focus();
21204         }else{
21205             Roo.log('editor - hiding textarea');
21206 //            Roo.log('out')
21207 //            Roo.log(this.pushValue()); 
21208             this.pushValue();
21209             
21210             this.inputEl().addClass(['hide', 'x-hidden']);
21211             this.inputEl().dom.setAttribute('tabIndex', -1);
21212             //this.deferFocus();
21213         }
21214          
21215         if(this.resizable){
21216             this.setSize(this.wrap.getSize());
21217         }
21218         
21219         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
21220     },
21221  
21222     // private (for BoxComponent)
21223     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21224
21225     // private (for BoxComponent)
21226     getResizeEl : function(){
21227         return this.wrap;
21228     },
21229
21230     // private (for BoxComponent)
21231     getPositionEl : function(){
21232         return this.wrap;
21233     },
21234
21235     // private
21236     initEvents : function(){
21237         this.originalValue = this.getValue();
21238     },
21239
21240 //    /**
21241 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21242 //     * @method
21243 //     */
21244 //    markInvalid : Roo.emptyFn,
21245 //    /**
21246 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21247 //     * @method
21248 //     */
21249 //    clearInvalid : Roo.emptyFn,
21250
21251     setValue : function(v){
21252         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
21253         this.editorcore.pushValue();
21254     },
21255
21256      
21257     // private
21258     deferFocus : function(){
21259         this.focus.defer(10, this);
21260     },
21261
21262     // doc'ed in Field
21263     focus : function(){
21264         this.editorcore.focus();
21265         
21266     },
21267       
21268
21269     // private
21270     onDestroy : function(){
21271         
21272         
21273         
21274         if(this.rendered){
21275             
21276             for (var i =0; i < this.toolbars.length;i++) {
21277                 // fixme - ask toolbars for heights?
21278                 this.toolbars[i].onDestroy();
21279             }
21280             
21281             this.wrap.dom.innerHTML = '';
21282             this.wrap.remove();
21283         }
21284     },
21285
21286     // private
21287     onFirstFocus : function(){
21288         //Roo.log("onFirstFocus");
21289         this.editorcore.onFirstFocus();
21290          for (var i =0; i < this.toolbars.length;i++) {
21291             this.toolbars[i].onFirstFocus();
21292         }
21293         
21294     },
21295     
21296     // private
21297     syncValue : function()
21298     {   
21299         this.editorcore.syncValue();
21300     },
21301     
21302     pushValue : function()
21303     {   
21304         this.editorcore.pushValue();
21305     }
21306      
21307     
21308     // hide stuff that is not compatible
21309     /**
21310      * @event blur
21311      * @hide
21312      */
21313     /**
21314      * @event change
21315      * @hide
21316      */
21317     /**
21318      * @event focus
21319      * @hide
21320      */
21321     /**
21322      * @event specialkey
21323      * @hide
21324      */
21325     /**
21326      * @cfg {String} fieldClass @hide
21327      */
21328     /**
21329      * @cfg {String} focusClass @hide
21330      */
21331     /**
21332      * @cfg {String} autoCreate @hide
21333      */
21334     /**
21335      * @cfg {String} inputType @hide
21336      */
21337     /**
21338      * @cfg {String} invalidClass @hide
21339      */
21340     /**
21341      * @cfg {String} invalidText @hide
21342      */
21343     /**
21344      * @cfg {String} msgFx @hide
21345      */
21346     /**
21347      * @cfg {String} validateOnBlur @hide
21348      */
21349 });
21350  
21351     
21352    
21353    
21354    
21355       
21356 Roo.namespace('Roo.bootstrap.htmleditor');
21357 /**
21358  * @class Roo.bootstrap.HtmlEditorToolbar1
21359  * Basic Toolbar
21360  * 
21361  * Usage:
21362  *
21363  new Roo.bootstrap.HtmlEditor({
21364     ....
21365     toolbars : [
21366         new Roo.bootstrap.HtmlEditorToolbar1({
21367             disable : { fonts: 1 , format: 1, ..., ... , ...],
21368             btns : [ .... ]
21369         })
21370     }
21371      
21372  * 
21373  * @cfg {Object} disable List of elements to disable..
21374  * @cfg {Array} btns List of additional buttons.
21375  * 
21376  * 
21377  * NEEDS Extra CSS? 
21378  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
21379  */
21380  
21381 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
21382 {
21383     
21384     Roo.apply(this, config);
21385     
21386     // default disabled, based on 'good practice'..
21387     this.disable = this.disable || {};
21388     Roo.applyIf(this.disable, {
21389         fontSize : true,
21390         colors : true,
21391         specialElements : true
21392     });
21393     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
21394     
21395     this.editor = config.editor;
21396     this.editorcore = config.editor.editorcore;
21397     
21398     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
21399     
21400     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
21401     // dont call parent... till later.
21402 }
21403 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
21404      
21405     bar : true,
21406     
21407     editor : false,
21408     editorcore : false,
21409     
21410     
21411     formats : [
21412         "p" ,  
21413         "h1","h2","h3","h4","h5","h6", 
21414         "pre", "code", 
21415         "abbr", "acronym", "address", "cite", "samp", "var",
21416         'div','span'
21417     ],
21418     
21419     onRender : function(ct, position)
21420     {
21421        // Roo.log("Call onRender: " + this.xtype);
21422         
21423        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
21424        Roo.log(this.el);
21425        this.el.dom.style.marginBottom = '0';
21426        var _this = this;
21427        var editorcore = this.editorcore;
21428        var editor= this.editor;
21429        
21430        var children = [];
21431        var btn = function(id,cmd , toggle, handler){
21432        
21433             var  event = toggle ? 'toggle' : 'click';
21434        
21435             var a = {
21436                 size : 'sm',
21437                 xtype: 'Button',
21438                 xns: Roo.bootstrap,
21439                 glyphicon : id,
21440                 cmd : id || cmd,
21441                 enableToggle:toggle !== false,
21442                 //html : 'submit'
21443                 pressed : toggle ? false : null,
21444                 listeners : {}
21445             };
21446             a.listeners[toggle ? 'toggle' : 'click'] = function() {
21447                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
21448             };
21449             children.push(a);
21450             return a;
21451        }
21452         
21453         var style = {
21454                 xtype: 'Button',
21455                 size : 'sm',
21456                 xns: Roo.bootstrap,
21457                 glyphicon : 'font',
21458                 //html : 'submit'
21459                 menu : {
21460                     xtype: 'Menu',
21461                     xns: Roo.bootstrap,
21462                     items:  []
21463                 }
21464         };
21465         Roo.each(this.formats, function(f) {
21466             style.menu.items.push({
21467                 xtype :'MenuItem',
21468                 xns: Roo.bootstrap,
21469                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21470                 tagname : f,
21471                 listeners : {
21472                     click : function()
21473                     {
21474                         editorcore.insertTag(this.tagname);
21475                         editor.focus();
21476                     }
21477                 }
21478                 
21479             });
21480         });
21481          children.push(style);   
21482             
21483             
21484         btn('bold',false,true);
21485         btn('italic',false,true);
21486         btn('align-left', 'justifyleft',true);
21487         btn('align-center', 'justifycenter',true);
21488         btn('align-right' , 'justifyright',true);
21489         btn('link', false, false, function(btn) {
21490             //Roo.log("create link?");
21491             var url = prompt(this.createLinkText, this.defaultLinkValue);
21492             if(url && url != 'http:/'+'/'){
21493                 this.editorcore.relayCmd('createlink', url);
21494             }
21495         }),
21496         btn('list','insertunorderedlist',true);
21497         btn('pencil', false,true, function(btn){
21498                 Roo.log(this);
21499                 
21500                 this.toggleSourceEdit(btn.pressed);
21501         });
21502         /*
21503         var cog = {
21504                 xtype: 'Button',
21505                 size : 'sm',
21506                 xns: Roo.bootstrap,
21507                 glyphicon : 'cog',
21508                 //html : 'submit'
21509                 menu : {
21510                     xtype: 'Menu',
21511                     xns: Roo.bootstrap,
21512                     items:  []
21513                 }
21514         };
21515         
21516         cog.menu.items.push({
21517             xtype :'MenuItem',
21518             xns: Roo.bootstrap,
21519             html : Clean styles,
21520             tagname : f,
21521             listeners : {
21522                 click : function()
21523                 {
21524                     editorcore.insertTag(this.tagname);
21525                     editor.focus();
21526                 }
21527             }
21528             
21529         });
21530        */
21531         
21532          
21533        this.xtype = 'NavSimplebar';
21534         
21535         for(var i=0;i< children.length;i++) {
21536             
21537             this.buttons.add(this.addxtypeChild(children[i]));
21538             
21539         }
21540         
21541         editor.on('editorevent', this.updateToolbar, this);
21542     },
21543     onBtnClick : function(id)
21544     {
21545        this.editorcore.relayCmd(id);
21546        this.editorcore.focus();
21547     },
21548     
21549     /**
21550      * Protected method that will not generally be called directly. It triggers
21551      * a toolbar update by reading the markup state of the current selection in the editor.
21552      */
21553     updateToolbar: function(){
21554
21555         if(!this.editorcore.activated){
21556             this.editor.onFirstFocus(); // is this neeed?
21557             return;
21558         }
21559
21560         var btns = this.buttons; 
21561         var doc = this.editorcore.doc;
21562         btns.get('bold').setActive(doc.queryCommandState('bold'));
21563         btns.get('italic').setActive(doc.queryCommandState('italic'));
21564         //btns.get('underline').setActive(doc.queryCommandState('underline'));
21565         
21566         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21567         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21568         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21569         
21570         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21571         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21572          /*
21573         
21574         var ans = this.editorcore.getAllAncestors();
21575         if (this.formatCombo) {
21576             
21577             
21578             var store = this.formatCombo.store;
21579             this.formatCombo.setValue("");
21580             for (var i =0; i < ans.length;i++) {
21581                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21582                     // select it..
21583                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21584                     break;
21585                 }
21586             }
21587         }
21588         
21589         
21590         
21591         // hides menus... - so this cant be on a menu...
21592         Roo.bootstrap.MenuMgr.hideAll();
21593         */
21594         Roo.bootstrap.MenuMgr.hideAll();
21595         //this.editorsyncValue();
21596     },
21597     onFirstFocus: function() {
21598         this.buttons.each(function(item){
21599            item.enable();
21600         });
21601     },
21602     toggleSourceEdit : function(sourceEditMode){
21603         
21604           
21605         if(sourceEditMode){
21606             Roo.log("disabling buttons");
21607            this.buttons.each( function(item){
21608                 if(item.cmd != 'pencil'){
21609                     item.disable();
21610                 }
21611             });
21612           
21613         }else{
21614             Roo.log("enabling buttons");
21615             if(this.editorcore.initialized){
21616                 this.buttons.each( function(item){
21617                     item.enable();
21618                 });
21619             }
21620             
21621         }
21622         Roo.log("calling toggole on editor");
21623         // tell the editor that it's been pressed..
21624         this.editor.toggleSourceEdit(sourceEditMode);
21625        
21626     }
21627 });
21628
21629
21630
21631
21632
21633 /**
21634  * @class Roo.bootstrap.Table.AbstractSelectionModel
21635  * @extends Roo.util.Observable
21636  * Abstract base class for grid SelectionModels.  It provides the interface that should be
21637  * implemented by descendant classes.  This class should not be directly instantiated.
21638  * @constructor
21639  */
21640 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21641     this.locked = false;
21642     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21643 };
21644
21645
21646 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
21647     /** @ignore Called by the grid automatically. Do not call directly. */
21648     init : function(grid){
21649         this.grid = grid;
21650         this.initEvents();
21651     },
21652
21653     /**
21654      * Locks the selections.
21655      */
21656     lock : function(){
21657         this.locked = true;
21658     },
21659
21660     /**
21661      * Unlocks the selections.
21662      */
21663     unlock : function(){
21664         this.locked = false;
21665     },
21666
21667     /**
21668      * Returns true if the selections are locked.
21669      * @return {Boolean}
21670      */
21671     isLocked : function(){
21672         return this.locked;
21673     }
21674 });
21675 /**
21676  * @extends Roo.bootstrap.Table.AbstractSelectionModel
21677  * @class Roo.bootstrap.Table.RowSelectionModel
21678  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21679  * It supports multiple selections and keyboard selection/navigation. 
21680  * @constructor
21681  * @param {Object} config
21682  */
21683
21684 Roo.bootstrap.Table.RowSelectionModel = function(config){
21685     Roo.apply(this, config);
21686     this.selections = new Roo.util.MixedCollection(false, function(o){
21687         return o.id;
21688     });
21689
21690     this.last = false;
21691     this.lastActive = false;
21692
21693     this.addEvents({
21694         /**
21695              * @event selectionchange
21696              * Fires when the selection changes
21697              * @param {SelectionModel} this
21698              */
21699             "selectionchange" : true,
21700         /**
21701              * @event afterselectionchange
21702              * Fires after the selection changes (eg. by key press or clicking)
21703              * @param {SelectionModel} this
21704              */
21705             "afterselectionchange" : true,
21706         /**
21707              * @event beforerowselect
21708              * Fires when a row is selected being selected, return false to cancel.
21709              * @param {SelectionModel} this
21710              * @param {Number} rowIndex The selected index
21711              * @param {Boolean} keepExisting False if other selections will be cleared
21712              */
21713             "beforerowselect" : true,
21714         /**
21715              * @event rowselect
21716              * Fires when a row is selected.
21717              * @param {SelectionModel} this
21718              * @param {Number} rowIndex The selected index
21719              * @param {Roo.data.Record} r The record
21720              */
21721             "rowselect" : true,
21722         /**
21723              * @event rowdeselect
21724              * Fires when a row is deselected.
21725              * @param {SelectionModel} this
21726              * @param {Number} rowIndex The selected index
21727              */
21728         "rowdeselect" : true
21729     });
21730     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21731     this.locked = false;
21732 };
21733
21734 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
21735     /**
21736      * @cfg {Boolean} singleSelect
21737      * True to allow selection of only one row at a time (defaults to false)
21738      */
21739     singleSelect : false,
21740
21741     // private
21742     initEvents : function(){
21743
21744         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21745             this.grid.on("mousedown", this.handleMouseDown, this);
21746         }else{ // allow click to work like normal
21747             this.grid.on("rowclick", this.handleDragableRowClick, this);
21748         }
21749
21750         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21751             "up" : function(e){
21752                 if(!e.shiftKey){
21753                     this.selectPrevious(e.shiftKey);
21754                 }else if(this.last !== false && this.lastActive !== false){
21755                     var last = this.last;
21756                     this.selectRange(this.last,  this.lastActive-1);
21757                     this.grid.getView().focusRow(this.lastActive);
21758                     if(last !== false){
21759                         this.last = last;
21760                     }
21761                 }else{
21762                     this.selectFirstRow();
21763                 }
21764                 this.fireEvent("afterselectionchange", this);
21765             },
21766             "down" : function(e){
21767                 if(!e.shiftKey){
21768                     this.selectNext(e.shiftKey);
21769                 }else if(this.last !== false && this.lastActive !== false){
21770                     var last = this.last;
21771                     this.selectRange(this.last,  this.lastActive+1);
21772                     this.grid.getView().focusRow(this.lastActive);
21773                     if(last !== false){
21774                         this.last = last;
21775                     }
21776                 }else{
21777                     this.selectFirstRow();
21778                 }
21779                 this.fireEvent("afterselectionchange", this);
21780             },
21781             scope: this
21782         });
21783
21784         var view = this.grid.view;
21785         view.on("refresh", this.onRefresh, this);
21786         view.on("rowupdated", this.onRowUpdated, this);
21787         view.on("rowremoved", this.onRemove, this);
21788     },
21789
21790     // private
21791     onRefresh : function(){
21792         var ds = this.grid.dataSource, i, v = this.grid.view;
21793         var s = this.selections;
21794         s.each(function(r){
21795             if((i = ds.indexOfId(r.id)) != -1){
21796                 v.onRowSelect(i);
21797             }else{
21798                 s.remove(r);
21799             }
21800         });
21801     },
21802
21803     // private
21804     onRemove : function(v, index, r){
21805         this.selections.remove(r);
21806     },
21807
21808     // private
21809     onRowUpdated : function(v, index, r){
21810         if(this.isSelected(r)){
21811             v.onRowSelect(index);
21812         }
21813     },
21814
21815     /**
21816      * Select records.
21817      * @param {Array} records The records to select
21818      * @param {Boolean} keepExisting (optional) True to keep existing selections
21819      */
21820     selectRecords : function(records, keepExisting){
21821         if(!keepExisting){
21822             this.clearSelections();
21823         }
21824         var ds = this.grid.dataSource;
21825         for(var i = 0, len = records.length; i < len; i++){
21826             this.selectRow(ds.indexOf(records[i]), true);
21827         }
21828     },
21829
21830     /**
21831      * Gets the number of selected rows.
21832      * @return {Number}
21833      */
21834     getCount : function(){
21835         return this.selections.length;
21836     },
21837
21838     /**
21839      * Selects the first row in the grid.
21840      */
21841     selectFirstRow : function(){
21842         this.selectRow(0);
21843     },
21844
21845     /**
21846      * Select the last row.
21847      * @param {Boolean} keepExisting (optional) True to keep existing selections
21848      */
21849     selectLastRow : function(keepExisting){
21850         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21851     },
21852
21853     /**
21854      * Selects the row immediately following the last selected row.
21855      * @param {Boolean} keepExisting (optional) True to keep existing selections
21856      */
21857     selectNext : function(keepExisting){
21858         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21859             this.selectRow(this.last+1, keepExisting);
21860             this.grid.getView().focusRow(this.last);
21861         }
21862     },
21863
21864     /**
21865      * Selects the row that precedes the last selected row.
21866      * @param {Boolean} keepExisting (optional) True to keep existing selections
21867      */
21868     selectPrevious : function(keepExisting){
21869         if(this.last){
21870             this.selectRow(this.last-1, keepExisting);
21871             this.grid.getView().focusRow(this.last);
21872         }
21873     },
21874
21875     /**
21876      * Returns the selected records
21877      * @return {Array} Array of selected records
21878      */
21879     getSelections : function(){
21880         return [].concat(this.selections.items);
21881     },
21882
21883     /**
21884      * Returns the first selected record.
21885      * @return {Record}
21886      */
21887     getSelected : function(){
21888         return this.selections.itemAt(0);
21889     },
21890
21891
21892     /**
21893      * Clears all selections.
21894      */
21895     clearSelections : function(fast){
21896         if(this.locked) {
21897             return;
21898         }
21899         if(fast !== true){
21900             var ds = this.grid.dataSource;
21901             var s = this.selections;
21902             s.each(function(r){
21903                 this.deselectRow(ds.indexOfId(r.id));
21904             }, this);
21905             s.clear();
21906         }else{
21907             this.selections.clear();
21908         }
21909         this.last = false;
21910     },
21911
21912
21913     /**
21914      * Selects all rows.
21915      */
21916     selectAll : function(){
21917         if(this.locked) {
21918             return;
21919         }
21920         this.selections.clear();
21921         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
21922             this.selectRow(i, true);
21923         }
21924     },
21925
21926     /**
21927      * Returns True if there is a selection.
21928      * @return {Boolean}
21929      */
21930     hasSelection : function(){
21931         return this.selections.length > 0;
21932     },
21933
21934     /**
21935      * Returns True if the specified row is selected.
21936      * @param {Number/Record} record The record or index of the record to check
21937      * @return {Boolean}
21938      */
21939     isSelected : function(index){
21940         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
21941         return (r && this.selections.key(r.id) ? true : false);
21942     },
21943
21944     /**
21945      * Returns True if the specified record id is selected.
21946      * @param {String} id The id of record to check
21947      * @return {Boolean}
21948      */
21949     isIdSelected : function(id){
21950         return (this.selections.key(id) ? true : false);
21951     },
21952
21953     // private
21954     handleMouseDown : function(e, t){
21955         var view = this.grid.getView(), rowIndex;
21956         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
21957             return;
21958         };
21959         if(e.shiftKey && this.last !== false){
21960             var last = this.last;
21961             this.selectRange(last, rowIndex, e.ctrlKey);
21962             this.last = last; // reset the last
21963             view.focusRow(rowIndex);
21964         }else{
21965             var isSelected = this.isSelected(rowIndex);
21966             if(e.button !== 0 && isSelected){
21967                 view.focusRow(rowIndex);
21968             }else if(e.ctrlKey && isSelected){
21969                 this.deselectRow(rowIndex);
21970             }else if(!isSelected){
21971                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
21972                 view.focusRow(rowIndex);
21973             }
21974         }
21975         this.fireEvent("afterselectionchange", this);
21976     },
21977     // private
21978     handleDragableRowClick :  function(grid, rowIndex, e) 
21979     {
21980         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
21981             this.selectRow(rowIndex, false);
21982             grid.view.focusRow(rowIndex);
21983              this.fireEvent("afterselectionchange", this);
21984         }
21985     },
21986     
21987     /**
21988      * Selects multiple rows.
21989      * @param {Array} rows Array of the indexes of the row to select
21990      * @param {Boolean} keepExisting (optional) True to keep existing selections
21991      */
21992     selectRows : function(rows, keepExisting){
21993         if(!keepExisting){
21994             this.clearSelections();
21995         }
21996         for(var i = 0, len = rows.length; i < len; i++){
21997             this.selectRow(rows[i], true);
21998         }
21999     },
22000
22001     /**
22002      * Selects a range of rows. All rows in between startRow and endRow are also selected.
22003      * @param {Number} startRow The index of the first row in the range
22004      * @param {Number} endRow The index of the last row in the range
22005      * @param {Boolean} keepExisting (optional) True to retain existing selections
22006      */
22007     selectRange : function(startRow, endRow, keepExisting){
22008         if(this.locked) {
22009             return;
22010         }
22011         if(!keepExisting){
22012             this.clearSelections();
22013         }
22014         if(startRow <= endRow){
22015             for(var i = startRow; i <= endRow; i++){
22016                 this.selectRow(i, true);
22017             }
22018         }else{
22019             for(var i = startRow; i >= endRow; i--){
22020                 this.selectRow(i, true);
22021             }
22022         }
22023     },
22024
22025     /**
22026      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
22027      * @param {Number} startRow The index of the first row in the range
22028      * @param {Number} endRow The index of the last row in the range
22029      */
22030     deselectRange : function(startRow, endRow, preventViewNotify){
22031         if(this.locked) {
22032             return;
22033         }
22034         for(var i = startRow; i <= endRow; i++){
22035             this.deselectRow(i, preventViewNotify);
22036         }
22037     },
22038
22039     /**
22040      * Selects a row.
22041      * @param {Number} row The index of the row to select
22042      * @param {Boolean} keepExisting (optional) True to keep existing selections
22043      */
22044     selectRow : function(index, keepExisting, preventViewNotify){
22045         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
22046             return;
22047         }
22048         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
22049             if(!keepExisting || this.singleSelect){
22050                 this.clearSelections();
22051             }
22052             var r = this.grid.dataSource.getAt(index);
22053             this.selections.add(r);
22054             this.last = this.lastActive = index;
22055             if(!preventViewNotify){
22056                 this.grid.getView().onRowSelect(index);
22057             }
22058             this.fireEvent("rowselect", this, index, r);
22059             this.fireEvent("selectionchange", this);
22060         }
22061     },
22062
22063     /**
22064      * Deselects a row.
22065      * @param {Number} row The index of the row to deselect
22066      */
22067     deselectRow : function(index, preventViewNotify){
22068         if(this.locked) {
22069             return;
22070         }
22071         if(this.last == index){
22072             this.last = false;
22073         }
22074         if(this.lastActive == index){
22075             this.lastActive = false;
22076         }
22077         var r = this.grid.dataSource.getAt(index);
22078         this.selections.remove(r);
22079         if(!preventViewNotify){
22080             this.grid.getView().onRowDeselect(index);
22081         }
22082         this.fireEvent("rowdeselect", this, index);
22083         this.fireEvent("selectionchange", this);
22084     },
22085
22086     // private
22087     restoreLast : function(){
22088         if(this._last){
22089             this.last = this._last;
22090         }
22091     },
22092
22093     // private
22094     acceptsNav : function(row, col, cm){
22095         return !cm.isHidden(col) && cm.isCellEditable(col, row);
22096     },
22097
22098     // private
22099     onEditorKey : function(field, e){
22100         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22101         if(k == e.TAB){
22102             e.stopEvent();
22103             ed.completeEdit();
22104             if(e.shiftKey){
22105                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22106             }else{
22107                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22108             }
22109         }else if(k == e.ENTER && !e.ctrlKey){
22110             e.stopEvent();
22111             ed.completeEdit();
22112             if(e.shiftKey){
22113                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22114             }else{
22115                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22116             }
22117         }else if(k == e.ESC){
22118             ed.cancelEdit();
22119         }
22120         if(newCell){
22121             g.startEditing(newCell[0], newCell[1]);
22122         }
22123     }
22124 });/*
22125  * Based on:
22126  * Ext JS Library 1.1.1
22127  * Copyright(c) 2006-2007, Ext JS, LLC.
22128  *
22129  * Originally Released Under LGPL - original licence link has changed is not relivant.
22130  *
22131  * Fork - LGPL
22132  * <script type="text/javascript">
22133  */
22134  
22135 /**
22136  * @class Roo.bootstrap.PagingToolbar
22137  * @extends Roo.bootstrap.NavSimplebar
22138  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
22139  * @constructor
22140  * Create a new PagingToolbar
22141  * @param {Object} config The config object
22142  * @param {Roo.data.Store} store
22143  */
22144 Roo.bootstrap.PagingToolbar = function(config)
22145 {
22146     // old args format still supported... - xtype is prefered..
22147         // created from xtype...
22148     
22149     this.ds = config.dataSource;
22150     
22151     if (config.store && !this.ds) {
22152         this.store= Roo.factory(config.store, Roo.data);
22153         this.ds = this.store;
22154         this.ds.xmodule = this.xmodule || false;
22155     }
22156     
22157     this.toolbarItems = [];
22158     if (config.items) {
22159         this.toolbarItems = config.items;
22160     }
22161     
22162     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
22163     
22164     this.cursor = 0;
22165     
22166     if (this.ds) { 
22167         this.bind(this.ds);
22168     }
22169     
22170     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
22171     
22172 };
22173
22174 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
22175     /**
22176      * @cfg {Roo.data.Store} dataSource
22177      * The underlying data store providing the paged data
22178      */
22179     /**
22180      * @cfg {String/HTMLElement/Element} container
22181      * container The id or element that will contain the toolbar
22182      */
22183     /**
22184      * @cfg {Boolean} displayInfo
22185      * True to display the displayMsg (defaults to false)
22186      */
22187     /**
22188      * @cfg {Number} pageSize
22189      * The number of records to display per page (defaults to 20)
22190      */
22191     pageSize: 20,
22192     /**
22193      * @cfg {String} displayMsg
22194      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
22195      */
22196     displayMsg : 'Displaying {0} - {1} of {2}',
22197     /**
22198      * @cfg {String} emptyMsg
22199      * The message to display when no records are found (defaults to "No data to display")
22200      */
22201     emptyMsg : 'No data to display',
22202     /**
22203      * Customizable piece of the default paging text (defaults to "Page")
22204      * @type String
22205      */
22206     beforePageText : "Page",
22207     /**
22208      * Customizable piece of the default paging text (defaults to "of %0")
22209      * @type String
22210      */
22211     afterPageText : "of {0}",
22212     /**
22213      * Customizable piece of the default paging text (defaults to "First Page")
22214      * @type String
22215      */
22216     firstText : "First Page",
22217     /**
22218      * Customizable piece of the default paging text (defaults to "Previous Page")
22219      * @type String
22220      */
22221     prevText : "Previous Page",
22222     /**
22223      * Customizable piece of the default paging text (defaults to "Next Page")
22224      * @type String
22225      */
22226     nextText : "Next Page",
22227     /**
22228      * Customizable piece of the default paging text (defaults to "Last Page")
22229      * @type String
22230      */
22231     lastText : "Last Page",
22232     /**
22233      * Customizable piece of the default paging text (defaults to "Refresh")
22234      * @type String
22235      */
22236     refreshText : "Refresh",
22237
22238     buttons : false,
22239     // private
22240     onRender : function(ct, position) 
22241     {
22242         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
22243         this.navgroup.parentId = this.id;
22244         this.navgroup.onRender(this.el, null);
22245         // add the buttons to the navgroup
22246         
22247         if(this.displayInfo){
22248             Roo.log(this.el.select('ul.navbar-nav',true).first());
22249             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
22250             this.displayEl = this.el.select('.x-paging-info', true).first();
22251 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
22252 //            this.displayEl = navel.el.select('span',true).first();
22253         }
22254         
22255         var _this = this;
22256         
22257         if(this.buttons){
22258             Roo.each(_this.buttons, function(e){ // this might need to use render????
22259                Roo.factory(e).onRender(_this.el, null);
22260             });
22261         }
22262             
22263         Roo.each(_this.toolbarItems, function(e) {
22264             _this.navgroup.addItem(e);
22265         });
22266         
22267         
22268         this.first = this.navgroup.addItem({
22269             tooltip: this.firstText,
22270             cls: "prev",
22271             icon : 'fa fa-backward',
22272             disabled: true,
22273             preventDefault: true,
22274             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
22275         });
22276         
22277         this.prev =  this.navgroup.addItem({
22278             tooltip: this.prevText,
22279             cls: "prev",
22280             icon : 'fa fa-step-backward',
22281             disabled: true,
22282             preventDefault: true,
22283             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
22284         });
22285     //this.addSeparator();
22286         
22287         
22288         var field = this.navgroup.addItem( {
22289             tagtype : 'span',
22290             cls : 'x-paging-position',
22291             
22292             html : this.beforePageText  +
22293                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22294                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
22295          } ); //?? escaped?
22296         
22297         this.field = field.el.select('input', true).first();
22298         this.field.on("keydown", this.onPagingKeydown, this);
22299         this.field.on("focus", function(){this.dom.select();});
22300     
22301     
22302         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
22303         //this.field.setHeight(18);
22304         //this.addSeparator();
22305         this.next = this.navgroup.addItem({
22306             tooltip: this.nextText,
22307             cls: "next",
22308             html : ' <i class="fa fa-step-forward">',
22309             disabled: true,
22310             preventDefault: true,
22311             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
22312         });
22313         this.last = this.navgroup.addItem({
22314             tooltip: this.lastText,
22315             icon : 'fa fa-forward',
22316             cls: "next",
22317             disabled: true,
22318             preventDefault: true,
22319             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
22320         });
22321     //this.addSeparator();
22322         this.loading = this.navgroup.addItem({
22323             tooltip: this.refreshText,
22324             icon: 'fa fa-refresh',
22325             preventDefault: true,
22326             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
22327         });
22328         
22329     },
22330
22331     // private
22332     updateInfo : function(){
22333         if(this.displayEl){
22334             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
22335             var msg = count == 0 ?
22336                 this.emptyMsg :
22337                 String.format(
22338                     this.displayMsg,
22339                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
22340                 );
22341             this.displayEl.update(msg);
22342         }
22343     },
22344
22345     // private
22346     onLoad : function(ds, r, o){
22347        this.cursor = o.params ? o.params.start : 0;
22348        var d = this.getPageData(),
22349             ap = d.activePage,
22350             ps = d.pages;
22351         
22352        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
22353        this.field.dom.value = ap;
22354        this.first.setDisabled(ap == 1);
22355        this.prev.setDisabled(ap == 1);
22356        this.next.setDisabled(ap == ps);
22357        this.last.setDisabled(ap == ps);
22358        this.loading.enable();
22359        this.updateInfo();
22360     },
22361
22362     // private
22363     getPageData : function(){
22364         var total = this.ds.getTotalCount();
22365         return {
22366             total : total,
22367             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
22368             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
22369         };
22370     },
22371
22372     // private
22373     onLoadError : function(){
22374         this.loading.enable();
22375     },
22376
22377     // private
22378     onPagingKeydown : function(e){
22379         var k = e.getKey();
22380         var d = this.getPageData();
22381         if(k == e.RETURN){
22382             var v = this.field.dom.value, pageNum;
22383             if(!v || isNaN(pageNum = parseInt(v, 10))){
22384                 this.field.dom.value = d.activePage;
22385                 return;
22386             }
22387             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
22388             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22389             e.stopEvent();
22390         }
22391         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))
22392         {
22393           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
22394           this.field.dom.value = pageNum;
22395           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
22396           e.stopEvent();
22397         }
22398         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22399         {
22400           var v = this.field.dom.value, pageNum; 
22401           var increment = (e.shiftKey) ? 10 : 1;
22402           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
22403                 increment *= -1;
22404           }
22405           if(!v || isNaN(pageNum = parseInt(v, 10))) {
22406             this.field.dom.value = d.activePage;
22407             return;
22408           }
22409           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
22410           {
22411             this.field.dom.value = parseInt(v, 10) + increment;
22412             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
22413             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22414           }
22415           e.stopEvent();
22416         }
22417     },
22418
22419     // private
22420     beforeLoad : function(){
22421         if(this.loading){
22422             this.loading.disable();
22423         }
22424     },
22425
22426     // private
22427     onClick : function(which){
22428         
22429         var ds = this.ds;
22430         if (!ds) {
22431             return;
22432         }
22433         
22434         switch(which){
22435             case "first":
22436                 ds.load({params:{start: 0, limit: this.pageSize}});
22437             break;
22438             case "prev":
22439                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
22440             break;
22441             case "next":
22442                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
22443             break;
22444             case "last":
22445                 var total = ds.getTotalCount();
22446                 var extra = total % this.pageSize;
22447                 var lastStart = extra ? (total - extra) : total-this.pageSize;
22448                 ds.load({params:{start: lastStart, limit: this.pageSize}});
22449             break;
22450             case "refresh":
22451                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22452             break;
22453         }
22454     },
22455
22456     /**
22457      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22458      * @param {Roo.data.Store} store The data store to unbind
22459      */
22460     unbind : function(ds){
22461         ds.un("beforeload", this.beforeLoad, this);
22462         ds.un("load", this.onLoad, this);
22463         ds.un("loadexception", this.onLoadError, this);
22464         ds.un("remove", this.updateInfo, this);
22465         ds.un("add", this.updateInfo, this);
22466         this.ds = undefined;
22467     },
22468
22469     /**
22470      * Binds the paging toolbar to the specified {@link Roo.data.Store}
22471      * @param {Roo.data.Store} store The data store to bind
22472      */
22473     bind : function(ds){
22474         ds.on("beforeload", this.beforeLoad, this);
22475         ds.on("load", this.onLoad, this);
22476         ds.on("loadexception", this.onLoadError, this);
22477         ds.on("remove", this.updateInfo, this);
22478         ds.on("add", this.updateInfo, this);
22479         this.ds = ds;
22480     }
22481 });/*
22482  * - LGPL
22483  *
22484  * element
22485  * 
22486  */
22487
22488 /**
22489  * @class Roo.bootstrap.MessageBar
22490  * @extends Roo.bootstrap.Component
22491  * Bootstrap MessageBar class
22492  * @cfg {String} html contents of the MessageBar
22493  * @cfg {String} weight (info | success | warning | danger) default info
22494  * @cfg {String} beforeClass insert the bar before the given class
22495  * @cfg {Boolean} closable (true | false) default false
22496  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22497  * 
22498  * @constructor
22499  * Create a new Element
22500  * @param {Object} config The config object
22501  */
22502
22503 Roo.bootstrap.MessageBar = function(config){
22504     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22505 };
22506
22507 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
22508     
22509     html: '',
22510     weight: 'info',
22511     closable: false,
22512     fixed: false,
22513     beforeClass: 'bootstrap-sticky-wrap',
22514     
22515     getAutoCreate : function(){
22516         
22517         var cfg = {
22518             tag: 'div',
22519             cls: 'alert alert-dismissable alert-' + this.weight,
22520             cn: [
22521                 {
22522                     tag: 'span',
22523                     cls: 'message',
22524                     html: this.html || ''
22525                 }
22526             ]
22527         };
22528         
22529         if(this.fixed){
22530             cfg.cls += ' alert-messages-fixed';
22531         }
22532         
22533         if(this.closable){
22534             cfg.cn.push({
22535                 tag: 'button',
22536                 cls: 'close',
22537                 html: 'x'
22538             });
22539         }
22540         
22541         return cfg;
22542     },
22543     
22544     onRender : function(ct, position)
22545     {
22546         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22547         
22548         if(!this.el){
22549             var cfg = Roo.apply({},  this.getAutoCreate());
22550             cfg.id = Roo.id();
22551             
22552             if (this.cls) {
22553                 cfg.cls += ' ' + this.cls;
22554             }
22555             if (this.style) {
22556                 cfg.style = this.style;
22557             }
22558             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22559             
22560             this.el.setVisibilityMode(Roo.Element.DISPLAY);
22561         }
22562         
22563         this.el.select('>button.close').on('click', this.hide, this);
22564         
22565     },
22566     
22567     show : function()
22568     {
22569         if (!this.rendered) {
22570             this.render();
22571         }
22572         
22573         this.el.show();
22574         
22575         this.fireEvent('show', this);
22576         
22577     },
22578     
22579     hide : function()
22580     {
22581         if (!this.rendered) {
22582             this.render();
22583         }
22584         
22585         this.el.hide();
22586         
22587         this.fireEvent('hide', this);
22588     },
22589     
22590     update : function()
22591     {
22592 //        var e = this.el.dom.firstChild;
22593 //        
22594 //        if(this.closable){
22595 //            e = e.nextSibling;
22596 //        }
22597 //        
22598 //        e.data = this.html || '';
22599
22600         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22601     }
22602    
22603 });
22604
22605  
22606
22607      /*
22608  * - LGPL
22609  *
22610  * Graph
22611  * 
22612  */
22613
22614
22615 /**
22616  * @class Roo.bootstrap.Graph
22617  * @extends Roo.bootstrap.Component
22618  * Bootstrap Graph class
22619 > Prameters
22620  -sm {number} sm 4
22621  -md {number} md 5
22622  @cfg {String} graphtype  bar | vbar | pie
22623  @cfg {number} g_x coodinator | centre x (pie)
22624  @cfg {number} g_y coodinator | centre y (pie)
22625  @cfg {number} g_r radius (pie)
22626  @cfg {number} g_height height of the chart (respected by all elements in the set)
22627  @cfg {number} g_width width of the chart (respected by all elements in the set)
22628  @cfg {Object} title The title of the chart
22629     
22630  -{Array}  values
22631  -opts (object) options for the chart 
22632      o {
22633      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22634      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22635      o vgutter (number)
22636      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.
22637      o stacked (boolean) whether or not to tread values as in a stacked bar chart
22638      o to
22639      o stretch (boolean)
22640      o }
22641  -opts (object) options for the pie
22642      o{
22643      o cut
22644      o startAngle (number)
22645      o endAngle (number)
22646      } 
22647  *
22648  * @constructor
22649  * Create a new Input
22650  * @param {Object} config The config object
22651  */
22652
22653 Roo.bootstrap.Graph = function(config){
22654     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22655     
22656     this.addEvents({
22657         // img events
22658         /**
22659          * @event click
22660          * The img click event for the img.
22661          * @param {Roo.EventObject} e
22662          */
22663         "click" : true
22664     });
22665 };
22666
22667 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
22668     
22669     sm: 4,
22670     md: 5,
22671     graphtype: 'bar',
22672     g_height: 250,
22673     g_width: 400,
22674     g_x: 50,
22675     g_y: 50,
22676     g_r: 30,
22677     opts:{
22678         //g_colors: this.colors,
22679         g_type: 'soft',
22680         g_gutter: '20%'
22681
22682     },
22683     title : false,
22684
22685     getAutoCreate : function(){
22686         
22687         var cfg = {
22688             tag: 'div',
22689             html : null
22690         };
22691         
22692         
22693         return  cfg;
22694     },
22695
22696     onRender : function(ct,position){
22697         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22698         this.raphael = Raphael(this.el.dom);
22699         
22700                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22701                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22702                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22703                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22704                 /*
22705                 r.text(160, 10, "Single Series Chart").attr(txtattr);
22706                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22707                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22708                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22709                 
22710                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22711                 r.barchart(330, 10, 300, 220, data1);
22712                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22713                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22714                 */
22715                 
22716                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22717                 // r.barchart(30, 30, 560, 250,  xdata, {
22718                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22719                 //     axis : "0 0 1 1",
22720                 //     axisxlabels :  xdata
22721                 //     //yvalues : cols,
22722                    
22723                 // });
22724 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22725 //        
22726 //        this.load(null,xdata,{
22727 //                axis : "0 0 1 1",
22728 //                axisxlabels :  xdata
22729 //                });
22730
22731     },
22732
22733     load : function(graphtype,xdata,opts){
22734         this.raphael.clear();
22735         if(!graphtype) {
22736             graphtype = this.graphtype;
22737         }
22738         if(!opts){
22739             opts = this.opts;
22740         }
22741         var r = this.raphael,
22742             fin = function () {
22743                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22744             },
22745             fout = function () {
22746                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22747             },
22748             pfin = function() {
22749                 this.sector.stop();
22750                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22751
22752                 if (this.label) {
22753                     this.label[0].stop();
22754                     this.label[0].attr({ r: 7.5 });
22755                     this.label[1].attr({ "font-weight": 800 });
22756                 }
22757             },
22758             pfout = function() {
22759                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22760
22761                 if (this.label) {
22762                     this.label[0].animate({ r: 5 }, 500, "bounce");
22763                     this.label[1].attr({ "font-weight": 400 });
22764                 }
22765             };
22766
22767         switch(graphtype){
22768             case 'bar':
22769                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22770                 break;
22771             case 'hbar':
22772                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22773                 break;
22774             case 'pie':
22775 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
22776 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22777 //            
22778                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22779                 
22780                 break;
22781
22782         }
22783         
22784         if(this.title){
22785             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22786         }
22787         
22788     },
22789     
22790     setTitle: function(o)
22791     {
22792         this.title = o;
22793     },
22794     
22795     initEvents: function() {
22796         
22797         if(!this.href){
22798             this.el.on('click', this.onClick, this);
22799         }
22800     },
22801     
22802     onClick : function(e)
22803     {
22804         Roo.log('img onclick');
22805         this.fireEvent('click', this, e);
22806     }
22807    
22808 });
22809
22810  
22811 /*
22812  * - LGPL
22813  *
22814  * numberBox
22815  * 
22816  */
22817 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22818
22819 /**
22820  * @class Roo.bootstrap.dash.NumberBox
22821  * @extends Roo.bootstrap.Component
22822  * Bootstrap NumberBox class
22823  * @cfg {String} headline Box headline
22824  * @cfg {String} content Box content
22825  * @cfg {String} icon Box icon
22826  * @cfg {String} footer Footer text
22827  * @cfg {String} fhref Footer href
22828  * 
22829  * @constructor
22830  * Create a new NumberBox
22831  * @param {Object} config The config object
22832  */
22833
22834
22835 Roo.bootstrap.dash.NumberBox = function(config){
22836     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22837     
22838 };
22839
22840 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
22841     
22842     headline : '',
22843     content : '',
22844     icon : '',
22845     footer : '',
22846     fhref : '',
22847     ficon : '',
22848     
22849     getAutoCreate : function(){
22850         
22851         var cfg = {
22852             tag : 'div',
22853             cls : 'small-box ',
22854             cn : [
22855                 {
22856                     tag : 'div',
22857                     cls : 'inner',
22858                     cn :[
22859                         {
22860                             tag : 'h3',
22861                             cls : 'roo-headline',
22862                             html : this.headline
22863                         },
22864                         {
22865                             tag : 'p',
22866                             cls : 'roo-content',
22867                             html : this.content
22868                         }
22869                     ]
22870                 }
22871             ]
22872         };
22873         
22874         if(this.icon){
22875             cfg.cn.push({
22876                 tag : 'div',
22877                 cls : 'icon',
22878                 cn :[
22879                     {
22880                         tag : 'i',
22881                         cls : 'ion ' + this.icon
22882                     }
22883                 ]
22884             });
22885         }
22886         
22887         if(this.footer){
22888             var footer = {
22889                 tag : 'a',
22890                 cls : 'small-box-footer',
22891                 href : this.fhref || '#',
22892                 html : this.footer
22893             };
22894             
22895             cfg.cn.push(footer);
22896             
22897         }
22898         
22899         return  cfg;
22900     },
22901
22902     onRender : function(ct,position){
22903         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
22904
22905
22906        
22907                 
22908     },
22909
22910     setHeadline: function (value)
22911     {
22912         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
22913     },
22914     
22915     setFooter: function (value, href)
22916     {
22917         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
22918         
22919         if(href){
22920             this.el.select('a.small-box-footer',true).first().attr('href', href);
22921         }
22922         
22923     },
22924
22925     setContent: function (value)
22926     {
22927         this.el.select('.roo-content',true).first().dom.innerHTML = value;
22928     },
22929
22930     initEvents: function() 
22931     {   
22932         
22933     }
22934     
22935 });
22936
22937  
22938 /*
22939  * - LGPL
22940  *
22941  * TabBox
22942  * 
22943  */
22944 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22945
22946 /**
22947  * @class Roo.bootstrap.dash.TabBox
22948  * @extends Roo.bootstrap.Component
22949  * Bootstrap TabBox class
22950  * @cfg {String} title Title of the TabBox
22951  * @cfg {String} icon Icon of the TabBox
22952  * @cfg {Boolean} showtabs (true|false) show the tabs default true
22953  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
22954  * 
22955  * @constructor
22956  * Create a new TabBox
22957  * @param {Object} config The config object
22958  */
22959
22960
22961 Roo.bootstrap.dash.TabBox = function(config){
22962     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
22963     this.addEvents({
22964         // raw events
22965         /**
22966          * @event addpane
22967          * When a pane is added
22968          * @param {Roo.bootstrap.dash.TabPane} pane
22969          */
22970         "addpane" : true,
22971         /**
22972          * @event activatepane
22973          * When a pane is activated
22974          * @param {Roo.bootstrap.dash.TabPane} pane
22975          */
22976         "activatepane" : true
22977         
22978          
22979     });
22980     
22981     this.panes = [];
22982 };
22983
22984 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
22985
22986     title : '',
22987     icon : false,
22988     showtabs : true,
22989     tabScrollable : false,
22990     
22991     getChildContainer : function()
22992     {
22993         return this.el.select('.tab-content', true).first();
22994     },
22995     
22996     getAutoCreate : function(){
22997         
22998         var header = {
22999             tag: 'li',
23000             cls: 'pull-left header',
23001             html: this.title,
23002             cn : []
23003         };
23004         
23005         if(this.icon){
23006             header.cn.push({
23007                 tag: 'i',
23008                 cls: 'fa ' + this.icon
23009             });
23010         }
23011         
23012         var h = {
23013             tag: 'ul',
23014             cls: 'nav nav-tabs pull-right',
23015             cn: [
23016                 header
23017             ]
23018         };
23019         
23020         if(this.tabScrollable){
23021             h = {
23022                 tag: 'div',
23023                 cls: 'tab-header',
23024                 cn: [
23025                     {
23026                         tag: 'ul',
23027                         cls: 'nav nav-tabs pull-right',
23028                         cn: [
23029                             header
23030                         ]
23031                     }
23032                 ]
23033             };
23034         }
23035         
23036         var cfg = {
23037             tag: 'div',
23038             cls: 'nav-tabs-custom',
23039             cn: [
23040                 h,
23041                 {
23042                     tag: 'div',
23043                     cls: 'tab-content no-padding',
23044                     cn: []
23045                 }
23046             ]
23047         };
23048
23049         return  cfg;
23050     },
23051     initEvents : function()
23052     {
23053         //Roo.log('add add pane handler');
23054         this.on('addpane', this.onAddPane, this);
23055     },
23056      /**
23057      * Updates the box title
23058      * @param {String} html to set the title to.
23059      */
23060     setTitle : function(value)
23061     {
23062         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23063     },
23064     onAddPane : function(pane)
23065     {
23066         this.panes.push(pane);
23067         //Roo.log('addpane');
23068         //Roo.log(pane);
23069         // tabs are rendere left to right..
23070         if(!this.showtabs){
23071             return;
23072         }
23073         
23074         var ctr = this.el.select('.nav-tabs', true).first();
23075          
23076          
23077         var existing = ctr.select('.nav-tab',true);
23078         var qty = existing.getCount();;
23079         
23080         
23081         var tab = ctr.createChild({
23082             tag : 'li',
23083             cls : 'nav-tab' + (qty ? '' : ' active'),
23084             cn : [
23085                 {
23086                     tag : 'a',
23087                     href:'#',
23088                     html : pane.title
23089                 }
23090             ]
23091         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23092         pane.tab = tab;
23093         
23094         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23095         if (!qty) {
23096             pane.el.addClass('active');
23097         }
23098         
23099                 
23100     },
23101     onTabClick : function(ev,un,ob,pane)
23102     {
23103         //Roo.log('tab - prev default');
23104         ev.preventDefault();
23105         
23106         
23107         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23108         pane.tab.addClass('active');
23109         //Roo.log(pane.title);
23110         this.getChildContainer().select('.tab-pane',true).removeClass('active');
23111         // technically we should have a deactivate event.. but maybe add later.
23112         // and it should not de-activate the selected tab...
23113         this.fireEvent('activatepane', pane);
23114         pane.el.addClass('active');
23115         pane.fireEvent('activate');
23116         
23117         
23118     },
23119     
23120     getActivePane : function()
23121     {
23122         var r = false;
23123         Roo.each(this.panes, function(p) {
23124             if(p.el.hasClass('active')){
23125                 r = p;
23126                 return false;
23127             }
23128             
23129             return;
23130         });
23131         
23132         return r;
23133     }
23134     
23135     
23136 });
23137
23138  
23139 /*
23140  * - LGPL
23141  *
23142  * Tab pane
23143  * 
23144  */
23145 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23146 /**
23147  * @class Roo.bootstrap.TabPane
23148  * @extends Roo.bootstrap.Component
23149  * Bootstrap TabPane class
23150  * @cfg {Boolean} active (false | true) Default false
23151  * @cfg {String} title title of panel
23152
23153  * 
23154  * @constructor
23155  * Create a new TabPane
23156  * @param {Object} config The config object
23157  */
23158
23159 Roo.bootstrap.dash.TabPane = function(config){
23160     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
23161     
23162     this.addEvents({
23163         // raw events
23164         /**
23165          * @event activate
23166          * When a pane is activated
23167          * @param {Roo.bootstrap.dash.TabPane} pane
23168          */
23169         "activate" : true
23170          
23171     });
23172 };
23173
23174 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
23175     
23176     active : false,
23177     title : '',
23178     
23179     // the tabBox that this is attached to.
23180     tab : false,
23181      
23182     getAutoCreate : function() 
23183     {
23184         var cfg = {
23185             tag: 'div',
23186             cls: 'tab-pane'
23187         };
23188         
23189         if(this.active){
23190             cfg.cls += ' active';
23191         }
23192         
23193         return cfg;
23194     },
23195     initEvents  : function()
23196     {
23197         //Roo.log('trigger add pane handler');
23198         this.parent().fireEvent('addpane', this)
23199     },
23200     
23201      /**
23202      * Updates the tab title 
23203      * @param {String} html to set the title to.
23204      */
23205     setTitle: function(str)
23206     {
23207         if (!this.tab) {
23208             return;
23209         }
23210         this.title = str;
23211         this.tab.select('a', true).first().dom.innerHTML = str;
23212         
23213     }
23214     
23215     
23216     
23217 });
23218
23219  
23220
23221
23222  /*
23223  * - LGPL
23224  *
23225  * menu
23226  * 
23227  */
23228 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23229
23230 /**
23231  * @class Roo.bootstrap.menu.Menu
23232  * @extends Roo.bootstrap.Component
23233  * Bootstrap Menu class - container for Menu
23234  * @cfg {String} html Text of the menu
23235  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
23236  * @cfg {String} icon Font awesome icon
23237  * @cfg {String} pos Menu align to (top | bottom) default bottom
23238  * 
23239  * 
23240  * @constructor
23241  * Create a new Menu
23242  * @param {Object} config The config object
23243  */
23244
23245
23246 Roo.bootstrap.menu.Menu = function(config){
23247     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
23248     
23249     this.addEvents({
23250         /**
23251          * @event beforeshow
23252          * Fires before this menu is displayed
23253          * @param {Roo.bootstrap.menu.Menu} this
23254          */
23255         beforeshow : true,
23256         /**
23257          * @event beforehide
23258          * Fires before this menu is hidden
23259          * @param {Roo.bootstrap.menu.Menu} this
23260          */
23261         beforehide : true,
23262         /**
23263          * @event show
23264          * Fires after this menu is displayed
23265          * @param {Roo.bootstrap.menu.Menu} this
23266          */
23267         show : true,
23268         /**
23269          * @event hide
23270          * Fires after this menu is hidden
23271          * @param {Roo.bootstrap.menu.Menu} this
23272          */
23273         hide : true,
23274         /**
23275          * @event click
23276          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
23277          * @param {Roo.bootstrap.menu.Menu} this
23278          * @param {Roo.EventObject} e
23279          */
23280         click : true
23281     });
23282     
23283 };
23284
23285 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
23286     
23287     submenu : false,
23288     html : '',
23289     weight : 'default',
23290     icon : false,
23291     pos : 'bottom',
23292     
23293     
23294     getChildContainer : function() {
23295         if(this.isSubMenu){
23296             return this.el;
23297         }
23298         
23299         return this.el.select('ul.dropdown-menu', true).first();  
23300     },
23301     
23302     getAutoCreate : function()
23303     {
23304         var text = [
23305             {
23306                 tag : 'span',
23307                 cls : 'roo-menu-text',
23308                 html : this.html
23309             }
23310         ];
23311         
23312         if(this.icon){
23313             text.unshift({
23314                 tag : 'i',
23315                 cls : 'fa ' + this.icon
23316             })
23317         }
23318         
23319         
23320         var cfg = {
23321             tag : 'div',
23322             cls : 'btn-group',
23323             cn : [
23324                 {
23325                     tag : 'button',
23326                     cls : 'dropdown-button btn btn-' + this.weight,
23327                     cn : text
23328                 },
23329                 {
23330                     tag : 'button',
23331                     cls : 'dropdown-toggle btn btn-' + this.weight,
23332                     cn : [
23333                         {
23334                             tag : 'span',
23335                             cls : 'caret'
23336                         }
23337                     ]
23338                 },
23339                 {
23340                     tag : 'ul',
23341                     cls : 'dropdown-menu'
23342                 }
23343             ]
23344             
23345         };
23346         
23347         if(this.pos == 'top'){
23348             cfg.cls += ' dropup';
23349         }
23350         
23351         if(this.isSubMenu){
23352             cfg = {
23353                 tag : 'ul',
23354                 cls : 'dropdown-menu'
23355             }
23356         }
23357         
23358         return cfg;
23359     },
23360     
23361     onRender : function(ct, position)
23362     {
23363         this.isSubMenu = ct.hasClass('dropdown-submenu');
23364         
23365         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
23366     },
23367     
23368     initEvents : function() 
23369     {
23370         if(this.isSubMenu){
23371             return;
23372         }
23373         
23374         this.hidden = true;
23375         
23376         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
23377         this.triggerEl.on('click', this.onTriggerPress, this);
23378         
23379         this.buttonEl = this.el.select('button.dropdown-button', true).first();
23380         this.buttonEl.on('click', this.onClick, this);
23381         
23382     },
23383     
23384     list : function()
23385     {
23386         if(this.isSubMenu){
23387             return this.el;
23388         }
23389         
23390         return this.el.select('ul.dropdown-menu', true).first();
23391     },
23392     
23393     onClick : function(e)
23394     {
23395         this.fireEvent("click", this, e);
23396     },
23397     
23398     onTriggerPress  : function(e)
23399     {   
23400         if (this.isVisible()) {
23401             this.hide();
23402         } else {
23403             this.show();
23404         }
23405     },
23406     
23407     isVisible : function(){
23408         return !this.hidden;
23409     },
23410     
23411     show : function()
23412     {
23413         this.fireEvent("beforeshow", this);
23414         
23415         this.hidden = false;
23416         this.el.addClass('open');
23417         
23418         Roo.get(document).on("mouseup", this.onMouseUp, this);
23419         
23420         this.fireEvent("show", this);
23421         
23422         
23423     },
23424     
23425     hide : function()
23426     {
23427         this.fireEvent("beforehide", this);
23428         
23429         this.hidden = true;
23430         this.el.removeClass('open');
23431         
23432         Roo.get(document).un("mouseup", this.onMouseUp);
23433         
23434         this.fireEvent("hide", this);
23435     },
23436     
23437     onMouseUp : function()
23438     {
23439         this.hide();
23440     }
23441     
23442 });
23443
23444  
23445  /*
23446  * - LGPL
23447  *
23448  * menu item
23449  * 
23450  */
23451 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23452
23453 /**
23454  * @class Roo.bootstrap.menu.Item
23455  * @extends Roo.bootstrap.Component
23456  * Bootstrap MenuItem class
23457  * @cfg {Boolean} submenu (true | false) default false
23458  * @cfg {String} html text of the item
23459  * @cfg {String} href the link
23460  * @cfg {Boolean} disable (true | false) default false
23461  * @cfg {Boolean} preventDefault (true | false) default true
23462  * @cfg {String} icon Font awesome icon
23463  * @cfg {String} pos Submenu align to (left | right) default right 
23464  * 
23465  * 
23466  * @constructor
23467  * Create a new Item
23468  * @param {Object} config The config object
23469  */
23470
23471
23472 Roo.bootstrap.menu.Item = function(config){
23473     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23474     this.addEvents({
23475         /**
23476          * @event mouseover
23477          * Fires when the mouse is hovering over this menu
23478          * @param {Roo.bootstrap.menu.Item} this
23479          * @param {Roo.EventObject} e
23480          */
23481         mouseover : true,
23482         /**
23483          * @event mouseout
23484          * Fires when the mouse exits this menu
23485          * @param {Roo.bootstrap.menu.Item} this
23486          * @param {Roo.EventObject} e
23487          */
23488         mouseout : true,
23489         // raw events
23490         /**
23491          * @event click
23492          * The raw click event for the entire grid.
23493          * @param {Roo.EventObject} e
23494          */
23495         click : true
23496     });
23497 };
23498
23499 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
23500     
23501     submenu : false,
23502     href : '',
23503     html : '',
23504     preventDefault: true,
23505     disable : false,
23506     icon : false,
23507     pos : 'right',
23508     
23509     getAutoCreate : function()
23510     {
23511         var text = [
23512             {
23513                 tag : 'span',
23514                 cls : 'roo-menu-item-text',
23515                 html : this.html
23516             }
23517         ];
23518         
23519         if(this.icon){
23520             text.unshift({
23521                 tag : 'i',
23522                 cls : 'fa ' + this.icon
23523             })
23524         }
23525         
23526         var cfg = {
23527             tag : 'li',
23528             cn : [
23529                 {
23530                     tag : 'a',
23531                     href : this.href || '#',
23532                     cn : text
23533                 }
23534             ]
23535         };
23536         
23537         if(this.disable){
23538             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23539         }
23540         
23541         if(this.submenu){
23542             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23543             
23544             if(this.pos == 'left'){
23545                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23546             }
23547         }
23548         
23549         return cfg;
23550     },
23551     
23552     initEvents : function() 
23553     {
23554         this.el.on('mouseover', this.onMouseOver, this);
23555         this.el.on('mouseout', this.onMouseOut, this);
23556         
23557         this.el.select('a', true).first().on('click', this.onClick, this);
23558         
23559     },
23560     
23561     onClick : function(e)
23562     {
23563         if(this.preventDefault){
23564             e.preventDefault();
23565         }
23566         
23567         this.fireEvent("click", this, e);
23568     },
23569     
23570     onMouseOver : function(e)
23571     {
23572         if(this.submenu && this.pos == 'left'){
23573             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23574         }
23575         
23576         this.fireEvent("mouseover", this, e);
23577     },
23578     
23579     onMouseOut : function(e)
23580     {
23581         this.fireEvent("mouseout", this, e);
23582     }
23583 });
23584
23585  
23586
23587  /*
23588  * - LGPL
23589  *
23590  * menu separator
23591  * 
23592  */
23593 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23594
23595 /**
23596  * @class Roo.bootstrap.menu.Separator
23597  * @extends Roo.bootstrap.Component
23598  * Bootstrap Separator class
23599  * 
23600  * @constructor
23601  * Create a new Separator
23602  * @param {Object} config The config object
23603  */
23604
23605
23606 Roo.bootstrap.menu.Separator = function(config){
23607     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23608 };
23609
23610 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
23611     
23612     getAutoCreate : function(){
23613         var cfg = {
23614             tag : 'li',
23615             cls: 'divider'
23616         };
23617         
23618         return cfg;
23619     }
23620    
23621 });
23622
23623  
23624
23625  /*
23626  * - LGPL
23627  *
23628  * Tooltip
23629  * 
23630  */
23631
23632 /**
23633  * @class Roo.bootstrap.Tooltip
23634  * Bootstrap Tooltip class
23635  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23636  * to determine which dom element triggers the tooltip.
23637  * 
23638  * It needs to add support for additional attributes like tooltip-position
23639  * 
23640  * @constructor
23641  * Create a new Toolti
23642  * @param {Object} config The config object
23643  */
23644
23645 Roo.bootstrap.Tooltip = function(config){
23646     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23647 };
23648
23649 Roo.apply(Roo.bootstrap.Tooltip, {
23650     /**
23651      * @function init initialize tooltip monitoring.
23652      * @static
23653      */
23654     currentEl : false,
23655     currentTip : false,
23656     currentRegion : false,
23657     
23658     //  init : delay?
23659     
23660     init : function()
23661     {
23662         Roo.get(document).on('mouseover', this.enter ,this);
23663         Roo.get(document).on('mouseout', this.leave, this);
23664          
23665         
23666         this.currentTip = new Roo.bootstrap.Tooltip();
23667     },
23668     
23669     enter : function(ev)
23670     {
23671         var dom = ev.getTarget();
23672         
23673         //Roo.log(['enter',dom]);
23674         var el = Roo.fly(dom);
23675         if (this.currentEl) {
23676             //Roo.log(dom);
23677             //Roo.log(this.currentEl);
23678             //Roo.log(this.currentEl.contains(dom));
23679             if (this.currentEl == el) {
23680                 return;
23681             }
23682             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23683                 return;
23684             }
23685
23686         }
23687         
23688         if (this.currentTip.el) {
23689             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
23690         }    
23691         //Roo.log(ev);
23692         var bindEl = el;
23693         
23694         // you can not look for children, as if el is the body.. then everythign is the child..
23695         if (!el.attr('tooltip')) { //
23696             if (!el.select("[tooltip]").elements.length) {
23697                 return;
23698             }
23699             // is the mouse over this child...?
23700             bindEl = el.select("[tooltip]").first();
23701             var xy = ev.getXY();
23702             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23703                 //Roo.log("not in region.");
23704                 return;
23705             }
23706             //Roo.log("child element over..");
23707             
23708         }
23709         this.currentEl = bindEl;
23710         this.currentTip.bind(bindEl);
23711         this.currentRegion = Roo.lib.Region.getRegion(dom);
23712         this.currentTip.enter();
23713         
23714     },
23715     leave : function(ev)
23716     {
23717         var dom = ev.getTarget();
23718         //Roo.log(['leave',dom]);
23719         if (!this.currentEl) {
23720             return;
23721         }
23722         
23723         
23724         if (dom != this.currentEl.dom) {
23725             return;
23726         }
23727         var xy = ev.getXY();
23728         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
23729             return;
23730         }
23731         // only activate leave if mouse cursor is outside... bounding box..
23732         
23733         
23734         
23735         
23736         if (this.currentTip) {
23737             this.currentTip.leave();
23738         }
23739         //Roo.log('clear currentEl');
23740         this.currentEl = false;
23741         
23742         
23743     },
23744     alignment : {
23745         'left' : ['r-l', [-2,0], 'right'],
23746         'right' : ['l-r', [2,0], 'left'],
23747         'bottom' : ['t-b', [0,2], 'top'],
23748         'top' : [ 'b-t', [0,-2], 'bottom']
23749     }
23750     
23751 });
23752
23753
23754 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
23755     
23756     
23757     bindEl : false,
23758     
23759     delay : null, // can be { show : 300 , hide: 500}
23760     
23761     timeout : null,
23762     
23763     hoverState : null, //???
23764     
23765     placement : 'bottom', 
23766     
23767     getAutoCreate : function(){
23768     
23769         var cfg = {
23770            cls : 'tooltip',
23771            role : 'tooltip',
23772            cn : [
23773                 {
23774                     cls : 'tooltip-arrow'
23775                 },
23776                 {
23777                     cls : 'tooltip-inner'
23778                 }
23779            ]
23780         };
23781         
23782         return cfg;
23783     },
23784     bind : function(el)
23785     {
23786         this.bindEl = el;
23787     },
23788       
23789     
23790     enter : function () {
23791        
23792         if (this.timeout != null) {
23793             clearTimeout(this.timeout);
23794         }
23795         
23796         this.hoverState = 'in';
23797          //Roo.log("enter - show");
23798         if (!this.delay || !this.delay.show) {
23799             this.show();
23800             return;
23801         }
23802         var _t = this;
23803         this.timeout = setTimeout(function () {
23804             if (_t.hoverState == 'in') {
23805                 _t.show();
23806             }
23807         }, this.delay.show);
23808     },
23809     leave : function()
23810     {
23811         clearTimeout(this.timeout);
23812     
23813         this.hoverState = 'out';
23814          if (!this.delay || !this.delay.hide) {
23815             this.hide();
23816             return;
23817         }
23818        
23819         var _t = this;
23820         this.timeout = setTimeout(function () {
23821             //Roo.log("leave - timeout");
23822             
23823             if (_t.hoverState == 'out') {
23824                 _t.hide();
23825                 Roo.bootstrap.Tooltip.currentEl = false;
23826             }
23827         }, delay);
23828     },
23829     
23830     show : function ()
23831     {
23832         if (!this.el) {
23833             this.render(document.body);
23834         }
23835         // set content.
23836         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23837         
23838         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23839         
23840         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23841         
23842         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23843         
23844         var placement = typeof this.placement == 'function' ?
23845             this.placement.call(this, this.el, on_el) :
23846             this.placement;
23847             
23848         var autoToken = /\s?auto?\s?/i;
23849         var autoPlace = autoToken.test(placement);
23850         if (autoPlace) {
23851             placement = placement.replace(autoToken, '') || 'top';
23852         }
23853         
23854         //this.el.detach()
23855         //this.el.setXY([0,0]);
23856         this.el.show();
23857         //this.el.dom.style.display='block';
23858         
23859         //this.el.appendTo(on_el);
23860         
23861         var p = this.getPosition();
23862         var box = this.el.getBox();
23863         
23864         if (autoPlace) {
23865             // fixme..
23866         }
23867         
23868         var align = Roo.bootstrap.Tooltip.alignment[placement];
23869         
23870         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
23871         
23872         if(placement == 'top' || placement == 'bottom'){
23873             if(xy[0] < 0){
23874                 placement = 'right';
23875             }
23876             
23877             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
23878                 placement = 'left';
23879             }
23880         }
23881         
23882         align = Roo.bootstrap.Tooltip.alignment[placement];
23883         
23884         this.el.alignTo(this.bindEl, align[0],align[1]);
23885         //var arrow = this.el.select('.arrow',true).first();
23886         //arrow.set(align[2], 
23887         
23888         this.el.addClass(placement);
23889         
23890         this.el.addClass('in fade');
23891         
23892         this.hoverState = null;
23893         
23894         if (this.el.hasClass('fade')) {
23895             // fade it?
23896         }
23897         
23898     },
23899     hide : function()
23900     {
23901          
23902         if (!this.el) {
23903             return;
23904         }
23905         //this.el.setXY([0,0]);
23906         this.el.removeClass('in');
23907         //this.el.hide();
23908         
23909     }
23910     
23911 });
23912  
23913
23914  /*
23915  * - LGPL
23916  *
23917  * Location Picker
23918  * 
23919  */
23920
23921 /**
23922  * @class Roo.bootstrap.LocationPicker
23923  * @extends Roo.bootstrap.Component
23924  * Bootstrap LocationPicker class
23925  * @cfg {Number} latitude Position when init default 0
23926  * @cfg {Number} longitude Position when init default 0
23927  * @cfg {Number} zoom default 15
23928  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
23929  * @cfg {Boolean} mapTypeControl default false
23930  * @cfg {Boolean} disableDoubleClickZoom default false
23931  * @cfg {Boolean} scrollwheel default true
23932  * @cfg {Boolean} streetViewControl default false
23933  * @cfg {Number} radius default 0
23934  * @cfg {String} locationName
23935  * @cfg {Boolean} draggable default true
23936  * @cfg {Boolean} enableAutocomplete default false
23937  * @cfg {Boolean} enableReverseGeocode default true
23938  * @cfg {String} markerTitle
23939  * 
23940  * @constructor
23941  * Create a new LocationPicker
23942  * @param {Object} config The config object
23943  */
23944
23945
23946 Roo.bootstrap.LocationPicker = function(config){
23947     
23948     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
23949     
23950     this.addEvents({
23951         /**
23952          * @event initial
23953          * Fires when the picker initialized.
23954          * @param {Roo.bootstrap.LocationPicker} this
23955          * @param {Google Location} location
23956          */
23957         initial : true,
23958         /**
23959          * @event positionchanged
23960          * Fires when the picker position changed.
23961          * @param {Roo.bootstrap.LocationPicker} this
23962          * @param {Google Location} location
23963          */
23964         positionchanged : true,
23965         /**
23966          * @event resize
23967          * Fires when the map resize.
23968          * @param {Roo.bootstrap.LocationPicker} this
23969          */
23970         resize : true,
23971         /**
23972          * @event show
23973          * Fires when the map show.
23974          * @param {Roo.bootstrap.LocationPicker} this
23975          */
23976         show : true,
23977         /**
23978          * @event hide
23979          * Fires when the map hide.
23980          * @param {Roo.bootstrap.LocationPicker} this
23981          */
23982         hide : true,
23983         /**
23984          * @event mapClick
23985          * Fires when click the map.
23986          * @param {Roo.bootstrap.LocationPicker} this
23987          * @param {Map event} e
23988          */
23989         mapClick : true,
23990         /**
23991          * @event mapRightClick
23992          * Fires when right click the map.
23993          * @param {Roo.bootstrap.LocationPicker} this
23994          * @param {Map event} e
23995          */
23996         mapRightClick : true,
23997         /**
23998          * @event markerClick
23999          * Fires when click the marker.
24000          * @param {Roo.bootstrap.LocationPicker} this
24001          * @param {Map event} e
24002          */
24003         markerClick : true,
24004         /**
24005          * @event markerRightClick
24006          * Fires when right click the marker.
24007          * @param {Roo.bootstrap.LocationPicker} this
24008          * @param {Map event} e
24009          */
24010         markerRightClick : true,
24011         /**
24012          * @event OverlayViewDraw
24013          * Fires when OverlayView Draw
24014          * @param {Roo.bootstrap.LocationPicker} this
24015          */
24016         OverlayViewDraw : true,
24017         /**
24018          * @event OverlayViewOnAdd
24019          * Fires when OverlayView Draw
24020          * @param {Roo.bootstrap.LocationPicker} this
24021          */
24022         OverlayViewOnAdd : true,
24023         /**
24024          * @event OverlayViewOnRemove
24025          * Fires when OverlayView Draw
24026          * @param {Roo.bootstrap.LocationPicker} this
24027          */
24028         OverlayViewOnRemove : true,
24029         /**
24030          * @event OverlayViewShow
24031          * Fires when OverlayView Draw
24032          * @param {Roo.bootstrap.LocationPicker} this
24033          * @param {Pixel} cpx
24034          */
24035         OverlayViewShow : true,
24036         /**
24037          * @event OverlayViewHide
24038          * Fires when OverlayView Draw
24039          * @param {Roo.bootstrap.LocationPicker} this
24040          */
24041         OverlayViewHide : true,
24042         /**
24043          * @event loadexception
24044          * Fires when load google lib failed.
24045          * @param {Roo.bootstrap.LocationPicker} this
24046          */
24047         loadexception : true
24048     });
24049         
24050 };
24051
24052 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
24053     
24054     gMapContext: false,
24055     
24056     latitude: 0,
24057     longitude: 0,
24058     zoom: 15,
24059     mapTypeId: false,
24060     mapTypeControl: false,
24061     disableDoubleClickZoom: false,
24062     scrollwheel: true,
24063     streetViewControl: false,
24064     radius: 0,
24065     locationName: '',
24066     draggable: true,
24067     enableAutocomplete: false,
24068     enableReverseGeocode: true,
24069     markerTitle: '',
24070     
24071     getAutoCreate: function()
24072     {
24073
24074         var cfg = {
24075             tag: 'div',
24076             cls: 'roo-location-picker'
24077         };
24078         
24079         return cfg
24080     },
24081     
24082     initEvents: function(ct, position)
24083     {       
24084         if(!this.el.getWidth() || this.isApplied()){
24085             return;
24086         }
24087         
24088         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24089         
24090         this.initial();
24091     },
24092     
24093     initial: function()
24094     {
24095         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24096             this.fireEvent('loadexception', this);
24097             return;
24098         }
24099         
24100         if(!this.mapTypeId){
24101             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24102         }
24103         
24104         this.gMapContext = this.GMapContext();
24105         
24106         this.initOverlayView();
24107         
24108         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24109         
24110         var _this = this;
24111                 
24112         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24113             _this.setPosition(_this.gMapContext.marker.position);
24114         });
24115         
24116         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
24117             _this.fireEvent('mapClick', this, event);
24118             
24119         });
24120
24121         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
24122             _this.fireEvent('mapRightClick', this, event);
24123             
24124         });
24125         
24126         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
24127             _this.fireEvent('markerClick', this, event);
24128             
24129         });
24130
24131         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
24132             _this.fireEvent('markerRightClick', this, event);
24133             
24134         });
24135         
24136         this.setPosition(this.gMapContext.location);
24137         
24138         this.fireEvent('initial', this, this.gMapContext.location);
24139     },
24140     
24141     initOverlayView: function()
24142     {
24143         var _this = this;
24144         
24145         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
24146             
24147             draw: function()
24148             {
24149                 _this.fireEvent('OverlayViewDraw', _this);
24150             },
24151             
24152             onAdd: function()
24153             {
24154                 _this.fireEvent('OverlayViewOnAdd', _this);
24155             },
24156             
24157             onRemove: function()
24158             {
24159                 _this.fireEvent('OverlayViewOnRemove', _this);
24160             },
24161             
24162             show: function(cpx)
24163             {
24164                 _this.fireEvent('OverlayViewShow', _this, cpx);
24165             },
24166             
24167             hide: function()
24168             {
24169                 _this.fireEvent('OverlayViewHide', _this);
24170             }
24171             
24172         });
24173     },
24174     
24175     fromLatLngToContainerPixel: function(event)
24176     {
24177         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
24178     },
24179     
24180     isApplied: function() 
24181     {
24182         return this.getGmapContext() == false ? false : true;
24183     },
24184     
24185     getGmapContext: function() 
24186     {
24187         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
24188     },
24189     
24190     GMapContext: function() 
24191     {
24192         var position = new google.maps.LatLng(this.latitude, this.longitude);
24193         
24194         var _map = new google.maps.Map(this.el.dom, {
24195             center: position,
24196             zoom: this.zoom,
24197             mapTypeId: this.mapTypeId,
24198             mapTypeControl: this.mapTypeControl,
24199             disableDoubleClickZoom: this.disableDoubleClickZoom,
24200             scrollwheel: this.scrollwheel,
24201             streetViewControl: this.streetViewControl,
24202             locationName: this.locationName,
24203             draggable: this.draggable,
24204             enableAutocomplete: this.enableAutocomplete,
24205             enableReverseGeocode: this.enableReverseGeocode
24206         });
24207         
24208         var _marker = new google.maps.Marker({
24209             position: position,
24210             map: _map,
24211             title: this.markerTitle,
24212             draggable: this.draggable
24213         });
24214         
24215         return {
24216             map: _map,
24217             marker: _marker,
24218             circle: null,
24219             location: position,
24220             radius: this.radius,
24221             locationName: this.locationName,
24222             addressComponents: {
24223                 formatted_address: null,
24224                 addressLine1: null,
24225                 addressLine2: null,
24226                 streetName: null,
24227                 streetNumber: null,
24228                 city: null,
24229                 district: null,
24230                 state: null,
24231                 stateOrProvince: null
24232             },
24233             settings: this,
24234             domContainer: this.el.dom,
24235             geodecoder: new google.maps.Geocoder()
24236         };
24237     },
24238     
24239     drawCircle: function(center, radius, options) 
24240     {
24241         if (this.gMapContext.circle != null) {
24242             this.gMapContext.circle.setMap(null);
24243         }
24244         if (radius > 0) {
24245             radius *= 1;
24246             options = Roo.apply({}, options, {
24247                 strokeColor: "#0000FF",
24248                 strokeOpacity: .35,
24249                 strokeWeight: 2,
24250                 fillColor: "#0000FF",
24251                 fillOpacity: .2
24252             });
24253             
24254             options.map = this.gMapContext.map;
24255             options.radius = radius;
24256             options.center = center;
24257             this.gMapContext.circle = new google.maps.Circle(options);
24258             return this.gMapContext.circle;
24259         }
24260         
24261         return null;
24262     },
24263     
24264     setPosition: function(location) 
24265     {
24266         this.gMapContext.location = location;
24267         this.gMapContext.marker.setPosition(location);
24268         this.gMapContext.map.panTo(location);
24269         this.drawCircle(location, this.gMapContext.radius, {});
24270         
24271         var _this = this;
24272         
24273         if (this.gMapContext.settings.enableReverseGeocode) {
24274             this.gMapContext.geodecoder.geocode({
24275                 latLng: this.gMapContext.location
24276             }, function(results, status) {
24277                 
24278                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
24279                     _this.gMapContext.locationName = results[0].formatted_address;
24280                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
24281                     
24282                     _this.fireEvent('positionchanged', this, location);
24283                 }
24284             });
24285             
24286             return;
24287         }
24288         
24289         this.fireEvent('positionchanged', this, location);
24290     },
24291     
24292     resize: function()
24293     {
24294         google.maps.event.trigger(this.gMapContext.map, "resize");
24295         
24296         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24297         
24298         this.fireEvent('resize', this);
24299     },
24300     
24301     setPositionByLatLng: function(latitude, longitude)
24302     {
24303         this.setPosition(new google.maps.LatLng(latitude, longitude));
24304     },
24305     
24306     getCurrentPosition: function() 
24307     {
24308         return {
24309             latitude: this.gMapContext.location.lat(),
24310             longitude: this.gMapContext.location.lng()
24311         };
24312     },
24313     
24314     getAddressName: function() 
24315     {
24316         return this.gMapContext.locationName;
24317     },
24318     
24319     getAddressComponents: function() 
24320     {
24321         return this.gMapContext.addressComponents;
24322     },
24323     
24324     address_component_from_google_geocode: function(address_components) 
24325     {
24326         var result = {};
24327         
24328         for (var i = 0; i < address_components.length; i++) {
24329             var component = address_components[i];
24330             if (component.types.indexOf("postal_code") >= 0) {
24331                 result.postalCode = component.short_name;
24332             } else if (component.types.indexOf("street_number") >= 0) {
24333                 result.streetNumber = component.short_name;
24334             } else if (component.types.indexOf("route") >= 0) {
24335                 result.streetName = component.short_name;
24336             } else if (component.types.indexOf("neighborhood") >= 0) {
24337                 result.city = component.short_name;
24338             } else if (component.types.indexOf("locality") >= 0) {
24339                 result.city = component.short_name;
24340             } else if (component.types.indexOf("sublocality") >= 0) {
24341                 result.district = component.short_name;
24342             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
24343                 result.stateOrProvince = component.short_name;
24344             } else if (component.types.indexOf("country") >= 0) {
24345                 result.country = component.short_name;
24346             }
24347         }
24348         
24349         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
24350         result.addressLine2 = "";
24351         return result;
24352     },
24353     
24354     setZoomLevel: function(zoom)
24355     {
24356         this.gMapContext.map.setZoom(zoom);
24357     },
24358     
24359     show: function()
24360     {
24361         if(!this.el){
24362             return;
24363         }
24364         
24365         this.el.show();
24366         
24367         this.resize();
24368         
24369         this.fireEvent('show', this);
24370     },
24371     
24372     hide: function()
24373     {
24374         if(!this.el){
24375             return;
24376         }
24377         
24378         this.el.hide();
24379         
24380         this.fireEvent('hide', this);
24381     }
24382     
24383 });
24384
24385 Roo.apply(Roo.bootstrap.LocationPicker, {
24386     
24387     OverlayView : function(map, options)
24388     {
24389         options = options || {};
24390         
24391         this.setMap(map);
24392     }
24393     
24394     
24395 });/*
24396  * - LGPL
24397  *
24398  * Alert
24399  * 
24400  */
24401
24402 /**
24403  * @class Roo.bootstrap.Alert
24404  * @extends Roo.bootstrap.Component
24405  * Bootstrap Alert class
24406  * @cfg {String} title The title of alert
24407  * @cfg {String} html The content of alert
24408  * @cfg {String} weight (  success | info | warning | danger )
24409  * @cfg {String} faicon font-awesomeicon
24410  * 
24411  * @constructor
24412  * Create a new alert
24413  * @param {Object} config The config object
24414  */
24415
24416
24417 Roo.bootstrap.Alert = function(config){
24418     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
24419     
24420 };
24421
24422 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
24423     
24424     title: '',
24425     html: '',
24426     weight: false,
24427     faicon: false,
24428     
24429     getAutoCreate : function()
24430     {
24431         
24432         var cfg = {
24433             tag : 'div',
24434             cls : 'alert',
24435             cn : [
24436                 {
24437                     tag : 'i',
24438                     cls : 'roo-alert-icon'
24439                     
24440                 },
24441                 {
24442                     tag : 'b',
24443                     cls : 'roo-alert-title',
24444                     html : this.title
24445                 },
24446                 {
24447                     tag : 'span',
24448                     cls : 'roo-alert-text',
24449                     html : this.html
24450                 }
24451             ]
24452         };
24453         
24454         if(this.faicon){
24455             cfg.cn[0].cls += ' fa ' + this.faicon;
24456         }
24457         
24458         if(this.weight){
24459             cfg.cls += ' alert-' + this.weight;
24460         }
24461         
24462         return cfg;
24463     },
24464     
24465     initEvents: function() 
24466     {
24467         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24468     },
24469     
24470     setTitle : function(str)
24471     {
24472         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24473     },
24474     
24475     setText : function(str)
24476     {
24477         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24478     },
24479     
24480     setWeight : function(weight)
24481     {
24482         if(this.weight){
24483             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24484         }
24485         
24486         this.weight = weight;
24487         
24488         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24489     },
24490     
24491     setIcon : function(icon)
24492     {
24493         if(this.faicon){
24494             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24495         }
24496         
24497         this.faicon = icon;
24498         
24499         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24500     },
24501     
24502     hide: function() 
24503     {
24504         this.el.hide();   
24505     },
24506     
24507     show: function() 
24508     {  
24509         this.el.show();   
24510     }
24511     
24512 });
24513
24514  
24515 /*
24516 * Licence: LGPL
24517 */
24518
24519 /**
24520  * @class Roo.bootstrap.UploadCropbox
24521  * @extends Roo.bootstrap.Component
24522  * Bootstrap UploadCropbox class
24523  * @cfg {String} emptyText show when image has been loaded
24524  * @cfg {String} rotateNotify show when image too small to rotate
24525  * @cfg {Number} errorTimeout default 3000
24526  * @cfg {Number} minWidth default 300
24527  * @cfg {Number} minHeight default 300
24528  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24529  * @cfg {Boolean} isDocument (true|false) default false
24530  * @cfg {String} url action url
24531  * @cfg {String} paramName default 'imageUpload'
24532  * @cfg {String} method default POST
24533  * @cfg {Boolean} loadMask (true|false) default true
24534  * @cfg {Boolean} loadingText default 'Loading...'
24535  * 
24536  * @constructor
24537  * Create a new UploadCropbox
24538  * @param {Object} config The config object
24539  */
24540
24541 Roo.bootstrap.UploadCropbox = function(config){
24542     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
24543     
24544     this.addEvents({
24545         /**
24546          * @event beforeselectfile
24547          * Fire before select file
24548          * @param {Roo.bootstrap.UploadCropbox} this
24549          */
24550         "beforeselectfile" : true,
24551         /**
24552          * @event initial
24553          * Fire after initEvent
24554          * @param {Roo.bootstrap.UploadCropbox} this
24555          */
24556         "initial" : true,
24557         /**
24558          * @event crop
24559          * Fire after initEvent
24560          * @param {Roo.bootstrap.UploadCropbox} this
24561          * @param {String} data
24562          */
24563         "crop" : true,
24564         /**
24565          * @event prepare
24566          * Fire when preparing the file data
24567          * @param {Roo.bootstrap.UploadCropbox} this
24568          * @param {Object} file
24569          */
24570         "prepare" : true,
24571         /**
24572          * @event exception
24573          * Fire when get exception
24574          * @param {Roo.bootstrap.UploadCropbox} this
24575          * @param {XMLHttpRequest} xhr
24576          */
24577         "exception" : true,
24578         /**
24579          * @event beforeloadcanvas
24580          * Fire before load the canvas
24581          * @param {Roo.bootstrap.UploadCropbox} this
24582          * @param {String} src
24583          */
24584         "beforeloadcanvas" : true,
24585         /**
24586          * @event trash
24587          * Fire when trash image
24588          * @param {Roo.bootstrap.UploadCropbox} this
24589          */
24590         "trash" : true,
24591         /**
24592          * @event download
24593          * Fire when download the image
24594          * @param {Roo.bootstrap.UploadCropbox} this
24595          */
24596         "download" : true,
24597         /**
24598          * @event footerbuttonclick
24599          * Fire when footerbuttonclick
24600          * @param {Roo.bootstrap.UploadCropbox} this
24601          * @param {String} type
24602          */
24603         "footerbuttonclick" : true,
24604         /**
24605          * @event resize
24606          * Fire when resize
24607          * @param {Roo.bootstrap.UploadCropbox} this
24608          */
24609         "resize" : true,
24610         /**
24611          * @event rotate
24612          * Fire when rotate the image
24613          * @param {Roo.bootstrap.UploadCropbox} this
24614          * @param {String} pos
24615          */
24616         "rotate" : true,
24617         /**
24618          * @event inspect
24619          * Fire when inspect the file
24620          * @param {Roo.bootstrap.UploadCropbox} this
24621          * @param {Object} file
24622          */
24623         "inspect" : true,
24624         /**
24625          * @event upload
24626          * Fire when xhr upload the file
24627          * @param {Roo.bootstrap.UploadCropbox} this
24628          * @param {Object} data
24629          */
24630         "upload" : true,
24631         /**
24632          * @event arrange
24633          * Fire when arrange the file data
24634          * @param {Roo.bootstrap.UploadCropbox} this
24635          * @param {Object} formData
24636          */
24637         "arrange" : true
24638     });
24639     
24640     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24641 };
24642
24643 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
24644     
24645     emptyText : 'Click to upload image',
24646     rotateNotify : 'Image is too small to rotate',
24647     errorTimeout : 3000,
24648     scale : 0,
24649     baseScale : 1,
24650     rotate : 0,
24651     dragable : false,
24652     pinching : false,
24653     mouseX : 0,
24654     mouseY : 0,
24655     cropData : false,
24656     minWidth : 300,
24657     minHeight : 300,
24658     file : false,
24659     exif : {},
24660     baseRotate : 1,
24661     cropType : 'image/jpeg',
24662     buttons : false,
24663     canvasLoaded : false,
24664     isDocument : false,
24665     method : 'POST',
24666     paramName : 'imageUpload',
24667     loadMask : true,
24668     loadingText : 'Loading...',
24669     maskEl : false,
24670     
24671     getAutoCreate : function()
24672     {
24673         var cfg = {
24674             tag : 'div',
24675             cls : 'roo-upload-cropbox',
24676             cn : [
24677                 {
24678                     tag : 'input',
24679                     cls : 'roo-upload-cropbox-selector',
24680                     type : 'file'
24681                 },
24682                 {
24683                     tag : 'div',
24684                     cls : 'roo-upload-cropbox-body',
24685                     style : 'cursor:pointer',
24686                     cn : [
24687                         {
24688                             tag : 'div',
24689                             cls : 'roo-upload-cropbox-preview'
24690                         },
24691                         {
24692                             tag : 'div',
24693                             cls : 'roo-upload-cropbox-thumb'
24694                         },
24695                         {
24696                             tag : 'div',
24697                             cls : 'roo-upload-cropbox-empty-notify',
24698                             html : this.emptyText
24699                         },
24700                         {
24701                             tag : 'div',
24702                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
24703                             html : this.rotateNotify
24704                         }
24705                     ]
24706                 },
24707                 {
24708                     tag : 'div',
24709                     cls : 'roo-upload-cropbox-footer',
24710                     cn : {
24711                         tag : 'div',
24712                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24713                         cn : []
24714                     }
24715                 }
24716             ]
24717         };
24718         
24719         return cfg;
24720     },
24721     
24722     onRender : function(ct, position)
24723     {
24724         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
24725         
24726         if (this.buttons.length) {
24727             
24728             Roo.each(this.buttons, function(bb) {
24729                 
24730                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
24731                 
24732                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
24733                 
24734             }, this);
24735         }
24736         
24737         if(this.loadMask){
24738             this.maskEl = this.el;
24739         }
24740     },
24741     
24742     initEvents : function()
24743     {
24744         this.urlAPI = (window.createObjectURL && window) || 
24745                                 (window.URL && URL.revokeObjectURL && URL) || 
24746                                 (window.webkitURL && webkitURL);
24747                         
24748         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
24749         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24750         
24751         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
24752         this.selectorEl.hide();
24753         
24754         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
24755         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24756         
24757         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
24758         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24759         this.thumbEl.hide();
24760         
24761         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
24762         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24763         
24764         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
24765         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24766         this.errorEl.hide();
24767         
24768         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
24769         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24770         this.footerEl.hide();
24771         
24772         this.setThumbBoxSize();
24773         
24774         this.bind();
24775         
24776         this.resize();
24777         
24778         this.fireEvent('initial', this);
24779     },
24780
24781     bind : function()
24782     {
24783         var _this = this;
24784         
24785         window.addEventListener("resize", function() { _this.resize(); } );
24786         
24787         this.bodyEl.on('click', this.beforeSelectFile, this);
24788         
24789         if(Roo.isTouch){
24790             this.bodyEl.on('touchstart', this.onTouchStart, this);
24791             this.bodyEl.on('touchmove', this.onTouchMove, this);
24792             this.bodyEl.on('touchend', this.onTouchEnd, this);
24793         }
24794         
24795         if(!Roo.isTouch){
24796             this.bodyEl.on('mousedown', this.onMouseDown, this);
24797             this.bodyEl.on('mousemove', this.onMouseMove, this);
24798             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
24799             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
24800             Roo.get(document).on('mouseup', this.onMouseUp, this);
24801         }
24802         
24803         this.selectorEl.on('change', this.onFileSelected, this);
24804     },
24805     
24806     reset : function()
24807     {    
24808         this.scale = 0;
24809         this.baseScale = 1;
24810         this.rotate = 0;
24811         this.baseRotate = 1;
24812         this.dragable = false;
24813         this.pinching = false;
24814         this.mouseX = 0;
24815         this.mouseY = 0;
24816         this.cropData = false;
24817         this.notifyEl.dom.innerHTML = this.emptyText;
24818         
24819         this.selectorEl.dom.value = '';
24820         
24821     },
24822     
24823     resize : function()
24824     {
24825         if(this.fireEvent('resize', this) != false){
24826             this.setThumbBoxPosition();
24827             this.setCanvasPosition();
24828         }
24829     },
24830     
24831     onFooterButtonClick : function(e, el, o, type)
24832     {
24833         switch (type) {
24834             case 'rotate-left' :
24835                 this.onRotateLeft(e);
24836                 break;
24837             case 'rotate-right' :
24838                 this.onRotateRight(e);
24839                 break;
24840             case 'picture' :
24841                 this.beforeSelectFile(e);
24842                 break;
24843             case 'trash' :
24844                 this.trash(e);
24845                 break;
24846             case 'crop' :
24847                 this.crop(e);
24848                 break;
24849             case 'download' :
24850                 this.download(e);
24851                 break;
24852             default :
24853                 break;
24854         }
24855         
24856         this.fireEvent('footerbuttonclick', this, type);
24857     },
24858     
24859     beforeSelectFile : function(e)
24860     {
24861         e.preventDefault();
24862         
24863         if(this.fireEvent('beforeselectfile', this) != false){
24864             this.selectorEl.dom.click();
24865         }
24866     },
24867     
24868     onFileSelected : function(e)
24869     {
24870         e.preventDefault();
24871         
24872         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
24873             return;
24874         }
24875         
24876         var file = this.selectorEl.dom.files[0];
24877         
24878         if(this.fireEvent('inspect', this, file) != false){
24879             this.prepare(file);
24880         }
24881         
24882     },
24883     
24884     trash : function(e)
24885     {
24886         this.fireEvent('trash', this);
24887     },
24888     
24889     download : function(e)
24890     {
24891         this.fireEvent('download', this);
24892     },
24893     
24894     loadCanvas : function(src)
24895     {   
24896         if(this.fireEvent('beforeloadcanvas', this, src) != false){
24897             
24898             this.reset();
24899             
24900             this.imageEl = document.createElement('img');
24901             
24902             var _this = this;
24903             
24904             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
24905             
24906             this.imageEl.src = src;
24907         }
24908     },
24909     
24910     onLoadCanvas : function()
24911     {   
24912         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
24913         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
24914         
24915         this.bodyEl.un('click', this.beforeSelectFile, this);
24916         
24917         this.notifyEl.hide();
24918         this.thumbEl.show();
24919         this.footerEl.show();
24920         
24921         this.baseRotateLevel();
24922         
24923         if(this.isDocument){
24924             this.setThumbBoxSize();
24925         }
24926         
24927         this.setThumbBoxPosition();
24928         
24929         this.baseScaleLevel();
24930         
24931         this.draw();
24932         
24933         this.resize();
24934         
24935         this.canvasLoaded = true;
24936         
24937         if(this.loadMask){
24938             this.maskEl.unmask();
24939         }
24940         
24941     },
24942     
24943     setCanvasPosition : function()
24944     {   
24945         if(!this.canvasEl){
24946             return;
24947         }
24948         
24949         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
24950         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
24951         
24952         this.previewEl.setLeft(pw);
24953         this.previewEl.setTop(ph);
24954         
24955     },
24956     
24957     onMouseDown : function(e)
24958     {   
24959         e.stopEvent();
24960         
24961         this.dragable = true;
24962         this.pinching = false;
24963         
24964         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
24965             this.dragable = false;
24966             return;
24967         }
24968         
24969         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24970         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24971         
24972     },
24973     
24974     onMouseMove : function(e)
24975     {   
24976         e.stopEvent();
24977         
24978         if(!this.canvasLoaded){
24979             return;
24980         }
24981         
24982         if (!this.dragable){
24983             return;
24984         }
24985         
24986         var minX = Math.ceil(this.thumbEl.getLeft(true));
24987         var minY = Math.ceil(this.thumbEl.getTop(true));
24988         
24989         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
24990         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
24991         
24992         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24993         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24994         
24995         x = x - this.mouseX;
24996         y = y - this.mouseY;
24997         
24998         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
24999         var bgY = Math.ceil(y + this.previewEl.getTop(true));
25000         
25001         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
25002         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
25003         
25004         this.previewEl.setLeft(bgX);
25005         this.previewEl.setTop(bgY);
25006         
25007         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25008         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25009     },
25010     
25011     onMouseUp : function(e)
25012     {   
25013         e.stopEvent();
25014         
25015         this.dragable = false;
25016     },
25017     
25018     onMouseWheel : function(e)
25019     {   
25020         e.stopEvent();
25021         
25022         this.startScale = this.scale;
25023         
25024         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
25025         
25026         if(!this.zoomable()){
25027             this.scale = this.startScale;
25028             return;
25029         }
25030         
25031         this.draw();
25032         
25033         return;
25034     },
25035     
25036     zoomable : function()
25037     {
25038         var minScale = this.thumbEl.getWidth() / this.minWidth;
25039         
25040         if(this.minWidth < this.minHeight){
25041             minScale = this.thumbEl.getHeight() / this.minHeight;
25042         }
25043         
25044         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
25045         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
25046         
25047         if(
25048                 this.isDocument &&
25049                 (this.rotate == 0 || this.rotate == 180) && 
25050                 (
25051                     width > this.imageEl.OriginWidth || 
25052                     height > this.imageEl.OriginHeight ||
25053                     (width < this.minWidth && height < this.minHeight)
25054                 )
25055         ){
25056             return false;
25057         }
25058         
25059         if(
25060                 this.isDocument &&
25061                 (this.rotate == 90 || this.rotate == 270) && 
25062                 (
25063                     width > this.imageEl.OriginWidth || 
25064                     height > this.imageEl.OriginHeight ||
25065                     (width < this.minHeight && height < this.minWidth)
25066                 )
25067         ){
25068             return false;
25069         }
25070         
25071         if(
25072                 !this.isDocument &&
25073                 (this.rotate == 0 || this.rotate == 180) && 
25074                 (
25075                     width < this.minWidth || 
25076                     width > this.imageEl.OriginWidth || 
25077                     height < this.minHeight || 
25078                     height > this.imageEl.OriginHeight
25079                 )
25080         ){
25081             return false;
25082         }
25083         
25084         if(
25085                 !this.isDocument &&
25086                 (this.rotate == 90 || this.rotate == 270) && 
25087                 (
25088                     width < this.minHeight || 
25089                     width > this.imageEl.OriginWidth || 
25090                     height < this.minWidth || 
25091                     height > this.imageEl.OriginHeight
25092                 )
25093         ){
25094             return false;
25095         }
25096         
25097         return true;
25098         
25099     },
25100     
25101     onRotateLeft : function(e)
25102     {   
25103         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25104             
25105             var minScale = this.thumbEl.getWidth() / this.minWidth;
25106             
25107             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25108             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25109             
25110             this.startScale = this.scale;
25111             
25112             while (this.getScaleLevel() < minScale){
25113             
25114                 this.scale = this.scale + 1;
25115                 
25116                 if(!this.zoomable()){
25117                     break;
25118                 }
25119                 
25120                 if(
25121                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25122                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25123                 ){
25124                     continue;
25125                 }
25126                 
25127                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25128
25129                 this.draw();
25130                 
25131                 return;
25132             }
25133             
25134             this.scale = this.startScale;
25135             
25136             this.onRotateFail();
25137             
25138             return false;
25139         }
25140         
25141         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25142
25143         if(this.isDocument){
25144             this.setThumbBoxSize();
25145             this.setThumbBoxPosition();
25146             this.setCanvasPosition();
25147         }
25148         
25149         this.draw();
25150         
25151         this.fireEvent('rotate', this, 'left');
25152         
25153     },
25154     
25155     onRotateRight : function(e)
25156     {
25157         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25158             
25159             var minScale = this.thumbEl.getWidth() / this.minWidth;
25160         
25161             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25162             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25163             
25164             this.startScale = this.scale;
25165             
25166             while (this.getScaleLevel() < minScale){
25167             
25168                 this.scale = this.scale + 1;
25169                 
25170                 if(!this.zoomable()){
25171                     break;
25172                 }
25173                 
25174                 if(
25175                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25176                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25177                 ){
25178                     continue;
25179                 }
25180                 
25181                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25182
25183                 this.draw();
25184                 
25185                 return;
25186             }
25187             
25188             this.scale = this.startScale;
25189             
25190             this.onRotateFail();
25191             
25192             return false;
25193         }
25194         
25195         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25196
25197         if(this.isDocument){
25198             this.setThumbBoxSize();
25199             this.setThumbBoxPosition();
25200             this.setCanvasPosition();
25201         }
25202         
25203         this.draw();
25204         
25205         this.fireEvent('rotate', this, 'right');
25206     },
25207     
25208     onRotateFail : function()
25209     {
25210         this.errorEl.show(true);
25211         
25212         var _this = this;
25213         
25214         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
25215     },
25216     
25217     draw : function()
25218     {
25219         this.previewEl.dom.innerHTML = '';
25220         
25221         var canvasEl = document.createElement("canvas");
25222         
25223         var contextEl = canvasEl.getContext("2d");
25224         
25225         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25226         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25227         var center = this.imageEl.OriginWidth / 2;
25228         
25229         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
25230             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25231             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25232             center = this.imageEl.OriginHeight / 2;
25233         }
25234         
25235         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
25236         
25237         contextEl.translate(center, center);
25238         contextEl.rotate(this.rotate * Math.PI / 180);
25239
25240         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25241         
25242         this.canvasEl = document.createElement("canvas");
25243         
25244         this.contextEl = this.canvasEl.getContext("2d");
25245         
25246         switch (this.rotate) {
25247             case 0 :
25248                 
25249                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25250                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25251                 
25252                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25253                 
25254                 break;
25255             case 90 : 
25256                 
25257                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25258                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25259                 
25260                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25261                     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);
25262                     break;
25263                 }
25264                 
25265                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25266                 
25267                 break;
25268             case 180 :
25269                 
25270                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25271                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25272                 
25273                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25274                     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);
25275                     break;
25276                 }
25277                 
25278                 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);
25279                 
25280                 break;
25281             case 270 :
25282                 
25283                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25284                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25285         
25286                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25287                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25288                     break;
25289                 }
25290                 
25291                 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);
25292                 
25293                 break;
25294             default : 
25295                 break;
25296         }
25297         
25298         this.previewEl.appendChild(this.canvasEl);
25299         
25300         this.setCanvasPosition();
25301     },
25302     
25303     crop : function()
25304     {
25305         if(!this.canvasLoaded){
25306             return;
25307         }
25308         
25309         var imageCanvas = document.createElement("canvas");
25310         
25311         var imageContext = imageCanvas.getContext("2d");
25312         
25313         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25314         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25315         
25316         var center = imageCanvas.width / 2;
25317         
25318         imageContext.translate(center, center);
25319         
25320         imageContext.rotate(this.rotate * Math.PI / 180);
25321         
25322         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25323         
25324         var canvas = document.createElement("canvas");
25325         
25326         var context = canvas.getContext("2d");
25327                 
25328         canvas.width = this.minWidth;
25329         canvas.height = this.minHeight;
25330
25331         switch (this.rotate) {
25332             case 0 :
25333                 
25334                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25335                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25336                 
25337                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25338                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25339                 
25340                 var targetWidth = this.minWidth - 2 * x;
25341                 var targetHeight = this.minHeight - 2 * y;
25342                 
25343                 var scale = 1;
25344                 
25345                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25346                     scale = targetWidth / width;
25347                 }
25348                 
25349                 if(x > 0 && y == 0){
25350                     scale = targetHeight / height;
25351                 }
25352                 
25353                 if(x > 0 && y > 0){
25354                     scale = targetWidth / width;
25355                     
25356                     if(width < height){
25357                         scale = targetHeight / height;
25358                     }
25359                 }
25360                 
25361                 context.scale(scale, scale);
25362                 
25363                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25364                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25365
25366                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25367                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25368
25369                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25370                 
25371                 break;
25372             case 90 : 
25373                 
25374                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25375                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25376                 
25377                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25378                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25379                 
25380                 var targetWidth = this.minWidth - 2 * x;
25381                 var targetHeight = this.minHeight - 2 * y;
25382                 
25383                 var scale = 1;
25384                 
25385                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25386                     scale = targetWidth / width;
25387                 }
25388                 
25389                 if(x > 0 && y == 0){
25390                     scale = targetHeight / height;
25391                 }
25392                 
25393                 if(x > 0 && y > 0){
25394                     scale = targetWidth / width;
25395                     
25396                     if(width < height){
25397                         scale = targetHeight / height;
25398                     }
25399                 }
25400                 
25401                 context.scale(scale, scale);
25402                 
25403                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25404                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25405
25406                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25407                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25408                 
25409                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25410                 
25411                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25412                 
25413                 break;
25414             case 180 :
25415                 
25416                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25417                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25418                 
25419                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25420                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25421                 
25422                 var targetWidth = this.minWidth - 2 * x;
25423                 var targetHeight = this.minHeight - 2 * y;
25424                 
25425                 var scale = 1;
25426                 
25427                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25428                     scale = targetWidth / width;
25429                 }
25430                 
25431                 if(x > 0 && y == 0){
25432                     scale = targetHeight / height;
25433                 }
25434                 
25435                 if(x > 0 && y > 0){
25436                     scale = targetWidth / width;
25437                     
25438                     if(width < height){
25439                         scale = targetHeight / height;
25440                     }
25441                 }
25442                 
25443                 context.scale(scale, scale);
25444                 
25445                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25446                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25447
25448                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25449                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25450
25451                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25452                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25453                 
25454                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25455                 
25456                 break;
25457             case 270 :
25458                 
25459                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25460                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25461                 
25462                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25463                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25464                 
25465                 var targetWidth = this.minWidth - 2 * x;
25466                 var targetHeight = this.minHeight - 2 * y;
25467                 
25468                 var scale = 1;
25469                 
25470                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25471                     scale = targetWidth / width;
25472                 }
25473                 
25474                 if(x > 0 && y == 0){
25475                     scale = targetHeight / height;
25476                 }
25477                 
25478                 if(x > 0 && y > 0){
25479                     scale = targetWidth / width;
25480                     
25481                     if(width < height){
25482                         scale = targetHeight / height;
25483                     }
25484                 }
25485                 
25486                 context.scale(scale, scale);
25487                 
25488                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25489                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25490
25491                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25492                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25493                 
25494                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25495                 
25496                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25497                 
25498                 break;
25499             default : 
25500                 break;
25501         }
25502         
25503         this.cropData = canvas.toDataURL(this.cropType);
25504         
25505         if(this.fireEvent('crop', this, this.cropData) !== false){
25506             this.process(this.file, this.cropData);
25507         }
25508         
25509         return;
25510         
25511     },
25512     
25513     setThumbBoxSize : function()
25514     {
25515         var width, height;
25516         
25517         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
25518             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
25519             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
25520             
25521             this.minWidth = width;
25522             this.minHeight = height;
25523             
25524             if(this.rotate == 90 || this.rotate == 270){
25525                 this.minWidth = height;
25526                 this.minHeight = width;
25527             }
25528         }
25529         
25530         height = 300;
25531         width = Math.ceil(this.minWidth * height / this.minHeight);
25532         
25533         if(this.minWidth > this.minHeight){
25534             width = 300;
25535             height = Math.ceil(this.minHeight * width / this.minWidth);
25536         }
25537         
25538         this.thumbEl.setStyle({
25539             width : width + 'px',
25540             height : height + 'px'
25541         });
25542
25543         return;
25544             
25545     },
25546     
25547     setThumbBoxPosition : function()
25548     {
25549         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
25550         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
25551         
25552         this.thumbEl.setLeft(x);
25553         this.thumbEl.setTop(y);
25554         
25555     },
25556     
25557     baseRotateLevel : function()
25558     {
25559         this.baseRotate = 1;
25560         
25561         if(
25562                 typeof(this.exif) != 'undefined' &&
25563                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
25564                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
25565         ){
25566             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
25567         }
25568         
25569         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
25570         
25571     },
25572     
25573     baseScaleLevel : function()
25574     {
25575         var width, height;
25576         
25577         if(this.isDocument){
25578             
25579             if(this.baseRotate == 6 || this.baseRotate == 8){
25580             
25581                 height = this.thumbEl.getHeight();
25582                 this.baseScale = height / this.imageEl.OriginWidth;
25583
25584                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
25585                     width = this.thumbEl.getWidth();
25586                     this.baseScale = width / this.imageEl.OriginHeight;
25587                 }
25588
25589                 return;
25590             }
25591
25592             height = this.thumbEl.getHeight();
25593             this.baseScale = height / this.imageEl.OriginHeight;
25594
25595             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
25596                 width = this.thumbEl.getWidth();
25597                 this.baseScale = width / this.imageEl.OriginWidth;
25598             }
25599
25600             return;
25601         }
25602         
25603         if(this.baseRotate == 6 || this.baseRotate == 8){
25604             
25605             width = this.thumbEl.getHeight();
25606             this.baseScale = width / this.imageEl.OriginHeight;
25607             
25608             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
25609                 height = this.thumbEl.getWidth();
25610                 this.baseScale = height / this.imageEl.OriginHeight;
25611             }
25612             
25613             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25614                 height = this.thumbEl.getWidth();
25615                 this.baseScale = height / this.imageEl.OriginHeight;
25616                 
25617                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
25618                     width = this.thumbEl.getHeight();
25619                     this.baseScale = width / this.imageEl.OriginWidth;
25620                 }
25621             }
25622             
25623             return;
25624         }
25625         
25626         width = this.thumbEl.getWidth();
25627         this.baseScale = width / this.imageEl.OriginWidth;
25628         
25629         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
25630             height = this.thumbEl.getHeight();
25631             this.baseScale = height / this.imageEl.OriginHeight;
25632         }
25633         
25634         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25635             
25636             height = this.thumbEl.getHeight();
25637             this.baseScale = height / this.imageEl.OriginHeight;
25638             
25639             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
25640                 width = this.thumbEl.getWidth();
25641                 this.baseScale = width / this.imageEl.OriginWidth;
25642             }
25643             
25644         }
25645         
25646         return;
25647     },
25648     
25649     getScaleLevel : function()
25650     {
25651         return this.baseScale * Math.pow(1.1, this.scale);
25652     },
25653     
25654     onTouchStart : function(e)
25655     {
25656         if(!this.canvasLoaded){
25657             this.beforeSelectFile(e);
25658             return;
25659         }
25660         
25661         var touches = e.browserEvent.touches;
25662         
25663         if(!touches){
25664             return;
25665         }
25666         
25667         if(touches.length == 1){
25668             this.onMouseDown(e);
25669             return;
25670         }
25671         
25672         if(touches.length != 2){
25673             return;
25674         }
25675         
25676         var coords = [];
25677         
25678         for(var i = 0, finger; finger = touches[i]; i++){
25679             coords.push(finger.pageX, finger.pageY);
25680         }
25681         
25682         var x = Math.pow(coords[0] - coords[2], 2);
25683         var y = Math.pow(coords[1] - coords[3], 2);
25684         
25685         this.startDistance = Math.sqrt(x + y);
25686         
25687         this.startScale = this.scale;
25688         
25689         this.pinching = true;
25690         this.dragable = false;
25691         
25692     },
25693     
25694     onTouchMove : function(e)
25695     {
25696         if(!this.pinching && !this.dragable){
25697             return;
25698         }
25699         
25700         var touches = e.browserEvent.touches;
25701         
25702         if(!touches){
25703             return;
25704         }
25705         
25706         if(this.dragable){
25707             this.onMouseMove(e);
25708             return;
25709         }
25710         
25711         var coords = [];
25712         
25713         for(var i = 0, finger; finger = touches[i]; i++){
25714             coords.push(finger.pageX, finger.pageY);
25715         }
25716         
25717         var x = Math.pow(coords[0] - coords[2], 2);
25718         var y = Math.pow(coords[1] - coords[3], 2);
25719         
25720         this.endDistance = Math.sqrt(x + y);
25721         
25722         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
25723         
25724         if(!this.zoomable()){
25725             this.scale = this.startScale;
25726             return;
25727         }
25728         
25729         this.draw();
25730         
25731     },
25732     
25733     onTouchEnd : function(e)
25734     {
25735         this.pinching = false;
25736         this.dragable = false;
25737         
25738     },
25739     
25740     process : function(file, crop)
25741     {
25742         if(this.loadMask){
25743             this.maskEl.mask(this.loadingText);
25744         }
25745         
25746         this.xhr = new XMLHttpRequest();
25747         
25748         file.xhr = this.xhr;
25749
25750         this.xhr.open(this.method, this.url, true);
25751         
25752         var headers = {
25753             "Accept": "application/json",
25754             "Cache-Control": "no-cache",
25755             "X-Requested-With": "XMLHttpRequest"
25756         };
25757         
25758         for (var headerName in headers) {
25759             var headerValue = headers[headerName];
25760             if (headerValue) {
25761                 this.xhr.setRequestHeader(headerName, headerValue);
25762             }
25763         }
25764         
25765         var _this = this;
25766         
25767         this.xhr.onload = function()
25768         {
25769             _this.xhrOnLoad(_this.xhr);
25770         }
25771         
25772         this.xhr.onerror = function()
25773         {
25774             _this.xhrOnError(_this.xhr);
25775         }
25776         
25777         var formData = new FormData();
25778
25779         formData.append('returnHTML', 'NO');
25780         
25781         if(crop){
25782             formData.append('crop', crop);
25783         }
25784         
25785         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
25786             formData.append(this.paramName, file, file.name);
25787         }
25788         
25789         if(typeof(file.filename) != 'undefined'){
25790             formData.append('filename', file.filename);
25791         }
25792         
25793         if(typeof(file.mimetype) != 'undefined'){
25794             formData.append('mimetype', file.mimetype);
25795         }
25796         
25797         if(this.fireEvent('arrange', this, formData) != false){
25798             this.xhr.send(formData);
25799         };
25800     },
25801     
25802     xhrOnLoad : function(xhr)
25803     {
25804         if(this.loadMask){
25805             this.maskEl.unmask();
25806         }
25807         
25808         if (xhr.readyState !== 4) {
25809             this.fireEvent('exception', this, xhr);
25810             return;
25811         }
25812
25813         var response = Roo.decode(xhr.responseText);
25814         
25815         if(!response.success){
25816             this.fireEvent('exception', this, xhr);
25817             return;
25818         }
25819         
25820         var response = Roo.decode(xhr.responseText);
25821         
25822         this.fireEvent('upload', this, response);
25823         
25824     },
25825     
25826     xhrOnError : function()
25827     {
25828         if(this.loadMask){
25829             this.maskEl.unmask();
25830         }
25831         
25832         Roo.log('xhr on error');
25833         
25834         var response = Roo.decode(xhr.responseText);
25835           
25836         Roo.log(response);
25837         
25838     },
25839     
25840     prepare : function(file)
25841     {   
25842         if(this.loadMask){
25843             this.maskEl.mask(this.loadingText);
25844         }
25845         
25846         this.file = false;
25847         this.exif = {};
25848         
25849         if(typeof(file) === 'string'){
25850             this.loadCanvas(file);
25851             return;
25852         }
25853         
25854         if(!file || !this.urlAPI){
25855             return;
25856         }
25857         
25858         this.file = file;
25859         this.cropType = file.type;
25860         
25861         var _this = this;
25862         
25863         if(this.fireEvent('prepare', this, this.file) != false){
25864             
25865             var reader = new FileReader();
25866             
25867             reader.onload = function (e) {
25868                 if (e.target.error) {
25869                     Roo.log(e.target.error);
25870                     return;
25871                 }
25872                 
25873                 var buffer = e.target.result,
25874                     dataView = new DataView(buffer),
25875                     offset = 2,
25876                     maxOffset = dataView.byteLength - 4,
25877                     markerBytes,
25878                     markerLength;
25879                 
25880                 if (dataView.getUint16(0) === 0xffd8) {
25881                     while (offset < maxOffset) {
25882                         markerBytes = dataView.getUint16(offset);
25883                         
25884                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
25885                             markerLength = dataView.getUint16(offset + 2) + 2;
25886                             if (offset + markerLength > dataView.byteLength) {
25887                                 Roo.log('Invalid meta data: Invalid segment size.');
25888                                 break;
25889                             }
25890                             
25891                             if(markerBytes == 0xffe1){
25892                                 _this.parseExifData(
25893                                     dataView,
25894                                     offset,
25895                                     markerLength
25896                                 );
25897                             }
25898                             
25899                             offset += markerLength;
25900                             
25901                             continue;
25902                         }
25903                         
25904                         break;
25905                     }
25906                     
25907                 }
25908                 
25909                 var url = _this.urlAPI.createObjectURL(_this.file);
25910                 
25911                 _this.loadCanvas(url);
25912                 
25913                 return;
25914             }
25915             
25916             reader.readAsArrayBuffer(this.file);
25917             
25918         }
25919         
25920     },
25921     
25922     parseExifData : function(dataView, offset, length)
25923     {
25924         var tiffOffset = offset + 10,
25925             littleEndian,
25926             dirOffset;
25927     
25928         if (dataView.getUint32(offset + 4) !== 0x45786966) {
25929             // No Exif data, might be XMP data instead
25930             return;
25931         }
25932         
25933         // Check for the ASCII code for "Exif" (0x45786966):
25934         if (dataView.getUint32(offset + 4) !== 0x45786966) {
25935             // No Exif data, might be XMP data instead
25936             return;
25937         }
25938         if (tiffOffset + 8 > dataView.byteLength) {
25939             Roo.log('Invalid Exif data: Invalid segment size.');
25940             return;
25941         }
25942         // Check for the two null bytes:
25943         if (dataView.getUint16(offset + 8) !== 0x0000) {
25944             Roo.log('Invalid Exif data: Missing byte alignment offset.');
25945             return;
25946         }
25947         // Check the byte alignment:
25948         switch (dataView.getUint16(tiffOffset)) {
25949         case 0x4949:
25950             littleEndian = true;
25951             break;
25952         case 0x4D4D:
25953             littleEndian = false;
25954             break;
25955         default:
25956             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
25957             return;
25958         }
25959         // Check for the TIFF tag marker (0x002A):
25960         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
25961             Roo.log('Invalid Exif data: Missing TIFF marker.');
25962             return;
25963         }
25964         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
25965         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
25966         
25967         this.parseExifTags(
25968             dataView,
25969             tiffOffset,
25970             tiffOffset + dirOffset,
25971             littleEndian
25972         );
25973     },
25974     
25975     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
25976     {
25977         var tagsNumber,
25978             dirEndOffset,
25979             i;
25980         if (dirOffset + 6 > dataView.byteLength) {
25981             Roo.log('Invalid Exif data: Invalid directory offset.');
25982             return;
25983         }
25984         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
25985         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
25986         if (dirEndOffset + 4 > dataView.byteLength) {
25987             Roo.log('Invalid Exif data: Invalid directory size.');
25988             return;
25989         }
25990         for (i = 0; i < tagsNumber; i += 1) {
25991             this.parseExifTag(
25992                 dataView,
25993                 tiffOffset,
25994                 dirOffset + 2 + 12 * i, // tag offset
25995                 littleEndian
25996             );
25997         }
25998         // Return the offset to the next directory:
25999         return dataView.getUint32(dirEndOffset, littleEndian);
26000     },
26001     
26002     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
26003     {
26004         var tag = dataView.getUint16(offset, littleEndian);
26005         
26006         this.exif[tag] = this.getExifValue(
26007             dataView,
26008             tiffOffset,
26009             offset,
26010             dataView.getUint16(offset + 2, littleEndian), // tag type
26011             dataView.getUint32(offset + 4, littleEndian), // tag length
26012             littleEndian
26013         );
26014     },
26015     
26016     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
26017     {
26018         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
26019             tagSize,
26020             dataOffset,
26021             values,
26022             i,
26023             str,
26024             c;
26025     
26026         if (!tagType) {
26027             Roo.log('Invalid Exif data: Invalid tag type.');
26028             return;
26029         }
26030         
26031         tagSize = tagType.size * length;
26032         // Determine if the value is contained in the dataOffset bytes,
26033         // or if the value at the dataOffset is a pointer to the actual data:
26034         dataOffset = tagSize > 4 ?
26035                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
26036         if (dataOffset + tagSize > dataView.byteLength) {
26037             Roo.log('Invalid Exif data: Invalid data offset.');
26038             return;
26039         }
26040         if (length === 1) {
26041             return tagType.getValue(dataView, dataOffset, littleEndian);
26042         }
26043         values = [];
26044         for (i = 0; i < length; i += 1) {
26045             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
26046         }
26047         
26048         if (tagType.ascii) {
26049             str = '';
26050             // Concatenate the chars:
26051             for (i = 0; i < values.length; i += 1) {
26052                 c = values[i];
26053                 // Ignore the terminating NULL byte(s):
26054                 if (c === '\u0000') {
26055                     break;
26056                 }
26057                 str += c;
26058             }
26059             return str;
26060         }
26061         return values;
26062     }
26063     
26064 });
26065
26066 Roo.apply(Roo.bootstrap.UploadCropbox, {
26067     tags : {
26068         'Orientation': 0x0112
26069     },
26070     
26071     Orientation: {
26072             1: 0, //'top-left',
26073 //            2: 'top-right',
26074             3: 180, //'bottom-right',
26075 //            4: 'bottom-left',
26076 //            5: 'left-top',
26077             6: 90, //'right-top',
26078 //            7: 'right-bottom',
26079             8: 270 //'left-bottom'
26080     },
26081     
26082     exifTagTypes : {
26083         // byte, 8-bit unsigned int:
26084         1: {
26085             getValue: function (dataView, dataOffset) {
26086                 return dataView.getUint8(dataOffset);
26087             },
26088             size: 1
26089         },
26090         // ascii, 8-bit byte:
26091         2: {
26092             getValue: function (dataView, dataOffset) {
26093                 return String.fromCharCode(dataView.getUint8(dataOffset));
26094             },
26095             size: 1,
26096             ascii: true
26097         },
26098         // short, 16 bit int:
26099         3: {
26100             getValue: function (dataView, dataOffset, littleEndian) {
26101                 return dataView.getUint16(dataOffset, littleEndian);
26102             },
26103             size: 2
26104         },
26105         // long, 32 bit int:
26106         4: {
26107             getValue: function (dataView, dataOffset, littleEndian) {
26108                 return dataView.getUint32(dataOffset, littleEndian);
26109             },
26110             size: 4
26111         },
26112         // rational = two long values, first is numerator, second is denominator:
26113         5: {
26114             getValue: function (dataView, dataOffset, littleEndian) {
26115                 return dataView.getUint32(dataOffset, littleEndian) /
26116                     dataView.getUint32(dataOffset + 4, littleEndian);
26117             },
26118             size: 8
26119         },
26120         // slong, 32 bit signed int:
26121         9: {
26122             getValue: function (dataView, dataOffset, littleEndian) {
26123                 return dataView.getInt32(dataOffset, littleEndian);
26124             },
26125             size: 4
26126         },
26127         // srational, two slongs, first is numerator, second is denominator:
26128         10: {
26129             getValue: function (dataView, dataOffset, littleEndian) {
26130                 return dataView.getInt32(dataOffset, littleEndian) /
26131                     dataView.getInt32(dataOffset + 4, littleEndian);
26132             },
26133             size: 8
26134         }
26135     },
26136     
26137     footer : {
26138         STANDARD : [
26139             {
26140                 tag : 'div',
26141                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26142                 action : 'rotate-left',
26143                 cn : [
26144                     {
26145                         tag : 'button',
26146                         cls : 'btn btn-default',
26147                         html : '<i class="fa fa-undo"></i>'
26148                     }
26149                 ]
26150             },
26151             {
26152                 tag : 'div',
26153                 cls : 'btn-group roo-upload-cropbox-picture',
26154                 action : 'picture',
26155                 cn : [
26156                     {
26157                         tag : 'button',
26158                         cls : 'btn btn-default',
26159                         html : '<i class="fa fa-picture-o"></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         DOCUMENT : [
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-download',
26192                 action : 'download',
26193                 cn : [
26194                     {
26195                         tag : 'button',
26196                         cls : 'btn btn-default',
26197                         html : '<i class="fa fa-download"></i>'
26198                     }
26199                 ]
26200             },
26201             {
26202                 tag : 'div',
26203                 cls : 'btn-group roo-upload-cropbox-crop',
26204                 action : 'crop',
26205                 cn : [
26206                     {
26207                         tag : 'button',
26208                         cls : 'btn btn-default',
26209                         html : '<i class="fa fa-crop"></i>'
26210                     }
26211                 ]
26212             },
26213             {
26214                 tag : 'div',
26215                 cls : 'btn-group roo-upload-cropbox-trash',
26216                 action : 'trash',
26217                 cn : [
26218                     {
26219                         tag : 'button',
26220                         cls : 'btn btn-default',
26221                         html : '<i class="fa fa-trash"></i>'
26222                     }
26223                 ]
26224             },
26225             {
26226                 tag : 'div',
26227                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26228                 action : 'rotate-right',
26229                 cn : [
26230                     {
26231                         tag : 'button',
26232                         cls : 'btn btn-default',
26233                         html : '<i class="fa fa-repeat"></i>'
26234                     }
26235                 ]
26236             }
26237         ],
26238         ROTATOR : [
26239             {
26240                 tag : 'div',
26241                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26242                 action : 'rotate-left',
26243                 cn : [
26244                     {
26245                         tag : 'button',
26246                         cls : 'btn btn-default',
26247                         html : '<i class="fa fa-undo"></i>'
26248                     }
26249                 ]
26250             },
26251             {
26252                 tag : 'div',
26253                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26254                 action : 'rotate-right',
26255                 cn : [
26256                     {
26257                         tag : 'button',
26258                         cls : 'btn btn-default',
26259                         html : '<i class="fa fa-repeat"></i>'
26260                     }
26261                 ]
26262             }
26263         ]
26264     }
26265 });
26266
26267 /*
26268 * Licence: LGPL
26269 */
26270
26271 /**
26272  * @class Roo.bootstrap.DocumentManager
26273  * @extends Roo.bootstrap.Component
26274  * Bootstrap DocumentManager class
26275  * @cfg {String} paramName default 'imageUpload'
26276  * @cfg {String} method default POST
26277  * @cfg {String} url action url
26278  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
26279  * @cfg {Boolean} multiple multiple upload default true
26280  * @cfg {Number} thumbSize default 300
26281  * @cfg {String} fieldLabel
26282  * @cfg {Number} labelWidth default 4
26283  * @cfg {String} labelAlign (left|top) default left
26284  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
26285  * 
26286  * @constructor
26287  * Create a new DocumentManager
26288  * @param {Object} config The config object
26289  */
26290
26291 Roo.bootstrap.DocumentManager = function(config){
26292     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26293     
26294     this.addEvents({
26295         /**
26296          * @event initial
26297          * Fire when initial the DocumentManager
26298          * @param {Roo.bootstrap.DocumentManager} this
26299          */
26300         "initial" : true,
26301         /**
26302          * @event inspect
26303          * inspect selected file
26304          * @param {Roo.bootstrap.DocumentManager} this
26305          * @param {File} file
26306          */
26307         "inspect" : true,
26308         /**
26309          * @event exception
26310          * Fire when xhr load exception
26311          * @param {Roo.bootstrap.DocumentManager} this
26312          * @param {XMLHttpRequest} xhr
26313          */
26314         "exception" : true,
26315         /**
26316          * @event prepare
26317          * prepare the form data
26318          * @param {Roo.bootstrap.DocumentManager} this
26319          * @param {Object} formData
26320          */
26321         "prepare" : true,
26322         /**
26323          * @event remove
26324          * Fire when remove the file
26325          * @param {Roo.bootstrap.DocumentManager} this
26326          * @param {Object} file
26327          */
26328         "remove" : true,
26329         /**
26330          * @event refresh
26331          * Fire after refresh the file
26332          * @param {Roo.bootstrap.DocumentManager} this
26333          */
26334         "refresh" : true,
26335         /**
26336          * @event click
26337          * Fire after click the image
26338          * @param {Roo.bootstrap.DocumentManager} this
26339          * @param {Object} file
26340          */
26341         "click" : true,
26342         /**
26343          * @event edit
26344          * Fire when upload a image and editable set to true
26345          * @param {Roo.bootstrap.DocumentManager} this
26346          * @param {Object} file
26347          */
26348         "edit" : true,
26349         /**
26350          * @event beforeselectfile
26351          * Fire before select file
26352          * @param {Roo.bootstrap.DocumentManager} this
26353          */
26354         "beforeselectfile" : true,
26355         /**
26356          * @event process
26357          * Fire before process file
26358          * @param {Roo.bootstrap.DocumentManager} this
26359          * @param {Object} file
26360          */
26361         "process" : true
26362         
26363     });
26364 };
26365
26366 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
26367     
26368     boxes : 0,
26369     inputName : '',
26370     thumbSize : 300,
26371     multiple : true,
26372     files : [],
26373     method : 'POST',
26374     url : '',
26375     paramName : 'imageUpload',
26376     fieldLabel : '',
26377     labelWidth : 4,
26378     labelAlign : 'left',
26379     editable : true,
26380     delegates : [],
26381     
26382     
26383     xhr : false, 
26384     
26385     getAutoCreate : function()
26386     {   
26387         var managerWidget = {
26388             tag : 'div',
26389             cls : 'roo-document-manager',
26390             cn : [
26391                 {
26392                     tag : 'input',
26393                     cls : 'roo-document-manager-selector',
26394                     type : 'file'
26395                 },
26396                 {
26397                     tag : 'div',
26398                     cls : 'roo-document-manager-uploader',
26399                     cn : [
26400                         {
26401                             tag : 'div',
26402                             cls : 'roo-document-manager-upload-btn',
26403                             html : '<i class="fa fa-plus"></i>'
26404                         }
26405                     ]
26406                     
26407                 }
26408             ]
26409         };
26410         
26411         var content = [
26412             {
26413                 tag : 'div',
26414                 cls : 'column col-md-12',
26415                 cn : managerWidget
26416             }
26417         ];
26418         
26419         if(this.fieldLabel.length){
26420             
26421             content = [
26422                 {
26423                     tag : 'div',
26424                     cls : 'column col-md-12',
26425                     html : this.fieldLabel
26426                 },
26427                 {
26428                     tag : 'div',
26429                     cls : 'column col-md-12',
26430                     cn : managerWidget
26431                 }
26432             ];
26433
26434             if(this.labelAlign == 'left'){
26435                 content = [
26436                     {
26437                         tag : 'div',
26438                         cls : 'column col-md-' + this.labelWidth,
26439                         html : this.fieldLabel
26440                     },
26441                     {
26442                         tag : 'div',
26443                         cls : 'column col-md-' + (12 - this.labelWidth),
26444                         cn : managerWidget
26445                     }
26446                 ];
26447                 
26448             }
26449         }
26450         
26451         var cfg = {
26452             tag : 'div',
26453             cls : 'row clearfix',
26454             cn : content
26455         };
26456         
26457         return cfg;
26458         
26459     },
26460     
26461     initEvents : function()
26462     {
26463         this.managerEl = this.el.select('.roo-document-manager', true).first();
26464         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26465         
26466         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
26467         this.selectorEl.hide();
26468         
26469         if(this.multiple){
26470             this.selectorEl.attr('multiple', 'multiple');
26471         }
26472         
26473         this.selectorEl.on('change', this.onFileSelected, this);
26474         
26475         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
26476         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26477         
26478         this.uploader.on('click', this.onUploaderClick, this);
26479         
26480         this.renderProgressDialog();
26481         
26482         var _this = this;
26483         
26484         window.addEventListener("resize", function() { _this.refresh(); } );
26485         
26486         this.fireEvent('initial', this);
26487     },
26488     
26489     renderProgressDialog : function()
26490     {
26491         var _this = this;
26492         
26493         this.progressDialog = new Roo.bootstrap.Modal({
26494             cls : 'roo-document-manager-progress-dialog',
26495             allow_close : false,
26496             title : '',
26497             buttons : [
26498                 {
26499                     name  :'cancel',
26500                     weight : 'danger',
26501                     html : 'Cancel'
26502                 }
26503             ], 
26504             listeners : { 
26505                 btnclick : function() {
26506                     _this.uploadCancel();
26507                     this.hide();
26508                 }
26509             }
26510         });
26511          
26512         this.progressDialog.render(Roo.get(document.body));
26513          
26514         this.progress = new Roo.bootstrap.Progress({
26515             cls : 'roo-document-manager-progress',
26516             active : true,
26517             striped : true
26518         });
26519         
26520         this.progress.render(this.progressDialog.getChildContainer());
26521         
26522         this.progressBar = new Roo.bootstrap.ProgressBar({
26523             cls : 'roo-document-manager-progress-bar',
26524             aria_valuenow : 0,
26525             aria_valuemin : 0,
26526             aria_valuemax : 12,
26527             panel : 'success'
26528         });
26529         
26530         this.progressBar.render(this.progress.getChildContainer());
26531     },
26532     
26533     onUploaderClick : function(e)
26534     {
26535         e.preventDefault();
26536      
26537         if(this.fireEvent('beforeselectfile', this) != false){
26538             this.selectorEl.dom.click();
26539         }
26540         
26541     },
26542     
26543     onFileSelected : function(e)
26544     {
26545         e.preventDefault();
26546         
26547         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26548             return;
26549         }
26550         
26551         Roo.each(this.selectorEl.dom.files, function(file){
26552             if(this.fireEvent('inspect', this, file) != false){
26553                 this.files.push(file);
26554             }
26555         }, this);
26556         
26557         this.queue();
26558         
26559     },
26560     
26561     queue : function()
26562     {
26563         this.selectorEl.dom.value = '';
26564         
26565         if(!this.files.length){
26566             return;
26567         }
26568         
26569         if(this.boxes > 0 && this.files.length > this.boxes){
26570             this.files = this.files.slice(0, this.boxes);
26571         }
26572         
26573         this.uploader.show();
26574         
26575         if(this.boxes > 0 && this.files.length > this.boxes - 1){
26576             this.uploader.hide();
26577         }
26578         
26579         var _this = this;
26580         
26581         var files = [];
26582         
26583         var docs = [];
26584         
26585         Roo.each(this.files, function(file){
26586             
26587             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26588                 var f = this.renderPreview(file);
26589                 files.push(f);
26590                 return;
26591             }
26592             
26593             if(file.type.indexOf('image') != -1){
26594                 this.delegates.push(
26595                     (function(){
26596                         _this.process(file);
26597                     }).createDelegate(this)
26598                 );
26599         
26600                 return;
26601             }
26602             
26603             docs.push(
26604                 (function(){
26605                     _this.process(file);
26606                 }).createDelegate(this)
26607             );
26608             
26609         }, this);
26610         
26611         this.files = files;
26612         
26613         this.delegates = this.delegates.concat(docs);
26614         
26615         if(!this.delegates.length){
26616             this.refresh();
26617             return;
26618         }
26619         
26620         this.progressBar.aria_valuemax = this.delegates.length;
26621         
26622         this.arrange();
26623         
26624         return;
26625     },
26626     
26627     arrange : function()
26628     {
26629         if(!this.delegates.length){
26630             this.progressDialog.hide();
26631             this.refresh();
26632             return;
26633         }
26634         
26635         var delegate = this.delegates.shift();
26636         
26637         this.progressDialog.show();
26638         
26639         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
26640         
26641         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
26642         
26643         delegate();
26644     },
26645     
26646     refresh : function()
26647     {
26648         this.uploader.show();
26649         
26650         if(this.boxes > 0 && this.files.length > this.boxes - 1){
26651             this.uploader.hide();
26652         }
26653         
26654         Roo.isTouch ? this.closable(false) : this.closable(true);
26655         
26656         this.fireEvent('refresh', this);
26657     },
26658     
26659     onRemove : function(e, el, o)
26660     {
26661         e.preventDefault();
26662         
26663         this.fireEvent('remove', this, o);
26664         
26665     },
26666     
26667     remove : function(o)
26668     {
26669         var files = [];
26670         
26671         Roo.each(this.files, function(file){
26672             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
26673                 files.push(file);
26674                 return;
26675             }
26676
26677             o.target.remove();
26678
26679         }, this);
26680         
26681         this.files = files;
26682         
26683         this.refresh();
26684     },
26685     
26686     clear : function()
26687     {
26688         Roo.each(this.files, function(file){
26689             if(!file.target){
26690                 return;
26691             }
26692             
26693             file.target.remove();
26694
26695         }, this);
26696         
26697         this.files = [];
26698         
26699         this.refresh();
26700     },
26701     
26702     onClick : function(e, el, o)
26703     {
26704         e.preventDefault();
26705         
26706         this.fireEvent('click', this, o);
26707         
26708     },
26709     
26710     closable : function(closable)
26711     {
26712         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
26713             
26714             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26715             
26716             if(closable){
26717                 el.show();
26718                 return;
26719             }
26720             
26721             el.hide();
26722             
26723         }, this);
26724     },
26725     
26726     xhrOnLoad : function(xhr)
26727     {
26728         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26729             el.remove();
26730         }, this);
26731         
26732         if (xhr.readyState !== 4) {
26733             this.arrange();
26734             this.fireEvent('exception', this, xhr);
26735             return;
26736         }
26737
26738         var response = Roo.decode(xhr.responseText);
26739         
26740         if(!response.success){
26741             this.arrange();
26742             this.fireEvent('exception', this, xhr);
26743             return;
26744         }
26745         
26746         var file = this.renderPreview(response.data);
26747         
26748         this.files.push(file);
26749         
26750         this.arrange();
26751         
26752     },
26753     
26754     xhrOnError : function()
26755     {
26756         Roo.log('xhr on error');
26757         
26758         var response = Roo.decode(xhr.responseText);
26759           
26760         Roo.log(response);
26761         
26762         this.arrange();
26763     },
26764     
26765     process : function(file)
26766     {
26767         if(this.fireEvent('process', this, file) !== false){
26768             if(this.editable && file.type.indexOf('image') != -1){
26769                 this.fireEvent('edit', this, file);
26770                 return;
26771             }
26772
26773             this.uploadStart(file, false);
26774
26775             return;
26776         }
26777         
26778     },
26779     
26780     uploadStart : function(file, crop)
26781     {
26782         this.xhr = new XMLHttpRequest();
26783         
26784         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26785             this.arrange();
26786             return;
26787         }
26788         
26789         file.xhr = this.xhr;
26790             
26791         this.managerEl.createChild({
26792             tag : 'div',
26793             cls : 'roo-document-manager-loading',
26794             cn : [
26795                 {
26796                     tag : 'div',
26797                     tooltip : file.name,
26798                     cls : 'roo-document-manager-thumb',
26799                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26800                 }
26801             ]
26802
26803         });
26804
26805         this.xhr.open(this.method, this.url, true);
26806         
26807         var headers = {
26808             "Accept": "application/json",
26809             "Cache-Control": "no-cache",
26810             "X-Requested-With": "XMLHttpRequest"
26811         };
26812         
26813         for (var headerName in headers) {
26814             var headerValue = headers[headerName];
26815             if (headerValue) {
26816                 this.xhr.setRequestHeader(headerName, headerValue);
26817             }
26818         }
26819         
26820         var _this = this;
26821         
26822         this.xhr.onload = function()
26823         {
26824             _this.xhrOnLoad(_this.xhr);
26825         }
26826         
26827         this.xhr.onerror = function()
26828         {
26829             _this.xhrOnError(_this.xhr);
26830         }
26831         
26832         var formData = new FormData();
26833
26834         formData.append('returnHTML', 'NO');
26835         
26836         if(crop){
26837             formData.append('crop', crop);
26838         }
26839         
26840         formData.append(this.paramName, file, file.name);
26841         
26842         if(this.fireEvent('prepare', this, formData) != false){
26843             this.xhr.send(formData);
26844         };
26845     },
26846     
26847     uploadCancel : function()
26848     {
26849         if (this.xhr) {
26850             this.xhr.abort();
26851         }
26852         
26853         
26854         this.delegates = [];
26855         
26856         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26857             el.remove();
26858         }, this);
26859         
26860         this.arrange();
26861     },
26862     
26863     renderPreview : function(file)
26864     {
26865         if(typeof(file.target) != 'undefined' && file.target){
26866             return file;
26867         }
26868         
26869         var previewEl = this.managerEl.createChild({
26870             tag : 'div',
26871             cls : 'roo-document-manager-preview',
26872             cn : [
26873                 {
26874                     tag : 'div',
26875                     tooltip : file.filename,
26876                     cls : 'roo-document-manager-thumb',
26877                     html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
26878                 },
26879                 {
26880                     tag : 'button',
26881                     cls : 'close',
26882                     html : '<i class="fa fa-times-circle"></i>'
26883                 }
26884             ]
26885         });
26886
26887         var close = previewEl.select('button.close', true).first();
26888
26889         close.on('click', this.onRemove, this, file);
26890
26891         file.target = previewEl;
26892
26893         var image = previewEl.select('img', true).first();
26894         
26895         var _this = this;
26896         
26897         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
26898         
26899         image.on('click', this.onClick, this, file);
26900         
26901         return file;
26902         
26903     },
26904     
26905     onPreviewLoad : function(file, image)
26906     {
26907         if(typeof(file.target) == 'undefined' || !file.target){
26908             return;
26909         }
26910         
26911         var width = image.dom.naturalWidth || image.dom.width;
26912         var height = image.dom.naturalHeight || image.dom.height;
26913         
26914         if(width > height){
26915             file.target.addClass('wide');
26916             return;
26917         }
26918         
26919         file.target.addClass('tall');
26920         return;
26921         
26922     },
26923     
26924     uploadFromSource : function(file, crop)
26925     {
26926         this.xhr = new XMLHttpRequest();
26927         
26928         this.managerEl.createChild({
26929             tag : 'div',
26930             cls : 'roo-document-manager-loading',
26931             cn : [
26932                 {
26933                     tag : 'div',
26934                     tooltip : file.name,
26935                     cls : 'roo-document-manager-thumb',
26936                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26937                 }
26938             ]
26939
26940         });
26941
26942         this.xhr.open(this.method, this.url, true);
26943         
26944         var headers = {
26945             "Accept": "application/json",
26946             "Cache-Control": "no-cache",
26947             "X-Requested-With": "XMLHttpRequest"
26948         };
26949         
26950         for (var headerName in headers) {
26951             var headerValue = headers[headerName];
26952             if (headerValue) {
26953                 this.xhr.setRequestHeader(headerName, headerValue);
26954             }
26955         }
26956         
26957         var _this = this;
26958         
26959         this.xhr.onload = function()
26960         {
26961             _this.xhrOnLoad(_this.xhr);
26962         }
26963         
26964         this.xhr.onerror = function()
26965         {
26966             _this.xhrOnError(_this.xhr);
26967         }
26968         
26969         var formData = new FormData();
26970
26971         formData.append('returnHTML', 'NO');
26972         
26973         formData.append('crop', crop);
26974         
26975         if(typeof(file.filename) != 'undefined'){
26976             formData.append('filename', file.filename);
26977         }
26978         
26979         if(typeof(file.mimetype) != 'undefined'){
26980             formData.append('mimetype', file.mimetype);
26981         }
26982         
26983         if(this.fireEvent('prepare', this, formData) != false){
26984             this.xhr.send(formData);
26985         };
26986     }
26987 });
26988
26989 /*
26990 * Licence: LGPL
26991 */
26992
26993 /**
26994  * @class Roo.bootstrap.DocumentViewer
26995  * @extends Roo.bootstrap.Component
26996  * Bootstrap DocumentViewer class
26997  * 
26998  * @constructor
26999  * Create a new DocumentViewer
27000  * @param {Object} config The config object
27001  */
27002
27003 Roo.bootstrap.DocumentViewer = function(config){
27004     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
27005     
27006     this.addEvents({
27007         /**
27008          * @event initial
27009          * Fire after initEvent
27010          * @param {Roo.bootstrap.DocumentViewer} this
27011          */
27012         "initial" : true,
27013         /**
27014          * @event click
27015          * Fire after click
27016          * @param {Roo.bootstrap.DocumentViewer} this
27017          */
27018         "click" : true,
27019         /**
27020          * @event trash
27021          * Fire after trash button
27022          * @param {Roo.bootstrap.DocumentViewer} this
27023          */
27024         "trash" : true
27025         
27026     });
27027 };
27028
27029 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
27030     
27031     getAutoCreate : function()
27032     {
27033         var cfg = {
27034             tag : 'div',
27035             cls : 'roo-document-viewer',
27036             cn : [
27037                 {
27038                     tag : 'div',
27039                     cls : 'roo-document-viewer-body',
27040                     cn : [
27041                         {
27042                             tag : 'div',
27043                             cls : 'roo-document-viewer-thumb',
27044                             cn : [
27045                                 {
27046                                     tag : 'img',
27047                                     cls : 'roo-document-viewer-image'
27048                                 }
27049                             ]
27050                         }
27051                     ]
27052                 },
27053                 {
27054                     tag : 'div',
27055                     cls : 'roo-document-viewer-footer',
27056                     cn : {
27057                         tag : 'div',
27058                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
27059                         cn : [
27060                             {
27061                                 tag : 'div',
27062                                 cls : 'btn-group',
27063                                 cn : [
27064                                     {
27065                                         tag : 'button',
27066                                         cls : 'btn btn-default roo-document-viewer-trash',
27067                                         html : '<i class="fa fa-trash"></i>'
27068                                     }
27069                                 ]
27070                             }
27071                         ]
27072                     }
27073                 }
27074             ]
27075         };
27076         
27077         return cfg;
27078     },
27079     
27080     initEvents : function()
27081     {
27082         
27083         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27084         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27085         
27086         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27087         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27088         
27089         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27090         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27091         
27092         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27093         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27094         
27095         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27096         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27097         
27098         this.bodyEl.on('click', this.onClick, this);
27099         
27100         this.trashBtn.on('click', this.onTrash, this);
27101         
27102     },
27103     
27104     initial : function()
27105     {
27106 //        this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27107         
27108         
27109         this.fireEvent('initial', this);
27110         
27111     },
27112     
27113     onClick : function(e)
27114     {
27115         e.preventDefault();
27116         
27117         this.fireEvent('click', this);
27118     },
27119     
27120     onTrash : function(e)
27121     {
27122         e.preventDefault();
27123         
27124         this.fireEvent('trash', this);
27125     }
27126     
27127 });
27128 /*
27129  * - LGPL
27130  *
27131  * nav progress bar
27132  * 
27133  */
27134
27135 /**
27136  * @class Roo.bootstrap.NavProgressBar
27137  * @extends Roo.bootstrap.Component
27138  * Bootstrap NavProgressBar class
27139  * 
27140  * @constructor
27141  * Create a new nav progress bar
27142  * @param {Object} config The config object
27143  */
27144
27145 Roo.bootstrap.NavProgressBar = function(config){
27146     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
27147
27148     this.bullets = this.bullets || [];
27149    
27150 //    Roo.bootstrap.NavProgressBar.register(this);
27151      this.addEvents({
27152         /**
27153              * @event changed
27154              * Fires when the active item changes
27155              * @param {Roo.bootstrap.NavProgressBar} this
27156              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
27157              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
27158          */
27159         'changed': true
27160      });
27161     
27162 };
27163
27164 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
27165     
27166     bullets : [],
27167     barItems : [],
27168     
27169     getAutoCreate : function()
27170     {
27171         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
27172         
27173         cfg = {
27174             tag : 'div',
27175             cls : 'roo-navigation-bar-group',
27176             cn : [
27177                 {
27178                     tag : 'div',
27179                     cls : 'roo-navigation-top-bar'
27180                 },
27181                 {
27182                     tag : 'div',
27183                     cls : 'roo-navigation-bullets-bar',
27184                     cn : [
27185                         {
27186                             tag : 'ul',
27187                             cls : 'roo-navigation-bar'
27188                         }
27189                     ]
27190                 },
27191                 
27192                 {
27193                     tag : 'div',
27194                     cls : 'roo-navigation-bottom-bar'
27195                 }
27196             ]
27197             
27198         };
27199         
27200         return cfg;
27201         
27202     },
27203     
27204     initEvents: function() 
27205     {
27206         
27207     },
27208     
27209     onRender : function(ct, position) 
27210     {
27211         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27212         
27213         if(this.bullets.length){
27214             Roo.each(this.bullets, function(b){
27215                this.addItem(b);
27216             }, this);
27217         }
27218         
27219         this.format();
27220         
27221     },
27222     
27223     addItem : function(cfg)
27224     {
27225         var item = new Roo.bootstrap.NavProgressItem(cfg);
27226         
27227         item.parentId = this.id;
27228         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
27229         
27230         if(cfg.html){
27231             var top = new Roo.bootstrap.Element({
27232                 tag : 'div',
27233                 cls : 'roo-navigation-bar-text'
27234             });
27235             
27236             var bottom = new Roo.bootstrap.Element({
27237                 tag : 'div',
27238                 cls : 'roo-navigation-bar-text'
27239             });
27240             
27241             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
27242             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
27243             
27244             var topText = new Roo.bootstrap.Element({
27245                 tag : 'span',
27246                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
27247             });
27248             
27249             var bottomText = new Roo.bootstrap.Element({
27250                 tag : 'span',
27251                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
27252             });
27253             
27254             topText.onRender(top.el, null);
27255             bottomText.onRender(bottom.el, null);
27256             
27257             item.topEl = top;
27258             item.bottomEl = bottom;
27259         }
27260         
27261         this.barItems.push(item);
27262         
27263         return item;
27264     },
27265     
27266     getActive : function()
27267     {
27268         var active = false;
27269         
27270         Roo.each(this.barItems, function(v){
27271             
27272             if (!v.isActive()) {
27273                 return;
27274             }
27275             
27276             active = v;
27277             return false;
27278             
27279         });
27280         
27281         return active;
27282     },
27283     
27284     setActiveItem : function(item)
27285     {
27286         var prev = false;
27287         
27288         Roo.each(this.barItems, function(v){
27289             if (v.rid == item.rid) {
27290                 return ;
27291             }
27292             
27293             if (v.isActive()) {
27294                 v.setActive(false);
27295                 prev = v;
27296             }
27297         });
27298
27299         item.setActive(true);
27300         
27301         this.fireEvent('changed', this, item, prev);
27302     },
27303     
27304     getBarItem: function(rid)
27305     {
27306         var ret = false;
27307         
27308         Roo.each(this.barItems, function(e) {
27309             if (e.rid != rid) {
27310                 return;
27311             }
27312             
27313             ret =  e;
27314             return false;
27315         });
27316         
27317         return ret;
27318     },
27319     
27320     indexOfItem : function(item)
27321     {
27322         var index = false;
27323         
27324         Roo.each(this.barItems, function(v, i){
27325             
27326             if (v.rid != item.rid) {
27327                 return;
27328             }
27329             
27330             index = i;
27331             return false
27332         });
27333         
27334         return index;
27335     },
27336     
27337     setActiveNext : function()
27338     {
27339         var i = this.indexOfItem(this.getActive());
27340         
27341         if (i > this.barItems.length) {
27342             return;
27343         }
27344         
27345         this.setActiveItem(this.barItems[i+1]);
27346     },
27347     
27348     setActivePrev : function()
27349     {
27350         var i = this.indexOfItem(this.getActive());
27351         
27352         if (i  < 1) {
27353             return;
27354         }
27355         
27356         this.setActiveItem(this.barItems[i-1]);
27357     },
27358     
27359     format : function()
27360     {
27361         if(!this.barItems.length){
27362             return;
27363         }
27364      
27365         var width = 100 / this.barItems.length;
27366         
27367         Roo.each(this.barItems, function(i){
27368             i.el.setStyle('width', width + '%');
27369             i.topEl.el.setStyle('width', width + '%');
27370             i.bottomEl.el.setStyle('width', width + '%');
27371         }, this);
27372         
27373     }
27374     
27375 });
27376 /*
27377  * - LGPL
27378  *
27379  * Nav Progress Item
27380  * 
27381  */
27382
27383 /**
27384  * @class Roo.bootstrap.NavProgressItem
27385  * @extends Roo.bootstrap.Component
27386  * Bootstrap NavProgressItem class
27387  * @cfg {String} rid the reference id
27388  * @cfg {Boolean} active (true|false) Is item active default false
27389  * @cfg {Boolean} disabled (true|false) Is item active default false
27390  * @cfg {String} html
27391  * @cfg {String} position (top|bottom) text position default bottom
27392  * @cfg {String} icon show icon instead of number
27393  * 
27394  * @constructor
27395  * Create a new NavProgressItem
27396  * @param {Object} config The config object
27397  */
27398 Roo.bootstrap.NavProgressItem = function(config){
27399     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
27400     this.addEvents({
27401         // raw events
27402         /**
27403          * @event click
27404          * The raw click event for the entire grid.
27405          * @param {Roo.bootstrap.NavProgressItem} this
27406          * @param {Roo.EventObject} e
27407          */
27408         "click" : true
27409     });
27410    
27411 };
27412
27413 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
27414     
27415     rid : '',
27416     active : false,
27417     disabled : false,
27418     html : '',
27419     position : 'bottom',
27420     icon : false,
27421     
27422     getAutoCreate : function()
27423     {
27424         var iconCls = 'roo-navigation-bar-item-icon';
27425         
27426         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
27427         
27428         var cfg = {
27429             tag: 'li',
27430             cls: 'roo-navigation-bar-item',
27431             cn : [
27432                 {
27433                     tag : 'i',
27434                     cls : iconCls
27435                 }
27436             ]
27437         };
27438         
27439         if(this.active){
27440             cfg.cls += ' active';
27441         }
27442         if(this.disabled){
27443             cfg.cls += ' disabled';
27444         }
27445         
27446         return cfg;
27447     },
27448     
27449     disable : function()
27450     {
27451         this.setDisabled(true);
27452     },
27453     
27454     enable : function()
27455     {
27456         this.setDisabled(false);
27457     },
27458     
27459     initEvents: function() 
27460     {
27461         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
27462         
27463         this.iconEl.on('click', this.onClick, this);
27464     },
27465     
27466     onClick : function(e)
27467     {
27468         e.preventDefault();
27469         
27470         if(this.disabled){
27471             return;
27472         }
27473         
27474         if(this.fireEvent('click', this, e) === false){
27475             return;
27476         };
27477         
27478         this.parent().setActiveItem(this);
27479     },
27480     
27481     isActive: function () 
27482     {
27483         return this.active;
27484     },
27485     
27486     setActive : function(state)
27487     {
27488         if(this.active == state){
27489             return;
27490         }
27491         
27492         this.active = state;
27493         
27494         if (state) {
27495             this.el.addClass('active');
27496             return;
27497         }
27498         
27499         this.el.removeClass('active');
27500         
27501         return;
27502     },
27503     
27504     setDisabled : function(state)
27505     {
27506         if(this.disabled == state){
27507             return;
27508         }
27509         
27510         this.disabled = state;
27511         
27512         if (state) {
27513             this.el.addClass('disabled');
27514             return;
27515         }
27516         
27517         this.el.removeClass('disabled');
27518     },
27519     
27520     tooltipEl : function()
27521     {
27522         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
27523     }
27524 });
27525  
27526
27527  /*
27528  * - LGPL
27529  *
27530  * FieldLabel
27531  * 
27532  */
27533
27534 /**
27535  * @class Roo.bootstrap.FieldLabel
27536  * @extends Roo.bootstrap.Component
27537  * Bootstrap FieldLabel class
27538  * @cfg {String} html contents of the element
27539  * @cfg {String} tag tag of the element default label
27540  * @cfg {String} cls class of the element
27541  * @cfg {String} target label target 
27542  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
27543  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
27544  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
27545  * @cfg {String} iconTooltip default "This field is required"
27546  * 
27547  * @constructor
27548  * Create a new FieldLabel
27549  * @param {Object} config The config object
27550  */
27551
27552 Roo.bootstrap.FieldLabel = function(config){
27553     Roo.bootstrap.Element.superclass.constructor.call(this, config);
27554     
27555     this.addEvents({
27556             /**
27557              * @event invalid
27558              * Fires after the field has been marked as invalid.
27559              * @param {Roo.form.FieldLabel} this
27560              * @param {String} msg The validation message
27561              */
27562             invalid : true,
27563             /**
27564              * @event valid
27565              * Fires after the field has been validated with no errors.
27566              * @param {Roo.form.FieldLabel} this
27567              */
27568             valid : true
27569         });
27570 };
27571
27572 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
27573     
27574     tag: 'label',
27575     cls: '',
27576     html: '',
27577     target: '',
27578     allowBlank : true,
27579     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
27580     validClass : 'text-success fa fa-lg fa-check',
27581     iconTooltip : 'This field is required',
27582     
27583     getAutoCreate : function(){
27584         
27585         var cfg = {
27586             tag : this.tag,
27587             cls : 'roo-bootstrap-field-label ' + this.cls,
27588             for : this.target,
27589             cn : [
27590                 {
27591                     tag : 'i',
27592                     cls : '',
27593                     tooltip : this.iconTooltip
27594                 },
27595                 {
27596                     tag : 'span',
27597                     html : this.html
27598                 }
27599             ] 
27600         };
27601         
27602         return cfg;
27603     },
27604     
27605     initEvents: function() 
27606     {
27607         Roo.bootstrap.Element.superclass.initEvents.call(this);
27608         
27609         this.iconEl = this.el.select('i', true).first();
27610         
27611         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
27612         
27613         Roo.bootstrap.FieldLabel.register(this);
27614     },
27615     
27616     /**
27617      * Mark this field as valid
27618      */
27619     markValid : function()
27620     {
27621         this.iconEl.show();
27622         
27623         this.iconEl.removeClass(this.invalidClass);
27624         
27625         this.iconEl.addClass(this.validClass);
27626         
27627         this.fireEvent('valid', this);
27628     },
27629     
27630     /**
27631      * Mark this field as invalid
27632      * @param {String} msg The validation message
27633      */
27634     markInvalid : function(msg)
27635     {
27636         this.iconEl.show();
27637         
27638         this.iconEl.removeClass(this.validClass);
27639         
27640         this.iconEl.addClass(this.invalidClass);
27641         
27642         this.fireEvent('invalid', this, msg);
27643     }
27644     
27645    
27646 });
27647
27648 Roo.apply(Roo.bootstrap.FieldLabel, {
27649     
27650     groups: {},
27651     
27652      /**
27653     * register a FieldLabel Group
27654     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
27655     */
27656     register : function(label)
27657     {
27658         if(this.groups.hasOwnProperty(label.target)){
27659             return;
27660         }
27661      
27662         this.groups[label.target] = label;
27663         
27664     },
27665     /**
27666     * fetch a FieldLabel Group based on the target
27667     * @param {string} target
27668     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
27669     */
27670     get: function(target) {
27671         if (typeof(this.groups[target]) == 'undefined') {
27672             return false;
27673         }
27674         
27675         return this.groups[target] ;
27676     }
27677 });
27678
27679  
27680
27681  /*
27682  * - LGPL
27683  *
27684  * page DateSplitField.
27685  * 
27686  */
27687
27688
27689 /**
27690  * @class Roo.bootstrap.DateSplitField
27691  * @extends Roo.bootstrap.Component
27692  * Bootstrap DateSplitField class
27693  * @cfg {string} fieldLabel - the label associated
27694  * @cfg {Number} labelWidth set the width of label (0-12)
27695  * @cfg {String} labelAlign (top|left)
27696  * @cfg {Boolean} dayAllowBlank (true|false) default false
27697  * @cfg {Boolean} monthAllowBlank (true|false) default false
27698  * @cfg {Boolean} yearAllowBlank (true|false) default false
27699  * @cfg {string} dayPlaceholder 
27700  * @cfg {string} monthPlaceholder
27701  * @cfg {string} yearPlaceholder
27702  * @cfg {string} dayFormat default 'd'
27703  * @cfg {string} monthFormat default 'm'
27704  * @cfg {string} yearFormat default 'Y'
27705
27706  *     
27707  * @constructor
27708  * Create a new DateSplitField
27709  * @param {Object} config The config object
27710  */
27711
27712 Roo.bootstrap.DateSplitField = function(config){
27713     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
27714     
27715     this.addEvents({
27716         // raw events
27717          /**
27718          * @event years
27719          * getting the data of years
27720          * @param {Roo.bootstrap.DateSplitField} this
27721          * @param {Object} years
27722          */
27723         "years" : true,
27724         /**
27725          * @event days
27726          * getting the data of days
27727          * @param {Roo.bootstrap.DateSplitField} this
27728          * @param {Object} days
27729          */
27730         "days" : true,
27731         /**
27732          * @event invalid
27733          * Fires after the field has been marked as invalid.
27734          * @param {Roo.form.Field} this
27735          * @param {String} msg The validation message
27736          */
27737         invalid : true,
27738        /**
27739          * @event valid
27740          * Fires after the field has been validated with no errors.
27741          * @param {Roo.form.Field} this
27742          */
27743         valid : true
27744     });
27745 };
27746
27747 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
27748     
27749     fieldLabel : '',
27750     labelAlign : 'top',
27751     labelWidth : 3,
27752     dayAllowBlank : false,
27753     monthAllowBlank : false,
27754     yearAllowBlank : false,
27755     dayPlaceholder : '',
27756     monthPlaceholder : '',
27757     yearPlaceholder : '',
27758     dayFormat : 'd',
27759     monthFormat : 'm',
27760     yearFormat : 'Y',
27761     isFormField : true,
27762     
27763     getAutoCreate : function()
27764     {
27765         var cfg = {
27766             tag : 'div',
27767             cls : 'row roo-date-split-field-group',
27768             cn : [
27769                 {
27770                     tag : 'input',
27771                     type : 'hidden',
27772                     cls : 'form-hidden-field roo-date-split-field-group-value',
27773                     name : this.name
27774                 }
27775             ]
27776         };
27777         
27778         if(this.fieldLabel){
27779             cfg.cn.push({
27780                 tag : 'div',
27781                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
27782                 cn : [
27783                     {
27784                         tag : 'label',
27785                         html : this.fieldLabel
27786                     }
27787                 ]
27788             });
27789         }
27790         
27791         Roo.each(['day', 'month', 'year'], function(t){
27792             cfg.cn.push({
27793                 tag : 'div',
27794                 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
27795             });
27796         }, this);
27797         
27798         return cfg;
27799     },
27800     
27801     inputEl: function ()
27802     {
27803         return this.el.select('.roo-date-split-field-group-value', true).first();
27804     },
27805     
27806     onRender : function(ct, position) 
27807     {
27808         var _this = this;
27809         
27810         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27811         
27812         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
27813         
27814         this.dayField = new Roo.bootstrap.ComboBox({
27815             allowBlank : this.dayAllowBlank,
27816             alwaysQuery : true,
27817             displayField : 'value',
27818             editable : false,
27819             fieldLabel : '',
27820             forceSelection : true,
27821             mode : 'local',
27822             placeholder : this.dayPlaceholder,
27823             selectOnFocus : true,
27824             tpl : '<div class="select2-result"><b>{value}</b></div>',
27825             triggerAction : 'all',
27826             typeAhead : true,
27827             valueField : 'value',
27828             store : new Roo.data.SimpleStore({
27829                 data : (function() {    
27830                     var days = [];
27831                     _this.fireEvent('days', _this, days);
27832                     return days;
27833                 })(),
27834                 fields : [ 'value' ]
27835             }),
27836             listeners : {
27837                 select : function (_self, record, index)
27838                 {
27839                     _this.setValue(_this.getValue());
27840                 }
27841             }
27842         });
27843
27844         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
27845         
27846         this.monthField = new Roo.bootstrap.MonthField({
27847             after : '<i class=\"fa fa-calendar\"></i>',
27848             allowBlank : this.monthAllowBlank,
27849             placeholder : this.monthPlaceholder,
27850             readOnly : true,
27851             listeners : {
27852                 render : function (_self)
27853                 {
27854                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
27855                         e.preventDefault();
27856                         _self.focus();
27857                     });
27858                 },
27859                 select : function (_self, oldvalue, newvalue)
27860                 {
27861                     _this.setValue(_this.getValue());
27862                 }
27863             }
27864         });
27865         
27866         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
27867         
27868         this.yearField = new Roo.bootstrap.ComboBox({
27869             allowBlank : this.yearAllowBlank,
27870             alwaysQuery : true,
27871             displayField : 'value',
27872             editable : false,
27873             fieldLabel : '',
27874             forceSelection : true,
27875             mode : 'local',
27876             placeholder : this.yearPlaceholder,
27877             selectOnFocus : true,
27878             tpl : '<div class="select2-result"><b>{value}</b></div>',
27879             triggerAction : 'all',
27880             typeAhead : true,
27881             valueField : 'value',
27882             store : new Roo.data.SimpleStore({
27883                 data : (function() {
27884                     var years = [];
27885                     _this.fireEvent('years', _this, years);
27886                     return years;
27887                 })(),
27888                 fields : [ 'value' ]
27889             }),
27890             listeners : {
27891                 select : function (_self, record, index)
27892                 {
27893                     _this.setValue(_this.getValue());
27894                 }
27895             }
27896         });
27897
27898         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
27899     },
27900     
27901     setValue : function(v, format)
27902     {
27903         this.inputEl.dom.value = v;
27904         
27905         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
27906         
27907         var d = Date.parseDate(v, f);
27908         
27909         if(!d){
27910             this.validate();
27911             return;
27912         }
27913         
27914         this.setDay(d.format(this.dayFormat));
27915         this.setMonth(d.format(this.monthFormat));
27916         this.setYear(d.format(this.yearFormat));
27917         
27918         this.validate();
27919         
27920         return;
27921     },
27922     
27923     setDay : function(v)
27924     {
27925         this.dayField.setValue(v);
27926         this.inputEl.dom.value = this.getValue();
27927         this.validate();
27928         return;
27929     },
27930     
27931     setMonth : function(v)
27932     {
27933         this.monthField.setValue(v, true);
27934         this.inputEl.dom.value = this.getValue();
27935         this.validate();
27936         return;
27937     },
27938     
27939     setYear : function(v)
27940     {
27941         this.yearField.setValue(v);
27942         this.inputEl.dom.value = this.getValue();
27943         this.validate();
27944         return;
27945     },
27946     
27947     getDay : function()
27948     {
27949         return this.dayField.getValue();
27950     },
27951     
27952     getMonth : function()
27953     {
27954         return this.monthField.getValue();
27955     },
27956     
27957     getYear : function()
27958     {
27959         return this.yearField.getValue();
27960     },
27961     
27962     getValue : function()
27963     {
27964         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
27965         
27966         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
27967         
27968         return date;
27969     },
27970     
27971     reset : function()
27972     {
27973         this.setDay('');
27974         this.setMonth('');
27975         this.setYear('');
27976         this.inputEl.dom.value = '';
27977         this.validate();
27978         return;
27979     },
27980     
27981     validate : function()
27982     {
27983         var d = this.dayField.validate();
27984         var m = this.monthField.validate();
27985         var y = this.yearField.validate();
27986         
27987         var valid = true;
27988         
27989         if(
27990                 (!this.dayAllowBlank && !d) ||
27991                 (!this.monthAllowBlank && !m) ||
27992                 (!this.yearAllowBlank && !y)
27993         ){
27994             valid = false;
27995         }
27996         
27997         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
27998             return valid;
27999         }
28000         
28001         if(valid){
28002             this.markValid();
28003             return valid;
28004         }
28005         
28006         this.markInvalid();
28007         
28008         return valid;
28009     },
28010     
28011     markValid : function()
28012     {
28013         
28014         var label = this.el.select('label', true).first();
28015         var icon = this.el.select('i.fa-star', true).first();
28016
28017         if(label && icon){
28018             icon.remove();
28019         }
28020         
28021         this.fireEvent('valid', this);
28022     },
28023     
28024      /**
28025      * Mark this field as invalid
28026      * @param {String} msg The validation message
28027      */
28028     markInvalid : function(msg)
28029     {
28030         
28031         var label = this.el.select('label', true).first();
28032         var icon = this.el.select('i.fa-star', true).first();
28033
28034         if(label && !icon){
28035             this.el.select('.roo-date-split-field-label', true).createChild({
28036                 tag : 'i',
28037                 cls : 'text-danger fa fa-lg fa-star',
28038                 tooltip : 'This field is required',
28039                 style : 'margin-right:5px;'
28040             }, label, true);
28041         }
28042         
28043         this.fireEvent('invalid', this, msg);
28044     },
28045     
28046     clearInvalid : function()
28047     {
28048         var label = this.el.select('label', true).first();
28049         var icon = this.el.select('i.fa-star', true).first();
28050
28051         if(label && icon){
28052             icon.remove();
28053         }
28054         
28055         this.fireEvent('valid', this);
28056     },
28057     
28058     getName: function()
28059     {
28060         return this.name;
28061     }
28062     
28063 });
28064
28065