Roo/form/ComboBoxArray.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         
105         cfg.id = this.id || Roo.id();
106         
107         // fill in the extra attributes 
108         if (this.xattr && typeof(this.xattr) =='object') {
109             for (var i in this.xattr) {
110                 cfg[i] = this.xattr[i];
111             }
112         }
113         
114         if(this.dataId){
115             cfg.dataId = this.dataId;
116         }
117         
118         if (this.cls) {
119             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
120         }
121         
122         if (this.style) { // fixme needs to support more complex style data.
123             cfg.style = this.style;
124         }
125         
126         if(this.name){
127             cfg.name = this.name;
128         }
129         
130         this.el = ct.createChild(cfg, position);
131         
132         if (this.tooltip) {
133             this.tooltipEl().attr('tooltip', this.tooltip);
134         }
135         
136         if(this.tabIndex !== undefined){
137             this.el.dom.setAttribute('tabIndex', this.tabIndex);
138         }
139         this.initEvents();
140         
141         
142     },
143     /**
144      * Fetch the element to add children to
145      * @return {Roo.Element} defaults to this.el
146      */
147     getChildContainer : function()
148     {
149         return this.el;
150     },
151     /**
152      * Fetch the element to display the tooltip on.
153      * @return {Roo.Element} defaults to this.el
154      */
155     tooltipEl : function()
156     {
157         return this.el;
158     },
159         
160     addxtype  : function(tree,cntr)
161     {
162         var cn = this;
163         
164         cn = Roo.factory(tree);
165            
166         cn.parentType = this.xtype; //??
167         cn.parentId = this.id;
168         
169         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
170         if (typeof(cn.container_method) == 'string') {
171             cntr = cn.container_method;
172         }
173         
174         
175         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
176         
177         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
178         
179         var build_from_html =  Roo.XComponent.build_from_html;
180           
181         var is_body  = (tree.xtype == 'Body') ;
182           
183         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
184           
185         var self_cntr_el = Roo.get(this[cntr](false));
186         
187         // do not try and build conditional elements 
188         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
189             return false;
190         }
191         
192         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
193             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
194                 return this.addxtypeChild(tree,cntr);
195             }
196             
197             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
198                 
199             if(echild){
200                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
201             }
202             
203             Roo.log('skipping render');
204             return cn;
205             
206         }
207         
208         var ret = false;
209         if (!build_from_html) {
210             return false;
211         }
212         
213         // this i think handles overlaying multiple children of the same type
214         // with the sam eelement.. - which might be buggy..
215         while (true) {
216             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
217             
218             if (!echild) {
219                 break;
220             }
221             
222             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
223                 break;
224             }
225             
226             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
227         }
228         return ret;
229     },
230     
231     addxtypeChild : function (tree, cntr)
232     {
233         Roo.debug && Roo.log('addxtypeChild:' + cntr);
234         var cn = this;
235         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
236         
237         
238         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
239                     (typeof(tree['flexy:foreach']) != 'undefined');
240           
241         
242         
243          skip_children = false;
244         // render the element if it's not BODY.
245         if (tree.xtype != 'Body') {
246            
247             cn = Roo.factory(tree);
248            
249             cn.parentType = this.xtype; //??
250             cn.parentId = this.id;
251             
252             var build_from_html =  Roo.XComponent.build_from_html;
253             
254             
255             // does the container contain child eleemnts with 'xtype' attributes.
256             // that match this xtype..
257             // note - when we render we create these as well..
258             // so we should check to see if body has xtype set.
259             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
260                
261                 var self_cntr_el = Roo.get(this[cntr](false));
262                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
263                 if (echild) { 
264                     //Roo.log(Roo.XComponent.build_from_html);
265                     //Roo.log("got echild:");
266                     //Roo.log(echild);
267                 }
268                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
269                 // and are not displayed -this causes this to use up the wrong element when matching.
270                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
271                 
272                 
273                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
274                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
275                   
276                   
277                   
278                     cn.el = echild;
279                   //  Roo.log("GOT");
280                     //echild.dom.removeAttribute('xtype');
281                 } else {
282                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
283                     Roo.debug && Roo.log(self_cntr_el);
284                     Roo.debug && Roo.log(echild);
285                     Roo.debug && Roo.log(cn);
286                 }
287             }
288            
289             
290            
291             // if object has flexy:if - then it may or may not be rendered.
292             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
293                 // skip a flexy if element.
294                 Roo.debug && Roo.log('skipping render');
295                 Roo.debug && Roo.log(tree);
296                 if (!cn.el) {
297                     Roo.debug && Roo.log('skipping all children');
298                     skip_children = true;
299                 }
300                 
301              } else {
302                  
303                 // actually if flexy:foreach is found, we really want to create 
304                 // multiple copies here...
305                 //Roo.log('render');
306                 //Roo.log(this[cntr]());
307                 cn.render(this[cntr](true));
308              }
309             // then add the element..
310         }
311         
312         
313         // handle the kids..
314         
315         var nitems = [];
316         /*
317         if (typeof (tree.menu) != 'undefined') {
318             tree.menu.parentType = cn.xtype;
319             tree.menu.triggerEl = cn.el;
320             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
321             
322         }
323         */
324         if (!tree.items || !tree.items.length) {
325             cn.items = nitems;
326             return cn;
327         }
328         var items = tree.items;
329         delete tree.items;
330         
331         //Roo.log(items.length);
332             // add the items..
333         if (!skip_children) {    
334             for(var i =0;i < items.length;i++) {
335                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
336             }
337         }
338         
339         cn.items = nitems;
340         
341         this.fireEvent('childrenrendered', this);
342         
343         return cn;
344     },
345     /**
346      * Show a component - removes 'hidden' class
347      */
348     show : function()
349     {
350         if (this.el) {
351             this.el.removeClass('hidden');
352         }
353     },
354     /**
355      * Hide a component - adds 'hidden' class
356      */
357     hide: function()
358     {
359         if (this.el && !this.el.hasClass('hidden')) {
360             this.el.addClass('hidden');
361         }
362         
363     }
364 });
365
366  /*
367  * - LGPL
368  *
369  * Body
370  * 
371  */
372
373 /**
374  * @class Roo.bootstrap.Body
375  * @extends Roo.bootstrap.Component
376  * Bootstrap Body class
377  * 
378  * @constructor
379  * Create a new body
380  * @param {Object} config The config object
381  */
382
383 Roo.bootstrap.Body = function(config){
384     Roo.bootstrap.Body.superclass.constructor.call(this, config);
385     this.el = Roo.get(document.body);
386     if (this.cls && this.cls.length) {
387         Roo.get(document.body).addClass(this.cls);
388     }
389 };
390
391 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
392       
393         autoCreate : {
394         cls: 'container'
395     },
396     onRender : function(ct, position)
397     {
398        /* Roo.log("Roo.bootstrap.Body - onRender");
399         if (this.cls && this.cls.length) {
400             Roo.get(document.body).addClass(this.cls);
401         }
402         // style??? xttr???
403         */
404     }
405     
406     
407  
408    
409 });
410
411  /*
412  * - LGPL
413  *
414  * button group
415  * 
416  */
417
418
419 /**
420  * @class Roo.bootstrap.ButtonGroup
421  * @extends Roo.bootstrap.Component
422  * Bootstrap ButtonGroup class
423  * @cfg {String} size lg | sm | xs (default empty normal)
424  * @cfg {String} align vertical | justified  (default none)
425  * @cfg {String} direction up | down (default down)
426  * @cfg {Boolean} toolbar false | true
427  * @cfg {Boolean} btn true | false
428  * 
429  * 
430  * @constructor
431  * Create a new Input
432  * @param {Object} config The config object
433  */
434
435 Roo.bootstrap.ButtonGroup = function(config){
436     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
437 };
438
439 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
440     
441     size: '',
442     align: '',
443     direction: '',
444     toolbar: false,
445     btn: true,
446
447     getAutoCreate : function(){
448         var cfg = {
449             cls: 'btn-group',
450             html : null
451         };
452         
453         cfg.html = this.html || cfg.html;
454         
455         if (this.toolbar) {
456             cfg = {
457                 cls: 'btn-toolbar',
458                 html: null
459             };
460             
461             return cfg;
462         }
463         
464         if (['vertical','justified'].indexOf(this.align)!==-1) {
465             cfg.cls = 'btn-group-' + this.align;
466             
467             if (this.align == 'justified') {
468                 console.log(this.items);
469             }
470         }
471         
472         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
473             cfg.cls += ' btn-group-' + this.size;
474         }
475         
476         if (this.direction == 'up') {
477             cfg.cls += ' dropup' ;
478         }
479         
480         return cfg;
481     }
482    
483 });
484
485  /*
486  * - LGPL
487  *
488  * button
489  * 
490  */
491
492 /**
493  * @class Roo.bootstrap.Button
494  * @extends Roo.bootstrap.Component
495  * Bootstrap Button class
496  * @cfg {String} html The button content
497  * @cfg {String} weight (  primary | success | info | warning | danger | link ) default 
498  * @cfg {String} size ( lg | sm | xs)
499  * @cfg {String} tag ( a | input | submit)
500  * @cfg {String} href empty or href
501  * @cfg {Boolean} disabled default false;
502  * @cfg {Boolean} isClose default false;
503  * @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)
504  * @cfg {String} badge text for badge
505  * @cfg {String} theme default 
506  * @cfg {Boolean} inverse 
507  * @cfg {Boolean} toggle 
508  * @cfg {String} ontext text for on toggle state
509  * @cfg {String} offtext text for off toggle state
510  * @cfg {Boolean} defaulton 
511  * @cfg {Boolean} preventDefault  default true
512  * @cfg {Boolean} removeClass remove the standard class..
513  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
514  * 
515  * @constructor
516  * Create a new button
517  * @param {Object} config The config object
518  */
519
520
521 Roo.bootstrap.Button = function(config){
522     Roo.bootstrap.Button.superclass.constructor.call(this, config);
523     this.addEvents({
524         // raw events
525         /**
526          * @event click
527          * When a butotn is pressed
528          * @param {Roo.bootstrap.Button} this
529          * @param {Roo.EventObject} e
530          */
531         "click" : true,
532          /**
533          * @event toggle
534          * After the button has been toggles
535          * @param {Roo.EventObject} e
536          * @param {boolean} pressed (also available as button.pressed)
537          */
538         "toggle" : true
539     });
540 };
541
542 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
543     html: false,
544     active: false,
545     weight: '',
546     size: '',
547     tag: 'button',
548     href: '',
549     disabled: false,
550     isClose: false,
551     glyphicon: '',
552     badge: '',
553     theme: 'default',
554     inverse: false,
555     
556     toggle: false,
557     ontext: 'ON',
558     offtext: 'OFF',
559     defaulton: true,
560     preventDefault: true,
561     removeClass: false,
562     name: false,
563     target: false,
564     
565     
566     pressed : null,
567      
568     
569     getAutoCreate : function(){
570         
571         var cfg = {
572             tag : 'button',
573             cls : 'roo-button',
574             html: ''
575         };
576         
577         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
578             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
579             this.tag = 'button';
580         } else {
581             cfg.tag = this.tag;
582         }
583         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
584         
585         if (this.toggle == true) {
586             cfg={
587                 tag: 'div',
588                 cls: 'slider-frame roo-button',
589                 cn: [
590                     {
591                         tag: 'span',
592                         'data-on-text':'ON',
593                         'data-off-text':'OFF',
594                         cls: 'slider-button',
595                         html: this.offtext
596                     }
597                 ]
598             };
599             
600             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
601                 cfg.cls += ' '+this.weight;
602             }
603             
604             return cfg;
605         }
606         
607         if (this.isClose) {
608             cfg.cls += ' close';
609             
610             cfg["aria-hidden"] = true;
611             
612             cfg.html = "&times;";
613             
614             return cfg;
615         }
616         
617          
618         if (this.theme==='default') {
619             cfg.cls = 'btn roo-button';
620             
621             //if (this.parentType != 'Navbar') {
622             this.weight = this.weight.length ?  this.weight : 'default';
623             //}
624             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
625                 
626                 cfg.cls += ' btn-' + this.weight;
627             }
628         } else if (this.theme==='glow') {
629             
630             cfg.tag = 'a';
631             cfg.cls = 'btn-glow roo-button';
632             
633             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
634                 
635                 cfg.cls += ' ' + this.weight;
636             }
637         }
638    
639         
640         if (this.inverse) {
641             this.cls += ' inverse';
642         }
643         
644         
645         if (this.active) {
646             cfg.cls += ' active';
647         }
648         
649         if (this.disabled) {
650             cfg.disabled = 'disabled';
651         }
652         
653         if (this.items) {
654             Roo.log('changing to ul' );
655             cfg.tag = 'ul';
656             this.glyphicon = 'caret';
657         }
658         
659         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
660          
661         //gsRoo.log(this.parentType);
662         if (this.parentType === 'Navbar' && !this.parent().bar) {
663             Roo.log('changing to li?');
664             
665             cfg.tag = 'li';
666             
667             cfg.cls = '';
668             cfg.cn =  [{
669                 tag : 'a',
670                 cls : 'roo-button',
671                 html : this.html,
672                 href : this.href || '#'
673             }];
674             if (this.menu) {
675                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
676                 cfg.cls += ' dropdown';
677             }   
678             
679             delete cfg.html;
680             
681         }
682         
683        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
684         
685         if (this.glyphicon) {
686             cfg.html = ' ' + cfg.html;
687             
688             cfg.cn = [
689                 {
690                     tag: 'span',
691                     cls: 'glyphicon glyphicon-' + this.glyphicon
692                 }
693             ];
694         }
695         
696         if (this.badge) {
697             cfg.html += ' ';
698             
699             cfg.tag = 'a';
700             
701 //            cfg.cls='btn roo-button';
702             
703             cfg.href=this.href;
704             
705             var value = cfg.html;
706             
707             if(this.glyphicon){
708                 value = {
709                             tag: 'span',
710                             cls: 'glyphicon glyphicon-' + this.glyphicon,
711                             html: this.html
712                         };
713                 
714             }
715             
716             cfg.cn = [
717                 value,
718                 {
719                     tag: 'span',
720                     cls: 'badge',
721                     html: this.badge
722                 }
723             ];
724             
725             cfg.html='';
726         }
727         
728         if (this.menu) {
729             cfg.cls += ' dropdown';
730             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
731         }
732         
733         if (cfg.tag !== 'a' && this.href !== '') {
734             throw "Tag must be a to set href.";
735         } else if (this.href.length > 0) {
736             cfg.href = this.href;
737         }
738         
739         if(this.removeClass){
740             cfg.cls = '';
741         }
742         
743         if(this.target){
744             cfg.target = this.target;
745         }
746         
747         return cfg;
748     },
749     initEvents: function() {
750        // Roo.log('init events?');
751 //        Roo.log(this.el.dom);
752         // add the menu...
753         
754         if (typeof (this.menu) != 'undefined') {
755             this.menu.parentType = this.xtype;
756             this.menu.triggerEl = this.el;
757             this.addxtype(Roo.apply({}, this.menu));
758         }
759
760
761        if (this.el.hasClass('roo-button')) {
762             this.el.on('click', this.onClick, this);
763        } else {
764             this.el.select('.roo-button').on('click', this.onClick, this);
765        }
766        
767        if(this.removeClass){
768            this.el.on('click', this.onClick, this);
769        }
770        
771        this.el.enableDisplayMode();
772         
773     },
774     onClick : function(e)
775     {
776         if (this.disabled) {
777             return;
778         }
779         
780         
781         Roo.log('button on click ');
782         if(this.preventDefault){
783             e.preventDefault();
784         }
785         if (this.pressed === true || this.pressed === false) {
786             this.pressed = !this.pressed;
787             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
788             this.fireEvent('toggle', this, e, this.pressed);
789         }
790         
791         
792         this.fireEvent('click', this, e);
793     },
794     
795     /**
796      * Enables this button
797      */
798     enable : function()
799     {
800         this.disabled = false;
801         this.el.removeClass('disabled');
802     },
803     
804     /**
805      * Disable this button
806      */
807     disable : function()
808     {
809         this.disabled = true;
810         this.el.addClass('disabled');
811     },
812      /**
813      * sets the active state on/off, 
814      * @param {Boolean} state (optional) Force a particular state
815      */
816     setActive : function(v) {
817         
818         this.el[v ? 'addClass' : 'removeClass']('active');
819     },
820      /**
821      * toggles the current active state 
822      */
823     toggleActive : function()
824     {
825        var active = this.el.hasClass('active');
826        this.setActive(!active);
827        
828         
829     },
830     setText : function(str)
831     {
832         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
833     },
834     getText : function()
835     {
836         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
837     },
838     hide: function() {
839        
840      
841         this.el.hide();   
842     },
843     show: function() {
844        
845         this.el.show();   
846     }
847     
848     
849 });
850
851  /*
852  * - LGPL
853  *
854  * column
855  * 
856  */
857
858 /**
859  * @class Roo.bootstrap.Column
860  * @extends Roo.bootstrap.Component
861  * Bootstrap Column class
862  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
863  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
864  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
865  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
866  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
867  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
868  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
869  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
870  *
871  * 
872  * @cfg {Boolean} hidden (true|false) hide the element
873  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
874  * @cfg {String} fa (ban|check|...) font awesome icon
875  * @cfg {Number} fasize (1|2|....) font awsome size
876
877  * @cfg {String} icon (info-sign|check|...) glyphicon name
878
879  * @cfg {String} html content of column.
880  * 
881  * @constructor
882  * Create a new Column
883  * @param {Object} config The config object
884  */
885
886 Roo.bootstrap.Column = function(config){
887     Roo.bootstrap.Column.superclass.constructor.call(this, config);
888 };
889
890 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
891     
892     xs: false,
893     sm: false,
894     md: false,
895     lg: false,
896     xsoff: false,
897     smoff: false,
898     mdoff: false,
899     lgoff: false,
900     html: '',
901     offset: 0,
902     alert: false,
903     fa: false,
904     icon : false,
905     hidden : false,
906     fasize : 1,
907     
908     getAutoCreate : function(){
909         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
910         
911         cfg = {
912             tag: 'div',
913             cls: 'column'
914         };
915         
916         var settings=this;
917         ['xs','sm','md','lg'].map(function(size){
918             //Roo.log( size + ':' + settings[size]);
919             
920             if (settings[size+'off'] !== false) {
921                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
922             }
923             
924             if (settings[size] === false) {
925                 return;
926             }
927             
928             if (!settings[size]) { // 0 = hidden
929                 cfg.cls += ' hidden-' + size;
930                 return;
931             }
932             cfg.cls += ' col-' + size + '-' + settings[size];
933             
934         });
935         
936         if (this.hidden) {
937             cfg.cls += ' hidden';
938         }
939         
940         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
941             cfg.cls +=' alert alert-' + this.alert;
942         }
943         
944         
945         if (this.html.length) {
946             cfg.html = this.html;
947         }
948         if (this.fa) {
949             var fasize = '';
950             if (this.fasize > 1) {
951                 fasize = ' fa-' + this.fasize + 'x';
952             }
953             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
954             
955             
956         }
957         if (this.icon) {
958             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
959         }
960         
961         return cfg;
962     }
963    
964 });
965
966  
967
968  /*
969  * - LGPL
970  *
971  * page container.
972  * 
973  */
974
975
976 /**
977  * @class Roo.bootstrap.Container
978  * @extends Roo.bootstrap.Component
979  * Bootstrap Container class
980  * @cfg {Boolean} jumbotron is it a jumbotron element
981  * @cfg {String} html content of element
982  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
983  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
984  * @cfg {String} header content of header (for panel)
985  * @cfg {String} footer content of footer (for panel)
986  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
987  * @cfg {String} tag (header|aside|section) type of HTML tag.
988  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
989  * @cfg {String} fa font awesome icon
990  * @cfg {String} icon (info-sign|check|...) glyphicon name
991  * @cfg {Boolean} hidden (true|false) hide the element
992  * @cfg {Boolean} expandable (true|false) default false
993  * @cfg {Boolean} expanded (true|false) default true
994  * @cfg {String} rheader contet on the right of header
995  * @cfg {Boolean} clickable (true|false) default false
996
997  *     
998  * @constructor
999  * Create a new Container
1000  * @param {Object} config The config object
1001  */
1002
1003 Roo.bootstrap.Container = function(config){
1004     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1005     
1006     this.addEvents({
1007         // raw events
1008          /**
1009          * @event expand
1010          * After the panel has been expand
1011          * 
1012          * @param {Roo.bootstrap.Container} this
1013          */
1014         "expand" : true,
1015         /**
1016          * @event collapse
1017          * After the panel has been collapsed
1018          * 
1019          * @param {Roo.bootstrap.Container} this
1020          */
1021         "collapse" : true,
1022         /**
1023          * @event click
1024          * When a element is chick
1025          * @param {Roo.bootstrap.Container} this
1026          * @param {Roo.EventObject} e
1027          */
1028         "click" : true
1029     });
1030 };
1031
1032 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1033     
1034     jumbotron : false,
1035     well: '',
1036     panel : '',
1037     header: '',
1038     footer : '',
1039     sticky: '',
1040     tag : false,
1041     alert : false,
1042     fa: false,
1043     icon : false,
1044     expandable : false,
1045     rheader : '',
1046     expanded : true,
1047     clickable: false,
1048   
1049      
1050     getChildContainer : function() {
1051         
1052         if(!this.el){
1053             return false;
1054         }
1055         
1056         if (this.panel.length) {
1057             return this.el.select('.panel-body',true).first();
1058         }
1059         
1060         return this.el;
1061     },
1062     
1063     
1064     getAutoCreate : function(){
1065         
1066         var cfg = {
1067             tag : this.tag || 'div',
1068             html : '',
1069             cls : ''
1070         };
1071         if (this.jumbotron) {
1072             cfg.cls = 'jumbotron';
1073         }
1074         
1075         
1076         
1077         // - this is applied by the parent..
1078         //if (this.cls) {
1079         //    cfg.cls = this.cls + '';
1080         //}
1081         
1082         if (this.sticky.length) {
1083             
1084             var bd = Roo.get(document.body);
1085             if (!bd.hasClass('bootstrap-sticky')) {
1086                 bd.addClass('bootstrap-sticky');
1087                 Roo.select('html',true).setStyle('height', '100%');
1088             }
1089              
1090             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1091         }
1092         
1093         
1094         if (this.well.length) {
1095             switch (this.well) {
1096                 case 'lg':
1097                 case 'sm':
1098                     cfg.cls +=' well well-' +this.well;
1099                     break;
1100                 default:
1101                     cfg.cls +=' well';
1102                     break;
1103             }
1104         }
1105         
1106         if (this.hidden) {
1107             cfg.cls += ' hidden';
1108         }
1109         
1110         
1111         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1112             cfg.cls +=' alert alert-' + this.alert;
1113         }
1114         
1115         var body = cfg;
1116         
1117         if (this.panel.length) {
1118             cfg.cls += ' panel panel-' + this.panel;
1119             cfg.cn = [];
1120             if (this.header.length) {
1121                 
1122                 var h = [];
1123                 
1124                 if(this.expandable){
1125                     
1126                     cfg.cls = cfg.cls + ' expandable';
1127                     
1128                     h.push({
1129                         tag: 'i',
1130                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1131                     });
1132                     
1133                 }
1134                 
1135                 h.push(
1136                     {
1137                         tag: 'span',
1138                         cls : 'panel-title',
1139                         html : (this.expandable ? '&nbsp;' : '') + this.header
1140                     },
1141                     {
1142                         tag: 'span',
1143                         cls: 'panel-header-right',
1144                         html: this.rheader
1145                     }
1146                 );
1147                 
1148                 cfg.cn.push({
1149                     cls : 'panel-heading',
1150                     style : this.expandable ? 'cursor: pointer' : '',
1151                     cn : h
1152                 });
1153                 
1154             }
1155             
1156             body = false;
1157             cfg.cn.push({
1158                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1159                 html : this.html
1160             });
1161             
1162             
1163             if (this.footer.length) {
1164                 cfg.cn.push({
1165                     cls : 'panel-footer',
1166                     html : this.footer
1167                     
1168                 });
1169             }
1170             
1171         }
1172         
1173         if (body) {
1174             body.html = this.html || cfg.html;
1175             // prefix with the icons..
1176             if (this.fa) {
1177                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1178             }
1179             if (this.icon) {
1180                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1181             }
1182             
1183             
1184         }
1185         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1186             cfg.cls =  'container';
1187         }
1188         
1189         return cfg;
1190     },
1191     
1192     initEvents: function() 
1193     {
1194         if(this.expandable){
1195             var headerEl = this.headerEl();
1196         
1197             if(headerEl){
1198                 headerEl.on('click', this.onToggleClick, this);
1199             }
1200         }
1201         
1202         if(this.clickable){
1203             this.el.on('click', this.onClick, this);
1204         }
1205         
1206     },
1207     
1208     onToggleClick : function()
1209     {
1210         var headerEl = this.headerEl();
1211         
1212         if(!headerEl){
1213             return;
1214         }
1215         
1216         if(this.expanded){
1217             this.collapse();
1218             return;
1219         }
1220         
1221         this.expand();
1222     },
1223     
1224     expand : function()
1225     {
1226         if(this.fireEvent('expand', this)) {
1227             
1228             this.expanded = true;
1229             
1230             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1231             
1232             this.el.select('.panel-body',true).first().removeClass('hide');
1233             
1234             var toggleEl = this.toggleEl();
1235
1236             if(!toggleEl){
1237                 return;
1238             }
1239
1240             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1241         }
1242         
1243     },
1244     
1245     collapse : function()
1246     {
1247         if(this.fireEvent('collapse', this)) {
1248             
1249             this.expanded = false;
1250             
1251             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1252             this.el.select('.panel-body',true).first().addClass('hide');
1253         
1254             var toggleEl = this.toggleEl();
1255
1256             if(!toggleEl){
1257                 return;
1258             }
1259
1260             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1261         }
1262     },
1263     
1264     toggleEl : function()
1265     {
1266         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1267             return;
1268         }
1269         
1270         return this.el.select('.panel-heading .fa',true).first();
1271     },
1272     
1273     headerEl : function()
1274     {
1275         if(!this.el || !this.panel.length || !this.header.length){
1276             return;
1277         }
1278         
1279         return this.el.select('.panel-heading',true).first()
1280     },
1281     
1282     titleEl : function()
1283     {
1284         if(!this.el || !this.panel.length || !this.header.length){
1285             return;
1286         }
1287         
1288         return this.el.select('.panel-title',true).first();
1289     },
1290     
1291     setTitle : function(v)
1292     {
1293         var titleEl = this.titleEl();
1294         
1295         if(!titleEl){
1296             return;
1297         }
1298         
1299         titleEl.dom.innerHTML = v;
1300     },
1301     
1302     getTitle : function()
1303     {
1304         
1305         var titleEl = this.titleEl();
1306         
1307         if(!titleEl){
1308             return '';
1309         }
1310         
1311         return titleEl.dom.innerHTML;
1312     },
1313     
1314     setRightTitle : function(v)
1315     {
1316         var t = this.el.select('.panel-header-right',true).first();
1317         
1318         if(!t){
1319             return;
1320         }
1321         
1322         t.dom.innerHTML = v;
1323     },
1324     
1325     onClick : function(e)
1326     {
1327         e.preventDefault();
1328         
1329         this.fireEvent('click', this, e);
1330     }
1331    
1332 });
1333
1334  /*
1335  * - LGPL
1336  *
1337  * image
1338  * 
1339  */
1340
1341
1342 /**
1343  * @class Roo.bootstrap.Img
1344  * @extends Roo.bootstrap.Component
1345  * Bootstrap Img class
1346  * @cfg {Boolean} imgResponsive false | true
1347  * @cfg {String} border rounded | circle | thumbnail
1348  * @cfg {String} src image source
1349  * @cfg {String} alt image alternative text
1350  * @cfg {String} href a tag href
1351  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1352  * @cfg {String} xsUrl xs image source
1353  * @cfg {String} smUrl sm image source
1354  * @cfg {String} mdUrl md image source
1355  * @cfg {String} lgUrl lg image source
1356  * 
1357  * @constructor
1358  * Create a new Input
1359  * @param {Object} config The config object
1360  */
1361
1362 Roo.bootstrap.Img = function(config){
1363     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1364     
1365     this.addEvents({
1366         // img events
1367         /**
1368          * @event click
1369          * The img click event for the img.
1370          * @param {Roo.EventObject} e
1371          */
1372         "click" : true
1373     });
1374 };
1375
1376 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1377     
1378     imgResponsive: true,
1379     border: '',
1380     src: 'about:blank',
1381     href: false,
1382     target: false,
1383     xsUrl: '',
1384     smUrl: '',
1385     mdUrl: '',
1386     lgUrl: '',
1387
1388     getAutoCreate : function()
1389     {   
1390         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1391             return this.createSingleImg();
1392         }
1393         
1394         var cfg = {
1395             tag: 'div',
1396             cls: 'roo-image-responsive-group',
1397             cn: []
1398         };
1399         var _this = this;
1400         
1401         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1402             
1403             if(!_this[size + 'Url']){
1404                 return;
1405             }
1406             
1407             var img = {
1408                 tag: 'img',
1409                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1410                 html: _this.html || cfg.html,
1411                 src: _this[size + 'Url']
1412             };
1413             
1414             img.cls += ' roo-image-responsive-' + size;
1415             
1416             var s = ['xs', 'sm', 'md', 'lg'];
1417             
1418             s.splice(s.indexOf(size), 1);
1419             
1420             Roo.each(s, function(ss){
1421                 img.cls += ' hidden-' + ss;
1422             });
1423             
1424             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1425                 cfg.cls += ' img-' + _this.border;
1426             }
1427             
1428             if(_this.alt){
1429                 cfg.alt = _this.alt;
1430             }
1431             
1432             if(_this.href){
1433                 var a = {
1434                     tag: 'a',
1435                     href: _this.href,
1436                     cn: [
1437                         img
1438                     ]
1439                 };
1440
1441                 if(this.target){
1442                     a.target = _this.target;
1443                 }
1444             }
1445             
1446             cfg.cn.push((_this.href) ? a : img);
1447             
1448         });
1449         
1450         return cfg;
1451     },
1452     
1453     createSingleImg : function()
1454     {
1455         var cfg = {
1456             tag: 'img',
1457             cls: (this.imgResponsive) ? 'img-responsive' : '',
1458             html : null,
1459             src : 'about:blank'  // just incase src get's set to undefined?!?
1460         };
1461         
1462         cfg.html = this.html || cfg.html;
1463         
1464         cfg.src = this.src || cfg.src;
1465         
1466         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1467             cfg.cls += ' img-' + this.border;
1468         }
1469         
1470         if(this.alt){
1471             cfg.alt = this.alt;
1472         }
1473         
1474         if(this.href){
1475             var a = {
1476                 tag: 'a',
1477                 href: this.href,
1478                 cn: [
1479                     cfg
1480                 ]
1481             };
1482             
1483             if(this.target){
1484                 a.target = this.target;
1485             }
1486             
1487         }
1488         
1489         return (this.href) ? a : cfg;
1490     },
1491     
1492     initEvents: function() 
1493     {
1494         if(!this.href){
1495             this.el.on('click', this.onClick, this);
1496         }
1497         
1498     },
1499     
1500     onClick : function(e)
1501     {
1502         Roo.log('img onclick');
1503         this.fireEvent('click', this, e);
1504     }
1505    
1506 });
1507
1508  /*
1509  * - LGPL
1510  *
1511  * image
1512  * 
1513  */
1514
1515
1516 /**
1517  * @class Roo.bootstrap.Link
1518  * @extends Roo.bootstrap.Component
1519  * Bootstrap Link Class
1520  * @cfg {String} alt image alternative text
1521  * @cfg {String} href a tag href
1522  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1523  * @cfg {String} html the content of the link.
1524  * @cfg {String} anchor name for the anchor link
1525
1526  * @cfg {Boolean} preventDefault (true | false) default false
1527
1528  * 
1529  * @constructor
1530  * Create a new Input
1531  * @param {Object} config The config object
1532  */
1533
1534 Roo.bootstrap.Link = function(config){
1535     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1536     
1537     this.addEvents({
1538         // img events
1539         /**
1540          * @event click
1541          * The img click event for the img.
1542          * @param {Roo.EventObject} e
1543          */
1544         "click" : true
1545     });
1546 };
1547
1548 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1549     
1550     href: false,
1551     target: false,
1552     preventDefault: false,
1553     anchor : false,
1554     alt : false,
1555
1556     getAutoCreate : function()
1557     {
1558         
1559         var cfg = {
1560             tag: 'a'
1561         };
1562         // anchor's do not require html/href...
1563         if (this.anchor === false) {
1564             cfg.html = this.html || '';
1565             cfg.href = this.href || '#';
1566         } else {
1567             cfg.name = this.anchor;
1568             if (this.html !== false) {
1569                 cfg.html = this.html;
1570             }
1571             if (this.href !== false) {
1572                 cfg.href = this.href;
1573             }
1574         }
1575         
1576         if(this.alt !== false){
1577             cfg.alt = this.alt;
1578         }
1579         
1580         
1581         if(this.target !== false) {
1582             cfg.target = this.target;
1583         }
1584         
1585         return cfg;
1586     },
1587     
1588     initEvents: function() {
1589         
1590         if(!this.href || this.preventDefault){
1591             this.el.on('click', this.onClick, this);
1592         }
1593     },
1594     
1595     onClick : function(e)
1596     {
1597         if(this.preventDefault){
1598             e.preventDefault();
1599         }
1600         //Roo.log('img onclick');
1601         this.fireEvent('click', this, e);
1602     }
1603    
1604 });
1605
1606  /*
1607  * - LGPL
1608  *
1609  * header
1610  * 
1611  */
1612
1613 /**
1614  * @class Roo.bootstrap.Header
1615  * @extends Roo.bootstrap.Component
1616  * Bootstrap Header class
1617  * @cfg {String} html content of header
1618  * @cfg {Number} level (1|2|3|4|5|6) default 1
1619  * 
1620  * @constructor
1621  * Create a new Header
1622  * @param {Object} config The config object
1623  */
1624
1625
1626 Roo.bootstrap.Header  = function(config){
1627     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1628 };
1629
1630 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1631     
1632     //href : false,
1633     html : false,
1634     level : 1,
1635     
1636     
1637     
1638     getAutoCreate : function(){
1639         
1640         
1641         
1642         var cfg = {
1643             tag: 'h' + (1 *this.level),
1644             html: this.html || ''
1645         } ;
1646         
1647         return cfg;
1648     }
1649    
1650 });
1651
1652  
1653
1654  /*
1655  * Based on:
1656  * Ext JS Library 1.1.1
1657  * Copyright(c) 2006-2007, Ext JS, LLC.
1658  *
1659  * Originally Released Under LGPL - original licence link has changed is not relivant.
1660  *
1661  * Fork - LGPL
1662  * <script type="text/javascript">
1663  */
1664  
1665 /**
1666  * @class Roo.bootstrap.MenuMgr
1667  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1668  * @singleton
1669  */
1670 Roo.bootstrap.MenuMgr = function(){
1671    var menus, active, groups = {}, attached = false, lastShow = new Date();
1672
1673    // private - called when first menu is created
1674    function init(){
1675        menus = {};
1676        active = new Roo.util.MixedCollection();
1677        Roo.get(document).addKeyListener(27, function(){
1678            if(active.length > 0){
1679                hideAll();
1680            }
1681        });
1682    }
1683
1684    // private
1685    function hideAll(){
1686        if(active && active.length > 0){
1687            var c = active.clone();
1688            c.each(function(m){
1689                m.hide();
1690            });
1691        }
1692    }
1693
1694    // private
1695    function onHide(m){
1696        active.remove(m);
1697        if(active.length < 1){
1698            Roo.get(document).un("mouseup", onMouseDown);
1699             
1700            attached = false;
1701        }
1702    }
1703
1704    // private
1705    function onShow(m){
1706        var last = active.last();
1707        lastShow = new Date();
1708        active.add(m);
1709        if(!attached){
1710           Roo.get(document).on("mouseup", onMouseDown);
1711            
1712            attached = true;
1713        }
1714        if(m.parentMenu){
1715           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1716           m.parentMenu.activeChild = m;
1717        }else if(last && last.isVisible()){
1718           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1719        }
1720    }
1721
1722    // private
1723    function onBeforeHide(m){
1724        if(m.activeChild){
1725            m.activeChild.hide();
1726        }
1727        if(m.autoHideTimer){
1728            clearTimeout(m.autoHideTimer);
1729            delete m.autoHideTimer;
1730        }
1731    }
1732
1733    // private
1734    function onBeforeShow(m){
1735        var pm = m.parentMenu;
1736        if(!pm && !m.allowOtherMenus){
1737            hideAll();
1738        }else if(pm && pm.activeChild && active != m){
1739            pm.activeChild.hide();
1740        }
1741    }
1742
1743    // private this should really trigger on mouseup..
1744    function onMouseDown(e){
1745         Roo.log("on Mouse Up");
1746         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu") && !e.getTarget('.user-menu')){
1747             Roo.log("hideAll");
1748             hideAll();
1749             e.stopEvent();
1750         }
1751         
1752         
1753    }
1754
1755    // private
1756    function onBeforeCheck(mi, state){
1757        if(state){
1758            var g = groups[mi.group];
1759            for(var i = 0, l = g.length; i < l; i++){
1760                if(g[i] != mi){
1761                    g[i].setChecked(false);
1762                }
1763            }
1764        }
1765    }
1766
1767    return {
1768
1769        /**
1770         * Hides all menus that are currently visible
1771         */
1772        hideAll : function(){
1773             hideAll();  
1774        },
1775
1776        // private
1777        register : function(menu){
1778            if(!menus){
1779                init();
1780            }
1781            menus[menu.id] = menu;
1782            menu.on("beforehide", onBeforeHide);
1783            menu.on("hide", onHide);
1784            menu.on("beforeshow", onBeforeShow);
1785            menu.on("show", onShow);
1786            var g = menu.group;
1787            if(g && menu.events["checkchange"]){
1788                if(!groups[g]){
1789                    groups[g] = [];
1790                }
1791                groups[g].push(menu);
1792                menu.on("checkchange", onCheck);
1793            }
1794        },
1795
1796         /**
1797          * Returns a {@link Roo.menu.Menu} object
1798          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1799          * be used to generate and return a new Menu instance.
1800          */
1801        get : function(menu){
1802            if(typeof menu == "string"){ // menu id
1803                return menus[menu];
1804            }else if(menu.events){  // menu instance
1805                return menu;
1806            }
1807            /*else if(typeof menu.length == 'number'){ // array of menu items?
1808                return new Roo.bootstrap.Menu({items:menu});
1809            }else{ // otherwise, must be a config
1810                return new Roo.bootstrap.Menu(menu);
1811            }
1812            */
1813            return false;
1814        },
1815
1816        // private
1817        unregister : function(menu){
1818            delete menus[menu.id];
1819            menu.un("beforehide", onBeforeHide);
1820            menu.un("hide", onHide);
1821            menu.un("beforeshow", onBeforeShow);
1822            menu.un("show", onShow);
1823            var g = menu.group;
1824            if(g && menu.events["checkchange"]){
1825                groups[g].remove(menu);
1826                menu.un("checkchange", onCheck);
1827            }
1828        },
1829
1830        // private
1831        registerCheckable : function(menuItem){
1832            var g = menuItem.group;
1833            if(g){
1834                if(!groups[g]){
1835                    groups[g] = [];
1836                }
1837                groups[g].push(menuItem);
1838                menuItem.on("beforecheckchange", onBeforeCheck);
1839            }
1840        },
1841
1842        // private
1843        unregisterCheckable : function(menuItem){
1844            var g = menuItem.group;
1845            if(g){
1846                groups[g].remove(menuItem);
1847                menuItem.un("beforecheckchange", onBeforeCheck);
1848            }
1849        }
1850    };
1851 }();/*
1852  * - LGPL
1853  *
1854  * menu
1855  * 
1856  */
1857
1858 /**
1859  * @class Roo.bootstrap.Menu
1860  * @extends Roo.bootstrap.Component
1861  * Bootstrap Menu class - container for MenuItems
1862  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1863  * 
1864  * @constructor
1865  * Create a new Menu
1866  * @param {Object} config The config object
1867  */
1868
1869
1870 Roo.bootstrap.Menu = function(config){
1871     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1872     if (this.registerMenu) {
1873         Roo.bootstrap.MenuMgr.register(this);
1874     }
1875     this.addEvents({
1876         /**
1877          * @event beforeshow
1878          * Fires before this menu is displayed
1879          * @param {Roo.menu.Menu} this
1880          */
1881         beforeshow : true,
1882         /**
1883          * @event beforehide
1884          * Fires before this menu is hidden
1885          * @param {Roo.menu.Menu} this
1886          */
1887         beforehide : true,
1888         /**
1889          * @event show
1890          * Fires after this menu is displayed
1891          * @param {Roo.menu.Menu} this
1892          */
1893         show : true,
1894         /**
1895          * @event hide
1896          * Fires after this menu is hidden
1897          * @param {Roo.menu.Menu} this
1898          */
1899         hide : true,
1900         /**
1901          * @event click
1902          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1903          * @param {Roo.menu.Menu} this
1904          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1905          * @param {Roo.EventObject} e
1906          */
1907         click : true,
1908         /**
1909          * @event mouseover
1910          * Fires when the mouse is hovering over this menu
1911          * @param {Roo.menu.Menu} this
1912          * @param {Roo.EventObject} e
1913          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1914          */
1915         mouseover : true,
1916         /**
1917          * @event mouseout
1918          * Fires when the mouse exits this menu
1919          * @param {Roo.menu.Menu} this
1920          * @param {Roo.EventObject} e
1921          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1922          */
1923         mouseout : true,
1924         /**
1925          * @event itemclick
1926          * Fires when a menu item contained in this menu is clicked
1927          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1928          * @param {Roo.EventObject} e
1929          */
1930         itemclick: true
1931     });
1932     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1933 };
1934
1935 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1936     
1937    /// html : false,
1938     //align : '',
1939     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1940     type: false,
1941     /**
1942      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1943      */
1944     registerMenu : true,
1945     
1946     menuItems :false, // stores the menu items..
1947     
1948     hidden:true,
1949     
1950     parentMenu : false,
1951     
1952     getChildContainer : function() {
1953         return this.el;  
1954     },
1955     
1956     getAutoCreate : function(){
1957          
1958         //if (['right'].indexOf(this.align)!==-1) {
1959         //    cfg.cn[1].cls += ' pull-right'
1960         //}
1961         
1962         
1963         var cfg = {
1964             tag : 'ul',
1965             cls : 'dropdown-menu' ,
1966             style : 'z-index:1000'
1967             
1968         };
1969         
1970         if (this.type === 'submenu') {
1971             cfg.cls = 'submenu active';
1972         }
1973         if (this.type === 'treeview') {
1974             cfg.cls = 'treeview-menu';
1975         }
1976         
1977         return cfg;
1978     },
1979     initEvents : function() {
1980         
1981        // Roo.log("ADD event");
1982        // Roo.log(this.triggerEl.dom);
1983         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
1984         
1985         this.triggerEl.addClass('dropdown-toggle');
1986         
1987         if (Roo.isTouch) {
1988             this.el.on('touchstart'  , this.onTouch, this);
1989         }
1990         this.el.on('click' , this.onClick, this);
1991
1992         this.el.on("mouseover", this.onMouseOver, this);
1993         this.el.on("mouseout", this.onMouseOut, this);
1994         
1995     },
1996     
1997     findTargetItem : function(e)
1998     {
1999         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2000         if(!t){
2001             return false;
2002         }
2003         //Roo.log(t);         Roo.log(t.id);
2004         if(t && t.id){
2005             //Roo.log(this.menuitems);
2006             return this.menuitems.get(t.id);
2007             
2008             //return this.items.get(t.menuItemId);
2009         }
2010         
2011         return false;
2012     },
2013     
2014     onTouch : function(e) 
2015     {
2016         //e.stopEvent(); this make the user popdown broken
2017         this.onClick(e);
2018     },
2019     
2020     onClick : function(e)
2021     {
2022         Roo.log("menu.onClick");
2023         var t = this.findTargetItem(e);
2024         if(!t || t.isContainer){
2025             return;
2026         }
2027         Roo.log(e);
2028         /*
2029         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2030             if(t == this.activeItem && t.shouldDeactivate(e)){
2031                 this.activeItem.deactivate();
2032                 delete this.activeItem;
2033                 return;
2034             }
2035             if(t.canActivate){
2036                 this.setActiveItem(t, true);
2037             }
2038             return;
2039             
2040             
2041         }
2042         */
2043        
2044         Roo.log('pass click event');
2045         
2046         t.onClick(e);
2047         
2048         this.fireEvent("click", this, t, e);
2049         
2050         this.hide();
2051     },
2052      onMouseOver : function(e){
2053         var t  = this.findTargetItem(e);
2054         //Roo.log(t);
2055         //if(t){
2056         //    if(t.canActivate && !t.disabled){
2057         //        this.setActiveItem(t, true);
2058         //    }
2059         //}
2060         
2061         this.fireEvent("mouseover", this, e, t);
2062     },
2063     isVisible : function(){
2064         return !this.hidden;
2065     },
2066      onMouseOut : function(e){
2067         var t  = this.findTargetItem(e);
2068         
2069         //if(t ){
2070         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2071         //        this.activeItem.deactivate();
2072         //        delete this.activeItem;
2073         //    }
2074         //}
2075         this.fireEvent("mouseout", this, e, t);
2076     },
2077     
2078     
2079     /**
2080      * Displays this menu relative to another element
2081      * @param {String/HTMLElement/Roo.Element} element The element to align to
2082      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2083      * the element (defaults to this.defaultAlign)
2084      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2085      */
2086     show : function(el, pos, parentMenu){
2087         this.parentMenu = parentMenu;
2088         if(!this.el){
2089             this.render();
2090         }
2091         this.fireEvent("beforeshow", this);
2092         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2093     },
2094      /**
2095      * Displays this menu at a specific xy position
2096      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2097      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2098      */
2099     showAt : function(xy, parentMenu, /* private: */_e){
2100         this.parentMenu = parentMenu;
2101         if(!this.el){
2102             this.render();
2103         }
2104         if(_e !== false){
2105             this.fireEvent("beforeshow", this);
2106             //xy = this.el.adjustForConstraints(xy);
2107         }
2108         
2109         //this.el.show();
2110         this.hideMenuItems();
2111         this.hidden = false;
2112         this.triggerEl.addClass('open');
2113         
2114         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2115             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2116         }
2117         
2118         this.el.setXY(xy);
2119         this.focus();
2120         this.fireEvent("show", this);
2121     },
2122     
2123     focus : function(){
2124         return;
2125         if(!this.hidden){
2126             this.doFocus.defer(50, this);
2127         }
2128     },
2129
2130     doFocus : function(){
2131         if(!this.hidden){
2132             this.focusEl.focus();
2133         }
2134     },
2135
2136     /**
2137      * Hides this menu and optionally all parent menus
2138      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2139      */
2140     hide : function(deep){
2141         
2142         this.hideMenuItems();
2143         if(this.el && this.isVisible()){
2144             this.fireEvent("beforehide", this);
2145             if(this.activeItem){
2146                 this.activeItem.deactivate();
2147                 this.activeItem = null;
2148             }
2149             this.triggerEl.removeClass('open');;
2150             this.hidden = true;
2151             this.fireEvent("hide", this);
2152         }
2153         if(deep === true && this.parentMenu){
2154             this.parentMenu.hide(true);
2155         }
2156     },
2157     
2158     onTriggerPress  : function(e)
2159     {
2160         
2161         Roo.log('trigger press');
2162         //Roo.log(e.getTarget());
2163        // Roo.log(this.triggerEl.dom);
2164         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
2165             return;
2166         }
2167         
2168         if (this.isVisible()) {
2169             Roo.log('hide');
2170             this.hide();
2171         } else {
2172             Roo.log('show');
2173             this.show(this.triggerEl, false, false);
2174         }
2175         
2176         e.stopEvent();
2177     },
2178     
2179          
2180        
2181     
2182     hideMenuItems : function()
2183     {
2184         //$(backdrop).remove()
2185         Roo.select('.open',true).each(function(aa) {
2186             
2187             aa.removeClass('open');
2188           //var parent = getParent($(this))
2189           //var relatedTarget = { relatedTarget: this }
2190           
2191            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2192           //if (e.isDefaultPrevented()) return
2193            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2194         })
2195     },
2196     addxtypeChild : function (tree, cntr) {
2197         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2198           
2199         this.menuitems.add(comp);
2200         return comp;
2201
2202     },
2203     getEl : function()
2204     {
2205         Roo.log(this.el);
2206         return this.el;
2207     }
2208 });
2209
2210  
2211  /*
2212  * - LGPL
2213  *
2214  * menu item
2215  * 
2216  */
2217
2218
2219 /**
2220  * @class Roo.bootstrap.MenuItem
2221  * @extends Roo.bootstrap.Component
2222  * Bootstrap MenuItem class
2223  * @cfg {String} html the menu label
2224  * @cfg {String} href the link
2225  * @cfg {Boolean} preventDefault (true | false) default true
2226  * @cfg {Boolean} isContainer (true | false) default false
2227  * 
2228  * 
2229  * @constructor
2230  * Create a new MenuItem
2231  * @param {Object} config The config object
2232  */
2233
2234
2235 Roo.bootstrap.MenuItem = function(config){
2236     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2237     this.addEvents({
2238         // raw events
2239         /**
2240          * @event click
2241          * The raw click event for the entire grid.
2242          * @param {Roo.bootstrap.MenuItem} this
2243          * @param {Roo.EventObject} e
2244          */
2245         "click" : true
2246     });
2247 };
2248
2249 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2250     
2251     href : false,
2252     html : false,
2253     preventDefault: true,
2254     isContainer : false,
2255     
2256     getAutoCreate : function(){
2257         
2258         if(this.isContainer){
2259             return {
2260                 tag: 'li',
2261                 cls: 'dropdown-menu-item'
2262             };
2263         }
2264         
2265         var cfg= {
2266             tag: 'li',
2267             cls: 'dropdown-menu-item',
2268             cn: [
2269                     {
2270                         tag : 'a',
2271                         href : '#',
2272                         html : 'Link'
2273                     }
2274                 ]
2275         };
2276         if (this.parent().type == 'treeview') {
2277             cfg.cls = 'treeview-menu';
2278         }
2279         
2280         cfg.cn[0].href = this.href || cfg.cn[0].href ;
2281         cfg.cn[0].html = this.html || cfg.cn[0].html ;
2282         return cfg;
2283     },
2284     
2285     initEvents: function() {
2286         
2287         //this.el.select('a').on('click', this.onClick, this);
2288         
2289     },
2290     onClick : function(e)
2291     {
2292         Roo.log('item on click ');
2293         //if(this.preventDefault){
2294         //    e.preventDefault();
2295         //}
2296         //this.parent().hideMenuItems();
2297         
2298         this.fireEvent('click', this, e);
2299     },
2300     getEl : function()
2301     {
2302         return this.el;
2303     }
2304 });
2305
2306  
2307
2308  /*
2309  * - LGPL
2310  *
2311  * menu separator
2312  * 
2313  */
2314
2315
2316 /**
2317  * @class Roo.bootstrap.MenuSeparator
2318  * @extends Roo.bootstrap.Component
2319  * Bootstrap MenuSeparator class
2320  * 
2321  * @constructor
2322  * Create a new MenuItem
2323  * @param {Object} config The config object
2324  */
2325
2326
2327 Roo.bootstrap.MenuSeparator = function(config){
2328     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2329 };
2330
2331 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2332     
2333     getAutoCreate : function(){
2334         var cfg = {
2335             cls: 'divider',
2336             tag : 'li'
2337         };
2338         
2339         return cfg;
2340     }
2341    
2342 });
2343
2344  
2345
2346  
2347 /*
2348 * Licence: LGPL
2349 */
2350
2351 /**
2352  * @class Roo.bootstrap.Modal
2353  * @extends Roo.bootstrap.Component
2354  * Bootstrap Modal class
2355  * @cfg {String} title Title of dialog
2356  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2357  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn 
2358  * @cfg {Boolean} specificTitle default false
2359  * @cfg {Array} buttons Array of buttons or standard button set..
2360  * @cfg {String} buttonPosition (left|right|center) default right
2361  * @cfg {Boolean} animate default true
2362  * @cfg {Boolean} allow_close default true
2363  * 
2364  * @constructor
2365  * Create a new Modal Dialog
2366  * @param {Object} config The config object
2367  */
2368
2369 Roo.bootstrap.Modal = function(config){
2370     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2371     this.addEvents({
2372         // raw events
2373         /**
2374          * @event btnclick
2375          * The raw btnclick event for the button
2376          * @param {Roo.EventObject} e
2377          */
2378         "btnclick" : true
2379     });
2380     this.buttons = this.buttons || [];
2381      
2382     if (this.tmpl) {
2383         this.tmpl = Roo.factory(this.tmpl);
2384     }
2385     
2386 };
2387
2388 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2389     
2390     title : 'test dialog',
2391    
2392     buttons : false,
2393     
2394     // set on load...
2395      
2396     html: false,
2397     
2398     tmp: false,
2399     
2400     specificTitle: false,
2401     
2402     buttonPosition: 'right',
2403     
2404     allow_close : true,
2405     
2406     animate : true,
2407     
2408     
2409      // private
2410     bodyEl:  false,
2411     footerEl:  false,
2412     titleEl:  false,
2413     closeEl:  false,
2414     
2415     
2416     onRender : function(ct, position)
2417     {
2418         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2419      
2420         if(!this.el){
2421             var cfg = Roo.apply({},  this.getAutoCreate());
2422             cfg.id = Roo.id();
2423             //if(!cfg.name){
2424             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2425             //}
2426             //if (!cfg.name.length) {
2427             //    delete cfg.name;
2428            // }
2429             if (this.cls) {
2430                 cfg.cls += ' ' + this.cls;
2431             }
2432             if (this.style) {
2433                 cfg.style = this.style;
2434             }
2435             this.el = Roo.get(document.body).createChild(cfg, position);
2436         }
2437         //var type = this.el.dom.type;
2438         
2439         
2440         if(this.tabIndex !== undefined){
2441             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2442         }
2443         
2444         
2445         this.bodyEl = this.el.select('.modal-body',true).first();
2446         this.closeEl = this.el.select('.modal-header .close', true).first();
2447         this.footerEl = this.el.select('.modal-footer',true).first();
2448         this.titleEl = this.el.select('.modal-title',true).first();
2449         
2450         
2451          
2452         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2453         this.maskEl.enableDisplayMode("block");
2454         this.maskEl.hide();
2455         //this.el.addClass("x-dlg-modal");
2456     
2457         if (this.buttons.length) {
2458             Roo.each(this.buttons, function(bb) {
2459                 var b = Roo.apply({}, bb);
2460                 b.xns = b.xns || Roo.bootstrap;
2461                 b.xtype = b.xtype || 'Button';
2462                 if (typeof(b.listeners) == 'undefined') {
2463                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2464                 }
2465                 
2466                 var btn = Roo.factory(b);
2467                 
2468                 btn.onRender(this.el.select('.modal-footer div').first());
2469                 
2470             },this);
2471         }
2472         // render the children.
2473         var nitems = [];
2474         
2475         if(typeof(this.items) != 'undefined'){
2476             var items = this.items;
2477             delete this.items;
2478
2479             for(var i =0;i < items.length;i++) {
2480                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2481             }
2482         }
2483         
2484         this.items = nitems;
2485         
2486         // where are these used - they used to be body/close/footer
2487         
2488        
2489         this.initEvents();
2490         //this.el.addClass([this.fieldClass, this.cls]);
2491         
2492     },
2493     
2494     getAutoCreate : function(){
2495         
2496         
2497         var bdy = {
2498                 cls : 'modal-body',
2499                 html : this.html || ''
2500         };
2501         
2502         var title = {
2503             tag: 'h4',
2504             cls : 'modal-title',
2505             html : this.title
2506         };
2507         
2508         if(this.specificTitle){
2509             title = this.title;
2510             
2511         };
2512         
2513         var header = [];
2514         if (this.allow_close) {
2515             header.push({
2516                 tag: 'button',
2517                 cls : 'close',
2518                 html : '&times'
2519             });
2520         }
2521         header.push(title);
2522         
2523         var modal = {
2524             cls: "modal",
2525             style : 'display: none',
2526             cn : [
2527                 {
2528                     cls: "modal-dialog",
2529                     cn : [
2530                         {
2531                             cls : "modal-content",
2532                             cn : [
2533                                 {
2534                                     cls : 'modal-header',
2535                                     cn : header
2536                                 },
2537                                 bdy,
2538                                 {
2539                                     cls : 'modal-footer',
2540                                     cn : [
2541                                         {
2542                                             tag: 'div',
2543                                             cls: 'btn-' + this.buttonPosition
2544                                         }
2545                                     ]
2546                                     
2547                                 }
2548                                 
2549                                 
2550                             ]
2551                             
2552                         }
2553                     ]
2554                         
2555                 }
2556             ]
2557         };
2558         
2559         if(this.animate){
2560             modal.cls += ' fade';
2561         }
2562         
2563         return modal;
2564           
2565     },
2566     getChildContainer : function() {
2567          
2568          return this.bodyEl;
2569         
2570     },
2571     getButtonContainer : function() {
2572          return this.el.select('.modal-footer div',true).first();
2573         
2574     },
2575     initEvents : function()
2576     {
2577         if (this.allow_close) {
2578             this.closeEl.on('click', this.hide, this);
2579         }
2580         
2581         var _this = this;
2582         
2583         window.addEventListener("resize", function() { _this.resize(); } );
2584
2585     },
2586     
2587     resize : function()
2588     {
2589         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2590     },
2591     
2592     show : function() {
2593         
2594         if (!this.rendered) {
2595             this.render();
2596         }
2597         
2598         this.el.setStyle('display', 'block');
2599         
2600         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2601             var _this = this;
2602             (function(){
2603                 this.el.addClass('in');
2604             }).defer(50, this);
2605         }else{
2606             this.el.addClass('in');
2607             
2608         }
2609         
2610         // not sure how we can show data in here.. 
2611         //if (this.tmpl) {
2612         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2613         //}
2614         
2615         Roo.get(document.body).addClass("x-body-masked");
2616         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2617         this.maskEl.show();
2618         this.el.setStyle('zIndex', '10001');
2619        
2620         this.fireEvent('show', this);
2621          
2622         
2623         
2624     },
2625     hide : function()
2626     {
2627         this.maskEl.hide();
2628         Roo.get(document.body).removeClass("x-body-masked");
2629         this.el.removeClass('in');
2630         this.el.select('.modal-dialog', true).first().setStyle('transform','');
2631         
2632         if(this.animate){ // why
2633             var _this = this;
2634             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2635         }else{
2636             this.el.setStyle('display', 'none');
2637         }
2638         
2639         this.fireEvent('hide', this);
2640     },
2641     
2642     addButton : function(str, cb)
2643     {
2644          
2645         
2646         var b = Roo.apply({}, { html : str } );
2647         b.xns = b.xns || Roo.bootstrap;
2648         b.xtype = b.xtype || 'Button';
2649         if (typeof(b.listeners) == 'undefined') {
2650             b.listeners = { click : cb.createDelegate(this)  };
2651         }
2652         
2653         var btn = Roo.factory(b);
2654            
2655         btn.onRender(this.el.select('.modal-footer div').first());
2656         
2657         return btn;   
2658        
2659     },
2660     
2661     setDefaultButton : function(btn)
2662     {
2663         //this.el.select('.modal-footer').()
2664     },
2665     resizeTo: function(w,h)
2666     {
2667         // skip..
2668     },
2669     setContentSize  : function(w, h)
2670     {
2671         
2672     },
2673     onButtonClick: function(btn,e)
2674     {
2675         //Roo.log([a,b,c]);
2676         this.fireEvent('btnclick', btn.name, e);
2677     },
2678      /**
2679      * Set the title of the Dialog
2680      * @param {String} str new Title
2681      */
2682     setTitle: function(str) {
2683         this.titleEl.dom.innerHTML = str;    
2684     },
2685     /**
2686      * Set the body of the Dialog
2687      * @param {String} str new Title
2688      */
2689     setBody: function(str) {
2690         this.bodyEl.dom.innerHTML = str;    
2691     },
2692     /**
2693      * Set the body of the Dialog using the template
2694      * @param {Obj} data - apply this data to the template and replace the body contents.
2695      */
2696     applyBody: function(obj)
2697     {
2698         if (!this.tmpl) {
2699             Roo.log("Error - using apply Body without a template");
2700             //code
2701         }
2702         this.tmpl.overwrite(this.bodyEl, obj);
2703     }
2704     
2705 });
2706
2707
2708 Roo.apply(Roo.bootstrap.Modal,  {
2709     /**
2710          * Button config that displays a single OK button
2711          * @type Object
2712          */
2713         OK :  [{
2714             name : 'ok',
2715             weight : 'primary',
2716             html : 'OK'
2717         }], 
2718         /**
2719          * Button config that displays Yes and No buttons
2720          * @type Object
2721          */
2722         YESNO : [
2723             {
2724                 name  : 'no',
2725                 html : 'No'
2726             },
2727             {
2728                 name  :'yes',
2729                 weight : 'primary',
2730                 html : 'Yes'
2731             }
2732         ],
2733         
2734         /**
2735          * Button config that displays OK and Cancel buttons
2736          * @type Object
2737          */
2738         OKCANCEL : [
2739             {
2740                name : 'cancel',
2741                 html : 'Cancel'
2742             },
2743             {
2744                 name : 'ok',
2745                 weight : 'primary',
2746                 html : 'OK'
2747             }
2748         ],
2749         /**
2750          * Button config that displays Yes, No and Cancel buttons
2751          * @type Object
2752          */
2753         YESNOCANCEL : [
2754             {
2755                 name : 'yes',
2756                 weight : 'primary',
2757                 html : 'Yes'
2758             },
2759             {
2760                 name : 'no',
2761                 html : 'No'
2762             },
2763             {
2764                 name : 'cancel',
2765                 html : 'Cancel'
2766             }
2767         ]
2768 });
2769  
2770  /*
2771  * - LGPL
2772  *
2773  * messagebox - can be used as a replace
2774  * 
2775  */
2776 /**
2777  * @class Roo.MessageBox
2778  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2779  * Example usage:
2780  *<pre><code>
2781 // Basic alert:
2782 Roo.Msg.alert('Status', 'Changes saved successfully.');
2783
2784 // Prompt for user data:
2785 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2786     if (btn == 'ok'){
2787         // process text value...
2788     }
2789 });
2790
2791 // Show a dialog using config options:
2792 Roo.Msg.show({
2793    title:'Save Changes?',
2794    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2795    buttons: Roo.Msg.YESNOCANCEL,
2796    fn: processResult,
2797    animEl: 'elId'
2798 });
2799 </code></pre>
2800  * @singleton
2801  */
2802 Roo.bootstrap.MessageBox = function(){
2803     var dlg, opt, mask, waitTimer;
2804     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2805     var buttons, activeTextEl, bwidth;
2806
2807     
2808     // private
2809     var handleButton = function(button){
2810         dlg.hide();
2811         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2812     };
2813
2814     // private
2815     var handleHide = function(){
2816         if(opt && opt.cls){
2817             dlg.el.removeClass(opt.cls);
2818         }
2819         //if(waitTimer){
2820         //    Roo.TaskMgr.stop(waitTimer);
2821         //    waitTimer = null;
2822         //}
2823     };
2824
2825     // private
2826     var updateButtons = function(b){
2827         var width = 0;
2828         if(!b){
2829             buttons["ok"].hide();
2830             buttons["cancel"].hide();
2831             buttons["yes"].hide();
2832             buttons["no"].hide();
2833             //dlg.footer.dom.style.display = 'none';
2834             return width;
2835         }
2836         dlg.footerEl.dom.style.display = '';
2837         for(var k in buttons){
2838             if(typeof buttons[k] != "function"){
2839                 if(b[k]){
2840                     buttons[k].show();
2841                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2842                     width += buttons[k].el.getWidth()+15;
2843                 }else{
2844                     buttons[k].hide();
2845                 }
2846             }
2847         }
2848         return width;
2849     };
2850
2851     // private
2852     var handleEsc = function(d, k, e){
2853         if(opt && opt.closable !== false){
2854             dlg.hide();
2855         }
2856         if(e){
2857             e.stopEvent();
2858         }
2859     };
2860
2861     return {
2862         /**
2863          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2864          * @return {Roo.BasicDialog} The BasicDialog element
2865          */
2866         getDialog : function(){
2867            if(!dlg){
2868                 dlg = new Roo.bootstrap.Modal( {
2869                     //draggable: true,
2870                     //resizable:false,
2871                     //constraintoviewport:false,
2872                     //fixedcenter:true,
2873                     //collapsible : false,
2874                     //shim:true,
2875                     //modal: true,
2876                   //  width:400,
2877                   //  height:100,
2878                     //buttonAlign:"center",
2879                     closeClick : function(){
2880                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2881                             handleButton("no");
2882                         }else{
2883                             handleButton("cancel");
2884                         }
2885                     }
2886                 });
2887                 dlg.render();
2888                 dlg.on("hide", handleHide);
2889                 mask = dlg.mask;
2890                 //dlg.addKeyListener(27, handleEsc);
2891                 buttons = {};
2892                 this.buttons = buttons;
2893                 var bt = this.buttonText;
2894                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2895                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2896                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2897                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2898                 //Roo.log(buttons);
2899                 bodyEl = dlg.bodyEl.createChild({
2900
2901                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2902                         '<textarea class="roo-mb-textarea"></textarea>' +
2903                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2904                 });
2905                 msgEl = bodyEl.dom.firstChild;
2906                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2907                 textboxEl.enableDisplayMode();
2908                 textboxEl.addKeyListener([10,13], function(){
2909                     if(dlg.isVisible() && opt && opt.buttons){
2910                         if(opt.buttons.ok){
2911                             handleButton("ok");
2912                         }else if(opt.buttons.yes){
2913                             handleButton("yes");
2914                         }
2915                     }
2916                 });
2917                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2918                 textareaEl.enableDisplayMode();
2919                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2920                 progressEl.enableDisplayMode();
2921                 var pf = progressEl.dom.firstChild;
2922                 if (pf) {
2923                     pp = Roo.get(pf.firstChild);
2924                     pp.setHeight(pf.offsetHeight);
2925                 }
2926                 
2927             }
2928             return dlg;
2929         },
2930
2931         /**
2932          * Updates the message box body text
2933          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2934          * the XHTML-compliant non-breaking space character '&amp;#160;')
2935          * @return {Roo.MessageBox} This message box
2936          */
2937         updateText : function(text){
2938             if(!dlg.isVisible() && !opt.width){
2939                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2940             }
2941             msgEl.innerHTML = text || '&#160;';
2942       
2943             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2944             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2945             var w = Math.max(
2946                     Math.min(opt.width || cw , this.maxWidth), 
2947                     Math.max(opt.minWidth || this.minWidth, bwidth)
2948             );
2949             if(opt.prompt){
2950                 activeTextEl.setWidth(w);
2951             }
2952             if(dlg.isVisible()){
2953                 dlg.fixedcenter = false;
2954             }
2955             // to big, make it scroll. = But as usual stupid IE does not support
2956             // !important..
2957             
2958             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2959                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2960                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2961             } else {
2962                 bodyEl.dom.style.height = '';
2963                 bodyEl.dom.style.overflowY = '';
2964             }
2965             if (cw > w) {
2966                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2967             } else {
2968                 bodyEl.dom.style.overflowX = '';
2969             }
2970             
2971             dlg.setContentSize(w, bodyEl.getHeight());
2972             if(dlg.isVisible()){
2973                 dlg.fixedcenter = true;
2974             }
2975             return this;
2976         },
2977
2978         /**
2979          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2980          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2981          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2982          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2983          * @return {Roo.MessageBox} This message box
2984          */
2985         updateProgress : function(value, text){
2986             if(text){
2987                 this.updateText(text);
2988             }
2989             if (pp) { // weird bug on my firefox - for some reason this is not defined
2990                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2991             }
2992             return this;
2993         },        
2994
2995         /**
2996          * Returns true if the message box is currently displayed
2997          * @return {Boolean} True if the message box is visible, else false
2998          */
2999         isVisible : function(){
3000             return dlg && dlg.isVisible();  
3001         },
3002
3003         /**
3004          * Hides the message box if it is displayed
3005          */
3006         hide : function(){
3007             if(this.isVisible()){
3008                 dlg.hide();
3009             }  
3010         },
3011
3012         /**
3013          * Displays a new message box, or reinitializes an existing message box, based on the config options
3014          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3015          * The following config object properties are supported:
3016          * <pre>
3017 Property    Type             Description
3018 ----------  ---------------  ------------------------------------------------------------------------------------
3019 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3020                                    closes (defaults to undefined)
3021 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3022                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3023 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3024                                    progress and wait dialogs will ignore this property and always hide the
3025                                    close button as they can only be closed programmatically.
3026 cls               String           A custom CSS class to apply to the message box element
3027 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3028                                    displayed (defaults to 75)
3029 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3030                                    function will be btn (the name of the button that was clicked, if applicable,
3031                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3032                                    Progress and wait dialogs will ignore this option since they do not respond to
3033                                    user actions and can only be closed programmatically, so any required function
3034                                    should be called by the same code after it closes the dialog.
3035 icon              String           A CSS class that provides a background image to be used as an icon for
3036                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3037 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3038 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3039 modal             Boolean          False to allow user interaction with the page while the message box is
3040                                    displayed (defaults to true)
3041 msg               String           A string that will replace the existing message box body text (defaults
3042                                    to the XHTML-compliant non-breaking space character '&#160;')
3043 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3044 progress          Boolean          True to display a progress bar (defaults to false)
3045 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3046 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3047 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3048 title             String           The title text
3049 value             String           The string value to set into the active textbox element if displayed
3050 wait              Boolean          True to display a progress bar (defaults to false)
3051 width             Number           The width of the dialog in pixels
3052 </pre>
3053          *
3054          * Example usage:
3055          * <pre><code>
3056 Roo.Msg.show({
3057    title: 'Address',
3058    msg: 'Please enter your address:',
3059    width: 300,
3060    buttons: Roo.MessageBox.OKCANCEL,
3061    multiline: true,
3062    fn: saveAddress,
3063    animEl: 'addAddressBtn'
3064 });
3065 </code></pre>
3066          * @param {Object} config Configuration options
3067          * @return {Roo.MessageBox} This message box
3068          */
3069         show : function(options)
3070         {
3071             
3072             // this causes nightmares if you show one dialog after another
3073             // especially on callbacks..
3074              
3075             if(this.isVisible()){
3076                 
3077                 this.hide();
3078                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3079                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3080                 Roo.log("New Dialog Message:" +  options.msg )
3081                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3082                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3083                 
3084             }
3085             var d = this.getDialog();
3086             opt = options;
3087             d.setTitle(opt.title || "&#160;");
3088             d.closeEl.setDisplayed(opt.closable !== false);
3089             activeTextEl = textboxEl;
3090             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3091             if(opt.prompt){
3092                 if(opt.multiline){
3093                     textboxEl.hide();
3094                     textareaEl.show();
3095                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3096                         opt.multiline : this.defaultTextHeight);
3097                     activeTextEl = textareaEl;
3098                 }else{
3099                     textboxEl.show();
3100                     textareaEl.hide();
3101                 }
3102             }else{
3103                 textboxEl.hide();
3104                 textareaEl.hide();
3105             }
3106             progressEl.setDisplayed(opt.progress === true);
3107             this.updateProgress(0);
3108             activeTextEl.dom.value = opt.value || "";
3109             if(opt.prompt){
3110                 dlg.setDefaultButton(activeTextEl);
3111             }else{
3112                 var bs = opt.buttons;
3113                 var db = null;
3114                 if(bs && bs.ok){
3115                     db = buttons["ok"];
3116                 }else if(bs && bs.yes){
3117                     db = buttons["yes"];
3118                 }
3119                 dlg.setDefaultButton(db);
3120             }
3121             bwidth = updateButtons(opt.buttons);
3122             this.updateText(opt.msg);
3123             if(opt.cls){
3124                 d.el.addClass(opt.cls);
3125             }
3126             d.proxyDrag = opt.proxyDrag === true;
3127             d.modal = opt.modal !== false;
3128             d.mask = opt.modal !== false ? mask : false;
3129             if(!d.isVisible()){
3130                 // force it to the end of the z-index stack so it gets a cursor in FF
3131                 document.body.appendChild(dlg.el.dom);
3132                 d.animateTarget = null;
3133                 d.show(options.animEl);
3134             }
3135             return this;
3136         },
3137
3138         /**
3139          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3140          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3141          * and closing the message box when the process is complete.
3142          * @param {String} title The title bar text
3143          * @param {String} msg The message box body text
3144          * @return {Roo.MessageBox} This message box
3145          */
3146         progress : function(title, msg){
3147             this.show({
3148                 title : title,
3149                 msg : msg,
3150                 buttons: false,
3151                 progress:true,
3152                 closable:false,
3153                 minWidth: this.minProgressWidth,
3154                 modal : true
3155             });
3156             return this;
3157         },
3158
3159         /**
3160          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3161          * If a callback function is passed it will be called after the user clicks the button, and the
3162          * id of the button that was clicked will be passed as the only parameter to the callback
3163          * (could also be the top-right close button).
3164          * @param {String} title The title bar text
3165          * @param {String} msg The message box body text
3166          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3167          * @param {Object} scope (optional) The scope of the callback function
3168          * @return {Roo.MessageBox} This message box
3169          */
3170         alert : function(title, msg, fn, scope){
3171             this.show({
3172                 title : title,
3173                 msg : msg,
3174                 buttons: this.OK,
3175                 fn: fn,
3176                 scope : scope,
3177                 modal : true
3178             });
3179             return this;
3180         },
3181
3182         /**
3183          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3184          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3185          * You are responsible for closing the message box when the process is complete.
3186          * @param {String} msg The message box body text
3187          * @param {String} title (optional) The title bar text
3188          * @return {Roo.MessageBox} This message box
3189          */
3190         wait : function(msg, title){
3191             this.show({
3192                 title : title,
3193                 msg : msg,
3194                 buttons: false,
3195                 closable:false,
3196                 progress:true,
3197                 modal:true,
3198                 width:300,
3199                 wait:true
3200             });
3201             waitTimer = Roo.TaskMgr.start({
3202                 run: function(i){
3203                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3204                 },
3205                 interval: 1000
3206             });
3207             return this;
3208         },
3209
3210         /**
3211          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3212          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3213          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3214          * @param {String} title The title bar text
3215          * @param {String} msg The message box body text
3216          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3217          * @param {Object} scope (optional) The scope of the callback function
3218          * @return {Roo.MessageBox} This message box
3219          */
3220         confirm : function(title, msg, fn, scope){
3221             this.show({
3222                 title : title,
3223                 msg : msg,
3224                 buttons: this.YESNO,
3225                 fn: fn,
3226                 scope : scope,
3227                 modal : true
3228             });
3229             return this;
3230         },
3231
3232         /**
3233          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3234          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3235          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3236          * (could also be the top-right close button) and the text that was entered will be passed as the two
3237          * parameters to the callback.
3238          * @param {String} title The title bar text
3239          * @param {String} msg The message box body text
3240          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3241          * @param {Object} scope (optional) The scope of the callback function
3242          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3243          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3244          * @return {Roo.MessageBox} This message box
3245          */
3246         prompt : function(title, msg, fn, scope, multiline){
3247             this.show({
3248                 title : title,
3249                 msg : msg,
3250                 buttons: this.OKCANCEL,
3251                 fn: fn,
3252                 minWidth:250,
3253                 scope : scope,
3254                 prompt:true,
3255                 multiline: multiline,
3256                 modal : true
3257             });
3258             return this;
3259         },
3260
3261         /**
3262          * Button config that displays a single OK button
3263          * @type Object
3264          */
3265         OK : {ok:true},
3266         /**
3267          * Button config that displays Yes and No buttons
3268          * @type Object
3269          */
3270         YESNO : {yes:true, no:true},
3271         /**
3272          * Button config that displays OK and Cancel buttons
3273          * @type Object
3274          */
3275         OKCANCEL : {ok:true, cancel:true},
3276         /**
3277          * Button config that displays Yes, No and Cancel buttons
3278          * @type Object
3279          */
3280         YESNOCANCEL : {yes:true, no:true, cancel:true},
3281
3282         /**
3283          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3284          * @type Number
3285          */
3286         defaultTextHeight : 75,
3287         /**
3288          * The maximum width in pixels of the message box (defaults to 600)
3289          * @type Number
3290          */
3291         maxWidth : 600,
3292         /**
3293          * The minimum width in pixels of the message box (defaults to 100)
3294          * @type Number
3295          */
3296         minWidth : 100,
3297         /**
3298          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3299          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3300          * @type Number
3301          */
3302         minProgressWidth : 250,
3303         /**
3304          * An object containing the default button text strings that can be overriden for localized language support.
3305          * Supported properties are: ok, cancel, yes and no.
3306          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3307          * @type Object
3308          */
3309         buttonText : {
3310             ok : "OK",
3311             cancel : "Cancel",
3312             yes : "Yes",
3313             no : "No"
3314         }
3315     };
3316 }();
3317
3318 /**
3319  * Shorthand for {@link Roo.MessageBox}
3320  */
3321 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3322 Roo.Msg = Roo.Msg || Roo.MessageBox;
3323 /*
3324  * - LGPL
3325  *
3326  * navbar
3327  * 
3328  */
3329
3330 /**
3331  * @class Roo.bootstrap.Navbar
3332  * @extends Roo.bootstrap.Component
3333  * Bootstrap Navbar class
3334
3335  * @constructor
3336  * Create a new Navbar
3337  * @param {Object} config The config object
3338  */
3339
3340
3341 Roo.bootstrap.Navbar = function(config){
3342     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3343     
3344 };
3345
3346 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3347     
3348     
3349    
3350     // private
3351     navItems : false,
3352     loadMask : false,
3353     
3354     
3355     getAutoCreate : function(){
3356         
3357         
3358         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3359         
3360     },
3361     
3362     initEvents :function ()
3363     {
3364         //Roo.log(this.el.select('.navbar-toggle',true));
3365         this.el.select('.navbar-toggle',true).on('click', function() {
3366            // Roo.log('click');
3367             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3368         }, this);
3369         
3370         var mark = {
3371             tag: "div",
3372             cls:"x-dlg-mask"
3373         };
3374         
3375         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3376         
3377         var size = this.el.getSize();
3378         this.maskEl.setSize(size.width, size.height);
3379         this.maskEl.enableDisplayMode("block");
3380         this.maskEl.hide();
3381         
3382         if(this.loadMask){
3383             this.maskEl.show();
3384         }
3385     },
3386     
3387     
3388     getChildContainer : function()
3389     {
3390         if (this.el.select('.collapse').getCount()) {
3391             return this.el.select('.collapse',true).first();
3392         }
3393         
3394         return this.el;
3395     },
3396     
3397     mask : function()
3398     {
3399         this.maskEl.show();
3400     },
3401     
3402     unmask : function()
3403     {
3404         this.maskEl.hide();
3405     } 
3406     
3407     
3408     
3409     
3410 });
3411
3412
3413
3414  
3415
3416  /*
3417  * - LGPL
3418  *
3419  * navbar
3420  * 
3421  */
3422
3423 /**
3424  * @class Roo.bootstrap.NavSimplebar
3425  * @extends Roo.bootstrap.Navbar
3426  * Bootstrap Sidebar class
3427  *
3428  * @cfg {Boolean} inverse is inverted color
3429  * 
3430  * @cfg {String} type (nav | pills | tabs)
3431  * @cfg {Boolean} arrangement stacked | justified
3432  * @cfg {String} align (left | right) alignment
3433  * 
3434  * @cfg {Boolean} main (true|false) main nav bar? default false
3435  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3436  * 
3437  * @cfg {String} tag (header|footer|nav|div) default is nav 
3438
3439  * 
3440  * 
3441  * 
3442  * @constructor
3443  * Create a new Sidebar
3444  * @param {Object} config The config object
3445  */
3446
3447
3448 Roo.bootstrap.NavSimplebar = function(config){
3449     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3450 };
3451
3452 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3453     
3454     inverse: false,
3455     
3456     type: false,
3457     arrangement: '',
3458     align : false,
3459     
3460     
3461     
3462     main : false,
3463     
3464     
3465     tag : false,
3466     
3467     
3468     getAutoCreate : function(){
3469         
3470         
3471         var cfg = {
3472             tag : this.tag || 'div',
3473             cls : 'navbar'
3474         };
3475           
3476         
3477         cfg.cn = [
3478             {
3479                 cls: 'nav',
3480                 tag : 'ul'
3481             }
3482         ];
3483         
3484          
3485         this.type = this.type || 'nav';
3486         if (['tabs','pills'].indexOf(this.type)!==-1) {
3487             cfg.cn[0].cls += ' nav-' + this.type
3488         
3489         
3490         } else {
3491             if (this.type!=='nav') {
3492                 Roo.log('nav type must be nav/tabs/pills')
3493             }
3494             cfg.cn[0].cls += ' navbar-nav'
3495         }
3496         
3497         
3498         
3499         
3500         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3501             cfg.cn[0].cls += ' nav-' + this.arrangement;
3502         }
3503         
3504         
3505         if (this.align === 'right') {
3506             cfg.cn[0].cls += ' navbar-right';
3507         }
3508         
3509         if (this.inverse) {
3510             cfg.cls += ' navbar-inverse';
3511             
3512         }
3513         
3514         
3515         return cfg;
3516     
3517         
3518     }
3519     
3520     
3521     
3522 });
3523
3524
3525
3526  
3527
3528  
3529        /*
3530  * - LGPL
3531  *
3532  * navbar
3533  * 
3534  */
3535
3536 /**
3537  * @class Roo.bootstrap.NavHeaderbar
3538  * @extends Roo.bootstrap.NavSimplebar
3539  * Bootstrap Sidebar class
3540  *
3541  * @cfg {String} brand what is brand
3542  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3543  * @cfg {String} brand_href href of the brand
3544  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3545  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3546  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3547  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3548  * 
3549  * @constructor
3550  * Create a new Sidebar
3551  * @param {Object} config The config object
3552  */
3553
3554
3555 Roo.bootstrap.NavHeaderbar = function(config){
3556     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3557       
3558 };
3559
3560 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3561     
3562     position: '',
3563     brand: '',
3564     brand_href: false,
3565     srButton : true,
3566     autohide : false,
3567     desktopCenter : false,
3568    
3569     
3570     getAutoCreate : function(){
3571         
3572         var   cfg = {
3573             tag: this.nav || 'nav',
3574             cls: 'navbar',
3575             role: 'navigation',
3576             cn: []
3577         };
3578         
3579         var cn = cfg.cn;
3580         if (this.desktopCenter) {
3581             cn.push({cls : 'container', cn : []});
3582             cn = cn[0].cn;
3583         }
3584         
3585         if(this.srButton){
3586             cn.push({
3587                 tag: 'div',
3588                 cls: 'navbar-header',
3589                 cn: [
3590                     {
3591                         tag: 'button',
3592                         type: 'button',
3593                         cls: 'navbar-toggle',
3594                         'data-toggle': 'collapse',
3595                         cn: [
3596                             {
3597                                 tag: 'span',
3598                                 cls: 'sr-only',
3599                                 html: 'Toggle navigation'
3600                             },
3601                             {
3602                                 tag: 'span',
3603                                 cls: 'icon-bar'
3604                             },
3605                             {
3606                                 tag: 'span',
3607                                 cls: 'icon-bar'
3608                             },
3609                             {
3610                                 tag: 'span',
3611                                 cls: 'icon-bar'
3612                             }
3613                         ]
3614                     }
3615                 ]
3616             });
3617         }
3618         
3619         cn.push({
3620             tag: 'div',
3621             cls: 'collapse navbar-collapse',
3622             cn : []
3623         });
3624         
3625         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3626         
3627         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3628             cfg.cls += ' navbar-' + this.position;
3629             
3630             // tag can override this..
3631             
3632             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3633         }
3634         
3635         if (this.brand !== '') {
3636             cn[0].cn.push({
3637                 tag: 'a',
3638                 href: this.brand_href ? this.brand_href : '#',
3639                 cls: 'navbar-brand',
3640                 cn: [
3641                 this.brand
3642                 ]
3643             });
3644         }
3645         
3646         if(this.main){
3647             cfg.cls += ' main-nav';
3648         }
3649         
3650         
3651         return cfg;
3652
3653         
3654     },
3655     getHeaderChildContainer : function()
3656     {
3657         if (this.el.select('.navbar-header').getCount()) {
3658             return this.el.select('.navbar-header',true).first();
3659         }
3660         
3661         return this.getChildContainer();
3662     },
3663     
3664     
3665     initEvents : function()
3666     {
3667         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3668         
3669         if (this.autohide) {
3670             
3671             var prevScroll = 0;
3672             var ft = this.el;
3673             
3674             Roo.get(document).on('scroll',function(e) {
3675                 var ns = Roo.get(document).getScroll().top;
3676                 var os = prevScroll;
3677                 prevScroll = ns;
3678                 
3679                 if(ns > os){
3680                     ft.removeClass('slideDown');
3681                     ft.addClass('slideUp');
3682                     return;
3683                 }
3684                 ft.removeClass('slideUp');
3685                 ft.addClass('slideDown');
3686                  
3687               
3688           },this);
3689         }
3690     }    
3691     
3692 });
3693
3694
3695
3696  
3697
3698  /*
3699  * - LGPL
3700  *
3701  * navbar
3702  * 
3703  */
3704
3705 /**
3706  * @class Roo.bootstrap.NavSidebar
3707  * @extends Roo.bootstrap.Navbar
3708  * Bootstrap Sidebar class
3709  * 
3710  * @constructor
3711  * Create a new Sidebar
3712  * @param {Object} config The config object
3713  */
3714
3715
3716 Roo.bootstrap.NavSidebar = function(config){
3717     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3718 };
3719
3720 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3721     
3722     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3723     
3724     getAutoCreate : function(){
3725         
3726         
3727         return  {
3728             tag: 'div',
3729             cls: 'sidebar sidebar-nav'
3730         };
3731     
3732         
3733     }
3734     
3735     
3736     
3737 });
3738
3739
3740
3741  
3742
3743  /*
3744  * - LGPL
3745  *
3746  * nav group
3747  * 
3748  */
3749
3750 /**
3751  * @class Roo.bootstrap.NavGroup
3752  * @extends Roo.bootstrap.Component
3753  * Bootstrap NavGroup class
3754  * @cfg {String} align (left|right)
3755  * @cfg {Boolean} inverse
3756  * @cfg {String} type (nav|pills|tab) default nav
3757  * @cfg {String} navId - reference Id for navbar.
3758
3759  * 
3760  * @constructor
3761  * Create a new nav group
3762  * @param {Object} config The config object
3763  */
3764
3765 Roo.bootstrap.NavGroup = function(config){
3766     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3767     this.navItems = [];
3768    
3769     Roo.bootstrap.NavGroup.register(this);
3770      this.addEvents({
3771         /**
3772              * @event changed
3773              * Fires when the active item changes
3774              * @param {Roo.bootstrap.NavGroup} this
3775              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3776              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3777          */
3778         'changed': true
3779      });
3780     
3781 };
3782
3783 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3784     
3785     align: '',
3786     inverse: false,
3787     form: false,
3788     type: 'nav',
3789     navId : '',
3790     // private
3791     
3792     navItems : false, 
3793     
3794     getAutoCreate : function()
3795     {
3796         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3797         
3798         cfg = {
3799             tag : 'ul',
3800             cls: 'nav' 
3801         };
3802         
3803         if (['tabs','pills'].indexOf(this.type)!==-1) {
3804             cfg.cls += ' nav-' + this.type
3805         } else {
3806             if (this.type!=='nav') {
3807                 Roo.log('nav type must be nav/tabs/pills')
3808             }
3809             cfg.cls += ' navbar-nav'
3810         }
3811         
3812         if (this.parent().sidebar) {
3813             cfg = {
3814                 tag: 'ul',
3815                 cls: 'dashboard-menu sidebar-menu'
3816             };
3817             
3818             return cfg;
3819         }
3820         
3821         if (this.form === true) {
3822             cfg = {
3823                 tag: 'form',
3824                 cls: 'navbar-form'
3825             };
3826             
3827             if (this.align === 'right') {
3828                 cfg.cls += ' navbar-right';
3829             } else {
3830                 cfg.cls += ' navbar-left';
3831             }
3832         }
3833         
3834         if (this.align === 'right') {
3835             cfg.cls += ' navbar-right';
3836         }
3837         
3838         if (this.inverse) {
3839             cfg.cls += ' navbar-inverse';
3840             
3841         }
3842         
3843         
3844         return cfg;
3845     },
3846     /**
3847     * sets the active Navigation item
3848     * @param {Roo.bootstrap.NavItem} the new current navitem
3849     */
3850     setActiveItem : function(item)
3851     {
3852         var prev = false;
3853         Roo.each(this.navItems, function(v){
3854             if (v == item) {
3855                 return ;
3856             }
3857             if (v.isActive()) {
3858                 v.setActive(false, true);
3859                 prev = v;
3860                 
3861             }
3862             
3863         });
3864
3865         item.setActive(true, true);
3866         this.fireEvent('changed', this, item, prev);
3867         
3868         
3869     },
3870     /**
3871     * gets the active Navigation item
3872     * @return {Roo.bootstrap.NavItem} the current navitem
3873     */
3874     getActive : function()
3875     {
3876         
3877         var prev = false;
3878         Roo.each(this.navItems, function(v){
3879             
3880             if (v.isActive()) {
3881                 prev = v;
3882                 
3883             }
3884             
3885         });
3886         return prev;
3887     },
3888     
3889     indexOfNav : function()
3890     {
3891         
3892         var prev = false;
3893         Roo.each(this.navItems, function(v,i){
3894             
3895             if (v.isActive()) {
3896                 prev = i;
3897                 
3898             }
3899             
3900         });
3901         return prev;
3902     },
3903     /**
3904     * adds a Navigation item
3905     * @param {Roo.bootstrap.NavItem} the navitem to add
3906     */
3907     addItem : function(cfg)
3908     {
3909         var cn = new Roo.bootstrap.NavItem(cfg);
3910         this.register(cn);
3911         cn.parentId = this.id;
3912         cn.onRender(this.el, null);
3913         return cn;
3914     },
3915     /**
3916     * register a Navigation item
3917     * @param {Roo.bootstrap.NavItem} the navitem to add
3918     */
3919     register : function(item)
3920     {
3921         this.navItems.push( item);
3922         item.navId = this.navId;
3923     
3924     },
3925     
3926     /**
3927     * clear all the Navigation item
3928     */
3929    
3930     clearAll : function()
3931     {
3932         this.navItems = [];
3933         this.el.dom.innerHTML = '';
3934     },
3935     
3936     getNavItem: function(tabId)
3937     {
3938         var ret = false;
3939         Roo.each(this.navItems, function(e) {
3940             if (e.tabId == tabId) {
3941                ret =  e;
3942                return false;
3943             }
3944             return true;
3945             
3946         });
3947         return ret;
3948     },
3949     
3950     setActiveNext : function()
3951     {
3952         var i = this.indexOfNav(this.getActive());
3953         if (i > this.navItems.length) {
3954             return;
3955         }
3956         this.setActiveItem(this.navItems[i+1]);
3957     },
3958     setActivePrev : function()
3959     {
3960         var i = this.indexOfNav(this.getActive());
3961         if (i  < 1) {
3962             return;
3963         }
3964         this.setActiveItem(this.navItems[i-1]);
3965     },
3966     clearWasActive : function(except) {
3967         Roo.each(this.navItems, function(e) {
3968             if (e.tabId != except.tabId && e.was_active) {
3969                e.was_active = false;
3970                return false;
3971             }
3972             return true;
3973             
3974         });
3975     },
3976     getWasActive : function ()
3977     {
3978         var r = false;
3979         Roo.each(this.navItems, function(e) {
3980             if (e.was_active) {
3981                r = e;
3982                return false;
3983             }
3984             return true;
3985             
3986         });
3987         return r;
3988     }
3989     
3990     
3991 });
3992
3993  
3994 Roo.apply(Roo.bootstrap.NavGroup, {
3995     
3996     groups: {},
3997      /**
3998     * register a Navigation Group
3999     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4000     */
4001     register : function(navgrp)
4002     {
4003         this.groups[navgrp.navId] = navgrp;
4004         
4005     },
4006     /**
4007     * fetch a Navigation Group based on the navigation ID
4008     * @param {string} the navgroup to add
4009     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4010     */
4011     get: function(navId) {
4012         if (typeof(this.groups[navId]) == 'undefined') {
4013             return false;
4014             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4015         }
4016         return this.groups[navId] ;
4017     }
4018     
4019     
4020     
4021 });
4022
4023  /*
4024  * - LGPL
4025  *
4026  * row
4027  * 
4028  */
4029
4030 /**
4031  * @class Roo.bootstrap.NavItem
4032  * @extends Roo.bootstrap.Component
4033  * Bootstrap Navbar.NavItem class
4034  * @cfg {String} href  link to
4035  * @cfg {String} html content of button
4036  * @cfg {String} badge text inside badge
4037  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4038  * @cfg {String} glyphicon name of glyphicon
4039  * @cfg {String} icon name of font awesome icon
4040  * @cfg {Boolean} active Is item active
4041  * @cfg {Boolean} disabled Is item disabled
4042  
4043  * @cfg {Boolean} preventDefault (true | false) default false
4044  * @cfg {String} tabId the tab that this item activates.
4045  * @cfg {String} tagtype (a|span) render as a href or span?
4046  * @cfg {Boolean} animateRef (true|false) link to element default false  
4047   
4048  * @constructor
4049  * Create a new Navbar Item
4050  * @param {Object} config The config object
4051  */
4052 Roo.bootstrap.NavItem = function(config){
4053     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4054     this.addEvents({
4055         // raw events
4056         /**
4057          * @event click
4058          * The raw click event for the entire grid.
4059          * @param {Roo.EventObject} e
4060          */
4061         "click" : true,
4062          /**
4063             * @event changed
4064             * Fires when the active item active state changes
4065             * @param {Roo.bootstrap.NavItem} this
4066             * @param {boolean} state the new state
4067              
4068          */
4069         'changed': true,
4070         /**
4071             * @event scrollto
4072             * Fires when scroll to element
4073             * @param {Roo.bootstrap.NavItem} this
4074             * @param {Object} options
4075             * @param {Roo.EventObject} e
4076              
4077          */
4078         'scrollto': true
4079     });
4080    
4081 };
4082
4083 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4084     
4085     href: false,
4086     html: '',
4087     badge: '',
4088     icon: false,
4089     glyphicon: false,
4090     active: false,
4091     preventDefault : false,
4092     tabId : false,
4093     tagtype : 'a',
4094     disabled : false,
4095     animateRef : false,
4096     was_active : false,
4097     
4098     getAutoCreate : function(){
4099          
4100         var cfg = {
4101             tag: 'li',
4102             cls: 'nav-item'
4103             
4104         };
4105         
4106         if (this.active) {
4107             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4108         }
4109         if (this.disabled) {
4110             cfg.cls += ' disabled';
4111         }
4112         
4113         if (this.href || this.html || this.glyphicon || this.icon) {
4114             cfg.cn = [
4115                 {
4116                     tag: this.tagtype,
4117                     href : this.href || "#",
4118                     html: this.html || ''
4119                 }
4120             ];
4121             
4122             if (this.icon) {
4123                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4124             }
4125
4126             if(this.glyphicon) {
4127                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4128             }
4129             
4130             if (this.menu) {
4131                 
4132                 cfg.cn[0].html += " <span class='caret'></span>";
4133              
4134             }
4135             
4136             if (this.badge !== '') {
4137                  
4138                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4139             }
4140         }
4141         
4142         
4143         
4144         return cfg;
4145     },
4146     initEvents: function() 
4147     {
4148         if (typeof (this.menu) != 'undefined') {
4149             this.menu.parentType = this.xtype;
4150             this.menu.triggerEl = this.el;
4151             this.menu = this.addxtype(Roo.apply({}, this.menu));
4152         }
4153         
4154         this.el.select('a',true).on('click', this.onClick, this);
4155         
4156         if(this.tagtype == 'span'){
4157             this.el.select('span',true).on('click', this.onClick, this);
4158         }
4159        
4160         // at this point parent should be available..
4161         this.parent().register(this);
4162     },
4163     
4164     onClick : function(e)
4165     {
4166         if(
4167                 this.preventDefault || 
4168                 this.href == '#' 
4169         ){
4170             
4171             e.preventDefault();
4172         }
4173         
4174         if (this.disabled) {
4175             return;
4176         }
4177         
4178         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4179         if (tg && tg.transition) {
4180             Roo.log("waiting for the transitionend");
4181             return;
4182         }
4183         
4184         
4185         
4186         //Roo.log("fire event clicked");
4187         if(this.fireEvent('click', this, e) === false){
4188             return;
4189         };
4190         
4191         if(this.tagtype == 'span'){
4192             return;
4193         }
4194         
4195         //Roo.log(this.href);
4196         var ael = this.el.select('a',true).first();
4197         //Roo.log(ael);
4198         
4199         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4200             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4201             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4202                 return; // ignore... - it's a 'hash' to another page.
4203             }
4204             
4205             e.preventDefault();
4206             this.scrollToElement(e);
4207         }
4208         
4209         
4210         var p =  this.parent();
4211    
4212         if (['tabs','pills'].indexOf(p.type)!==-1) {
4213             if (typeof(p.setActiveItem) !== 'undefined') {
4214                 p.setActiveItem(this);
4215             }
4216         }
4217         
4218         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4219         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4220             // remove the collapsed menu expand...
4221             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4222         }
4223     },
4224     
4225     isActive: function () {
4226         return this.active
4227     },
4228     setActive : function(state, fire, is_was_active)
4229     {
4230         if (this.active && !state && this.navId) {
4231             this.was_active = true;
4232             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4233             if (nv) {
4234                 nv.clearWasActive(this);
4235             }
4236             
4237         }
4238         this.active = state;
4239         
4240         if (!state ) {
4241             this.el.removeClass('active');
4242         } else if (!this.el.hasClass('active')) {
4243             this.el.addClass('active');
4244         }
4245         if (fire) {
4246             this.fireEvent('changed', this, state);
4247         }
4248         
4249         // show a panel if it's registered and related..
4250         
4251         if (!this.navId || !this.tabId || !state || is_was_active) {
4252             return;
4253         }
4254         
4255         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4256         if (!tg) {
4257             return;
4258         }
4259         var pan = tg.getPanelByName(this.tabId);
4260         if (!pan) {
4261             return;
4262         }
4263         // if we can not flip to new panel - go back to old nav highlight..
4264         if (false == tg.showPanel(pan)) {
4265             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4266             if (nv) {
4267                 var onav = nv.getWasActive();
4268                 if (onav) {
4269                     onav.setActive(true, false, true);
4270                 }
4271             }
4272             
4273         }
4274         
4275         
4276         
4277     },
4278      // this should not be here...
4279     setDisabled : function(state)
4280     {
4281         this.disabled = state;
4282         if (!state ) {
4283             this.el.removeClass('disabled');
4284         } else if (!this.el.hasClass('disabled')) {
4285             this.el.addClass('disabled');
4286         }
4287         
4288     },
4289     
4290     /**
4291      * Fetch the element to display the tooltip on.
4292      * @return {Roo.Element} defaults to this.el
4293      */
4294     tooltipEl : function()
4295     {
4296         return this.el.select('' + this.tagtype + '', true).first();
4297     },
4298     
4299     scrollToElement : function(e)
4300     {
4301         var c = document.body;
4302         
4303         /*
4304          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4305          */
4306         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4307             c = document.documentElement;
4308         }
4309         
4310         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4311         
4312         if(!target){
4313             return;
4314         }
4315
4316         var o = target.calcOffsetsTo(c);
4317         
4318         var options = {
4319             target : target,
4320             value : o[1]
4321         };
4322         
4323         this.fireEvent('scrollto', this, options, e);
4324         
4325         Roo.get(c).scrollTo('top', options.value, true);
4326         
4327         return;
4328     }
4329 });
4330  
4331
4332  /*
4333  * - LGPL
4334  *
4335  * sidebar item
4336  *
4337  *  li
4338  *    <span> icon </span>
4339  *    <span> text </span>
4340  *    <span>badge </span>
4341  */
4342
4343 /**
4344  * @class Roo.bootstrap.NavSidebarItem
4345  * @extends Roo.bootstrap.NavItem
4346  * Bootstrap Navbar.NavSidebarItem class
4347  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4348  * @constructor
4349  * Create a new Navbar Button
4350  * @param {Object} config The config object
4351  */
4352 Roo.bootstrap.NavSidebarItem = function(config){
4353     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4354     this.addEvents({
4355         // raw events
4356         /**
4357          * @event click
4358          * The raw click event for the entire grid.
4359          * @param {Roo.EventObject} e
4360          */
4361         "click" : true,
4362          /**
4363             * @event changed
4364             * Fires when the active item active state changes
4365             * @param {Roo.bootstrap.NavSidebarItem} this
4366             * @param {boolean} state the new state
4367              
4368          */
4369         'changed': true
4370     });
4371    
4372 };
4373
4374 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4375     
4376     badgeWeight : 'default',
4377     
4378     getAutoCreate : function(){
4379         
4380         
4381         var a = {
4382                 tag: 'a',
4383                 href : this.href || '#',
4384                 cls: '',
4385                 html : '',
4386                 cn : []
4387         };
4388         var cfg = {
4389             tag: 'li',
4390             cls: '',
4391             cn: [ a ]
4392         };
4393         var span = {
4394             tag: 'span',
4395             html : this.html || ''
4396         };
4397         
4398         
4399         if (this.active) {
4400             cfg.cls += ' active';
4401         }
4402         
4403         if (this.disabled) {
4404             cfg.cls += ' disabled';
4405         }
4406         
4407         // left icon..
4408         if (this.glyphicon || this.icon) {
4409             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4410             a.cn.push({ tag : 'i', cls : c }) ;
4411         }
4412         // html..
4413         a.cn.push(span);
4414         // then badge..
4415         if (this.badge !== '') {
4416             
4417             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4418         }
4419         // fi
4420         if (this.menu) {
4421             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4422             a.cls += 'dropdown-toggle treeview' ;
4423             
4424         }
4425         
4426         
4427         
4428         return cfg;
4429          
4430            
4431     },
4432     
4433     initEvents : function()
4434     { 
4435         this.el.on('click', this.onClick, this);
4436        
4437     
4438         if(this.badge !== ''){
4439  
4440             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4441         }
4442         
4443     },
4444     
4445     onClick : function(e)
4446     {
4447         if(this.disabled){
4448             e.preventDefault();
4449             return;
4450         }
4451         
4452         if(this.preventDefault){
4453             e.preventDefault();
4454         }
4455         
4456         this.fireEvent('click', this);
4457     },
4458     
4459     disable : function()
4460     {
4461         this.setDisabled(true);
4462     },
4463     
4464     enable : function()
4465     {
4466         this.setDisabled(false);
4467     },
4468     
4469     setDisabled : function(state)
4470     {
4471         if(this.disabled == state){
4472             return;
4473         }
4474         
4475         this.disabled = state;
4476         
4477         if (state) {
4478             this.el.addClass('disabled');
4479             return;
4480         }
4481         
4482         this.el.removeClass('disabled');
4483         
4484         return;
4485     },
4486     
4487     setActive : function(state)
4488     {
4489         if(this.active == state){
4490             return;
4491         }
4492         
4493         this.active = state;
4494         
4495         if (state) {
4496             this.el.addClass('active');
4497             return;
4498         }
4499         
4500         this.el.removeClass('active');
4501         
4502         return;
4503     },
4504     
4505     isActive: function () 
4506     {
4507         return this.active;
4508     },
4509     
4510     setBadge : function(str)
4511     {
4512         if(!this.badgeEl){
4513             return;
4514         }
4515         
4516         this.badgeEl.dom.innerHTML = str;
4517     }
4518     
4519    
4520      
4521  
4522 });
4523  
4524
4525  /*
4526  * - LGPL
4527  *
4528  * row
4529  * 
4530  */
4531
4532 /**
4533  * @class Roo.bootstrap.Row
4534  * @extends Roo.bootstrap.Component
4535  * Bootstrap Row class (contains columns...)
4536  * 
4537  * @constructor
4538  * Create a new Row
4539  * @param {Object} config The config object
4540  */
4541
4542 Roo.bootstrap.Row = function(config){
4543     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4544 };
4545
4546 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4547     
4548     getAutoCreate : function(){
4549        return {
4550             cls: 'row clearfix'
4551        };
4552     }
4553     
4554     
4555 });
4556
4557  
4558
4559  /*
4560  * - LGPL
4561  *
4562  * element
4563  * 
4564  */
4565
4566 /**
4567  * @class Roo.bootstrap.Element
4568  * @extends Roo.bootstrap.Component
4569  * Bootstrap Element class
4570  * @cfg {String} html contents of the element
4571  * @cfg {String} tag tag of the element
4572  * @cfg {String} cls class of the element
4573  * @cfg {Boolean} preventDefault (true|false) default false
4574  * @cfg {Boolean} clickable (true|false) default false
4575  * 
4576  * @constructor
4577  * Create a new Element
4578  * @param {Object} config The config object
4579  */
4580
4581 Roo.bootstrap.Element = function(config){
4582     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4583     
4584     this.addEvents({
4585         // raw events
4586         /**
4587          * @event click
4588          * When a element is chick
4589          * @param {Roo.bootstrap.Element} this
4590          * @param {Roo.EventObject} e
4591          */
4592         "click" : true
4593     });
4594 };
4595
4596 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4597     
4598     tag: 'div',
4599     cls: '',
4600     html: '',
4601     preventDefault: false, 
4602     clickable: false,
4603     
4604     getAutoCreate : function(){
4605         
4606         var cfg = {
4607             tag: this.tag,
4608             cls: this.cls,
4609             html: this.html
4610         };
4611         
4612         return cfg;
4613     },
4614     
4615     initEvents: function() 
4616     {
4617         Roo.bootstrap.Element.superclass.initEvents.call(this);
4618         
4619         if(this.clickable){
4620             this.el.on('click', this.onClick, this);
4621         }
4622         
4623     },
4624     
4625     onClick : function(e)
4626     {
4627         if(this.preventDefault){
4628             e.preventDefault();
4629         }
4630         
4631         this.fireEvent('click', this, e);
4632     },
4633     
4634     getValue : function()
4635     {
4636         return this.el.dom.innerHTML;
4637     },
4638     
4639     setValue : function(value)
4640     {
4641         this.el.dom.innerHTML = value;
4642     }
4643    
4644 });
4645
4646  
4647
4648  /*
4649  * - LGPL
4650  *
4651  * pagination
4652  * 
4653  */
4654
4655 /**
4656  * @class Roo.bootstrap.Pagination
4657  * @extends Roo.bootstrap.Component
4658  * Bootstrap Pagination class
4659  * @cfg {String} size xs | sm | md | lg
4660  * @cfg {Boolean} inverse false | true
4661  * 
4662  * @constructor
4663  * Create a new Pagination
4664  * @param {Object} config The config object
4665  */
4666
4667 Roo.bootstrap.Pagination = function(config){
4668     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4669 };
4670
4671 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4672     
4673     cls: false,
4674     size: false,
4675     inverse: false,
4676     
4677     getAutoCreate : function(){
4678         var cfg = {
4679             tag: 'ul',
4680                 cls: 'pagination'
4681         };
4682         if (this.inverse) {
4683             cfg.cls += ' inverse';
4684         }
4685         if (this.html) {
4686             cfg.html=this.html;
4687         }
4688         if (this.cls) {
4689             cfg.cls += " " + this.cls;
4690         }
4691         return cfg;
4692     }
4693    
4694 });
4695
4696  
4697
4698  /*
4699  * - LGPL
4700  *
4701  * Pagination item
4702  * 
4703  */
4704
4705
4706 /**
4707  * @class Roo.bootstrap.PaginationItem
4708  * @extends Roo.bootstrap.Component
4709  * Bootstrap PaginationItem class
4710  * @cfg {String} html text
4711  * @cfg {String} href the link
4712  * @cfg {Boolean} preventDefault (true | false) default true
4713  * @cfg {Boolean} active (true | false) default false
4714  * @cfg {Boolean} disabled default false
4715  * 
4716  * 
4717  * @constructor
4718  * Create a new PaginationItem
4719  * @param {Object} config The config object
4720  */
4721
4722
4723 Roo.bootstrap.PaginationItem = function(config){
4724     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4725     this.addEvents({
4726         // raw events
4727         /**
4728          * @event click
4729          * The raw click event for the entire grid.
4730          * @param {Roo.EventObject} e
4731          */
4732         "click" : true
4733     });
4734 };
4735
4736 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4737     
4738     href : false,
4739     html : false,
4740     preventDefault: true,
4741     active : false,
4742     cls : false,
4743     disabled: false,
4744     
4745     getAutoCreate : function(){
4746         var cfg= {
4747             tag: 'li',
4748             cn: [
4749                 {
4750                     tag : 'a',
4751                     href : this.href ? this.href : '#',
4752                     html : this.html ? this.html : ''
4753                 }
4754             ]
4755         };
4756         
4757         if(this.cls){
4758             cfg.cls = this.cls;
4759         }
4760         
4761         if(this.disabled){
4762             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4763         }
4764         
4765         if(this.active){
4766             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4767         }
4768         
4769         return cfg;
4770     },
4771     
4772     initEvents: function() {
4773         
4774         this.el.on('click', this.onClick, this);
4775         
4776     },
4777     onClick : function(e)
4778     {
4779         Roo.log('PaginationItem on click ');
4780         if(this.preventDefault){
4781             e.preventDefault();
4782         }
4783         
4784         if(this.disabled){
4785             return;
4786         }
4787         
4788         this.fireEvent('click', this, e);
4789     }
4790    
4791 });
4792
4793  
4794
4795  /*
4796  * - LGPL
4797  *
4798  * slider
4799  * 
4800  */
4801
4802
4803 /**
4804  * @class Roo.bootstrap.Slider
4805  * @extends Roo.bootstrap.Component
4806  * Bootstrap Slider class
4807  *    
4808  * @constructor
4809  * Create a new Slider
4810  * @param {Object} config The config object
4811  */
4812
4813 Roo.bootstrap.Slider = function(config){
4814     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4815 };
4816
4817 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4818     
4819     getAutoCreate : function(){
4820         
4821         var cfg = {
4822             tag: 'div',
4823             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4824             cn: [
4825                 {
4826                     tag: 'a',
4827                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4828                 }
4829             ]
4830         };
4831         
4832         return cfg;
4833     }
4834    
4835 });
4836
4837  /*
4838  * Based on:
4839  * Ext JS Library 1.1.1
4840  * Copyright(c) 2006-2007, Ext JS, LLC.
4841  *
4842  * Originally Released Under LGPL - original licence link has changed is not relivant.
4843  *
4844  * Fork - LGPL
4845  * <script type="text/javascript">
4846  */
4847  
4848
4849 /**
4850  * @class Roo.grid.ColumnModel
4851  * @extends Roo.util.Observable
4852  * This is the default implementation of a ColumnModel used by the Grid. It defines
4853  * the columns in the grid.
4854  * <br>Usage:<br>
4855  <pre><code>
4856  var colModel = new Roo.grid.ColumnModel([
4857         {header: "Ticker", width: 60, sortable: true, locked: true},
4858         {header: "Company Name", width: 150, sortable: true},
4859         {header: "Market Cap.", width: 100, sortable: true},
4860         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4861         {header: "Employees", width: 100, sortable: true, resizable: false}
4862  ]);
4863  </code></pre>
4864  * <p>
4865  
4866  * The config options listed for this class are options which may appear in each
4867  * individual column definition.
4868  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4869  * @constructor
4870  * @param {Object} config An Array of column config objects. See this class's
4871  * config objects for details.
4872 */
4873 Roo.grid.ColumnModel = function(config){
4874         /**
4875      * The config passed into the constructor
4876      */
4877     this.config = config;
4878     this.lookup = {};
4879
4880     // if no id, create one
4881     // if the column does not have a dataIndex mapping,
4882     // map it to the order it is in the config
4883     for(var i = 0, len = config.length; i < len; i++){
4884         var c = config[i];
4885         if(typeof c.dataIndex == "undefined"){
4886             c.dataIndex = i;
4887         }
4888         if(typeof c.renderer == "string"){
4889             c.renderer = Roo.util.Format[c.renderer];
4890         }
4891         if(typeof c.id == "undefined"){
4892             c.id = Roo.id();
4893         }
4894         if(c.editor && c.editor.xtype){
4895             c.editor  = Roo.factory(c.editor, Roo.grid);
4896         }
4897         if(c.editor && c.editor.isFormField){
4898             c.editor = new Roo.grid.GridEditor(c.editor);
4899         }
4900         this.lookup[c.id] = c;
4901     }
4902
4903     /**
4904      * The width of columns which have no width specified (defaults to 100)
4905      * @type Number
4906      */
4907     this.defaultWidth = 100;
4908
4909     /**
4910      * Default sortable of columns which have no sortable specified (defaults to false)
4911      * @type Boolean
4912      */
4913     this.defaultSortable = false;
4914
4915     this.addEvents({
4916         /**
4917              * @event widthchange
4918              * Fires when the width of a column changes.
4919              * @param {ColumnModel} this
4920              * @param {Number} columnIndex The column index
4921              * @param {Number} newWidth The new width
4922              */
4923             "widthchange": true,
4924         /**
4925              * @event headerchange
4926              * Fires when the text of a header changes.
4927              * @param {ColumnModel} this
4928              * @param {Number} columnIndex The column index
4929              * @param {Number} newText The new header text
4930              */
4931             "headerchange": true,
4932         /**
4933              * @event hiddenchange
4934              * Fires when a column is hidden or "unhidden".
4935              * @param {ColumnModel} this
4936              * @param {Number} columnIndex The column index
4937              * @param {Boolean} hidden true if hidden, false otherwise
4938              */
4939             "hiddenchange": true,
4940             /**
4941          * @event columnmoved
4942          * Fires when a column is moved.
4943          * @param {ColumnModel} this
4944          * @param {Number} oldIndex
4945          * @param {Number} newIndex
4946          */
4947         "columnmoved" : true,
4948         /**
4949          * @event columlockchange
4950          * Fires when a column's locked state is changed
4951          * @param {ColumnModel} this
4952          * @param {Number} colIndex
4953          * @param {Boolean} locked true if locked
4954          */
4955         "columnlockchange" : true
4956     });
4957     Roo.grid.ColumnModel.superclass.constructor.call(this);
4958 };
4959 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4960     /**
4961      * @cfg {String} header The header text to display in the Grid view.
4962      */
4963     /**
4964      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4965      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4966      * specified, the column's index is used as an index into the Record's data Array.
4967      */
4968     /**
4969      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4970      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4971      */
4972     /**
4973      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4974      * Defaults to the value of the {@link #defaultSortable} property.
4975      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4976      */
4977     /**
4978      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4979      */
4980     /**
4981      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4982      */
4983     /**
4984      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4985      */
4986     /**
4987      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4988      */
4989     /**
4990      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4991      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4992      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4993      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4994      */
4995        /**
4996      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4997      */
4998     /**
4999      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5000      */
5001     /**
5002      * @cfg {String} cursor (Optional)
5003      */
5004     /**
5005      * @cfg {String} tooltip (Optional)
5006      */
5007     /**
5008      * @cfg {Number} xs (Optional)
5009      */
5010     /**
5011      * @cfg {Number} sm (Optional)
5012      */
5013     /**
5014      * @cfg {Number} md (Optional)
5015      */
5016     /**
5017      * @cfg {Number} lg (Optional)
5018      */
5019     /**
5020      * Returns the id of the column at the specified index.
5021      * @param {Number} index The column index
5022      * @return {String} the id
5023      */
5024     getColumnId : function(index){
5025         return this.config[index].id;
5026     },
5027
5028     /**
5029      * Returns the column for a specified id.
5030      * @param {String} id The column id
5031      * @return {Object} the column
5032      */
5033     getColumnById : function(id){
5034         return this.lookup[id];
5035     },
5036
5037     
5038     /**
5039      * Returns the column for a specified dataIndex.
5040      * @param {String} dataIndex The column dataIndex
5041      * @return {Object|Boolean} the column or false if not found
5042      */
5043     getColumnByDataIndex: function(dataIndex){
5044         var index = this.findColumnIndex(dataIndex);
5045         return index > -1 ? this.config[index] : false;
5046     },
5047     
5048     /**
5049      * Returns the index for a specified column id.
5050      * @param {String} id The column id
5051      * @return {Number} the index, or -1 if not found
5052      */
5053     getIndexById : function(id){
5054         for(var i = 0, len = this.config.length; i < len; i++){
5055             if(this.config[i].id == id){
5056                 return i;
5057             }
5058         }
5059         return -1;
5060     },
5061     
5062     /**
5063      * Returns the index for a specified column dataIndex.
5064      * @param {String} dataIndex The column dataIndex
5065      * @return {Number} the index, or -1 if not found
5066      */
5067     
5068     findColumnIndex : function(dataIndex){
5069         for(var i = 0, len = this.config.length; i < len; i++){
5070             if(this.config[i].dataIndex == dataIndex){
5071                 return i;
5072             }
5073         }
5074         return -1;
5075     },
5076     
5077     
5078     moveColumn : function(oldIndex, newIndex){
5079         var c = this.config[oldIndex];
5080         this.config.splice(oldIndex, 1);
5081         this.config.splice(newIndex, 0, c);
5082         this.dataMap = null;
5083         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5084     },
5085
5086     isLocked : function(colIndex){
5087         return this.config[colIndex].locked === true;
5088     },
5089
5090     setLocked : function(colIndex, value, suppressEvent){
5091         if(this.isLocked(colIndex) == value){
5092             return;
5093         }
5094         this.config[colIndex].locked = value;
5095         if(!suppressEvent){
5096             this.fireEvent("columnlockchange", this, colIndex, value);
5097         }
5098     },
5099
5100     getTotalLockedWidth : function(){
5101         var totalWidth = 0;
5102         for(var i = 0; i < this.config.length; i++){
5103             if(this.isLocked(i) && !this.isHidden(i)){
5104                 this.totalWidth += this.getColumnWidth(i);
5105             }
5106         }
5107         return totalWidth;
5108     },
5109
5110     getLockedCount : function(){
5111         for(var i = 0, len = this.config.length; i < len; i++){
5112             if(!this.isLocked(i)){
5113                 return i;
5114             }
5115         }
5116     },
5117
5118     /**
5119      * Returns the number of columns.
5120      * @return {Number}
5121      */
5122     getColumnCount : function(visibleOnly){
5123         if(visibleOnly === true){
5124             var c = 0;
5125             for(var i = 0, len = this.config.length; i < len; i++){
5126                 if(!this.isHidden(i)){
5127                     c++;
5128                 }
5129             }
5130             return c;
5131         }
5132         return this.config.length;
5133     },
5134
5135     /**
5136      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5137      * @param {Function} fn
5138      * @param {Object} scope (optional)
5139      * @return {Array} result
5140      */
5141     getColumnsBy : function(fn, scope){
5142         var r = [];
5143         for(var i = 0, len = this.config.length; i < len; i++){
5144             var c = this.config[i];
5145             if(fn.call(scope||this, c, i) === true){
5146                 r[r.length] = c;
5147             }
5148         }
5149         return r;
5150     },
5151
5152     /**
5153      * Returns true if the specified column is sortable.
5154      * @param {Number} col The column index
5155      * @return {Boolean}
5156      */
5157     isSortable : function(col){
5158         if(typeof this.config[col].sortable == "undefined"){
5159             return this.defaultSortable;
5160         }
5161         return this.config[col].sortable;
5162     },
5163
5164     /**
5165      * Returns the rendering (formatting) function defined for the column.
5166      * @param {Number} col The column index.
5167      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5168      */
5169     getRenderer : function(col){
5170         if(!this.config[col].renderer){
5171             return Roo.grid.ColumnModel.defaultRenderer;
5172         }
5173         return this.config[col].renderer;
5174     },
5175
5176     /**
5177      * Sets the rendering (formatting) function for a column.
5178      * @param {Number} col The column index
5179      * @param {Function} fn The function to use to process the cell's raw data
5180      * to return HTML markup for the grid view. The render function is called with
5181      * the following parameters:<ul>
5182      * <li>Data value.</li>
5183      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5184      * <li>css A CSS style string to apply to the table cell.</li>
5185      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5186      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5187      * <li>Row index</li>
5188      * <li>Column index</li>
5189      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5190      */
5191     setRenderer : function(col, fn){
5192         this.config[col].renderer = fn;
5193     },
5194
5195     /**
5196      * Returns the width for the specified column.
5197      * @param {Number} col The column index
5198      * @return {Number}
5199      */
5200     getColumnWidth : function(col){
5201         return this.config[col].width * 1 || this.defaultWidth;
5202     },
5203
5204     /**
5205      * Sets the width for a column.
5206      * @param {Number} col The column index
5207      * @param {Number} width The new width
5208      */
5209     setColumnWidth : function(col, width, suppressEvent){
5210         this.config[col].width = width;
5211         this.totalWidth = null;
5212         if(!suppressEvent){
5213              this.fireEvent("widthchange", this, col, width);
5214         }
5215     },
5216
5217     /**
5218      * Returns the total width of all columns.
5219      * @param {Boolean} includeHidden True to include hidden column widths
5220      * @return {Number}
5221      */
5222     getTotalWidth : function(includeHidden){
5223         if(!this.totalWidth){
5224             this.totalWidth = 0;
5225             for(var i = 0, len = this.config.length; i < len; i++){
5226                 if(includeHidden || !this.isHidden(i)){
5227                     this.totalWidth += this.getColumnWidth(i);
5228                 }
5229             }
5230         }
5231         return this.totalWidth;
5232     },
5233
5234     /**
5235      * Returns the header for the specified column.
5236      * @param {Number} col The column index
5237      * @return {String}
5238      */
5239     getColumnHeader : function(col){
5240         return this.config[col].header;
5241     },
5242
5243     /**
5244      * Sets the header for a column.
5245      * @param {Number} col The column index
5246      * @param {String} header The new header
5247      */
5248     setColumnHeader : function(col, header){
5249         this.config[col].header = header;
5250         this.fireEvent("headerchange", this, col, header);
5251     },
5252
5253     /**
5254      * Returns the tooltip for the specified column.
5255      * @param {Number} col The column index
5256      * @return {String}
5257      */
5258     getColumnTooltip : function(col){
5259             return this.config[col].tooltip;
5260     },
5261     /**
5262      * Sets the tooltip for a column.
5263      * @param {Number} col The column index
5264      * @param {String} tooltip The new tooltip
5265      */
5266     setColumnTooltip : function(col, tooltip){
5267             this.config[col].tooltip = tooltip;
5268     },
5269
5270     /**
5271      * Returns the dataIndex for the specified column.
5272      * @param {Number} col The column index
5273      * @return {Number}
5274      */
5275     getDataIndex : function(col){
5276         return this.config[col].dataIndex;
5277     },
5278
5279     /**
5280      * Sets the dataIndex for a column.
5281      * @param {Number} col The column index
5282      * @param {Number} dataIndex The new dataIndex
5283      */
5284     setDataIndex : function(col, dataIndex){
5285         this.config[col].dataIndex = dataIndex;
5286     },
5287
5288     
5289     
5290     /**
5291      * Returns true if the cell is editable.
5292      * @param {Number} colIndex The column index
5293      * @param {Number} rowIndex The row index - this is nto actually used..?
5294      * @return {Boolean}
5295      */
5296     isCellEditable : function(colIndex, rowIndex){
5297         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5298     },
5299
5300     /**
5301      * Returns the editor defined for the cell/column.
5302      * return false or null to disable editing.
5303      * @param {Number} colIndex The column index
5304      * @param {Number} rowIndex The row index
5305      * @return {Object}
5306      */
5307     getCellEditor : function(colIndex, rowIndex){
5308         return this.config[colIndex].editor;
5309     },
5310
5311     /**
5312      * Sets if a column is editable.
5313      * @param {Number} col The column index
5314      * @param {Boolean} editable True if the column is editable
5315      */
5316     setEditable : function(col, editable){
5317         this.config[col].editable = editable;
5318     },
5319
5320
5321     /**
5322      * Returns true if the column is hidden.
5323      * @param {Number} colIndex The column index
5324      * @return {Boolean}
5325      */
5326     isHidden : function(colIndex){
5327         return this.config[colIndex].hidden;
5328     },
5329
5330
5331     /**
5332      * Returns true if the column width cannot be changed
5333      */
5334     isFixed : function(colIndex){
5335         return this.config[colIndex].fixed;
5336     },
5337
5338     /**
5339      * Returns true if the column can be resized
5340      * @return {Boolean}
5341      */
5342     isResizable : function(colIndex){
5343         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5344     },
5345     /**
5346      * Sets if a column is hidden.
5347      * @param {Number} colIndex The column index
5348      * @param {Boolean} hidden True if the column is hidden
5349      */
5350     setHidden : function(colIndex, hidden){
5351         this.config[colIndex].hidden = hidden;
5352         this.totalWidth = null;
5353         this.fireEvent("hiddenchange", this, colIndex, hidden);
5354     },
5355
5356     /**
5357      * Sets the editor for a column.
5358      * @param {Number} col The column index
5359      * @param {Object} editor The editor object
5360      */
5361     setEditor : function(col, editor){
5362         this.config[col].editor = editor;
5363     }
5364 });
5365
5366 Roo.grid.ColumnModel.defaultRenderer = function(value){
5367         if(typeof value == "string" && value.length < 1){
5368             return "&#160;";
5369         }
5370         return value;
5371 };
5372
5373 // Alias for backwards compatibility
5374 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5375 /*
5376  * Based on:
5377  * Ext JS Library 1.1.1
5378  * Copyright(c) 2006-2007, Ext JS, LLC.
5379  *
5380  * Originally Released Under LGPL - original licence link has changed is not relivant.
5381  *
5382  * Fork - LGPL
5383  * <script type="text/javascript">
5384  */
5385  
5386 /**
5387  * @class Roo.LoadMask
5388  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5389  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5390  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5391  * element's UpdateManager load indicator and will be destroyed after the initial load.
5392  * @constructor
5393  * Create a new LoadMask
5394  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5395  * @param {Object} config The config object
5396  */
5397 Roo.LoadMask = function(el, config){
5398     this.el = Roo.get(el);
5399     Roo.apply(this, config);
5400     if(this.store){
5401         this.store.on('beforeload', this.onBeforeLoad, this);
5402         this.store.on('load', this.onLoad, this);
5403         this.store.on('loadexception', this.onLoadException, this);
5404         this.removeMask = false;
5405     }else{
5406         var um = this.el.getUpdateManager();
5407         um.showLoadIndicator = false; // disable the default indicator
5408         um.on('beforeupdate', this.onBeforeLoad, this);
5409         um.on('update', this.onLoad, this);
5410         um.on('failure', this.onLoad, this);
5411         this.removeMask = true;
5412     }
5413 };
5414
5415 Roo.LoadMask.prototype = {
5416     /**
5417      * @cfg {Boolean} removeMask
5418      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5419      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5420      */
5421     /**
5422      * @cfg {String} msg
5423      * The text to display in a centered loading message box (defaults to 'Loading...')
5424      */
5425     msg : 'Loading...',
5426     /**
5427      * @cfg {String} msgCls
5428      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5429      */
5430     msgCls : 'x-mask-loading',
5431
5432     /**
5433      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5434      * @type Boolean
5435      */
5436     disabled: false,
5437
5438     /**
5439      * Disables the mask to prevent it from being displayed
5440      */
5441     disable : function(){
5442        this.disabled = true;
5443     },
5444
5445     /**
5446      * Enables the mask so that it can be displayed
5447      */
5448     enable : function(){
5449         this.disabled = false;
5450     },
5451     
5452     onLoadException : function()
5453     {
5454         Roo.log(arguments);
5455         
5456         if (typeof(arguments[3]) != 'undefined') {
5457             Roo.MessageBox.alert("Error loading",arguments[3]);
5458         } 
5459         /*
5460         try {
5461             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5462                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5463             }   
5464         } catch(e) {
5465             
5466         }
5467         */
5468     
5469         
5470         
5471         this.el.unmask(this.removeMask);
5472     },
5473     // private
5474     onLoad : function()
5475     {
5476         this.el.unmask(this.removeMask);
5477     },
5478
5479     // private
5480     onBeforeLoad : function(){
5481         if(!this.disabled){
5482             this.el.mask(this.msg, this.msgCls);
5483         }
5484     },
5485
5486     // private
5487     destroy : function(){
5488         if(this.store){
5489             this.store.un('beforeload', this.onBeforeLoad, this);
5490             this.store.un('load', this.onLoad, this);
5491             this.store.un('loadexception', this.onLoadException, this);
5492         }else{
5493             var um = this.el.getUpdateManager();
5494             um.un('beforeupdate', this.onBeforeLoad, this);
5495             um.un('update', this.onLoad, this);
5496             um.un('failure', this.onLoad, this);
5497         }
5498     }
5499 };/*
5500  * - LGPL
5501  *
5502  * table
5503  * 
5504  */
5505
5506 /**
5507  * @class Roo.bootstrap.Table
5508  * @extends Roo.bootstrap.Component
5509  * Bootstrap Table class
5510  * @cfg {String} cls table class
5511  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5512  * @cfg {String} bgcolor Specifies the background color for a table
5513  * @cfg {Number} border Specifies whether the table cells should have borders or not
5514  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5515  * @cfg {Number} cellspacing Specifies the space between cells
5516  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5517  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5518  * @cfg {String} sortable Specifies that the table should be sortable
5519  * @cfg {String} summary Specifies a summary of the content of a table
5520  * @cfg {Number} width Specifies the width of a table
5521  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5522  * 
5523  * @cfg {boolean} striped Should the rows be alternative striped
5524  * @cfg {boolean} bordered Add borders to the table
5525  * @cfg {boolean} hover Add hover highlighting
5526  * @cfg {boolean} condensed Format condensed
5527  * @cfg {boolean} responsive Format condensed
5528  * @cfg {Boolean} loadMask (true|false) default false
5529  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5530  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5531  * @cfg {Boolean} rowSelection (true|false) default false
5532  * @cfg {Boolean} cellSelection (true|false) default false
5533  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5534  
5535  * 
5536  * @constructor
5537  * Create a new Table
5538  * @param {Object} config The config object
5539  */
5540
5541 Roo.bootstrap.Table = function(config){
5542     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5543     
5544     // BC...
5545     this.rowSelection = (typeof(config.RowSelection) != 'undefined') ? config.RowSelection : this.rowSelection;
5546     this.cellSelection = (typeof(config.CellSelection) != 'undefined') ? config.CellSelection : this.cellSelection;
5547     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5548     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5549     
5550     
5551     if (this.sm) {
5552         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5553         this.sm = this.selModel;
5554         this.sm.xmodule = this.xmodule || false;
5555     }
5556     if (this.cm && typeof(this.cm.config) == 'undefined') {
5557         this.colModel = new Roo.grid.ColumnModel(this.cm);
5558         this.cm = this.colModel;
5559         this.cm.xmodule = this.xmodule || false;
5560     }
5561     if (this.store) {
5562         this.store= Roo.factory(this.store, Roo.data);
5563         this.ds = this.store;
5564         this.ds.xmodule = this.xmodule || false;
5565          
5566     }
5567     if (this.footer && this.store) {
5568         this.footer.dataSource = this.ds;
5569         this.footer = Roo.factory(this.footer);
5570     }
5571     
5572     /** @private */
5573     this.addEvents({
5574         /**
5575          * @event cellclick
5576          * Fires when a cell is clicked
5577          * @param {Roo.bootstrap.Table} this
5578          * @param {Roo.Element} el
5579          * @param {Number} rowIndex
5580          * @param {Number} columnIndex
5581          * @param {Roo.EventObject} e
5582          */
5583         "cellclick" : true,
5584         /**
5585          * @event celldblclick
5586          * Fires when a cell is double clicked
5587          * @param {Roo.bootstrap.Table} this
5588          * @param {Roo.Element} el
5589          * @param {Number} rowIndex
5590          * @param {Number} columnIndex
5591          * @param {Roo.EventObject} e
5592          */
5593         "celldblclick" : true,
5594         /**
5595          * @event rowclick
5596          * Fires when a row is clicked
5597          * @param {Roo.bootstrap.Table} this
5598          * @param {Roo.Element} el
5599          * @param {Number} rowIndex
5600          * @param {Roo.EventObject} e
5601          */
5602         "rowclick" : true,
5603         /**
5604          * @event rowdblclick
5605          * Fires when a row is double clicked
5606          * @param {Roo.bootstrap.Table} this
5607          * @param {Roo.Element} el
5608          * @param {Number} rowIndex
5609          * @param {Roo.EventObject} e
5610          */
5611         "rowdblclick" : true,
5612         /**
5613          * @event mouseover
5614          * Fires when a mouseover occur
5615          * @param {Roo.bootstrap.Table} this
5616          * @param {Roo.Element} el
5617          * @param {Number} rowIndex
5618          * @param {Number} columnIndex
5619          * @param {Roo.EventObject} e
5620          */
5621         "mouseover" : true,
5622         /**
5623          * @event mouseout
5624          * Fires when a mouseout occur
5625          * @param {Roo.bootstrap.Table} this
5626          * @param {Roo.Element} el
5627          * @param {Number} rowIndex
5628          * @param {Number} columnIndex
5629          * @param {Roo.EventObject} e
5630          */
5631         "mouseout" : true,
5632         /**
5633          * @event rowclass
5634          * Fires when a row is rendered, so you can change add a style to it.
5635          * @param {Roo.bootstrap.Table} this
5636          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5637          */
5638         'rowclass' : true,
5639           /**
5640          * @event rowsrendered
5641          * Fires when all the  rows have been rendered
5642          * @param {Roo.bootstrap.Table} this
5643          */
5644         'rowsrendered' : true
5645         
5646     });
5647 };
5648
5649 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5650     
5651     cls: false,
5652     align: false,
5653     bgcolor: false,
5654     border: false,
5655     cellpadding: false,
5656     cellspacing: false,
5657     frame: false,
5658     rules: false,
5659     sortable: false,
5660     summary: false,
5661     width: false,
5662     striped : false,
5663     bordered: false,
5664     hover:  false,
5665     condensed : false,
5666     responsive : false,
5667     sm : false,
5668     cm : false,
5669     store : false,
5670     loadMask : false,
5671     footerShow : true,
5672     headerShow : true,
5673   
5674     rowSelection : false,
5675     cellSelection : false,
5676     layout : false,
5677     
5678     // Roo.Element - the tbody
5679     mainBody: false, 
5680     
5681     getAutoCreate : function(){
5682         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5683         
5684         cfg = {
5685             tag: 'table',
5686             cls : 'table',
5687             cn : []
5688         };
5689             
5690         if (this.striped) {
5691             cfg.cls += ' table-striped';
5692         }
5693         
5694         if (this.hover) {
5695             cfg.cls += ' table-hover';
5696         }
5697         if (this.bordered) {
5698             cfg.cls += ' table-bordered';
5699         }
5700         if (this.condensed) {
5701             cfg.cls += ' table-condensed';
5702         }
5703         if (this.responsive) {
5704             cfg.cls += ' table-responsive';
5705         }
5706         
5707         if (this.cls) {
5708             cfg.cls+=  ' ' +this.cls;
5709         }
5710         
5711         // this lot should be simplifed...
5712         
5713         if (this.align) {
5714             cfg.align=this.align;
5715         }
5716         if (this.bgcolor) {
5717             cfg.bgcolor=this.bgcolor;
5718         }
5719         if (this.border) {
5720             cfg.border=this.border;
5721         }
5722         if (this.cellpadding) {
5723             cfg.cellpadding=this.cellpadding;
5724         }
5725         if (this.cellspacing) {
5726             cfg.cellspacing=this.cellspacing;
5727         }
5728         if (this.frame) {
5729             cfg.frame=this.frame;
5730         }
5731         if (this.rules) {
5732             cfg.rules=this.rules;
5733         }
5734         if (this.sortable) {
5735             cfg.sortable=this.sortable;
5736         }
5737         if (this.summary) {
5738             cfg.summary=this.summary;
5739         }
5740         if (this.width) {
5741             cfg.width=this.width;
5742         }
5743         if (this.layout) {
5744             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5745         }
5746         
5747         if(this.store || this.cm){
5748             if(this.headerShow){
5749                 cfg.cn.push(this.renderHeader());
5750             }
5751             
5752             cfg.cn.push(this.renderBody());
5753             
5754             if(this.footerShow){
5755                 cfg.cn.push(this.renderFooter());
5756             }
5757             
5758             cfg.cls+=  ' TableGrid';
5759         }
5760         
5761         return { cn : [ cfg ] };
5762     },
5763     
5764     initEvents : function()
5765     {   
5766         if(!this.store || !this.cm){
5767             return;
5768         }
5769         
5770         //Roo.log('initEvents with ds!!!!');
5771         
5772         this.mainBody = this.el.select('tbody', true).first();
5773         
5774         
5775         var _this = this;
5776         
5777         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5778             e.on('click', _this.sort, _this);
5779         });
5780         
5781         this.el.on("click", this.onClick, this);
5782         this.el.on("dblclick", this.onDblClick, this);
5783         
5784         // why is this done????? = it breaks dialogs??
5785         //this.parent().el.setStyle('position', 'relative');
5786         
5787         
5788         if (this.footer) {
5789             this.footer.parentId = this.id;
5790             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5791         }
5792         
5793         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5794         
5795         this.store.on('load', this.onLoad, this);
5796         this.store.on('beforeload', this.onBeforeLoad, this);
5797         this.store.on('update', this.onUpdate, this);
5798         this.store.on('add', this.onAdd, this);
5799         
5800     },
5801     
5802     onMouseover : function(e, el)
5803     {
5804         var cell = Roo.get(el);
5805         
5806         if(!cell){
5807             return;
5808         }
5809         
5810         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5811             cell = cell.findParent('td', false, true);
5812         }
5813         
5814         var row = cell.findParent('tr', false, true);
5815         var cellIndex = cell.dom.cellIndex;
5816         var rowIndex = row.dom.rowIndex - 1; // start from 0
5817         
5818         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5819         
5820     },
5821     
5822     onMouseout : function(e, el)
5823     {
5824         var cell = Roo.get(el);
5825         
5826         if(!cell){
5827             return;
5828         }
5829         
5830         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5831             cell = cell.findParent('td', false, true);
5832         }
5833         
5834         var row = cell.findParent('tr', false, true);
5835         var cellIndex = cell.dom.cellIndex;
5836         var rowIndex = row.dom.rowIndex - 1; // start from 0
5837         
5838         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5839         
5840     },
5841     
5842     onClick : function(e, el)
5843     {
5844         var cell = Roo.get(el);
5845         
5846         if(!cell || (!this.cellSelection && !this.rowSelection)){
5847             return;
5848         }
5849         
5850         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5851             cell = cell.findParent('td', false, true);
5852         }
5853         
5854         if(!cell || typeof(cell) == 'undefined'){
5855             return;
5856         }
5857         
5858         var row = cell.findParent('tr', false, true);
5859         
5860         if(!row || typeof(row) == 'undefined'){
5861             return;
5862         }
5863         
5864         var cellIndex = cell.dom.cellIndex;
5865         var rowIndex = this.getRowIndex(row);
5866         
5867         // why??? - should these not be based on SelectionModel?
5868         if(this.cellSelection){
5869             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5870         }
5871         
5872         if(this.rowSelection){
5873             this.fireEvent('rowclick', this, row, rowIndex, e);
5874         }
5875         
5876         
5877     },
5878     
5879     onDblClick : function(e,el)
5880     {
5881         var cell = Roo.get(el);
5882         
5883         if(!cell || (!this.CellSelection && !this.RowSelection)){
5884             return;
5885         }
5886         
5887         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5888             cell = cell.findParent('td', false, true);
5889         }
5890         
5891         if(!cell || typeof(cell) == 'undefined'){
5892             return;
5893         }
5894         
5895         var row = cell.findParent('tr', false, true);
5896         
5897         if(!row || typeof(row) == 'undefined'){
5898             return;
5899         }
5900         
5901         var cellIndex = cell.dom.cellIndex;
5902         var rowIndex = this.getRowIndex(row);
5903         
5904         if(this.CellSelection){
5905             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5906         }
5907         
5908         if(this.RowSelection){
5909             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5910         }
5911     },
5912     
5913     sort : function(e,el)
5914     {
5915         var col = Roo.get(el);
5916         
5917         if(!col.hasClass('sortable')){
5918             return;
5919         }
5920         
5921         var sort = col.attr('sort');
5922         var dir = 'ASC';
5923         
5924         if(col.hasClass('glyphicon-arrow-up')){
5925             dir = 'DESC';
5926         }
5927         
5928         this.store.sortInfo = {field : sort, direction : dir};
5929         
5930         if (this.footer) {
5931             Roo.log("calling footer first");
5932             this.footer.onClick('first');
5933         } else {
5934         
5935             this.store.load({ params : { start : 0 } });
5936         }
5937     },
5938     
5939     renderHeader : function()
5940     {
5941         var header = {
5942             tag: 'thead',
5943             cn : []
5944         };
5945         
5946         var cm = this.cm;
5947         
5948         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5949             
5950             var config = cm.config[i];
5951             
5952             var c = {
5953                 tag: 'th',
5954                 style : '',
5955                 html: cm.getColumnHeader(i)
5956             };
5957             
5958             var hh = '';
5959             
5960             if(typeof(config.lgHeader) != 'undefined'){
5961                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
5962             }
5963             
5964             if(typeof(config.mdHeader) != 'undefined'){
5965                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
5966             }
5967             
5968             if(typeof(config.smHeader) != 'undefined'){
5969                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
5970             }
5971             
5972             if(typeof(config.xsHeader) != 'undefined'){
5973                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
5974             }
5975             
5976             if(hh.length){
5977                 c.html = hh;
5978             }
5979             
5980             if(typeof(config.tooltip) != 'undefined'){
5981                 c.tooltip = config.tooltip;
5982             }
5983             
5984             if(typeof(config.colspan) != 'undefined'){
5985                 c.colspan = config.colspan;
5986             }
5987             
5988             if(typeof(config.hidden) != 'undefined' && config.hidden){
5989                 c.style += ' display:none;';
5990             }
5991             
5992             if(typeof(config.dataIndex) != 'undefined'){
5993                 c.sort = config.dataIndex;
5994             }
5995             
5996             if(typeof(config.sortable) != 'undefined' && config.sortable){
5997                 c.cls = 'sortable';
5998             }
5999             
6000             if(typeof(config.align) != 'undefined' && config.align.length){
6001                 c.style += ' text-align:' + config.align + ';';
6002             }
6003             
6004             if(typeof(config.width) != 'undefined'){
6005                 c.style += ' width:' + config.width + 'px;';
6006             }
6007             
6008             if(typeof(config.cls) != 'undefined'){
6009                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6010             }
6011             
6012             ['xs','sm','md','lg'].map(function(size){
6013                 
6014                 if(typeof(config[size]) == 'undefined'){
6015                     return;
6016                 }
6017                 
6018                 if (!config[size]) { // 0 = hidden
6019                     c.cls += ' hidden-' + size;
6020                     return;
6021                 }
6022                 
6023                 c.cls += ' col-' + size + '-' + config[size];
6024
6025             });
6026             
6027             header.cn.push(c)
6028         }
6029         
6030         return header;
6031     },
6032     
6033     renderBody : function()
6034     {
6035         var body = {
6036             tag: 'tbody',
6037             cn : [
6038                 {
6039                     tag: 'tr',
6040                     cn : [
6041                         {
6042                             tag : 'td',
6043                             colspan :  this.cm.getColumnCount()
6044                         }
6045                     ]
6046                 }
6047             ]
6048         };
6049         
6050         return body;
6051     },
6052     
6053     renderFooter : function()
6054     {
6055         var footer = {
6056             tag: 'tfoot',
6057             cn : [
6058                 {
6059                     tag: 'tr',
6060                     cn : [
6061                         {
6062                             tag : 'td',
6063                             colspan :  this.cm.getColumnCount()
6064                         }
6065                     ]
6066                 }
6067             ]
6068         };
6069         
6070         return footer;
6071     },
6072     
6073     
6074     
6075     onLoad : function()
6076     {
6077 //        Roo.log('ds onload');
6078         this.clear();
6079         
6080         var _this = this;
6081         var cm = this.cm;
6082         var ds = this.store;
6083         
6084         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6085             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
6086             
6087             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6088                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
6089             }
6090             
6091             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6092                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
6093             }
6094         });
6095         
6096         var tbody =  this.mainBody;
6097               
6098         if(ds.getCount() > 0){
6099             ds.data.each(function(d,rowIndex){
6100                 var row =  this.renderRow(cm, ds, rowIndex);
6101                 
6102                 tbody.createChild(row);
6103                 
6104                 var _this = this;
6105                 
6106                 if(row.cellObjects.length){
6107                     Roo.each(row.cellObjects, function(r){
6108                         _this.renderCellObject(r);
6109                     })
6110                 }
6111                 
6112             }, this);
6113         }
6114         
6115         Roo.each(this.el.select('tbody td', true).elements, function(e){
6116             e.on('mouseover', _this.onMouseover, _this);
6117         });
6118         
6119         Roo.each(this.el.select('tbody td', true).elements, function(e){
6120             e.on('mouseout', _this.onMouseout, _this);
6121         });
6122         this.fireEvent('rowsrendered', this);
6123         //if(this.loadMask){
6124         //    this.maskEl.hide();
6125         //}
6126     },
6127     
6128     
6129     onUpdate : function(ds,record)
6130     {
6131         this.refreshRow(record);
6132     },
6133     
6134     onRemove : function(ds, record, index, isUpdate){
6135         if(isUpdate !== true){
6136             this.fireEvent("beforerowremoved", this, index, record);
6137         }
6138         var bt = this.mainBody.dom;
6139         
6140         var rows = this.el.select('tbody > tr', true).elements;
6141         
6142         if(typeof(rows[index]) != 'undefined'){
6143             bt.removeChild(rows[index].dom);
6144         }
6145         
6146 //        if(bt.rows[index]){
6147 //            bt.removeChild(bt.rows[index]);
6148 //        }
6149         
6150         if(isUpdate !== true){
6151             //this.stripeRows(index);
6152             //this.syncRowHeights(index, index);
6153             //this.layout();
6154             this.fireEvent("rowremoved", this, index, record);
6155         }
6156     },
6157     
6158     onAdd : function(ds, records, rowIndex)
6159     {
6160         //Roo.log('on Add called');
6161         // - note this does not handle multiple adding very well..
6162         var bt = this.mainBody.dom;
6163         for (var i =0 ; i < records.length;i++) {
6164             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6165             //Roo.log(records[i]);
6166             //Roo.log(this.store.getAt(rowIndex+i));
6167             this.insertRow(this.store, rowIndex + i, false);
6168             return;
6169         }
6170         
6171     },
6172     
6173     
6174     refreshRow : function(record){
6175         var ds = this.store, index;
6176         if(typeof record == 'number'){
6177             index = record;
6178             record = ds.getAt(index);
6179         }else{
6180             index = ds.indexOf(record);
6181         }
6182         this.insertRow(ds, index, true);
6183         this.onRemove(ds, record, index+1, true);
6184         //this.syncRowHeights(index, index);
6185         //this.layout();
6186         this.fireEvent("rowupdated", this, index, record);
6187     },
6188     
6189     insertRow : function(dm, rowIndex, isUpdate){
6190         
6191         if(!isUpdate){
6192             this.fireEvent("beforerowsinserted", this, rowIndex);
6193         }
6194             //var s = this.getScrollState();
6195         var row = this.renderRow(this.cm, this.store, rowIndex);
6196         // insert before rowIndex..
6197         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6198         
6199         var _this = this;
6200                 
6201         if(row.cellObjects.length){
6202             Roo.each(row.cellObjects, function(r){
6203                 _this.renderCellObject(r);
6204             })
6205         }
6206             
6207         if(!isUpdate){
6208             this.fireEvent("rowsinserted", this, rowIndex);
6209             //this.syncRowHeights(firstRow, lastRow);
6210             //this.stripeRows(firstRow);
6211             //this.layout();
6212         }
6213         
6214     },
6215     
6216     
6217     getRowDom : function(rowIndex)
6218     {
6219         var rows = this.el.select('tbody > tr', true).elements;
6220         
6221         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6222         
6223     },
6224     // returns the object tree for a tr..
6225   
6226     
6227     renderRow : function(cm, ds, rowIndex) 
6228     {
6229         
6230         var d = ds.getAt(rowIndex);
6231         
6232         var row = {
6233             tag : 'tr',
6234             cn : []
6235         };
6236             
6237         var cellObjects = [];
6238         
6239         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6240             var config = cm.config[i];
6241             
6242             var renderer = cm.getRenderer(i);
6243             var value = '';
6244             var id = false;
6245             
6246             if(typeof(renderer) !== 'undefined'){
6247                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6248             }
6249             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6250             // and are rendered into the cells after the row is rendered - using the id for the element.
6251             
6252             if(typeof(value) === 'object'){
6253                 id = Roo.id();
6254                 cellObjects.push({
6255                     container : id,
6256                     cfg : value 
6257                 })
6258             }
6259             
6260             var rowcfg = {
6261                 record: d,
6262                 rowIndex : rowIndex,
6263                 colIndex : i,
6264                 rowClass : ''
6265             };
6266
6267             this.fireEvent('rowclass', this, rowcfg);
6268             
6269             var td = {
6270                 tag: 'td',
6271                 cls : rowcfg.rowClass,
6272                 style: '',
6273                 html: (typeof(value) === 'object') ? '' : value
6274             };
6275             
6276             if (id) {
6277                 td.id = id;
6278             }
6279             
6280             if(typeof(config.colspan) != 'undefined'){
6281                 td.colspan = config.colspan;
6282             }
6283             
6284             if(typeof(config.hidden) != 'undefined' && config.hidden){
6285                 td.style += ' display:none;';
6286             }
6287             
6288             if(typeof(config.align) != 'undefined' && config.align.length){
6289                 td.style += ' text-align:' + config.align + ';';
6290             }
6291             
6292             if(typeof(config.width) != 'undefined'){
6293                 td.style += ' width:' +  config.width + 'px;';
6294             }
6295             
6296             if(typeof(config.cursor) != 'undefined'){
6297                 td.style += ' cursor:' +  config.cursor + ';';
6298             }
6299             
6300             if(typeof(config.cls) != 'undefined'){
6301                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6302             }
6303             
6304             ['xs','sm','md','lg'].map(function(size){
6305                 
6306                 if(typeof(config[size]) == 'undefined'){
6307                     return;
6308                 }
6309                 
6310                 if (!config[size]) { // 0 = hidden
6311                     td.cls += ' hidden-' + size;
6312                     return;
6313                 }
6314                 
6315                 td.cls += ' col-' + size + '-' + config[size];
6316
6317             });
6318              
6319             row.cn.push(td);
6320            
6321         }
6322         
6323         row.cellObjects = cellObjects;
6324         
6325         return row;
6326           
6327     },
6328     
6329     
6330     
6331     onBeforeLoad : function()
6332     {
6333         //Roo.log('ds onBeforeLoad');
6334         
6335         //this.clear();
6336         
6337         //if(this.loadMask){
6338         //    this.maskEl.show();
6339         //}
6340     },
6341      /**
6342      * Remove all rows
6343      */
6344     clear : function()
6345     {
6346         this.el.select('tbody', true).first().dom.innerHTML = '';
6347     },
6348     /**
6349      * Show or hide a row.
6350      * @param {Number} rowIndex to show or hide
6351      * @param {Boolean} state hide
6352      */
6353     setRowVisibility : function(rowIndex, state)
6354     {
6355         var bt = this.mainBody.dom;
6356         
6357         var rows = this.el.select('tbody > tr', true).elements;
6358         
6359         if(typeof(rows[rowIndex]) == 'undefined'){
6360             return;
6361         }
6362         rows[rowIndex].dom.style.display = state ? '' : 'none';
6363     },
6364     
6365     
6366     getSelectionModel : function(){
6367         if(!this.selModel){
6368             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6369         }
6370         return this.selModel;
6371     },
6372     /*
6373      * Render the Roo.bootstrap object from renderder
6374      */
6375     renderCellObject : function(r)
6376     {
6377         var _this = this;
6378         
6379         var t = r.cfg.render(r.container);
6380         
6381         if(r.cfg.cn){
6382             Roo.each(r.cfg.cn, function(c){
6383                 var child = {
6384                     container: t.getChildContainer(),
6385                     cfg: c
6386                 };
6387                 _this.renderCellObject(child);
6388             })
6389         }
6390     },
6391     
6392     getRowIndex : function(row)
6393     {
6394         var rowIndex = -1;
6395         
6396         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6397             if(el != row){
6398                 return;
6399             }
6400             
6401             rowIndex = index;
6402         });
6403         
6404         return rowIndex;
6405     }
6406    
6407 });
6408
6409  
6410
6411  /*
6412  * - LGPL
6413  *
6414  * table cell
6415  * 
6416  */
6417
6418 /**
6419  * @class Roo.bootstrap.TableCell
6420  * @extends Roo.bootstrap.Component
6421  * Bootstrap TableCell class
6422  * @cfg {String} html cell contain text
6423  * @cfg {String} cls cell class
6424  * @cfg {String} tag cell tag (td|th) default td
6425  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6426  * @cfg {String} align Aligns the content in a cell
6427  * @cfg {String} axis Categorizes cells
6428  * @cfg {String} bgcolor Specifies the background color of a cell
6429  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6430  * @cfg {Number} colspan Specifies the number of columns a cell should span
6431  * @cfg {String} headers Specifies one or more header cells a cell is related to
6432  * @cfg {Number} height Sets the height of a cell
6433  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6434  * @cfg {Number} rowspan Sets the number of rows a cell should span
6435  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6436  * @cfg {String} valign Vertical aligns the content in a cell
6437  * @cfg {Number} width Specifies the width of a cell
6438  * 
6439  * @constructor
6440  * Create a new TableCell
6441  * @param {Object} config The config object
6442  */
6443
6444 Roo.bootstrap.TableCell = function(config){
6445     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6446 };
6447
6448 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6449     
6450     html: false,
6451     cls: false,
6452     tag: false,
6453     abbr: false,
6454     align: false,
6455     axis: false,
6456     bgcolor: false,
6457     charoff: false,
6458     colspan: false,
6459     headers: false,
6460     height: false,
6461     nowrap: false,
6462     rowspan: false,
6463     scope: false,
6464     valign: false,
6465     width: false,
6466     
6467     
6468     getAutoCreate : function(){
6469         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6470         
6471         cfg = {
6472             tag: 'td'
6473         };
6474         
6475         if(this.tag){
6476             cfg.tag = this.tag;
6477         }
6478         
6479         if (this.html) {
6480             cfg.html=this.html
6481         }
6482         if (this.cls) {
6483             cfg.cls=this.cls
6484         }
6485         if (this.abbr) {
6486             cfg.abbr=this.abbr
6487         }
6488         if (this.align) {
6489             cfg.align=this.align
6490         }
6491         if (this.axis) {
6492             cfg.axis=this.axis
6493         }
6494         if (this.bgcolor) {
6495             cfg.bgcolor=this.bgcolor
6496         }
6497         if (this.charoff) {
6498             cfg.charoff=this.charoff
6499         }
6500         if (this.colspan) {
6501             cfg.colspan=this.colspan
6502         }
6503         if (this.headers) {
6504             cfg.headers=this.headers
6505         }
6506         if (this.height) {
6507             cfg.height=this.height
6508         }
6509         if (this.nowrap) {
6510             cfg.nowrap=this.nowrap
6511         }
6512         if (this.rowspan) {
6513             cfg.rowspan=this.rowspan
6514         }
6515         if (this.scope) {
6516             cfg.scope=this.scope
6517         }
6518         if (this.valign) {
6519             cfg.valign=this.valign
6520         }
6521         if (this.width) {
6522             cfg.width=this.width
6523         }
6524         
6525         
6526         return cfg;
6527     }
6528    
6529 });
6530
6531  
6532
6533  /*
6534  * - LGPL
6535  *
6536  * table row
6537  * 
6538  */
6539
6540 /**
6541  * @class Roo.bootstrap.TableRow
6542  * @extends Roo.bootstrap.Component
6543  * Bootstrap TableRow class
6544  * @cfg {String} cls row class
6545  * @cfg {String} align Aligns the content in a table row
6546  * @cfg {String} bgcolor Specifies a background color for a table row
6547  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6548  * @cfg {String} valign Vertical aligns the content in a table row
6549  * 
6550  * @constructor
6551  * Create a new TableRow
6552  * @param {Object} config The config object
6553  */
6554
6555 Roo.bootstrap.TableRow = function(config){
6556     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6557 };
6558
6559 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6560     
6561     cls: false,
6562     align: false,
6563     bgcolor: false,
6564     charoff: false,
6565     valign: false,
6566     
6567     getAutoCreate : function(){
6568         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6569         
6570         cfg = {
6571             tag: 'tr'
6572         };
6573             
6574         if(this.cls){
6575             cfg.cls = this.cls;
6576         }
6577         if(this.align){
6578             cfg.align = this.align;
6579         }
6580         if(this.bgcolor){
6581             cfg.bgcolor = this.bgcolor;
6582         }
6583         if(this.charoff){
6584             cfg.charoff = this.charoff;
6585         }
6586         if(this.valign){
6587             cfg.valign = this.valign;
6588         }
6589         
6590         return cfg;
6591     }
6592    
6593 });
6594
6595  
6596
6597  /*
6598  * - LGPL
6599  *
6600  * table body
6601  * 
6602  */
6603
6604 /**
6605  * @class Roo.bootstrap.TableBody
6606  * @extends Roo.bootstrap.Component
6607  * Bootstrap TableBody class
6608  * @cfg {String} cls element class
6609  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6610  * @cfg {String} align Aligns the content inside the element
6611  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6612  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6613  * 
6614  * @constructor
6615  * Create a new TableBody
6616  * @param {Object} config The config object
6617  */
6618
6619 Roo.bootstrap.TableBody = function(config){
6620     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6621 };
6622
6623 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6624     
6625     cls: false,
6626     tag: false,
6627     align: false,
6628     charoff: false,
6629     valign: false,
6630     
6631     getAutoCreate : function(){
6632         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6633         
6634         cfg = {
6635             tag: 'tbody'
6636         };
6637             
6638         if (this.cls) {
6639             cfg.cls=this.cls
6640         }
6641         if(this.tag){
6642             cfg.tag = this.tag;
6643         }
6644         
6645         if(this.align){
6646             cfg.align = this.align;
6647         }
6648         if(this.charoff){
6649             cfg.charoff = this.charoff;
6650         }
6651         if(this.valign){
6652             cfg.valign = this.valign;
6653         }
6654         
6655         return cfg;
6656     }
6657     
6658     
6659 //    initEvents : function()
6660 //    {
6661 //        
6662 //        if(!this.store){
6663 //            return;
6664 //        }
6665 //        
6666 //        this.store = Roo.factory(this.store, Roo.data);
6667 //        this.store.on('load', this.onLoad, this);
6668 //        
6669 //        this.store.load();
6670 //        
6671 //    },
6672 //    
6673 //    onLoad: function () 
6674 //    {   
6675 //        this.fireEvent('load', this);
6676 //    }
6677 //    
6678 //   
6679 });
6680
6681  
6682
6683  /*
6684  * Based on:
6685  * Ext JS Library 1.1.1
6686  * Copyright(c) 2006-2007, Ext JS, LLC.
6687  *
6688  * Originally Released Under LGPL - original licence link has changed is not relivant.
6689  *
6690  * Fork - LGPL
6691  * <script type="text/javascript">
6692  */
6693
6694 // as we use this in bootstrap.
6695 Roo.namespace('Roo.form');
6696  /**
6697  * @class Roo.form.Action
6698  * Internal Class used to handle form actions
6699  * @constructor
6700  * @param {Roo.form.BasicForm} el The form element or its id
6701  * @param {Object} config Configuration options
6702  */
6703
6704  
6705  
6706 // define the action interface
6707 Roo.form.Action = function(form, options){
6708     this.form = form;
6709     this.options = options || {};
6710 };
6711 /**
6712  * Client Validation Failed
6713  * @const 
6714  */
6715 Roo.form.Action.CLIENT_INVALID = 'client';
6716 /**
6717  * Server Validation Failed
6718  * @const 
6719  */
6720 Roo.form.Action.SERVER_INVALID = 'server';
6721  /**
6722  * Connect to Server Failed
6723  * @const 
6724  */
6725 Roo.form.Action.CONNECT_FAILURE = 'connect';
6726 /**
6727  * Reading Data from Server Failed
6728  * @const 
6729  */
6730 Roo.form.Action.LOAD_FAILURE = 'load';
6731
6732 Roo.form.Action.prototype = {
6733     type : 'default',
6734     failureType : undefined,
6735     response : undefined,
6736     result : undefined,
6737
6738     // interface method
6739     run : function(options){
6740
6741     },
6742
6743     // interface method
6744     success : function(response){
6745
6746     },
6747
6748     // interface method
6749     handleResponse : function(response){
6750
6751     },
6752
6753     // default connection failure
6754     failure : function(response){
6755         
6756         this.response = response;
6757         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6758         this.form.afterAction(this, false);
6759     },
6760
6761     processResponse : function(response){
6762         this.response = response;
6763         if(!response.responseText){
6764             return true;
6765         }
6766         this.result = this.handleResponse(response);
6767         return this.result;
6768     },
6769
6770     // utility functions used internally
6771     getUrl : function(appendParams){
6772         var url = this.options.url || this.form.url || this.form.el.dom.action;
6773         if(appendParams){
6774             var p = this.getParams();
6775             if(p){
6776                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6777             }
6778         }
6779         return url;
6780     },
6781
6782     getMethod : function(){
6783         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6784     },
6785
6786     getParams : function(){
6787         var bp = this.form.baseParams;
6788         var p = this.options.params;
6789         if(p){
6790             if(typeof p == "object"){
6791                 p = Roo.urlEncode(Roo.applyIf(p, bp));
6792             }else if(typeof p == 'string' && bp){
6793                 p += '&' + Roo.urlEncode(bp);
6794             }
6795         }else if(bp){
6796             p = Roo.urlEncode(bp);
6797         }
6798         return p;
6799     },
6800
6801     createCallback : function(){
6802         return {
6803             success: this.success,
6804             failure: this.failure,
6805             scope: this,
6806             timeout: (this.form.timeout*1000),
6807             upload: this.form.fileUpload ? this.success : undefined
6808         };
6809     }
6810 };
6811
6812 Roo.form.Action.Submit = function(form, options){
6813     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6814 };
6815
6816 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6817     type : 'submit',
6818
6819     haveProgress : false,
6820     uploadComplete : false,
6821     
6822     // uploadProgress indicator.
6823     uploadProgress : function()
6824     {
6825         if (!this.form.progressUrl) {
6826             return;
6827         }
6828         
6829         if (!this.haveProgress) {
6830             Roo.MessageBox.progress("Uploading", "Uploading");
6831         }
6832         if (this.uploadComplete) {
6833            Roo.MessageBox.hide();
6834            return;
6835         }
6836         
6837         this.haveProgress = true;
6838    
6839         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6840         
6841         var c = new Roo.data.Connection();
6842         c.request({
6843             url : this.form.progressUrl,
6844             params: {
6845                 id : uid
6846             },
6847             method: 'GET',
6848             success : function(req){
6849                //console.log(data);
6850                 var rdata = false;
6851                 var edata;
6852                 try  {
6853                    rdata = Roo.decode(req.responseText)
6854                 } catch (e) {
6855                     Roo.log("Invalid data from server..");
6856                     Roo.log(edata);
6857                     return;
6858                 }
6859                 if (!rdata || !rdata.success) {
6860                     Roo.log(rdata);
6861                     Roo.MessageBox.alert(Roo.encode(rdata));
6862                     return;
6863                 }
6864                 var data = rdata.data;
6865                 
6866                 if (this.uploadComplete) {
6867                    Roo.MessageBox.hide();
6868                    return;
6869                 }
6870                    
6871                 if (data){
6872                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6873                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6874                     );
6875                 }
6876                 this.uploadProgress.defer(2000,this);
6877             },
6878        
6879             failure: function(data) {
6880                 Roo.log('progress url failed ');
6881                 Roo.log(data);
6882             },
6883             scope : this
6884         });
6885            
6886     },
6887     
6888     
6889     run : function()
6890     {
6891         // run get Values on the form, so it syncs any secondary forms.
6892         this.form.getValues();
6893         
6894         var o = this.options;
6895         var method = this.getMethod();
6896         var isPost = method == 'POST';
6897         if(o.clientValidation === false || this.form.isValid()){
6898             
6899             if (this.form.progressUrl) {
6900                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6901                     (new Date() * 1) + '' + Math.random());
6902                     
6903             } 
6904             
6905             
6906             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6907                 form:this.form.el.dom,
6908                 url:this.getUrl(!isPost),
6909                 method: method,
6910                 params:isPost ? this.getParams() : null,
6911                 isUpload: this.form.fileUpload
6912             }));
6913             
6914             this.uploadProgress();
6915
6916         }else if (o.clientValidation !== false){ // client validation failed
6917             this.failureType = Roo.form.Action.CLIENT_INVALID;
6918             this.form.afterAction(this, false);
6919         }
6920     },
6921
6922     success : function(response)
6923     {
6924         this.uploadComplete= true;
6925         if (this.haveProgress) {
6926             Roo.MessageBox.hide();
6927         }
6928         
6929         
6930         var result = this.processResponse(response);
6931         if(result === true || result.success){
6932             this.form.afterAction(this, true);
6933             return;
6934         }
6935         if(result.errors){
6936             this.form.markInvalid(result.errors);
6937             this.failureType = Roo.form.Action.SERVER_INVALID;
6938         }
6939         this.form.afterAction(this, false);
6940     },
6941     failure : function(response)
6942     {
6943         this.uploadComplete= true;
6944         if (this.haveProgress) {
6945             Roo.MessageBox.hide();
6946         }
6947         
6948         this.response = response;
6949         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6950         this.form.afterAction(this, false);
6951     },
6952     
6953     handleResponse : function(response){
6954         if(this.form.errorReader){
6955             var rs = this.form.errorReader.read(response);
6956             var errors = [];
6957             if(rs.records){
6958                 for(var i = 0, len = rs.records.length; i < len; i++) {
6959                     var r = rs.records[i];
6960                     errors[i] = r.data;
6961                 }
6962             }
6963             if(errors.length < 1){
6964                 errors = null;
6965             }
6966             return {
6967                 success : rs.success,
6968                 errors : errors
6969             };
6970         }
6971         var ret = false;
6972         try {
6973             ret = Roo.decode(response.responseText);
6974         } catch (e) {
6975             ret = {
6976                 success: false,
6977                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6978                 errors : []
6979             };
6980         }
6981         return ret;
6982         
6983     }
6984 });
6985
6986
6987 Roo.form.Action.Load = function(form, options){
6988     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6989     this.reader = this.form.reader;
6990 };
6991
6992 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6993     type : 'load',
6994
6995     run : function(){
6996         
6997         Roo.Ajax.request(Roo.apply(
6998                 this.createCallback(), {
6999                     method:this.getMethod(),
7000                     url:this.getUrl(false),
7001                     params:this.getParams()
7002         }));
7003     },
7004
7005     success : function(response){
7006         
7007         var result = this.processResponse(response);
7008         if(result === true || !result.success || !result.data){
7009             this.failureType = Roo.form.Action.LOAD_FAILURE;
7010             this.form.afterAction(this, false);
7011             return;
7012         }
7013         this.form.clearInvalid();
7014         this.form.setValues(result.data);
7015         this.form.afterAction(this, true);
7016     },
7017
7018     handleResponse : function(response){
7019         if(this.form.reader){
7020             var rs = this.form.reader.read(response);
7021             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7022             return {
7023                 success : rs.success,
7024                 data : data
7025             };
7026         }
7027         return Roo.decode(response.responseText);
7028     }
7029 });
7030
7031 Roo.form.Action.ACTION_TYPES = {
7032     'load' : Roo.form.Action.Load,
7033     'submit' : Roo.form.Action.Submit
7034 };/*
7035  * - LGPL
7036  *
7037  * form
7038  * 
7039  */
7040
7041 /**
7042  * @class Roo.bootstrap.Form
7043  * @extends Roo.bootstrap.Component
7044  * Bootstrap Form class
7045  * @cfg {String} method  GET | POST (default POST)
7046  * @cfg {String} labelAlign top | left (default top)
7047  * @cfg {String} align left  | right - for navbars
7048  * @cfg {Boolean} loadMask load mask when submit (default true)
7049
7050  * 
7051  * @constructor
7052  * Create a new Form
7053  * @param {Object} config The config object
7054  */
7055
7056
7057 Roo.bootstrap.Form = function(config){
7058     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7059     this.addEvents({
7060         /**
7061          * @event clientvalidation
7062          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7063          * @param {Form} this
7064          * @param {Boolean} valid true if the form has passed client-side validation
7065          */
7066         clientvalidation: true,
7067         /**
7068          * @event beforeaction
7069          * Fires before any action is performed. Return false to cancel the action.
7070          * @param {Form} this
7071          * @param {Action} action The action to be performed
7072          */
7073         beforeaction: true,
7074         /**
7075          * @event actionfailed
7076          * Fires when an action fails.
7077          * @param {Form} this
7078          * @param {Action} action The action that failed
7079          */
7080         actionfailed : true,
7081         /**
7082          * @event actioncomplete
7083          * Fires when an action is completed.
7084          * @param {Form} this
7085          * @param {Action} action The action that completed
7086          */
7087         actioncomplete : true
7088     });
7089     
7090 };
7091
7092 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7093       
7094      /**
7095      * @cfg {String} method
7096      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7097      */
7098     method : 'POST',
7099     /**
7100      * @cfg {String} url
7101      * The URL to use for form actions if one isn't supplied in the action options.
7102      */
7103     /**
7104      * @cfg {Boolean} fileUpload
7105      * Set to true if this form is a file upload.
7106      */
7107      
7108     /**
7109      * @cfg {Object} baseParams
7110      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7111      */
7112       
7113     /**
7114      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7115      */
7116     timeout: 30,
7117     /**
7118      * @cfg {Sting} align (left|right) for navbar forms
7119      */
7120     align : 'left',
7121
7122     // private
7123     activeAction : null,
7124  
7125     /**
7126      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7127      * element by passing it or its id or mask the form itself by passing in true.
7128      * @type Mixed
7129      */
7130     waitMsgTarget : false,
7131     
7132     loadMask : true,
7133     
7134     getAutoCreate : function(){
7135         
7136         var cfg = {
7137             tag: 'form',
7138             method : this.method || 'POST',
7139             id : this.id || Roo.id(),
7140             cls : ''
7141         };
7142         if (this.parent().xtype.match(/^Nav/)) {
7143             cfg.cls = 'navbar-form navbar-' + this.align;
7144             
7145         }
7146         
7147         if (this.labelAlign == 'left' ) {
7148             cfg.cls += ' form-horizontal';
7149         }
7150         
7151         
7152         return cfg;
7153     },
7154     initEvents : function()
7155     {
7156         this.el.on('submit', this.onSubmit, this);
7157         // this was added as random key presses on the form where triggering form submit.
7158         this.el.on('keypress', function(e) {
7159             if (e.getCharCode() != 13) {
7160                 return true;
7161             }
7162             // we might need to allow it for textareas.. and some other items.
7163             // check e.getTarget().
7164             
7165             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7166                 return true;
7167             }
7168         
7169             Roo.log("keypress blocked");
7170             
7171             e.preventDefault();
7172             return false;
7173         });
7174         
7175     },
7176     // private
7177     onSubmit : function(e){
7178         e.stopEvent();
7179     },
7180     
7181      /**
7182      * Returns true if client-side validation on the form is successful.
7183      * @return Boolean
7184      */
7185     isValid : function(){
7186         var items = this.getItems();
7187         var valid = true;
7188         items.each(function(f){
7189            if(!f.validate()){
7190                valid = false;
7191                
7192            }
7193         });
7194         return valid;
7195     },
7196     /**
7197      * Returns true if any fields in this form have changed since their original load.
7198      * @return Boolean
7199      */
7200     isDirty : function(){
7201         var dirty = false;
7202         var items = this.getItems();
7203         items.each(function(f){
7204            if(f.isDirty()){
7205                dirty = true;
7206                return false;
7207            }
7208            return true;
7209         });
7210         return dirty;
7211     },
7212      /**
7213      * Performs a predefined action (submit or load) or custom actions you define on this form.
7214      * @param {String} actionName The name of the action type
7215      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7216      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7217      * accept other config options):
7218      * <pre>
7219 Property          Type             Description
7220 ----------------  ---------------  ----------------------------------------------------------------------------------
7221 url               String           The url for the action (defaults to the form's url)
7222 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7223 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7224 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7225                                    validate the form on the client (defaults to false)
7226      * </pre>
7227      * @return {BasicForm} this
7228      */
7229     doAction : function(action, options){
7230         if(typeof action == 'string'){
7231             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7232         }
7233         if(this.fireEvent('beforeaction', this, action) !== false){
7234             this.beforeAction(action);
7235             action.run.defer(100, action);
7236         }
7237         return this;
7238     },
7239     
7240     // private
7241     beforeAction : function(action){
7242         var o = action.options;
7243         
7244         if(this.loadMask){
7245             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7246         }
7247         // not really supported yet.. ??
7248         
7249         //if(this.waitMsgTarget === true){
7250         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7251         //}else if(this.waitMsgTarget){
7252         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7253         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7254         //}else {
7255         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7256        // }
7257          
7258     },
7259
7260     // private
7261     afterAction : function(action, success){
7262         this.activeAction = null;
7263         var o = action.options;
7264         
7265         //if(this.waitMsgTarget === true){
7266             this.el.unmask();
7267         //}else if(this.waitMsgTarget){
7268         //    this.waitMsgTarget.unmask();
7269         //}else{
7270         //    Roo.MessageBox.updateProgress(1);
7271         //    Roo.MessageBox.hide();
7272        // }
7273         // 
7274         if(success){
7275             if(o.reset){
7276                 this.reset();
7277             }
7278             Roo.callback(o.success, o.scope, [this, action]);
7279             this.fireEvent('actioncomplete', this, action);
7280             
7281         }else{
7282             
7283             // failure condition..
7284             // we have a scenario where updates need confirming.
7285             // eg. if a locking scenario exists..
7286             // we look for { errors : { needs_confirm : true }} in the response.
7287             if (
7288                 (typeof(action.result) != 'undefined')  &&
7289                 (typeof(action.result.errors) != 'undefined')  &&
7290                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7291            ){
7292                 var _t = this;
7293                 Roo.log("not supported yet");
7294                  /*
7295                 
7296                 Roo.MessageBox.confirm(
7297                     "Change requires confirmation",
7298                     action.result.errorMsg,
7299                     function(r) {
7300                         if (r != 'yes') {
7301                             return;
7302                         }
7303                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7304                     }
7305                     
7306                 );
7307                 */
7308                 
7309                 
7310                 return;
7311             }
7312             
7313             Roo.callback(o.failure, o.scope, [this, action]);
7314             // show an error message if no failed handler is set..
7315             if (!this.hasListener('actionfailed')) {
7316                 Roo.log("need to add dialog support");
7317                 /*
7318                 Roo.MessageBox.alert("Error",
7319                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7320                         action.result.errorMsg :
7321                         "Saving Failed, please check your entries or try again"
7322                 );
7323                 */
7324             }
7325             
7326             this.fireEvent('actionfailed', this, action);
7327         }
7328         
7329     },
7330     /**
7331      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7332      * @param {String} id The value to search for
7333      * @return Field
7334      */
7335     findField : function(id){
7336         var items = this.getItems();
7337         var field = items.get(id);
7338         if(!field){
7339              items.each(function(f){
7340                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7341                     field = f;
7342                     return false;
7343                 }
7344                 return true;
7345             });
7346         }
7347         return field || null;
7348     },
7349      /**
7350      * Mark fields in this form invalid in bulk.
7351      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7352      * @return {BasicForm} this
7353      */
7354     markInvalid : function(errors){
7355         if(errors instanceof Array){
7356             for(var i = 0, len = errors.length; i < len; i++){
7357                 var fieldError = errors[i];
7358                 var f = this.findField(fieldError.id);
7359                 if(f){
7360                     f.markInvalid(fieldError.msg);
7361                 }
7362             }
7363         }else{
7364             var field, id;
7365             for(id in errors){
7366                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7367                     field.markInvalid(errors[id]);
7368                 }
7369             }
7370         }
7371         //Roo.each(this.childForms || [], function (f) {
7372         //    f.markInvalid(errors);
7373         //});
7374         
7375         return this;
7376     },
7377
7378     /**
7379      * Set values for fields in this form in bulk.
7380      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7381      * @return {BasicForm} this
7382      */
7383     setValues : function(values){
7384         if(values instanceof Array){ // array of objects
7385             for(var i = 0, len = values.length; i < len; i++){
7386                 var v = values[i];
7387                 var f = this.findField(v.id);
7388                 if(f){
7389                     f.setValue(v.value);
7390                     if(this.trackResetOnLoad){
7391                         f.originalValue = f.getValue();
7392                     }
7393                 }
7394             }
7395         }else{ // object hash
7396             var field, id;
7397             for(id in values){
7398                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7399                     
7400                     if (field.setFromData && 
7401                         field.valueField && 
7402                         field.displayField &&
7403                         // combos' with local stores can 
7404                         // be queried via setValue()
7405                         // to set their value..
7406                         (field.store && !field.store.isLocal)
7407                         ) {
7408                         // it's a combo
7409                         var sd = { };
7410                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7411                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7412                         field.setFromData(sd);
7413                         
7414                     } else {
7415                         field.setValue(values[id]);
7416                     }
7417                     
7418                     
7419                     if(this.trackResetOnLoad){
7420                         field.originalValue = field.getValue();
7421                     }
7422                 }
7423             }
7424         }
7425          
7426         //Roo.each(this.childForms || [], function (f) {
7427         //    f.setValues(values);
7428         //});
7429                 
7430         return this;
7431     },
7432
7433     /**
7434      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7435      * they are returned as an array.
7436      * @param {Boolean} asString
7437      * @return {Object}
7438      */
7439     getValues : function(asString){
7440         //if (this.childForms) {
7441             // copy values from the child forms
7442         //    Roo.each(this.childForms, function (f) {
7443         //        this.setValues(f.getValues());
7444         //    }, this);
7445         //}
7446         
7447         
7448         
7449         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7450         if(asString === true){
7451             return fs;
7452         }
7453         return Roo.urlDecode(fs);
7454     },
7455     
7456     /**
7457      * Returns the fields in this form as an object with key/value pairs. 
7458      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7459      * @return {Object}
7460      */
7461     getFieldValues : function(with_hidden)
7462     {
7463         var items = this.getItems();
7464         var ret = {};
7465         items.each(function(f){
7466             if (!f.getName()) {
7467                 return;
7468             }
7469             var v = f.getValue();
7470             if (f.inputType =='radio') {
7471                 if (typeof(ret[f.getName()]) == 'undefined') {
7472                     ret[f.getName()] = ''; // empty..
7473                 }
7474                 
7475                 if (!f.el.dom.checked) {
7476                     return;
7477                     
7478                 }
7479                 v = f.el.dom.value;
7480                 
7481             }
7482             
7483             // not sure if this supported any more..
7484             if ((typeof(v) == 'object') && f.getRawValue) {
7485                 v = f.getRawValue() ; // dates..
7486             }
7487             // combo boxes where name != hiddenName...
7488             if (f.name != f.getName()) {
7489                 ret[f.name] = f.getRawValue();
7490             }
7491             ret[f.getName()] = v;
7492         });
7493         
7494         return ret;
7495     },
7496
7497     /**
7498      * Clears all invalid messages in this form.
7499      * @return {BasicForm} this
7500      */
7501     clearInvalid : function(){
7502         var items = this.getItems();
7503         
7504         items.each(function(f){
7505            f.clearInvalid();
7506         });
7507         
7508         
7509         
7510         return this;
7511     },
7512
7513     /**
7514      * Resets this form.
7515      * @return {BasicForm} this
7516      */
7517     reset : function(){
7518         var items = this.getItems();
7519         items.each(function(f){
7520             f.reset();
7521         });
7522         
7523         Roo.each(this.childForms || [], function (f) {
7524             f.reset();
7525         });
7526        
7527         
7528         return this;
7529     },
7530     getItems : function()
7531     {
7532         var r=new Roo.util.MixedCollection(false, function(o){
7533             return o.id || (o.id = Roo.id());
7534         });
7535         var iter = function(el) {
7536             if (el.inputEl) {
7537                 r.add(el);
7538             }
7539             if (!el.items) {
7540                 return;
7541             }
7542             Roo.each(el.items,function(e) {
7543                 iter(e);
7544             });
7545             
7546             
7547         };
7548         
7549         iter(this);
7550         return r;
7551         
7552         
7553         
7554         
7555     }
7556     
7557 });
7558
7559  
7560 /*
7561  * Based on:
7562  * Ext JS Library 1.1.1
7563  * Copyright(c) 2006-2007, Ext JS, LLC.
7564  *
7565  * Originally Released Under LGPL - original licence link has changed is not relivant.
7566  *
7567  * Fork - LGPL
7568  * <script type="text/javascript">
7569  */
7570 /**
7571  * @class Roo.form.VTypes
7572  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7573  * @singleton
7574  */
7575 Roo.form.VTypes = function(){
7576     // closure these in so they are only created once.
7577     var alpha = /^[a-zA-Z_]+$/;
7578     var alphanum = /^[a-zA-Z0-9_]+$/;
7579     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
7580     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7581
7582     // All these messages and functions are configurable
7583     return {
7584         /**
7585          * The function used to validate email addresses
7586          * @param {String} value The email address
7587          */
7588         'email' : function(v){
7589             return email.test(v);
7590         },
7591         /**
7592          * The error text to display when the email validation function returns false
7593          * @type String
7594          */
7595         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7596         /**
7597          * The keystroke filter mask to be applied on email input
7598          * @type RegExp
7599          */
7600         'emailMask' : /[a-z0-9_\.\-@]/i,
7601
7602         /**
7603          * The function used to validate URLs
7604          * @param {String} value The URL
7605          */
7606         'url' : function(v){
7607             return url.test(v);
7608         },
7609         /**
7610          * The error text to display when the url validation function returns false
7611          * @type String
7612          */
7613         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7614         
7615         /**
7616          * The function used to validate alpha values
7617          * @param {String} value The value
7618          */
7619         'alpha' : function(v){
7620             return alpha.test(v);
7621         },
7622         /**
7623          * The error text to display when the alpha validation function returns false
7624          * @type String
7625          */
7626         'alphaText' : 'This field should only contain letters and _',
7627         /**
7628          * The keystroke filter mask to be applied on alpha input
7629          * @type RegExp
7630          */
7631         'alphaMask' : /[a-z_]/i,
7632
7633         /**
7634          * The function used to validate alphanumeric values
7635          * @param {String} value The value
7636          */
7637         'alphanum' : function(v){
7638             return alphanum.test(v);
7639         },
7640         /**
7641          * The error text to display when the alphanumeric validation function returns false
7642          * @type String
7643          */
7644         'alphanumText' : 'This field should only contain letters, numbers and _',
7645         /**
7646          * The keystroke filter mask to be applied on alphanumeric input
7647          * @type RegExp
7648          */
7649         'alphanumMask' : /[a-z0-9_]/i
7650     };
7651 }();/*
7652  * - LGPL
7653  *
7654  * Input
7655  * 
7656  */
7657
7658 /**
7659  * @class Roo.bootstrap.Input
7660  * @extends Roo.bootstrap.Component
7661  * Bootstrap Input class
7662  * @cfg {Boolean} disabled is it disabled
7663  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7664  * @cfg {String} name name of the input
7665  * @cfg {string} fieldLabel - the label associated
7666  * @cfg {string} placeholder - placeholder to put in text.
7667  * @cfg {string}  before - input group add on before
7668  * @cfg {string} after - input group add on after
7669  * @cfg {string} size - (lg|sm) or leave empty..
7670  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7671  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7672  * @cfg {Number} md colspan out of 12 for computer-sized screens
7673  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7674  * @cfg {string} value default value of the input
7675  * @cfg {Number} labelWidth set the width of label (0-12)
7676  * @cfg {String} labelAlign (top|left)
7677  * @cfg {Boolean} readOnly Specifies that the field should be read-only
7678  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7679
7680  * @cfg {String} align (left|center|right) Default left
7681  * @cfg {Boolean} forceFeedback (true|false) Default false
7682  * 
7683  * 
7684  * 
7685  * 
7686  * @constructor
7687  * Create a new Input
7688  * @param {Object} config The config object
7689  */
7690
7691 Roo.bootstrap.Input = function(config){
7692     Roo.bootstrap.Input.superclass.constructor.call(this, config);
7693    
7694         this.addEvents({
7695             /**
7696              * @event focus
7697              * Fires when this field receives input focus.
7698              * @param {Roo.form.Field} this
7699              */
7700             focus : true,
7701             /**
7702              * @event blur
7703              * Fires when this field loses input focus.
7704              * @param {Roo.form.Field} this
7705              */
7706             blur : true,
7707             /**
7708              * @event specialkey
7709              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
7710              * {@link Roo.EventObject#getKey} to determine which key was pressed.
7711              * @param {Roo.form.Field} this
7712              * @param {Roo.EventObject} e The event object
7713              */
7714             specialkey : true,
7715             /**
7716              * @event change
7717              * Fires just before the field blurs if the field value has changed.
7718              * @param {Roo.form.Field} this
7719              * @param {Mixed} newValue The new value
7720              * @param {Mixed} oldValue The original value
7721              */
7722             change : true,
7723             /**
7724              * @event invalid
7725              * Fires after the field has been marked as invalid.
7726              * @param {Roo.form.Field} this
7727              * @param {String} msg The validation message
7728              */
7729             invalid : true,
7730             /**
7731              * @event valid
7732              * Fires after the field has been validated with no errors.
7733              * @param {Roo.form.Field} this
7734              */
7735             valid : true,
7736              /**
7737              * @event keyup
7738              * Fires after the key up
7739              * @param {Roo.form.Field} this
7740              * @param {Roo.EventObject}  e The event Object
7741              */
7742             keyup : true
7743         });
7744 };
7745
7746 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
7747      /**
7748      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7749       automatic validation (defaults to "keyup").
7750      */
7751     validationEvent : "keyup",
7752      /**
7753      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7754      */
7755     validateOnBlur : true,
7756     /**
7757      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7758      */
7759     validationDelay : 250,
7760      /**
7761      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7762      */
7763     focusClass : "x-form-focus",  // not needed???
7764     
7765        
7766     /**
7767      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7768      */
7769     invalidClass : "has-warning",
7770     
7771     /**
7772      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7773      */
7774     validClass : "has-success",
7775     
7776     /**
7777      * @cfg {Boolean} hasFeedback (true|false) default true
7778      */
7779     hasFeedback : true,
7780     
7781     /**
7782      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7783      */
7784     invalidFeedbackClass : "glyphicon-warning-sign",
7785     
7786     /**
7787      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7788      */
7789     validFeedbackClass : "glyphicon-ok",
7790     
7791     /**
7792      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7793      */
7794     selectOnFocus : false,
7795     
7796      /**
7797      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7798      */
7799     maskRe : null,
7800        /**
7801      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7802      */
7803     vtype : null,
7804     
7805       /**
7806      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7807      */
7808     disableKeyFilter : false,
7809     
7810        /**
7811      * @cfg {Boolean} disabled True to disable the field (defaults to false).
7812      */
7813     disabled : false,
7814      /**
7815      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7816      */
7817     allowBlank : true,
7818     /**
7819      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7820      */
7821     blankText : "This field is required",
7822     
7823      /**
7824      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7825      */
7826     minLength : 0,
7827     /**
7828      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7829      */
7830     maxLength : Number.MAX_VALUE,
7831     /**
7832      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7833      */
7834     minLengthText : "The minimum length for this field is {0}",
7835     /**
7836      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7837      */
7838     maxLengthText : "The maximum length for this field is {0}",
7839   
7840     
7841     /**
7842      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7843      * If available, this function will be called only after the basic validators all return true, and will be passed the
7844      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7845      */
7846     validator : null,
7847     /**
7848      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7849      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7850      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7851      */
7852     regex : null,
7853     /**
7854      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7855      */
7856     regexText : "",
7857     
7858     autocomplete: false,
7859     
7860     
7861     fieldLabel : '',
7862     inputType : 'text',
7863     
7864     name : false,
7865     placeholder: false,
7866     before : false,
7867     after : false,
7868     size : false,
7869     hasFocus : false,
7870     preventMark: false,
7871     isFormField : true,
7872     value : '',
7873     labelWidth : 2,
7874     labelAlign : false,
7875     readOnly : false,
7876     align : false,
7877     formatedValue : false,
7878     forceFeedback : false,
7879     
7880     parentLabelAlign : function()
7881     {
7882         var parent = this;
7883         while (parent.parent()) {
7884             parent = parent.parent();
7885             if (typeof(parent.labelAlign) !='undefined') {
7886                 return parent.labelAlign;
7887             }
7888         }
7889         return 'left';
7890         
7891     },
7892     
7893     getAutoCreate : function(){
7894         
7895         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7896         
7897         var id = Roo.id();
7898         
7899         var cfg = {};
7900         
7901         if(this.inputType != 'hidden'){
7902             cfg.cls = 'form-group' //input-group
7903         }
7904         
7905         var input =  {
7906             tag: 'input',
7907             id : id,
7908             type : this.inputType,
7909             value : this.value,
7910             cls : 'form-control',
7911             placeholder : this.placeholder || '',
7912             autocomplete : this.autocomplete || 'new-password'
7913         };
7914         
7915         
7916         if(this.align){
7917             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7918         }
7919         
7920         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7921             input.maxLength = this.maxLength;
7922         }
7923         
7924         if (this.disabled) {
7925             input.disabled=true;
7926         }
7927         
7928         if (this.readOnly) {
7929             input.readonly=true;
7930         }
7931         
7932         if (this.name) {
7933             input.name = this.name;
7934         }
7935         if (this.size) {
7936             input.cls += ' input-' + this.size;
7937         }
7938         var settings=this;
7939         ['xs','sm','md','lg'].map(function(size){
7940             if (settings[size]) {
7941                 cfg.cls += ' col-' + size + '-' + settings[size];
7942             }
7943         });
7944         
7945         var inputblock = input;
7946         
7947         var feedback = {
7948             tag: 'span',
7949             cls: 'glyphicon form-control-feedback'
7950         };
7951             
7952         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7953             
7954             inputblock = {
7955                 cls : 'has-feedback',
7956                 cn :  [
7957                     input,
7958                     feedback
7959                 ] 
7960             };  
7961         }
7962         
7963         if (this.before || this.after) {
7964             
7965             inputblock = {
7966                 cls : 'input-group',
7967                 cn :  [] 
7968             };
7969             
7970             if (this.before && typeof(this.before) == 'string') {
7971                 
7972                 inputblock.cn.push({
7973                     tag :'span',
7974                     cls : 'roo-input-before input-group-addon',
7975                     html : this.before
7976                 });
7977             }
7978             if (this.before && typeof(this.before) == 'object') {
7979                 this.before = Roo.factory(this.before);
7980                 
7981                 inputblock.cn.push({
7982                     tag :'span',
7983                     cls : 'roo-input-before input-group-' +
7984                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7985                 });
7986             }
7987             
7988             inputblock.cn.push(input);
7989             
7990             if (this.after && typeof(this.after) == 'string') {
7991                 inputblock.cn.push({
7992                     tag :'span',
7993                     cls : 'roo-input-after input-group-addon',
7994                     html : this.after
7995                 });
7996             }
7997             if (this.after && typeof(this.after) == 'object') {
7998                 this.after = Roo.factory(this.after);
7999                 
8000                 inputblock.cn.push({
8001                     tag :'span',
8002                     cls : 'roo-input-after input-group-' +
8003                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8004                 });
8005             }
8006             
8007             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8008                 inputblock.cls += ' has-feedback';
8009                 inputblock.cn.push(feedback);
8010             }
8011         };
8012         
8013         if (align ==='left' && this.fieldLabel.length) {
8014                 
8015                 cfg.cn = [
8016                     
8017                     {
8018                         tag: 'label',
8019                         'for' :  id,
8020                         cls : 'control-label col-sm-' + this.labelWidth,
8021                         html : this.fieldLabel
8022                         
8023                     },
8024                     {
8025                         cls : "col-sm-" + (12 - this.labelWidth), 
8026                         cn: [
8027                             inputblock
8028                         ]
8029                     }
8030                     
8031                 ];
8032         } else if ( this.fieldLabel.length) {
8033                 
8034                  cfg.cn = [
8035                    
8036                     {
8037                         tag: 'label',
8038                         //cls : 'input-group-addon',
8039                         html : this.fieldLabel
8040                         
8041                     },
8042                     
8043                     inputblock
8044                     
8045                 ];
8046
8047         } else {
8048             
8049                 cfg.cn = [
8050                     
8051                         inputblock
8052                     
8053                 ];
8054                 
8055                 
8056         };
8057         
8058         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8059            cfg.cls += ' navbar-form';
8060         }
8061         
8062         return cfg;
8063         
8064     },
8065     /**
8066      * return the real input element.
8067      */
8068     inputEl: function ()
8069     {
8070         return this.el.select('input.form-control',true).first();
8071     },
8072     
8073     tooltipEl : function()
8074     {
8075         return this.inputEl();
8076     },
8077     
8078     setDisabled : function(v)
8079     {
8080         var i  = this.inputEl().dom;
8081         if (!v) {
8082             i.removeAttribute('disabled');
8083             return;
8084             
8085         }
8086         i.setAttribute('disabled','true');
8087     },
8088     initEvents : function()
8089     {
8090           
8091         this.inputEl().on("keydown" , this.fireKey,  this);
8092         this.inputEl().on("focus", this.onFocus,  this);
8093         this.inputEl().on("blur", this.onBlur,  this);
8094         
8095         this.inputEl().relayEvent('keyup', this);
8096  
8097         // reference to original value for reset
8098         this.originalValue = this.getValue();
8099         //Roo.form.TextField.superclass.initEvents.call(this);
8100         if(this.validationEvent == 'keyup'){
8101             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8102             this.inputEl().on('keyup', this.filterValidation, this);
8103         }
8104         else if(this.validationEvent !== false){
8105             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8106         }
8107         
8108         if(this.selectOnFocus){
8109             this.on("focus", this.preFocus, this);
8110             
8111         }
8112         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8113             this.inputEl().on("keypress", this.filterKeys, this);
8114         }
8115        /* if(this.grow){
8116             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8117             this.el.on("click", this.autoSize,  this);
8118         }
8119         */
8120         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8121             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8122         }
8123         
8124         if (typeof(this.before) == 'object') {
8125             this.before.render(this.el.select('.roo-input-before',true).first());
8126         }
8127         if (typeof(this.after) == 'object') {
8128             this.after.render(this.el.select('.roo-input-after',true).first());
8129         }
8130         
8131         
8132     },
8133     filterValidation : function(e){
8134         if(!e.isNavKeyPress()){
8135             this.validationTask.delay(this.validationDelay);
8136         }
8137     },
8138      /**
8139      * Validates the field value
8140      * @return {Boolean} True if the value is valid, else false
8141      */
8142     validate : function(){
8143         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8144         if(this.disabled || this.validateValue(this.getRawValue())){
8145             this.markValid();
8146             return true;
8147         }
8148         
8149         this.markInvalid();
8150         return false;
8151     },
8152     
8153     
8154     /**
8155      * Validates a value according to the field's validation rules and marks the field as invalid
8156      * if the validation fails
8157      * @param {Mixed} value The value to validate
8158      * @return {Boolean} True if the value is valid, else false
8159      */
8160     validateValue : function(value){
8161         if(value.length < 1)  { // if it's blank
8162             if(this.allowBlank){
8163                 return true;
8164             }
8165             return false;
8166         }
8167         
8168         if(value.length < this.minLength){
8169             return false;
8170         }
8171         if(value.length > this.maxLength){
8172             return false;
8173         }
8174         if(this.vtype){
8175             var vt = Roo.form.VTypes;
8176             if(!vt[this.vtype](value, this)){
8177                 return false;
8178             }
8179         }
8180         if(typeof this.validator == "function"){
8181             var msg = this.validator(value);
8182             if(msg !== true){
8183                 return false;
8184             }
8185         }
8186         
8187         if(this.regex && !this.regex.test(value)){
8188             return false;
8189         }
8190         
8191         return true;
8192     },
8193
8194     
8195     
8196      // private
8197     fireKey : function(e){
8198         //Roo.log('field ' + e.getKey());
8199         if(e.isNavKeyPress()){
8200             this.fireEvent("specialkey", this, e);
8201         }
8202     },
8203     focus : function (selectText){
8204         if(this.rendered){
8205             this.inputEl().focus();
8206             if(selectText === true){
8207                 this.inputEl().dom.select();
8208             }
8209         }
8210         return this;
8211     } ,
8212     
8213     onFocus : function(){
8214         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8215            // this.el.addClass(this.focusClass);
8216         }
8217         if(!this.hasFocus){
8218             this.hasFocus = true;
8219             this.startValue = this.getValue();
8220             this.fireEvent("focus", this);
8221         }
8222     },
8223     
8224     beforeBlur : Roo.emptyFn,
8225
8226     
8227     // private
8228     onBlur : function(){
8229         this.beforeBlur();
8230         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8231             //this.el.removeClass(this.focusClass);
8232         }
8233         this.hasFocus = false;
8234         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8235             this.validate();
8236         }
8237         var v = this.getValue();
8238         if(String(v) !== String(this.startValue)){
8239             this.fireEvent('change', this, v, this.startValue);
8240         }
8241         this.fireEvent("blur", this);
8242     },
8243     
8244     /**
8245      * Resets the current field value to the originally loaded value and clears any validation messages
8246      */
8247     reset : function(){
8248         this.setValue(this.originalValue);
8249         this.validate();
8250     },
8251      /**
8252      * Returns the name of the field
8253      * @return {Mixed} name The name field
8254      */
8255     getName: function(){
8256         return this.name;
8257     },
8258      /**
8259      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8260      * @return {Mixed} value The field value
8261      */
8262     getValue : function(){
8263         
8264         var v = this.inputEl().getValue();
8265         
8266         return v;
8267     },
8268     /**
8269      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8270      * @return {Mixed} value The field value
8271      */
8272     getRawValue : function(){
8273         var v = this.inputEl().getValue();
8274         
8275         return v;
8276     },
8277     
8278     /**
8279      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8280      * @param {Mixed} value The value to set
8281      */
8282     setRawValue : function(v){
8283         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8284     },
8285     
8286     selectText : function(start, end){
8287         var v = this.getRawValue();
8288         if(v.length > 0){
8289             start = start === undefined ? 0 : start;
8290             end = end === undefined ? v.length : end;
8291             var d = this.inputEl().dom;
8292             if(d.setSelectionRange){
8293                 d.setSelectionRange(start, end);
8294             }else if(d.createTextRange){
8295                 var range = d.createTextRange();
8296                 range.moveStart("character", start);
8297                 range.moveEnd("character", v.length-end);
8298                 range.select();
8299             }
8300         }
8301     },
8302     
8303     /**
8304      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8305      * @param {Mixed} value The value to set
8306      */
8307     setValue : function(v){
8308         this.value = v;
8309         if(this.rendered){
8310             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8311             this.validate();
8312         }
8313     },
8314     
8315     /*
8316     processValue : function(value){
8317         if(this.stripCharsRe){
8318             var newValue = value.replace(this.stripCharsRe, '');
8319             if(newValue !== value){
8320                 this.setRawValue(newValue);
8321                 return newValue;
8322             }
8323         }
8324         return value;
8325     },
8326   */
8327     preFocus : function(){
8328         
8329         if(this.selectOnFocus){
8330             this.inputEl().dom.select();
8331         }
8332     },
8333     filterKeys : function(e){
8334         var k = e.getKey();
8335         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8336             return;
8337         }
8338         var c = e.getCharCode(), cc = String.fromCharCode(c);
8339         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8340             return;
8341         }
8342         if(!this.maskRe.test(cc)){
8343             e.stopEvent();
8344         }
8345     },
8346      /**
8347      * Clear any invalid styles/messages for this field
8348      */
8349     clearInvalid : function(){
8350         
8351         if(!this.el || this.preventMark){ // not rendered
8352             return;
8353         }
8354         this.el.removeClass(this.invalidClass);
8355         
8356         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8357             
8358             var feedback = this.el.select('.form-control-feedback', true).first();
8359             
8360             if(feedback){
8361                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8362             }
8363             
8364         }
8365         
8366         this.fireEvent('valid', this);
8367     },
8368     
8369      /**
8370      * Mark this field as valid
8371      */
8372     markValid : function()
8373     {
8374         if(!this.el  || this.preventMark){ // not rendered
8375             return;
8376         }
8377         
8378         this.el.removeClass([this.invalidClass, this.validClass]);
8379         
8380         var feedback = this.el.select('.form-control-feedback', true).first();
8381             
8382         if(feedback){
8383             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8384         }
8385
8386         if(this.disabled || this.allowBlank){
8387             return;
8388         }
8389         
8390         var formGroup = this.el.findParent('.form-group', false, true);
8391         
8392         if(formGroup){
8393             
8394             var label = formGroup.select('label', true).first();
8395             var icon = formGroup.select('i.fa-star', true).first();
8396             
8397             if(label && icon){
8398                 icon.remove();
8399             }
8400         }
8401         
8402         this.el.addClass(this.validClass);
8403         
8404         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8405             
8406             var feedback = this.el.select('.form-control-feedback', true).first();
8407             
8408             if(feedback){
8409                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8410                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8411             }
8412             
8413         }
8414         
8415         this.fireEvent('valid', this);
8416     },
8417     
8418      /**
8419      * Mark this field as invalid
8420      * @param {String} msg The validation message
8421      */
8422     markInvalid : function(msg)
8423     {
8424         if(!this.el  || this.preventMark){ // not rendered
8425             return;
8426         }
8427         
8428         this.el.removeClass([this.invalidClass, this.validClass]);
8429         
8430         var feedback = this.el.select('.form-control-feedback', true).first();
8431             
8432         if(feedback){
8433             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8434         }
8435
8436         if(this.disabled || this.allowBlank){
8437             return;
8438         }
8439         
8440         var formGroup = this.el.findParent('.form-group', false, true);
8441         
8442         if(formGroup){
8443             var label = formGroup.select('label', true).first();
8444             var icon = formGroup.select('i.fa-star', true).first();
8445
8446             if(!this.getValue().length && label && !icon){
8447                 this.el.findParent('.form-group', false, true).createChild({
8448                     tag : 'i',
8449                     cls : 'text-danger fa fa-lg fa-star',
8450                     tooltip : 'This field is required',
8451                     style : 'margin-right:5px;'
8452                 }, label, true);
8453             }
8454         }
8455         
8456         
8457         this.el.addClass(this.invalidClass);
8458         
8459         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8460             
8461             var feedback = this.el.select('.form-control-feedback', true).first();
8462             
8463             if(feedback){
8464                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8465                 
8466                 if(this.getValue().length || this.forceFeedback){
8467                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8468                 }
8469                 
8470             }
8471             
8472         }
8473         
8474         this.fireEvent('invalid', this, msg);
8475     },
8476     // private
8477     SafariOnKeyDown : function(event)
8478     {
8479         // this is a workaround for a password hang bug on chrome/ webkit.
8480         
8481         var isSelectAll = false;
8482         
8483         if(this.inputEl().dom.selectionEnd > 0){
8484             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8485         }
8486         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8487             event.preventDefault();
8488             this.setValue('');
8489             return;
8490         }
8491         
8492         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
8493             
8494             event.preventDefault();
8495             // this is very hacky as keydown always get's upper case.
8496             //
8497             var cc = String.fromCharCode(event.getCharCode());
8498             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8499             
8500         }
8501     },
8502     adjustWidth : function(tag, w){
8503         tag = tag.toLowerCase();
8504         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8505             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8506                 if(tag == 'input'){
8507                     return w + 2;
8508                 }
8509                 if(tag == 'textarea'){
8510                     return w-2;
8511                 }
8512             }else if(Roo.isOpera){
8513                 if(tag == 'input'){
8514                     return w + 2;
8515                 }
8516                 if(tag == 'textarea'){
8517                     return w-2;
8518                 }
8519             }
8520         }
8521         return w;
8522     }
8523     
8524 });
8525
8526  
8527 /*
8528  * - LGPL
8529  *
8530  * Input
8531  * 
8532  */
8533
8534 /**
8535  * @class Roo.bootstrap.TextArea
8536  * @extends Roo.bootstrap.Input
8537  * Bootstrap TextArea class
8538  * @cfg {Number} cols Specifies the visible width of a text area
8539  * @cfg {Number} rows Specifies the visible number of lines in a text area
8540  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8541  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8542  * @cfg {string} html text
8543  * 
8544  * @constructor
8545  * Create a new TextArea
8546  * @param {Object} config The config object
8547  */
8548
8549 Roo.bootstrap.TextArea = function(config){
8550     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8551    
8552 };
8553
8554 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8555      
8556     cols : false,
8557     rows : 5,
8558     readOnly : false,
8559     warp : 'soft',
8560     resize : false,
8561     value: false,
8562     html: false,
8563     
8564     getAutoCreate : function(){
8565         
8566         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8567         
8568         var id = Roo.id();
8569         
8570         var cfg = {};
8571         
8572         var input =  {
8573             tag: 'textarea',
8574             id : id,
8575             warp : this.warp,
8576             rows : this.rows,
8577             value : this.value || '',
8578             html: this.html || '',
8579             cls : 'form-control',
8580             placeholder : this.placeholder || '' 
8581             
8582         };
8583         
8584         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8585             input.maxLength = this.maxLength;
8586         }
8587         
8588         if(this.resize){
8589             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8590         }
8591         
8592         if(this.cols){
8593             input.cols = this.cols;
8594         }
8595         
8596         if (this.readOnly) {
8597             input.readonly = true;
8598         }
8599         
8600         if (this.name) {
8601             input.name = this.name;
8602         }
8603         
8604         if (this.size) {
8605             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8606         }
8607         
8608         var settings=this;
8609         ['xs','sm','md','lg'].map(function(size){
8610             if (settings[size]) {
8611                 cfg.cls += ' col-' + size + '-' + settings[size];
8612             }
8613         });
8614         
8615         var inputblock = input;
8616         
8617         if(this.hasFeedback && !this.allowBlank){
8618             
8619             var feedback = {
8620                 tag: 'span',
8621                 cls: 'glyphicon form-control-feedback'
8622             };
8623
8624             inputblock = {
8625                 cls : 'has-feedback',
8626                 cn :  [
8627                     input,
8628                     feedback
8629                 ] 
8630             };  
8631         }
8632         
8633         
8634         if (this.before || this.after) {
8635             
8636             inputblock = {
8637                 cls : 'input-group',
8638                 cn :  [] 
8639             };
8640             if (this.before) {
8641                 inputblock.cn.push({
8642                     tag :'span',
8643                     cls : 'input-group-addon',
8644                     html : this.before
8645                 });
8646             }
8647             
8648             inputblock.cn.push(input);
8649             
8650             if(this.hasFeedback && !this.allowBlank){
8651                 inputblock.cls += ' has-feedback';
8652                 inputblock.cn.push(feedback);
8653             }
8654             
8655             if (this.after) {
8656                 inputblock.cn.push({
8657                     tag :'span',
8658                     cls : 'input-group-addon',
8659                     html : this.after
8660                 });
8661             }
8662             
8663         }
8664         
8665         if (align ==='left' && this.fieldLabel.length) {
8666 //                Roo.log("left and has label");
8667                 cfg.cn = [
8668                     
8669                     {
8670                         tag: 'label',
8671                         'for' :  id,
8672                         cls : 'control-label col-sm-' + this.labelWidth,
8673                         html : this.fieldLabel
8674                         
8675                     },
8676                     {
8677                         cls : "col-sm-" + (12 - this.labelWidth), 
8678                         cn: [
8679                             inputblock
8680                         ]
8681                     }
8682                     
8683                 ];
8684         } else if ( this.fieldLabel.length) {
8685 //                Roo.log(" label");
8686                  cfg.cn = [
8687                    
8688                     {
8689                         tag: 'label',
8690                         //cls : 'input-group-addon',
8691                         html : this.fieldLabel
8692                         
8693                     },
8694                     
8695                     inputblock
8696                     
8697                 ];
8698
8699         } else {
8700             
8701 //                   Roo.log(" no label && no align");
8702                 cfg.cn = [
8703                     
8704                         inputblock
8705                     
8706                 ];
8707                 
8708                 
8709         }
8710         
8711         if (this.disabled) {
8712             input.disabled=true;
8713         }
8714         
8715         return cfg;
8716         
8717     },
8718     /**
8719      * return the real textarea element.
8720      */
8721     inputEl: function ()
8722     {
8723         return this.el.select('textarea.form-control',true).first();
8724     },
8725     
8726     /**
8727      * Clear any invalid styles/messages for this field
8728      */
8729     clearInvalid : function()
8730     {
8731         
8732         if(!this.el || this.preventMark){ // not rendered
8733             return;
8734         }
8735         
8736         var label = this.el.select('label', true).first();
8737         var icon = this.el.select('i.fa-star', true).first();
8738         
8739         if(label && icon){
8740             icon.remove();
8741         }
8742         
8743         this.el.removeClass(this.invalidClass);
8744         
8745         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8746             
8747             var feedback = this.el.select('.form-control-feedback', true).first();
8748             
8749             if(feedback){
8750                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8751             }
8752             
8753         }
8754         
8755         this.fireEvent('valid', this);
8756     },
8757     
8758      /**
8759      * Mark this field as valid
8760      */
8761     markValid : function()
8762     {
8763         if(!this.el  || this.preventMark){ // not rendered
8764             return;
8765         }
8766         
8767         this.el.removeClass([this.invalidClass, this.validClass]);
8768         
8769         var feedback = this.el.select('.form-control-feedback', true).first();
8770             
8771         if(feedback){
8772             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8773         }
8774
8775         if(this.disabled || this.allowBlank){
8776             return;
8777         }
8778         
8779         var label = this.el.select('label', true).first();
8780         var icon = this.el.select('i.fa-star', true).first();
8781         
8782         if(label && icon){
8783             icon.remove();
8784         }
8785         
8786         this.el.addClass(this.validClass);
8787         
8788         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8789             
8790             var feedback = this.el.select('.form-control-feedback', true).first();
8791             
8792             if(feedback){
8793                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8794                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8795             }
8796             
8797         }
8798         
8799         this.fireEvent('valid', this);
8800     },
8801     
8802      /**
8803      * Mark this field as invalid
8804      * @param {String} msg The validation message
8805      */
8806     markInvalid : function(msg)
8807     {
8808         if(!this.el  || this.preventMark){ // not rendered
8809             return;
8810         }
8811         
8812         this.el.removeClass([this.invalidClass, this.validClass]);
8813         
8814         var feedback = this.el.select('.form-control-feedback', true).first();
8815             
8816         if(feedback){
8817             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8818         }
8819
8820         if(this.disabled || this.allowBlank){
8821             return;
8822         }
8823         
8824         var label = this.el.select('label', true).first();
8825         var icon = this.el.select('i.fa-star', true).first();
8826         
8827         if(!this.getValue().length && label && !icon){
8828             this.el.createChild({
8829                 tag : 'i',
8830                 cls : 'text-danger fa fa-lg fa-star',
8831                 tooltip : 'This field is required',
8832                 style : 'margin-right:5px;'
8833             }, label, true);
8834         }
8835
8836         this.el.addClass(this.invalidClass);
8837         
8838         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8839             
8840             var feedback = this.el.select('.form-control-feedback', true).first();
8841             
8842             if(feedback){
8843                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8844                 
8845                 if(this.getValue().length || this.forceFeedback){
8846                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8847                 }
8848                 
8849             }
8850             
8851         }
8852         
8853         this.fireEvent('invalid', this, msg);
8854     }
8855 });
8856
8857  
8858 /*
8859  * - LGPL
8860  *
8861  * trigger field - base class for combo..
8862  * 
8863  */
8864  
8865 /**
8866  * @class Roo.bootstrap.TriggerField
8867  * @extends Roo.bootstrap.Input
8868  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8869  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8870  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8871  * for which you can provide a custom implementation.  For example:
8872  * <pre><code>
8873 var trigger = new Roo.bootstrap.TriggerField();
8874 trigger.onTriggerClick = myTriggerFn;
8875 trigger.applyTo('my-field');
8876 </code></pre>
8877  *
8878  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8879  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8880  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
8881  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8882  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8883
8884  * @constructor
8885  * Create a new TriggerField.
8886  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8887  * to the base TextField)
8888  */
8889 Roo.bootstrap.TriggerField = function(config){
8890     this.mimicing = false;
8891     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8892 };
8893
8894 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
8895     /**
8896      * @cfg {String} triggerClass A CSS class to apply to the trigger
8897      */
8898      /**
8899      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8900      */
8901     hideTrigger:false,
8902
8903     /**
8904      * @cfg {Boolean} removable (true|false) special filter default false
8905      */
8906     removable : false,
8907     
8908     /** @cfg {Boolean} grow @hide */
8909     /** @cfg {Number} growMin @hide */
8910     /** @cfg {Number} growMax @hide */
8911
8912     /**
8913      * @hide 
8914      * @method
8915      */
8916     autoSize: Roo.emptyFn,
8917     // private
8918     monitorTab : true,
8919     // private
8920     deferHeight : true,
8921
8922     
8923     actionMode : 'wrap',
8924     
8925     caret : false,
8926     
8927     
8928     getAutoCreate : function(){
8929        
8930         var align = this.labelAlign || this.parentLabelAlign();
8931         
8932         var id = Roo.id();
8933         
8934         var cfg = {
8935             cls: 'form-group' //input-group
8936         };
8937         
8938         
8939         var input =  {
8940             tag: 'input',
8941             id : id,
8942             type : this.inputType,
8943             cls : 'form-control',
8944             autocomplete: 'new-password',
8945             placeholder : this.placeholder || '' 
8946             
8947         };
8948         if (this.name) {
8949             input.name = this.name;
8950         }
8951         if (this.size) {
8952             input.cls += ' input-' + this.size;
8953         }
8954         
8955         if (this.disabled) {
8956             input.disabled=true;
8957         }
8958         
8959         var inputblock = input;
8960         
8961         if(this.hasFeedback && !this.allowBlank){
8962             
8963             var feedback = {
8964                 tag: 'span',
8965                 cls: 'glyphicon form-control-feedback'
8966             };
8967             
8968             if(this.removable && !this.editable && !this.tickable){
8969                 inputblock = {
8970                     cls : 'has-feedback',
8971                     cn :  [
8972                         inputblock,
8973                         {
8974                             tag: 'button',
8975                             html : 'x',
8976                             cls : 'roo-combo-removable-btn close'
8977                         },
8978                         feedback
8979                     ] 
8980                 };
8981             } else {
8982                 inputblock = {
8983                     cls : 'has-feedback',
8984                     cn :  [
8985                         inputblock,
8986                         feedback
8987                     ] 
8988                 };
8989             }
8990
8991         } else {
8992             if(this.removable && !this.editable && !this.tickable){
8993                 inputblock = {
8994                     cls : 'roo-removable',
8995                     cn :  [
8996                         inputblock,
8997                         {
8998                             tag: 'button',
8999                             html : 'x',
9000                             cls : 'roo-combo-removable-btn close'
9001                         }
9002                     ] 
9003                 };
9004             }
9005         }
9006         
9007         if (this.before || this.after) {
9008             
9009             inputblock = {
9010                 cls : 'input-group',
9011                 cn :  [] 
9012             };
9013             if (this.before) {
9014                 inputblock.cn.push({
9015                     tag :'span',
9016                     cls : 'input-group-addon',
9017                     html : this.before
9018                 });
9019             }
9020             
9021             inputblock.cn.push(input);
9022             
9023             if(this.hasFeedback && !this.allowBlank){
9024                 inputblock.cls += ' has-feedback';
9025                 inputblock.cn.push(feedback);
9026             }
9027             
9028             if (this.after) {
9029                 inputblock.cn.push({
9030                     tag :'span',
9031                     cls : 'input-group-addon',
9032                     html : this.after
9033                 });
9034             }
9035             
9036         };
9037         
9038         var box = {
9039             tag: 'div',
9040             cn: [
9041                 {
9042                     tag: 'input',
9043                     type : 'hidden',
9044                     cls: 'form-hidden-field'
9045                 },
9046                 inputblock
9047             ]
9048             
9049         };
9050         
9051         if(this.multiple){
9052             box = {
9053                 tag: 'div',
9054                 cn: [
9055                     {
9056                         tag: 'input',
9057                         type : 'hidden',
9058                         cls: 'form-hidden-field'
9059                     },
9060                     {
9061                         tag: 'ul',
9062                         cls: 'select2-choices',
9063                         cn:[
9064                             {
9065                                 tag: 'li',
9066                                 cls: 'select2-search-field',
9067                                 cn: [
9068
9069                                     inputblock
9070                                 ]
9071                             }
9072                         ]
9073                     }
9074                 ]
9075             }
9076         };
9077         
9078         var combobox = {
9079             cls: 'select2-container input-group',
9080             cn: [
9081                 box
9082 //                {
9083 //                    tag: 'ul',
9084 //                    cls: 'typeahead typeahead-long dropdown-menu',
9085 //                    style: 'display:none'
9086 //                }
9087             ]
9088         };
9089         
9090         if(!this.multiple && this.showToggleBtn){
9091             
9092             var caret = {
9093                         tag: 'span',
9094                         cls: 'caret'
9095              };
9096             if (this.caret != false) {
9097                 caret = {
9098                      tag: 'i',
9099                      cls: 'fa fa-' + this.caret
9100                 };
9101                 
9102             }
9103             
9104             combobox.cn.push({
9105                 tag :'span',
9106                 cls : 'input-group-addon btn dropdown-toggle',
9107                 cn : [
9108                     caret,
9109                     {
9110                         tag: 'span',
9111                         cls: 'combobox-clear',
9112                         cn  : [
9113                             {
9114                                 tag : 'i',
9115                                 cls: 'icon-remove'
9116                             }
9117                         ]
9118                     }
9119                 ]
9120
9121             })
9122         }
9123         
9124         if(this.multiple){
9125             combobox.cls += ' select2-container-multi';
9126         }
9127         
9128         if (align ==='left' && this.fieldLabel.length) {
9129             
9130 //                Roo.log("left and has label");
9131                 cfg.cn = [
9132                     
9133                     {
9134                         tag: 'label',
9135                         'for' :  id,
9136                         cls : 'control-label col-sm-' + this.labelWidth,
9137                         html : this.fieldLabel
9138                         
9139                     },
9140                     {
9141                         cls : "col-sm-" + (12 - this.labelWidth), 
9142                         cn: [
9143                             combobox
9144                         ]
9145                     }
9146                     
9147                 ];
9148         } else if ( this.fieldLabel.length) {
9149 //                Roo.log(" label");
9150                  cfg.cn = [
9151                    
9152                     {
9153                         tag: 'label',
9154                         //cls : 'input-group-addon',
9155                         html : this.fieldLabel
9156                         
9157                     },
9158                     
9159                     combobox
9160                     
9161                 ];
9162
9163         } else {
9164             
9165 //                Roo.log(" no label && no align");
9166                 cfg = combobox
9167                      
9168                 
9169         }
9170          
9171         var settings=this;
9172         ['xs','sm','md','lg'].map(function(size){
9173             if (settings[size]) {
9174                 cfg.cls += ' col-' + size + '-' + settings[size];
9175             }
9176         });
9177         
9178         return cfg;
9179         
9180     },
9181     
9182     
9183     
9184     // private
9185     onResize : function(w, h){
9186 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9187 //        if(typeof w == 'number'){
9188 //            var x = w - this.trigger.getWidth();
9189 //            this.inputEl().setWidth(this.adjustWidth('input', x));
9190 //            this.trigger.setStyle('left', x+'px');
9191 //        }
9192     },
9193
9194     // private
9195     adjustSize : Roo.BoxComponent.prototype.adjustSize,
9196
9197     // private
9198     getResizeEl : function(){
9199         return this.inputEl();
9200     },
9201
9202     // private
9203     getPositionEl : function(){
9204         return this.inputEl();
9205     },
9206
9207     // private
9208     alignErrorIcon : function(){
9209         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9210     },
9211
9212     // private
9213     initEvents : function(){
9214         
9215         this.createList();
9216         
9217         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9218         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9219         if(!this.multiple && this.showToggleBtn){
9220             this.trigger = this.el.select('span.dropdown-toggle',true).first();
9221             if(this.hideTrigger){
9222                 this.trigger.setDisplayed(false);
9223             }
9224             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9225         }
9226         
9227         if(this.multiple){
9228             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9229         }
9230         
9231         if(this.removable && !this.editable && !this.tickable){
9232             var close = this.closeTriggerEl();
9233             
9234             if(close){
9235                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9236                 close.on('click', this.removeBtnClick, this, close);
9237             }
9238         }
9239         
9240         //this.trigger.addClassOnOver('x-form-trigger-over');
9241         //this.trigger.addClassOnClick('x-form-trigger-click');
9242         
9243         //if(!this.width){
9244         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9245         //}
9246     },
9247     
9248     closeTriggerEl : function()
9249     {
9250         var close = this.el.select('.roo-combo-removable-btn', true).first();
9251         return close ? close : false;
9252     },
9253     
9254     removeBtnClick : function(e, h, el)
9255     {
9256         e.preventDefault();
9257         
9258         if(this.fireEvent("remove", this) !== false){
9259             this.reset();
9260         }
9261     },
9262     
9263     createList : function()
9264     {
9265         this.list = Roo.get(document.body).createChild({
9266             tag: 'ul',
9267             cls: 'typeahead typeahead-long dropdown-menu',
9268             style: 'display:none'
9269         });
9270         
9271         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9272         
9273     },
9274
9275     // private
9276     initTrigger : function(){
9277        
9278     },
9279
9280     // private
9281     onDestroy : function(){
9282         if(this.trigger){
9283             this.trigger.removeAllListeners();
9284           //  this.trigger.remove();
9285         }
9286         //if(this.wrap){
9287         //    this.wrap.remove();
9288         //}
9289         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9290     },
9291
9292     // private
9293     onFocus : function(){
9294         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9295         /*
9296         if(!this.mimicing){
9297             this.wrap.addClass('x-trigger-wrap-focus');
9298             this.mimicing = true;
9299             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9300             if(this.monitorTab){
9301                 this.el.on("keydown", this.checkTab, this);
9302             }
9303         }
9304         */
9305     },
9306
9307     // private
9308     checkTab : function(e){
9309         if(e.getKey() == e.TAB){
9310             this.triggerBlur();
9311         }
9312     },
9313
9314     // private
9315     onBlur : function(){
9316         // do nothing
9317     },
9318
9319     // private
9320     mimicBlur : function(e, t){
9321         /*
9322         if(!this.wrap.contains(t) && this.validateBlur()){
9323             this.triggerBlur();
9324         }
9325         */
9326     },
9327
9328     // private
9329     triggerBlur : function(){
9330         this.mimicing = false;
9331         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9332         if(this.monitorTab){
9333             this.el.un("keydown", this.checkTab, this);
9334         }
9335         //this.wrap.removeClass('x-trigger-wrap-focus');
9336         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9337     },
9338
9339     // private
9340     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9341     validateBlur : function(e, t){
9342         return true;
9343     },
9344
9345     // private
9346     onDisable : function(){
9347         this.inputEl().dom.disabled = true;
9348         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9349         //if(this.wrap){
9350         //    this.wrap.addClass('x-item-disabled');
9351         //}
9352     },
9353
9354     // private
9355     onEnable : function(){
9356         this.inputEl().dom.disabled = false;
9357         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9358         //if(this.wrap){
9359         //    this.el.removeClass('x-item-disabled');
9360         //}
9361     },
9362
9363     // private
9364     onShow : function(){
9365         var ae = this.getActionEl();
9366         
9367         if(ae){
9368             ae.dom.style.display = '';
9369             ae.dom.style.visibility = 'visible';
9370         }
9371     },
9372
9373     // private
9374     
9375     onHide : function(){
9376         var ae = this.getActionEl();
9377         ae.dom.style.display = 'none';
9378     },
9379
9380     /**
9381      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
9382      * by an implementing function.
9383      * @method
9384      * @param {EventObject} e
9385      */
9386     onTriggerClick : Roo.emptyFn
9387 });
9388  /*
9389  * Based on:
9390  * Ext JS Library 1.1.1
9391  * Copyright(c) 2006-2007, Ext JS, LLC.
9392  *
9393  * Originally Released Under LGPL - original licence link has changed is not relivant.
9394  *
9395  * Fork - LGPL
9396  * <script type="text/javascript">
9397  */
9398
9399
9400 /**
9401  * @class Roo.data.SortTypes
9402  * @singleton
9403  * Defines the default sorting (casting?) comparison functions used when sorting data.
9404  */
9405 Roo.data.SortTypes = {
9406     /**
9407      * Default sort that does nothing
9408      * @param {Mixed} s The value being converted
9409      * @return {Mixed} The comparison value
9410      */
9411     none : function(s){
9412         return s;
9413     },
9414     
9415     /**
9416      * The regular expression used to strip tags
9417      * @type {RegExp}
9418      * @property
9419      */
9420     stripTagsRE : /<\/?[^>]+>/gi,
9421     
9422     /**
9423      * Strips all HTML tags to sort on text only
9424      * @param {Mixed} s The value being converted
9425      * @return {String} The comparison value
9426      */
9427     asText : function(s){
9428         return String(s).replace(this.stripTagsRE, "");
9429     },
9430     
9431     /**
9432      * Strips all HTML tags to sort on text only - Case insensitive
9433      * @param {Mixed} s The value being converted
9434      * @return {String} The comparison value
9435      */
9436     asUCText : function(s){
9437         return String(s).toUpperCase().replace(this.stripTagsRE, "");
9438     },
9439     
9440     /**
9441      * Case insensitive string
9442      * @param {Mixed} s The value being converted
9443      * @return {String} The comparison value
9444      */
9445     asUCString : function(s) {
9446         return String(s).toUpperCase();
9447     },
9448     
9449     /**
9450      * Date sorting
9451      * @param {Mixed} s The value being converted
9452      * @return {Number} The comparison value
9453      */
9454     asDate : function(s) {
9455         if(!s){
9456             return 0;
9457         }
9458         if(s instanceof Date){
9459             return s.getTime();
9460         }
9461         return Date.parse(String(s));
9462     },
9463     
9464     /**
9465      * Float sorting
9466      * @param {Mixed} s The value being converted
9467      * @return {Float} The comparison value
9468      */
9469     asFloat : function(s) {
9470         var val = parseFloat(String(s).replace(/,/g, ""));
9471         if(isNaN(val)) {
9472             val = 0;
9473         }
9474         return val;
9475     },
9476     
9477     /**
9478      * Integer sorting
9479      * @param {Mixed} s The value being converted
9480      * @return {Number} The comparison value
9481      */
9482     asInt : function(s) {
9483         var val = parseInt(String(s).replace(/,/g, ""));
9484         if(isNaN(val)) {
9485             val = 0;
9486         }
9487         return val;
9488     }
9489 };/*
9490  * Based on:
9491  * Ext JS Library 1.1.1
9492  * Copyright(c) 2006-2007, Ext JS, LLC.
9493  *
9494  * Originally Released Under LGPL - original licence link has changed is not relivant.
9495  *
9496  * Fork - LGPL
9497  * <script type="text/javascript">
9498  */
9499
9500 /**
9501 * @class Roo.data.Record
9502  * Instances of this class encapsulate both record <em>definition</em> information, and record
9503  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9504  * to access Records cached in an {@link Roo.data.Store} object.<br>
9505  * <p>
9506  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9507  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9508  * objects.<br>
9509  * <p>
9510  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9511  * @constructor
9512  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9513  * {@link #create}. The parameters are the same.
9514  * @param {Array} data An associative Array of data values keyed by the field name.
9515  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9516  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9517  * not specified an integer id is generated.
9518  */
9519 Roo.data.Record = function(data, id){
9520     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9521     this.data = data;
9522 };
9523
9524 /**
9525  * Generate a constructor for a specific record layout.
9526  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9527  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9528  * Each field definition object may contain the following properties: <ul>
9529  * <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,
9530  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9531  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9532  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9533  * is being used, then this is a string containing the javascript expression to reference the data relative to 
9534  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9535  * to the data item relative to the record element. If the mapping expression is the same as the field name,
9536  * this may be omitted.</p></li>
9537  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9538  * <ul><li>auto (Default, implies no conversion)</li>
9539  * <li>string</li>
9540  * <li>int</li>
9541  * <li>float</li>
9542  * <li>boolean</li>
9543  * <li>date</li></ul></p></li>
9544  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9545  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9546  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9547  * by the Reader into an object that will be stored in the Record. It is passed the
9548  * following parameters:<ul>
9549  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9550  * </ul></p></li>
9551  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9552  * </ul>
9553  * <br>usage:<br><pre><code>
9554 var TopicRecord = Roo.data.Record.create(
9555     {name: 'title', mapping: 'topic_title'},
9556     {name: 'author', mapping: 'username'},
9557     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9558     {name: 'lastPost', mapping: 'post_time', type: 'date'},
9559     {name: 'lastPoster', mapping: 'user2'},
9560     {name: 'excerpt', mapping: 'post_text'}
9561 );
9562
9563 var myNewRecord = new TopicRecord({
9564     title: 'Do my job please',
9565     author: 'noobie',
9566     totalPosts: 1,
9567     lastPost: new Date(),
9568     lastPoster: 'Animal',
9569     excerpt: 'No way dude!'
9570 });
9571 myStore.add(myNewRecord);
9572 </code></pre>
9573  * @method create
9574  * @static
9575  */
9576 Roo.data.Record.create = function(o){
9577     var f = function(){
9578         f.superclass.constructor.apply(this, arguments);
9579     };
9580     Roo.extend(f, Roo.data.Record);
9581     var p = f.prototype;
9582     p.fields = new Roo.util.MixedCollection(false, function(field){
9583         return field.name;
9584     });
9585     for(var i = 0, len = o.length; i < len; i++){
9586         p.fields.add(new Roo.data.Field(o[i]));
9587     }
9588     f.getField = function(name){
9589         return p.fields.get(name);  
9590     };
9591     return f;
9592 };
9593
9594 Roo.data.Record.AUTO_ID = 1000;
9595 Roo.data.Record.EDIT = 'edit';
9596 Roo.data.Record.REJECT = 'reject';
9597 Roo.data.Record.COMMIT = 'commit';
9598
9599 Roo.data.Record.prototype = {
9600     /**
9601      * Readonly flag - true if this record has been modified.
9602      * @type Boolean
9603      */
9604     dirty : false,
9605     editing : false,
9606     error: null,
9607     modified: null,
9608
9609     // private
9610     join : function(store){
9611         this.store = store;
9612     },
9613
9614     /**
9615      * Set the named field to the specified value.
9616      * @param {String} name The name of the field to set.
9617      * @param {Object} value The value to set the field to.
9618      */
9619     set : function(name, value){
9620         if(this.data[name] == value){
9621             return;
9622         }
9623         this.dirty = true;
9624         if(!this.modified){
9625             this.modified = {};
9626         }
9627         if(typeof this.modified[name] == 'undefined'){
9628             this.modified[name] = this.data[name];
9629         }
9630         this.data[name] = value;
9631         if(!this.editing && this.store){
9632             this.store.afterEdit(this);
9633         }       
9634     },
9635
9636     /**
9637      * Get the value of the named field.
9638      * @param {String} name The name of the field to get the value of.
9639      * @return {Object} The value of the field.
9640      */
9641     get : function(name){
9642         return this.data[name]; 
9643     },
9644
9645     // private
9646     beginEdit : function(){
9647         this.editing = true;
9648         this.modified = {}; 
9649     },
9650
9651     // private
9652     cancelEdit : function(){
9653         this.editing = false;
9654         delete this.modified;
9655     },
9656
9657     // private
9658     endEdit : function(){
9659         this.editing = false;
9660         if(this.dirty && this.store){
9661             this.store.afterEdit(this);
9662         }
9663     },
9664
9665     /**
9666      * Usually called by the {@link Roo.data.Store} which owns the Record.
9667      * Rejects all changes made to the Record since either creation, or the last commit operation.
9668      * Modified fields are reverted to their original values.
9669      * <p>
9670      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9671      * of reject operations.
9672      */
9673     reject : function(){
9674         var m = this.modified;
9675         for(var n in m){
9676             if(typeof m[n] != "function"){
9677                 this.data[n] = m[n];
9678             }
9679         }
9680         this.dirty = false;
9681         delete this.modified;
9682         this.editing = false;
9683         if(this.store){
9684             this.store.afterReject(this);
9685         }
9686     },
9687
9688     /**
9689      * Usually called by the {@link Roo.data.Store} which owns the Record.
9690      * Commits all changes made to the Record since either creation, or the last commit operation.
9691      * <p>
9692      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9693      * of commit operations.
9694      */
9695     commit : function(){
9696         this.dirty = false;
9697         delete this.modified;
9698         this.editing = false;
9699         if(this.store){
9700             this.store.afterCommit(this);
9701         }
9702     },
9703
9704     // private
9705     hasError : function(){
9706         return this.error != null;
9707     },
9708
9709     // private
9710     clearError : function(){
9711         this.error = null;
9712     },
9713
9714     /**
9715      * Creates a copy of this record.
9716      * @param {String} id (optional) A new record id if you don't want to use this record's id
9717      * @return {Record}
9718      */
9719     copy : function(newId) {
9720         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9721     }
9722 };/*
9723  * Based on:
9724  * Ext JS Library 1.1.1
9725  * Copyright(c) 2006-2007, Ext JS, LLC.
9726  *
9727  * Originally Released Under LGPL - original licence link has changed is not relivant.
9728  *
9729  * Fork - LGPL
9730  * <script type="text/javascript">
9731  */
9732
9733
9734
9735 /**
9736  * @class Roo.data.Store
9737  * @extends Roo.util.Observable
9738  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9739  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9740  * <p>
9741  * 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
9742  * has no knowledge of the format of the data returned by the Proxy.<br>
9743  * <p>
9744  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9745  * instances from the data object. These records are cached and made available through accessor functions.
9746  * @constructor
9747  * Creates a new Store.
9748  * @param {Object} config A config object containing the objects needed for the Store to access data,
9749  * and read the data into Records.
9750  */
9751 Roo.data.Store = function(config){
9752     this.data = new Roo.util.MixedCollection(false);
9753     this.data.getKey = function(o){
9754         return o.id;
9755     };
9756     this.baseParams = {};
9757     // private
9758     this.paramNames = {
9759         "start" : "start",
9760         "limit" : "limit",
9761         "sort" : "sort",
9762         "dir" : "dir",
9763         "multisort" : "_multisort"
9764     };
9765
9766     if(config && config.data){
9767         this.inlineData = config.data;
9768         delete config.data;
9769     }
9770
9771     Roo.apply(this, config);
9772     
9773     if(this.reader){ // reader passed
9774         this.reader = Roo.factory(this.reader, Roo.data);
9775         this.reader.xmodule = this.xmodule || false;
9776         if(!this.recordType){
9777             this.recordType = this.reader.recordType;
9778         }
9779         if(this.reader.onMetaChange){
9780             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9781         }
9782     }
9783
9784     if(this.recordType){
9785         this.fields = this.recordType.prototype.fields;
9786     }
9787     this.modified = [];
9788
9789     this.addEvents({
9790         /**
9791          * @event datachanged
9792          * Fires when the data cache has changed, and a widget which is using this Store
9793          * as a Record cache should refresh its view.
9794          * @param {Store} this
9795          */
9796         datachanged : true,
9797         /**
9798          * @event metachange
9799          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9800          * @param {Store} this
9801          * @param {Object} meta The JSON metadata
9802          */
9803         metachange : true,
9804         /**
9805          * @event add
9806          * Fires when Records have been added to the Store
9807          * @param {Store} this
9808          * @param {Roo.data.Record[]} records The array of Records added
9809          * @param {Number} index The index at which the record(s) were added
9810          */
9811         add : true,
9812         /**
9813          * @event remove
9814          * Fires when a Record has been removed from the Store
9815          * @param {Store} this
9816          * @param {Roo.data.Record} record The Record that was removed
9817          * @param {Number} index The index at which the record was removed
9818          */
9819         remove : true,
9820         /**
9821          * @event update
9822          * Fires when a Record has been updated
9823          * @param {Store} this
9824          * @param {Roo.data.Record} record The Record that was updated
9825          * @param {String} operation The update operation being performed.  Value may be one of:
9826          * <pre><code>
9827  Roo.data.Record.EDIT
9828  Roo.data.Record.REJECT
9829  Roo.data.Record.COMMIT
9830          * </code></pre>
9831          */
9832         update : true,
9833         /**
9834          * @event clear
9835          * Fires when the data cache has been cleared.
9836          * @param {Store} this
9837          */
9838         clear : true,
9839         /**
9840          * @event beforeload
9841          * Fires before a request is made for a new data object.  If the beforeload handler returns false
9842          * the load action will be canceled.
9843          * @param {Store} this
9844          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9845          */
9846         beforeload : true,
9847         /**
9848          * @event beforeloadadd
9849          * Fires after a new set of Records has been loaded.
9850          * @param {Store} this
9851          * @param {Roo.data.Record[]} records The Records that were loaded
9852          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9853          */
9854         beforeloadadd : true,
9855         /**
9856          * @event load
9857          * Fires after a new set of Records has been loaded, before they are added to the store.
9858          * @param {Store} this
9859          * @param {Roo.data.Record[]} records The Records that were loaded
9860          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9861          * @params {Object} return from reader
9862          */
9863         load : true,
9864         /**
9865          * @event loadexception
9866          * Fires if an exception occurs in the Proxy during loading.
9867          * Called with the signature of the Proxy's "loadexception" event.
9868          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9869          * 
9870          * @param {Proxy} 
9871          * @param {Object} return from JsonData.reader() - success, totalRecords, records
9872          * @param {Object} load options 
9873          * @param {Object} jsonData from your request (normally this contains the Exception)
9874          */
9875         loadexception : true
9876     });
9877     
9878     if(this.proxy){
9879         this.proxy = Roo.factory(this.proxy, Roo.data);
9880         this.proxy.xmodule = this.xmodule || false;
9881         this.relayEvents(this.proxy,  ["loadexception"]);
9882     }
9883     this.sortToggle = {};
9884     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9885
9886     Roo.data.Store.superclass.constructor.call(this);
9887
9888     if(this.inlineData){
9889         this.loadData(this.inlineData);
9890         delete this.inlineData;
9891     }
9892 };
9893
9894 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9895      /**
9896     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
9897     * without a remote query - used by combo/forms at present.
9898     */
9899     
9900     /**
9901     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9902     */
9903     /**
9904     * @cfg {Array} data Inline data to be loaded when the store is initialized.
9905     */
9906     /**
9907     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9908     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9909     */
9910     /**
9911     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9912     * on any HTTP request
9913     */
9914     /**
9915     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9916     */
9917     /**
9918     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9919     */
9920     multiSort: false,
9921     /**
9922     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9923     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9924     */
9925     remoteSort : false,
9926
9927     /**
9928     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9929      * loaded or when a record is removed. (defaults to false).
9930     */
9931     pruneModifiedRecords : false,
9932
9933     // private
9934     lastOptions : null,
9935
9936     /**
9937      * Add Records to the Store and fires the add event.
9938      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9939      */
9940     add : function(records){
9941         records = [].concat(records);
9942         for(var i = 0, len = records.length; i < len; i++){
9943             records[i].join(this);
9944         }
9945         var index = this.data.length;
9946         this.data.addAll(records);
9947         this.fireEvent("add", this, records, index);
9948     },
9949
9950     /**
9951      * Remove a Record from the Store and fires the remove event.
9952      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9953      */
9954     remove : function(record){
9955         var index = this.data.indexOf(record);
9956         this.data.removeAt(index);
9957         if(this.pruneModifiedRecords){
9958             this.modified.remove(record);
9959         }
9960         this.fireEvent("remove", this, record, index);
9961     },
9962
9963     /**
9964      * Remove all Records from the Store and fires the clear event.
9965      */
9966     removeAll : function(){
9967         this.data.clear();
9968         if(this.pruneModifiedRecords){
9969             this.modified = [];
9970         }
9971         this.fireEvent("clear", this);
9972     },
9973
9974     /**
9975      * Inserts Records to the Store at the given index and fires the add event.
9976      * @param {Number} index The start index at which to insert the passed Records.
9977      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9978      */
9979     insert : function(index, records){
9980         records = [].concat(records);
9981         for(var i = 0, len = records.length; i < len; i++){
9982             this.data.insert(index, records[i]);
9983             records[i].join(this);
9984         }
9985         this.fireEvent("add", this, records, index);
9986     },
9987
9988     /**
9989      * Get the index within the cache of the passed Record.
9990      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9991      * @return {Number} The index of the passed Record. Returns -1 if not found.
9992      */
9993     indexOf : function(record){
9994         return this.data.indexOf(record);
9995     },
9996
9997     /**
9998      * Get the index within the cache of the Record with the passed id.
9999      * @param {String} id The id of the Record to find.
10000      * @return {Number} The index of the Record. Returns -1 if not found.
10001      */
10002     indexOfId : function(id){
10003         return this.data.indexOfKey(id);
10004     },
10005
10006     /**
10007      * Get the Record with the specified id.
10008      * @param {String} id The id of the Record to find.
10009      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10010      */
10011     getById : function(id){
10012         return this.data.key(id);
10013     },
10014
10015     /**
10016      * Get the Record at the specified index.
10017      * @param {Number} index The index of the Record to find.
10018      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10019      */
10020     getAt : function(index){
10021         return this.data.itemAt(index);
10022     },
10023
10024     /**
10025      * Returns a range of Records between specified indices.
10026      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10027      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10028      * @return {Roo.data.Record[]} An array of Records
10029      */
10030     getRange : function(start, end){
10031         return this.data.getRange(start, end);
10032     },
10033
10034     // private
10035     storeOptions : function(o){
10036         o = Roo.apply({}, o);
10037         delete o.callback;
10038         delete o.scope;
10039         this.lastOptions = o;
10040     },
10041
10042     /**
10043      * Loads the Record cache from the configured Proxy using the configured Reader.
10044      * <p>
10045      * If using remote paging, then the first load call must specify the <em>start</em>
10046      * and <em>limit</em> properties in the options.params property to establish the initial
10047      * position within the dataset, and the number of Records to cache on each read from the Proxy.
10048      * <p>
10049      * <strong>It is important to note that for remote data sources, loading is asynchronous,
10050      * and this call will return before the new data has been loaded. Perform any post-processing
10051      * in a callback function, or in a "load" event handler.</strong>
10052      * <p>
10053      * @param {Object} options An object containing properties which control loading options:<ul>
10054      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10055      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10056      * passed the following arguments:<ul>
10057      * <li>r : Roo.data.Record[]</li>
10058      * <li>options: Options object from the load call</li>
10059      * <li>success: Boolean success indicator</li></ul></li>
10060      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10061      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10062      * </ul>
10063      */
10064     load : function(options){
10065         options = options || {};
10066         if(this.fireEvent("beforeload", this, options) !== false){
10067             this.storeOptions(options);
10068             var p = Roo.apply(options.params || {}, this.baseParams);
10069             // if meta was not loaded from remote source.. try requesting it.
10070             if (!this.reader.metaFromRemote) {
10071                 p._requestMeta = 1;
10072             }
10073             if(this.sortInfo && this.remoteSort){
10074                 var pn = this.paramNames;
10075                 p[pn["sort"]] = this.sortInfo.field;
10076                 p[pn["dir"]] = this.sortInfo.direction;
10077             }
10078             if (this.multiSort) {
10079                 var pn = this.paramNames;
10080                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10081             }
10082             
10083             this.proxy.load(p, this.reader, this.loadRecords, this, options);
10084         }
10085     },
10086
10087     /**
10088      * Reloads the Record cache from the configured Proxy using the configured Reader and
10089      * the options from the last load operation performed.
10090      * @param {Object} options (optional) An object containing properties which may override the options
10091      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10092      * the most recently used options are reused).
10093      */
10094     reload : function(options){
10095         this.load(Roo.applyIf(options||{}, this.lastOptions));
10096     },
10097
10098     // private
10099     // Called as a callback by the Reader during a load operation.
10100     loadRecords : function(o, options, success){
10101         if(!o || success === false){
10102             if(success !== false){
10103                 this.fireEvent("load", this, [], options, o);
10104             }
10105             if(options.callback){
10106                 options.callback.call(options.scope || this, [], options, false);
10107             }
10108             return;
10109         }
10110         // if data returned failure - throw an exception.
10111         if (o.success === false) {
10112             // show a message if no listener is registered.
10113             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10114                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10115             }
10116             // loadmask wil be hooked into this..
10117             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10118             return;
10119         }
10120         var r = o.records, t = o.totalRecords || r.length;
10121         
10122         this.fireEvent("beforeloadadd", this, r, options, o);
10123         
10124         if(!options || options.add !== true){
10125             if(this.pruneModifiedRecords){
10126                 this.modified = [];
10127             }
10128             for(var i = 0, len = r.length; i < len; i++){
10129                 r[i].join(this);
10130             }
10131             if(this.snapshot){
10132                 this.data = this.snapshot;
10133                 delete this.snapshot;
10134             }
10135             this.data.clear();
10136             this.data.addAll(r);
10137             this.totalLength = t;
10138             this.applySort();
10139             this.fireEvent("datachanged", this);
10140         }else{
10141             this.totalLength = Math.max(t, this.data.length+r.length);
10142             this.add(r);
10143         }
10144         this.fireEvent("load", this, r, options, o);
10145         if(options.callback){
10146             options.callback.call(options.scope || this, r, options, true);
10147         }
10148     },
10149
10150
10151     /**
10152      * Loads data from a passed data block. A Reader which understands the format of the data
10153      * must have been configured in the constructor.
10154      * @param {Object} data The data block from which to read the Records.  The format of the data expected
10155      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10156      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10157      */
10158     loadData : function(o, append){
10159         var r = this.reader.readRecords(o);
10160         this.loadRecords(r, {add: append}, true);
10161     },
10162
10163     /**
10164      * Gets the number of cached records.
10165      * <p>
10166      * <em>If using paging, this may not be the total size of the dataset. If the data object
10167      * used by the Reader contains the dataset size, then the getTotalCount() function returns
10168      * the data set size</em>
10169      */
10170     getCount : function(){
10171         return this.data.length || 0;
10172     },
10173
10174     /**
10175      * Gets the total number of records in the dataset as returned by the server.
10176      * <p>
10177      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10178      * the dataset size</em>
10179      */
10180     getTotalCount : function(){
10181         return this.totalLength || 0;
10182     },
10183
10184     /**
10185      * Returns the sort state of the Store as an object with two properties:
10186      * <pre><code>
10187  field {String} The name of the field by which the Records are sorted
10188  direction {String} The sort order, "ASC" or "DESC"
10189      * </code></pre>
10190      */
10191     getSortState : function(){
10192         return this.sortInfo;
10193     },
10194
10195     // private
10196     applySort : function(){
10197         if(this.sortInfo && !this.remoteSort){
10198             var s = this.sortInfo, f = s.field;
10199             var st = this.fields.get(f).sortType;
10200             var fn = function(r1, r2){
10201                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10202                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10203             };
10204             this.data.sort(s.direction, fn);
10205             if(this.snapshot && this.snapshot != this.data){
10206                 this.snapshot.sort(s.direction, fn);
10207             }
10208         }
10209     },
10210
10211     /**
10212      * Sets the default sort column and order to be used by the next load operation.
10213      * @param {String} fieldName The name of the field to sort by.
10214      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10215      */
10216     setDefaultSort : function(field, dir){
10217         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10218     },
10219
10220     /**
10221      * Sort the Records.
10222      * If remote sorting is used, the sort is performed on the server, and the cache is
10223      * reloaded. If local sorting is used, the cache is sorted internally.
10224      * @param {String} fieldName The name of the field to sort by.
10225      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10226      */
10227     sort : function(fieldName, dir){
10228         var f = this.fields.get(fieldName);
10229         if(!dir){
10230             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10231             
10232             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10233                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10234             }else{
10235                 dir = f.sortDir;
10236             }
10237         }
10238         this.sortToggle[f.name] = dir;
10239         this.sortInfo = {field: f.name, direction: dir};
10240         if(!this.remoteSort){
10241             this.applySort();
10242             this.fireEvent("datachanged", this);
10243         }else{
10244             this.load(this.lastOptions);
10245         }
10246     },
10247
10248     /**
10249      * Calls the specified function for each of the Records in the cache.
10250      * @param {Function} fn The function to call. The Record is passed as the first parameter.
10251      * Returning <em>false</em> aborts and exits the iteration.
10252      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10253      */
10254     each : function(fn, scope){
10255         this.data.each(fn, scope);
10256     },
10257
10258     /**
10259      * Gets all records modified since the last commit.  Modified records are persisted across load operations
10260      * (e.g., during paging).
10261      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10262      */
10263     getModifiedRecords : function(){
10264         return this.modified;
10265     },
10266
10267     // private
10268     createFilterFn : function(property, value, anyMatch){
10269         if(!value.exec){ // not a regex
10270             value = String(value);
10271             if(value.length == 0){
10272                 return false;
10273             }
10274             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10275         }
10276         return function(r){
10277             return value.test(r.data[property]);
10278         };
10279     },
10280
10281     /**
10282      * Sums the value of <i>property</i> for each record between start and end and returns the result.
10283      * @param {String} property A field on your records
10284      * @param {Number} start The record index to start at (defaults to 0)
10285      * @param {Number} end The last record index to include (defaults to length - 1)
10286      * @return {Number} The sum
10287      */
10288     sum : function(property, start, end){
10289         var rs = this.data.items, v = 0;
10290         start = start || 0;
10291         end = (end || end === 0) ? end : rs.length-1;
10292
10293         for(var i = start; i <= end; i++){
10294             v += (rs[i].data[property] || 0);
10295         }
10296         return v;
10297     },
10298
10299     /**
10300      * Filter the records by a specified property.
10301      * @param {String} field A field on your records
10302      * @param {String/RegExp} value Either a string that the field
10303      * should start with or a RegExp to test against the field
10304      * @param {Boolean} anyMatch True to match any part not just the beginning
10305      */
10306     filter : function(property, value, anyMatch){
10307         var fn = this.createFilterFn(property, value, anyMatch);
10308         return fn ? this.filterBy(fn) : this.clearFilter();
10309     },
10310
10311     /**
10312      * Filter by a function. The specified function will be called with each
10313      * record in this data source. If the function returns true the record is included,
10314      * otherwise it is filtered.
10315      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10316      * @param {Object} scope (optional) The scope of the function (defaults to this)
10317      */
10318     filterBy : function(fn, scope){
10319         this.snapshot = this.snapshot || this.data;
10320         this.data = this.queryBy(fn, scope||this);
10321         this.fireEvent("datachanged", this);
10322     },
10323
10324     /**
10325      * Query the records by a specified property.
10326      * @param {String} field A field on your records
10327      * @param {String/RegExp} value Either a string that the field
10328      * should start with or a RegExp to test against the field
10329      * @param {Boolean} anyMatch True to match any part not just the beginning
10330      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10331      */
10332     query : function(property, value, anyMatch){
10333         var fn = this.createFilterFn(property, value, anyMatch);
10334         return fn ? this.queryBy(fn) : this.data.clone();
10335     },
10336
10337     /**
10338      * Query by a function. The specified function will be called with each
10339      * record in this data source. If the function returns true the record is included
10340      * in the results.
10341      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10342      * @param {Object} scope (optional) The scope of the function (defaults to this)
10343       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10344      **/
10345     queryBy : function(fn, scope){
10346         var data = this.snapshot || this.data;
10347         return data.filterBy(fn, scope||this);
10348     },
10349
10350     /**
10351      * Collects unique values for a particular dataIndex from this store.
10352      * @param {String} dataIndex The property to collect
10353      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10354      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10355      * @return {Array} An array of the unique values
10356      **/
10357     collect : function(dataIndex, allowNull, bypassFilter){
10358         var d = (bypassFilter === true && this.snapshot) ?
10359                 this.snapshot.items : this.data.items;
10360         var v, sv, r = [], l = {};
10361         for(var i = 0, len = d.length; i < len; i++){
10362             v = d[i].data[dataIndex];
10363             sv = String(v);
10364             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10365                 l[sv] = true;
10366                 r[r.length] = v;
10367             }
10368         }
10369         return r;
10370     },
10371
10372     /**
10373      * Revert to a view of the Record cache with no filtering applied.
10374      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10375      */
10376     clearFilter : function(suppressEvent){
10377         if(this.snapshot && this.snapshot != this.data){
10378             this.data = this.snapshot;
10379             delete this.snapshot;
10380             if(suppressEvent !== true){
10381                 this.fireEvent("datachanged", this);
10382             }
10383         }
10384     },
10385
10386     // private
10387     afterEdit : function(record){
10388         if(this.modified.indexOf(record) == -1){
10389             this.modified.push(record);
10390         }
10391         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10392     },
10393     
10394     // private
10395     afterReject : function(record){
10396         this.modified.remove(record);
10397         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10398     },
10399
10400     // private
10401     afterCommit : function(record){
10402         this.modified.remove(record);
10403         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10404     },
10405
10406     /**
10407      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10408      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10409      */
10410     commitChanges : function(){
10411         var m = this.modified.slice(0);
10412         this.modified = [];
10413         for(var i = 0, len = m.length; i < len; i++){
10414             m[i].commit();
10415         }
10416     },
10417
10418     /**
10419      * Cancel outstanding changes on all changed records.
10420      */
10421     rejectChanges : function(){
10422         var m = this.modified.slice(0);
10423         this.modified = [];
10424         for(var i = 0, len = m.length; i < len; i++){
10425             m[i].reject();
10426         }
10427     },
10428
10429     onMetaChange : function(meta, rtype, o){
10430         this.recordType = rtype;
10431         this.fields = rtype.prototype.fields;
10432         delete this.snapshot;
10433         this.sortInfo = meta.sortInfo || this.sortInfo;
10434         this.modified = [];
10435         this.fireEvent('metachange', this, this.reader.meta);
10436     },
10437     
10438     moveIndex : function(data, type)
10439     {
10440         var index = this.indexOf(data);
10441         
10442         var newIndex = index + type;
10443         
10444         this.remove(data);
10445         
10446         this.insert(newIndex, data);
10447         
10448     }
10449 });/*
10450  * Based on:
10451  * Ext JS Library 1.1.1
10452  * Copyright(c) 2006-2007, Ext JS, LLC.
10453  *
10454  * Originally Released Under LGPL - original licence link has changed is not relivant.
10455  *
10456  * Fork - LGPL
10457  * <script type="text/javascript">
10458  */
10459
10460 /**
10461  * @class Roo.data.SimpleStore
10462  * @extends Roo.data.Store
10463  * Small helper class to make creating Stores from Array data easier.
10464  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10465  * @cfg {Array} fields An array of field definition objects, or field name strings.
10466  * @cfg {Array} data The multi-dimensional array of data
10467  * @constructor
10468  * @param {Object} config
10469  */
10470 Roo.data.SimpleStore = function(config){
10471     Roo.data.SimpleStore.superclass.constructor.call(this, {
10472         isLocal : true,
10473         reader: new Roo.data.ArrayReader({
10474                 id: config.id
10475             },
10476             Roo.data.Record.create(config.fields)
10477         ),
10478         proxy : new Roo.data.MemoryProxy(config.data)
10479     });
10480     this.load();
10481 };
10482 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10483  * Based on:
10484  * Ext JS Library 1.1.1
10485  * Copyright(c) 2006-2007, Ext JS, LLC.
10486  *
10487  * Originally Released Under LGPL - original licence link has changed is not relivant.
10488  *
10489  * Fork - LGPL
10490  * <script type="text/javascript">
10491  */
10492
10493 /**
10494 /**
10495  * @extends Roo.data.Store
10496  * @class Roo.data.JsonStore
10497  * Small helper class to make creating Stores for JSON data easier. <br/>
10498 <pre><code>
10499 var store = new Roo.data.JsonStore({
10500     url: 'get-images.php',
10501     root: 'images',
10502     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10503 });
10504 </code></pre>
10505  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10506  * JsonReader and HttpProxy (unless inline data is provided).</b>
10507  * @cfg {Array} fields An array of field definition objects, or field name strings.
10508  * @constructor
10509  * @param {Object} config
10510  */
10511 Roo.data.JsonStore = function(c){
10512     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10513         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10514         reader: new Roo.data.JsonReader(c, c.fields)
10515     }));
10516 };
10517 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10518  * Based on:
10519  * Ext JS Library 1.1.1
10520  * Copyright(c) 2006-2007, Ext JS, LLC.
10521  *
10522  * Originally Released Under LGPL - original licence link has changed is not relivant.
10523  *
10524  * Fork - LGPL
10525  * <script type="text/javascript">
10526  */
10527
10528  
10529 Roo.data.Field = function(config){
10530     if(typeof config == "string"){
10531         config = {name: config};
10532     }
10533     Roo.apply(this, config);
10534     
10535     if(!this.type){
10536         this.type = "auto";
10537     }
10538     
10539     var st = Roo.data.SortTypes;
10540     // named sortTypes are supported, here we look them up
10541     if(typeof this.sortType == "string"){
10542         this.sortType = st[this.sortType];
10543     }
10544     
10545     // set default sortType for strings and dates
10546     if(!this.sortType){
10547         switch(this.type){
10548             case "string":
10549                 this.sortType = st.asUCString;
10550                 break;
10551             case "date":
10552                 this.sortType = st.asDate;
10553                 break;
10554             default:
10555                 this.sortType = st.none;
10556         }
10557     }
10558
10559     // define once
10560     var stripRe = /[\$,%]/g;
10561
10562     // prebuilt conversion function for this field, instead of
10563     // switching every time we're reading a value
10564     if(!this.convert){
10565         var cv, dateFormat = this.dateFormat;
10566         switch(this.type){
10567             case "":
10568             case "auto":
10569             case undefined:
10570                 cv = function(v){ return v; };
10571                 break;
10572             case "string":
10573                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10574                 break;
10575             case "int":
10576                 cv = function(v){
10577                     return v !== undefined && v !== null && v !== '' ?
10578                            parseInt(String(v).replace(stripRe, ""), 10) : '';
10579                     };
10580                 break;
10581             case "float":
10582                 cv = function(v){
10583                     return v !== undefined && v !== null && v !== '' ?
10584                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
10585                     };
10586                 break;
10587             case "bool":
10588             case "boolean":
10589                 cv = function(v){ return v === true || v === "true" || v == 1; };
10590                 break;
10591             case "date":
10592                 cv = function(v){
10593                     if(!v){
10594                         return '';
10595                     }
10596                     if(v instanceof Date){
10597                         return v;
10598                     }
10599                     if(dateFormat){
10600                         if(dateFormat == "timestamp"){
10601                             return new Date(v*1000);
10602                         }
10603                         return Date.parseDate(v, dateFormat);
10604                     }
10605                     var parsed = Date.parse(v);
10606                     return parsed ? new Date(parsed) : null;
10607                 };
10608              break;
10609             
10610         }
10611         this.convert = cv;
10612     }
10613 };
10614
10615 Roo.data.Field.prototype = {
10616     dateFormat: null,
10617     defaultValue: "",
10618     mapping: null,
10619     sortType : null,
10620     sortDir : "ASC"
10621 };/*
10622  * Based on:
10623  * Ext JS Library 1.1.1
10624  * Copyright(c) 2006-2007, Ext JS, LLC.
10625  *
10626  * Originally Released Under LGPL - original licence link has changed is not relivant.
10627  *
10628  * Fork - LGPL
10629  * <script type="text/javascript">
10630  */
10631  
10632 // Base class for reading structured data from a data source.  This class is intended to be
10633 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10634
10635 /**
10636  * @class Roo.data.DataReader
10637  * Base class for reading structured data from a data source.  This class is intended to be
10638  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10639  */
10640
10641 Roo.data.DataReader = function(meta, recordType){
10642     
10643     this.meta = meta;
10644     
10645     this.recordType = recordType instanceof Array ? 
10646         Roo.data.Record.create(recordType) : recordType;
10647 };
10648
10649 Roo.data.DataReader.prototype = {
10650      /**
10651      * Create an empty record
10652      * @param {Object} data (optional) - overlay some values
10653      * @return {Roo.data.Record} record created.
10654      */
10655     newRow :  function(d) {
10656         var da =  {};
10657         this.recordType.prototype.fields.each(function(c) {
10658             switch( c.type) {
10659                 case 'int' : da[c.name] = 0; break;
10660                 case 'date' : da[c.name] = new Date(); break;
10661                 case 'float' : da[c.name] = 0.0; break;
10662                 case 'boolean' : da[c.name] = false; break;
10663                 default : da[c.name] = ""; break;
10664             }
10665             
10666         });
10667         return new this.recordType(Roo.apply(da, d));
10668     }
10669     
10670 };/*
10671  * Based on:
10672  * Ext JS Library 1.1.1
10673  * Copyright(c) 2006-2007, Ext JS, LLC.
10674  *
10675  * Originally Released Under LGPL - original licence link has changed is not relivant.
10676  *
10677  * Fork - LGPL
10678  * <script type="text/javascript">
10679  */
10680
10681 /**
10682  * @class Roo.data.DataProxy
10683  * @extends Roo.data.Observable
10684  * This class is an abstract base class for implementations which provide retrieval of
10685  * unformatted data objects.<br>
10686  * <p>
10687  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10688  * (of the appropriate type which knows how to parse the data object) to provide a block of
10689  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10690  * <p>
10691  * Custom implementations must implement the load method as described in
10692  * {@link Roo.data.HttpProxy#load}.
10693  */
10694 Roo.data.DataProxy = function(){
10695     this.addEvents({
10696         /**
10697          * @event beforeload
10698          * Fires before a network request is made to retrieve a data object.
10699          * @param {Object} This DataProxy object.
10700          * @param {Object} params The params parameter to the load function.
10701          */
10702         beforeload : true,
10703         /**
10704          * @event load
10705          * Fires before the load method's callback is called.
10706          * @param {Object} This DataProxy object.
10707          * @param {Object} o The data object.
10708          * @param {Object} arg The callback argument object passed to the load function.
10709          */
10710         load : true,
10711         /**
10712          * @event loadexception
10713          * Fires if an Exception occurs during data retrieval.
10714          * @param {Object} This DataProxy object.
10715          * @param {Object} o The data object.
10716          * @param {Object} arg The callback argument object passed to the load function.
10717          * @param {Object} e The Exception.
10718          */
10719         loadexception : true
10720     });
10721     Roo.data.DataProxy.superclass.constructor.call(this);
10722 };
10723
10724 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10725
10726     /**
10727      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10728      */
10729 /*
10730  * Based on:
10731  * Ext JS Library 1.1.1
10732  * Copyright(c) 2006-2007, Ext JS, LLC.
10733  *
10734  * Originally Released Under LGPL - original licence link has changed is not relivant.
10735  *
10736  * Fork - LGPL
10737  * <script type="text/javascript">
10738  */
10739 /**
10740  * @class Roo.data.MemoryProxy
10741  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10742  * to the Reader when its load method is called.
10743  * @constructor
10744  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10745  */
10746 Roo.data.MemoryProxy = function(data){
10747     if (data.data) {
10748         data = data.data;
10749     }
10750     Roo.data.MemoryProxy.superclass.constructor.call(this);
10751     this.data = data;
10752 };
10753
10754 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10755     /**
10756      * Load data from the requested source (in this case an in-memory
10757      * data object passed to the constructor), read the data object into
10758      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10759      * process that block using the passed callback.
10760      * @param {Object} params This parameter is not used by the MemoryProxy class.
10761      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10762      * object into a block of Roo.data.Records.
10763      * @param {Function} callback The function into which to pass the block of Roo.data.records.
10764      * The function must be passed <ul>
10765      * <li>The Record block object</li>
10766      * <li>The "arg" argument from the load function</li>
10767      * <li>A boolean success indicator</li>
10768      * </ul>
10769      * @param {Object} scope The scope in which to call the callback
10770      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10771      */
10772     load : function(params, reader, callback, scope, arg){
10773         params = params || {};
10774         var result;
10775         try {
10776             result = reader.readRecords(this.data);
10777         }catch(e){
10778             this.fireEvent("loadexception", this, arg, null, e);
10779             callback.call(scope, null, arg, false);
10780             return;
10781         }
10782         callback.call(scope, result, arg, true);
10783     },
10784     
10785     // private
10786     update : function(params, records){
10787         
10788     }
10789 });/*
10790  * Based on:
10791  * Ext JS Library 1.1.1
10792  * Copyright(c) 2006-2007, Ext JS, LLC.
10793  *
10794  * Originally Released Under LGPL - original licence link has changed is not relivant.
10795  *
10796  * Fork - LGPL
10797  * <script type="text/javascript">
10798  */
10799 /**
10800  * @class Roo.data.HttpProxy
10801  * @extends Roo.data.DataProxy
10802  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10803  * configured to reference a certain URL.<br><br>
10804  * <p>
10805  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10806  * from which the running page was served.<br><br>
10807  * <p>
10808  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10809  * <p>
10810  * Be aware that to enable the browser to parse an XML document, the server must set
10811  * the Content-Type header in the HTTP response to "text/xml".
10812  * @constructor
10813  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10814  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
10815  * will be used to make the request.
10816  */
10817 Roo.data.HttpProxy = function(conn){
10818     Roo.data.HttpProxy.superclass.constructor.call(this);
10819     // is conn a conn config or a real conn?
10820     this.conn = conn;
10821     this.useAjax = !conn || !conn.events;
10822   
10823 };
10824
10825 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10826     // thse are take from connection...
10827     
10828     /**
10829      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10830      */
10831     /**
10832      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10833      * extra parameters to each request made by this object. (defaults to undefined)
10834      */
10835     /**
10836      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10837      *  to each request made by this object. (defaults to undefined)
10838      */
10839     /**
10840      * @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)
10841      */
10842     /**
10843      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10844      */
10845      /**
10846      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10847      * @type Boolean
10848      */
10849   
10850
10851     /**
10852      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10853      * @type Boolean
10854      */
10855     /**
10856      * Return the {@link Roo.data.Connection} object being used by this Proxy.
10857      * @return {Connection} The Connection object. This object may be used to subscribe to events on
10858      * a finer-grained basis than the DataProxy events.
10859      */
10860     getConnection : function(){
10861         return this.useAjax ? Roo.Ajax : this.conn;
10862     },
10863
10864     /**
10865      * Load data from the configured {@link Roo.data.Connection}, read the data object into
10866      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10867      * process that block using the passed callback.
10868      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10869      * for the request to the remote server.
10870      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10871      * object into a block of Roo.data.Records.
10872      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10873      * The function must be passed <ul>
10874      * <li>The Record block object</li>
10875      * <li>The "arg" argument from the load function</li>
10876      * <li>A boolean success indicator</li>
10877      * </ul>
10878      * @param {Object} scope The scope in which to call the callback
10879      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10880      */
10881     load : function(params, reader, callback, scope, arg){
10882         if(this.fireEvent("beforeload", this, params) !== false){
10883             var  o = {
10884                 params : params || {},
10885                 request: {
10886                     callback : callback,
10887                     scope : scope,
10888                     arg : arg
10889                 },
10890                 reader: reader,
10891                 callback : this.loadResponse,
10892                 scope: this
10893             };
10894             if(this.useAjax){
10895                 Roo.applyIf(o, this.conn);
10896                 if(this.activeRequest){
10897                     Roo.Ajax.abort(this.activeRequest);
10898                 }
10899                 this.activeRequest = Roo.Ajax.request(o);
10900             }else{
10901                 this.conn.request(o);
10902             }
10903         }else{
10904             callback.call(scope||this, null, arg, false);
10905         }
10906     },
10907
10908     // private
10909     loadResponse : function(o, success, response){
10910         delete this.activeRequest;
10911         if(!success){
10912             this.fireEvent("loadexception", this, o, response);
10913             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10914             return;
10915         }
10916         var result;
10917         try {
10918             result = o.reader.read(response);
10919         }catch(e){
10920             this.fireEvent("loadexception", this, o, response, e);
10921             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10922             return;
10923         }
10924         
10925         this.fireEvent("load", this, o, o.request.arg);
10926         o.request.callback.call(o.request.scope, result, o.request.arg, true);
10927     },
10928
10929     // private
10930     update : function(dataSet){
10931
10932     },
10933
10934     // private
10935     updateResponse : function(dataSet){
10936
10937     }
10938 });/*
10939  * Based on:
10940  * Ext JS Library 1.1.1
10941  * Copyright(c) 2006-2007, Ext JS, LLC.
10942  *
10943  * Originally Released Under LGPL - original licence link has changed is not relivant.
10944  *
10945  * Fork - LGPL
10946  * <script type="text/javascript">
10947  */
10948
10949 /**
10950  * @class Roo.data.ScriptTagProxy
10951  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10952  * other than the originating domain of the running page.<br><br>
10953  * <p>
10954  * <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
10955  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10956  * <p>
10957  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10958  * source code that is used as the source inside a &lt;script> tag.<br><br>
10959  * <p>
10960  * In order for the browser to process the returned data, the server must wrap the data object
10961  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10962  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10963  * depending on whether the callback name was passed:
10964  * <p>
10965  * <pre><code>
10966 boolean scriptTag = false;
10967 String cb = request.getParameter("callback");
10968 if (cb != null) {
10969     scriptTag = true;
10970     response.setContentType("text/javascript");
10971 } else {
10972     response.setContentType("application/x-json");
10973 }
10974 Writer out = response.getWriter();
10975 if (scriptTag) {
10976     out.write(cb + "(");
10977 }
10978 out.print(dataBlock.toJsonString());
10979 if (scriptTag) {
10980     out.write(");");
10981 }
10982 </pre></code>
10983  *
10984  * @constructor
10985  * @param {Object} config A configuration object.
10986  */
10987 Roo.data.ScriptTagProxy = function(config){
10988     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10989     Roo.apply(this, config);
10990     this.head = document.getElementsByTagName("head")[0];
10991 };
10992
10993 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10994
10995 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10996     /**
10997      * @cfg {String} url The URL from which to request the data object.
10998      */
10999     /**
11000      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11001      */
11002     timeout : 30000,
11003     /**
11004      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11005      * the server the name of the callback function set up by the load call to process the returned data object.
11006      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11007      * javascript output which calls this named function passing the data object as its only parameter.
11008      */
11009     callbackParam : "callback",
11010     /**
11011      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11012      * name to the request.
11013      */
11014     nocache : true,
11015
11016     /**
11017      * Load data from the configured URL, read the data object into
11018      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11019      * process that block using the passed callback.
11020      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11021      * for the request to the remote server.
11022      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11023      * object into a block of Roo.data.Records.
11024      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11025      * The function must be passed <ul>
11026      * <li>The Record block object</li>
11027      * <li>The "arg" argument from the load function</li>
11028      * <li>A boolean success indicator</li>
11029      * </ul>
11030      * @param {Object} scope The scope in which to call the callback
11031      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11032      */
11033     load : function(params, reader, callback, scope, arg){
11034         if(this.fireEvent("beforeload", this, params) !== false){
11035
11036             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11037
11038             var url = this.url;
11039             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11040             if(this.nocache){
11041                 url += "&_dc=" + (new Date().getTime());
11042             }
11043             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11044             var trans = {
11045                 id : transId,
11046                 cb : "stcCallback"+transId,
11047                 scriptId : "stcScript"+transId,
11048                 params : params,
11049                 arg : arg,
11050                 url : url,
11051                 callback : callback,
11052                 scope : scope,
11053                 reader : reader
11054             };
11055             var conn = this;
11056
11057             window[trans.cb] = function(o){
11058                 conn.handleResponse(o, trans);
11059             };
11060
11061             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11062
11063             if(this.autoAbort !== false){
11064                 this.abort();
11065             }
11066
11067             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11068
11069             var script = document.createElement("script");
11070             script.setAttribute("src", url);
11071             script.setAttribute("type", "text/javascript");
11072             script.setAttribute("id", trans.scriptId);
11073             this.head.appendChild(script);
11074
11075             this.trans = trans;
11076         }else{
11077             callback.call(scope||this, null, arg, false);
11078         }
11079     },
11080
11081     // private
11082     isLoading : function(){
11083         return this.trans ? true : false;
11084     },
11085
11086     /**
11087      * Abort the current server request.
11088      */
11089     abort : function(){
11090         if(this.isLoading()){
11091             this.destroyTrans(this.trans);
11092         }
11093     },
11094
11095     // private
11096     destroyTrans : function(trans, isLoaded){
11097         this.head.removeChild(document.getElementById(trans.scriptId));
11098         clearTimeout(trans.timeoutId);
11099         if(isLoaded){
11100             window[trans.cb] = undefined;
11101             try{
11102                 delete window[trans.cb];
11103             }catch(e){}
11104         }else{
11105             // if hasn't been loaded, wait for load to remove it to prevent script error
11106             window[trans.cb] = function(){
11107                 window[trans.cb] = undefined;
11108                 try{
11109                     delete window[trans.cb];
11110                 }catch(e){}
11111             };
11112         }
11113     },
11114
11115     // private
11116     handleResponse : function(o, trans){
11117         this.trans = false;
11118         this.destroyTrans(trans, true);
11119         var result;
11120         try {
11121             result = trans.reader.readRecords(o);
11122         }catch(e){
11123             this.fireEvent("loadexception", this, o, trans.arg, e);
11124             trans.callback.call(trans.scope||window, null, trans.arg, false);
11125             return;
11126         }
11127         this.fireEvent("load", this, o, trans.arg);
11128         trans.callback.call(trans.scope||window, result, trans.arg, true);
11129     },
11130
11131     // private
11132     handleFailure : function(trans){
11133         this.trans = false;
11134         this.destroyTrans(trans, false);
11135         this.fireEvent("loadexception", this, null, trans.arg);
11136         trans.callback.call(trans.scope||window, null, trans.arg, false);
11137     }
11138 });/*
11139  * Based on:
11140  * Ext JS Library 1.1.1
11141  * Copyright(c) 2006-2007, Ext JS, LLC.
11142  *
11143  * Originally Released Under LGPL - original licence link has changed is not relivant.
11144  *
11145  * Fork - LGPL
11146  * <script type="text/javascript">
11147  */
11148
11149 /**
11150  * @class Roo.data.JsonReader
11151  * @extends Roo.data.DataReader
11152  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11153  * based on mappings in a provided Roo.data.Record constructor.
11154  * 
11155  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11156  * in the reply previously. 
11157  * 
11158  * <p>
11159  * Example code:
11160  * <pre><code>
11161 var RecordDef = Roo.data.Record.create([
11162     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
11163     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
11164 ]);
11165 var myReader = new Roo.data.JsonReader({
11166     totalProperty: "results",    // The property which contains the total dataset size (optional)
11167     root: "rows",                // The property which contains an Array of row objects
11168     id: "id"                     // The property within each row object that provides an ID for the record (optional)
11169 }, RecordDef);
11170 </code></pre>
11171  * <p>
11172  * This would consume a JSON file like this:
11173  * <pre><code>
11174 { 'results': 2, 'rows': [
11175     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11176     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11177 }
11178 </code></pre>
11179  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11180  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11181  * paged from the remote server.
11182  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11183  * @cfg {String} root name of the property which contains the Array of row objects.
11184  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11185  * @cfg {Array} fields Array of field definition objects
11186  * @constructor
11187  * Create a new JsonReader
11188  * @param {Object} meta Metadata configuration options
11189  * @param {Object} recordType Either an Array of field definition objects,
11190  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11191  */
11192 Roo.data.JsonReader = function(meta, recordType){
11193     
11194     meta = meta || {};
11195     // set some defaults:
11196     Roo.applyIf(meta, {
11197         totalProperty: 'total',
11198         successProperty : 'success',
11199         root : 'data',
11200         id : 'id'
11201     });
11202     
11203     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11204 };
11205 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11206     
11207     /**
11208      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
11209      * Used by Store query builder to append _requestMeta to params.
11210      * 
11211      */
11212     metaFromRemote : false,
11213     /**
11214      * This method is only used by a DataProxy which has retrieved data from a remote server.
11215      * @param {Object} response The XHR object which contains the JSON data in its responseText.
11216      * @return {Object} data A data block which is used by an Roo.data.Store object as
11217      * a cache of Roo.data.Records.
11218      */
11219     read : function(response){
11220         var json = response.responseText;
11221        
11222         var o = /* eval:var:o */ eval("("+json+")");
11223         if(!o) {
11224             throw {message: "JsonReader.read: Json object not found"};
11225         }
11226         
11227         if(o.metaData){
11228             
11229             delete this.ef;
11230             this.metaFromRemote = true;
11231             this.meta = o.metaData;
11232             this.recordType = Roo.data.Record.create(o.metaData.fields);
11233             this.onMetaChange(this.meta, this.recordType, o);
11234         }
11235         return this.readRecords(o);
11236     },
11237
11238     // private function a store will implement
11239     onMetaChange : function(meta, recordType, o){
11240
11241     },
11242
11243     /**
11244          * @ignore
11245          */
11246     simpleAccess: function(obj, subsc) {
11247         return obj[subsc];
11248     },
11249
11250         /**
11251          * @ignore
11252          */
11253     getJsonAccessor: function(){
11254         var re = /[\[\.]/;
11255         return function(expr) {
11256             try {
11257                 return(re.test(expr))
11258                     ? new Function("obj", "return obj." + expr)
11259                     : function(obj){
11260                         return obj[expr];
11261                     };
11262             } catch(e){}
11263             return Roo.emptyFn;
11264         };
11265     }(),
11266
11267     /**
11268      * Create a data block containing Roo.data.Records from an XML document.
11269      * @param {Object} o An object which contains an Array of row objects in the property specified
11270      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11271      * which contains the total size of the dataset.
11272      * @return {Object} data A data block which is used by an Roo.data.Store object as
11273      * a cache of Roo.data.Records.
11274      */
11275     readRecords : function(o){
11276         /**
11277          * After any data loads, the raw JSON data is available for further custom processing.
11278          * @type Object
11279          */
11280         this.o = o;
11281         var s = this.meta, Record = this.recordType,
11282             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11283
11284 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
11285         if (!this.ef) {
11286             if(s.totalProperty) {
11287                     this.getTotal = this.getJsonAccessor(s.totalProperty);
11288                 }
11289                 if(s.successProperty) {
11290                     this.getSuccess = this.getJsonAccessor(s.successProperty);
11291                 }
11292                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11293                 if (s.id) {
11294                         var g = this.getJsonAccessor(s.id);
11295                         this.getId = function(rec) {
11296                                 var r = g(rec);  
11297                                 return (r === undefined || r === "") ? null : r;
11298                         };
11299                 } else {
11300                         this.getId = function(){return null;};
11301                 }
11302             this.ef = [];
11303             for(var jj = 0; jj < fl; jj++){
11304                 f = fi[jj];
11305                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11306                 this.ef[jj] = this.getJsonAccessor(map);
11307             }
11308         }
11309
11310         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11311         if(s.totalProperty){
11312             var vt = parseInt(this.getTotal(o), 10);
11313             if(!isNaN(vt)){
11314                 totalRecords = vt;
11315             }
11316         }
11317         if(s.successProperty){
11318             var vs = this.getSuccess(o);
11319             if(vs === false || vs === 'false'){
11320                 success = false;
11321             }
11322         }
11323         var records = [];
11324         for(var i = 0; i < c; i++){
11325                 var n = root[i];
11326             var values = {};
11327             var id = this.getId(n);
11328             for(var j = 0; j < fl; j++){
11329                 f = fi[j];
11330             var v = this.ef[j](n);
11331             if (!f.convert) {
11332                 Roo.log('missing convert for ' + f.name);
11333                 Roo.log(f);
11334                 continue;
11335             }
11336             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11337             }
11338             var record = new Record(values, id);
11339             record.json = n;
11340             records[i] = record;
11341         }
11342         return {
11343             raw : o,
11344             success : success,
11345             records : records,
11346             totalRecords : totalRecords
11347         };
11348     }
11349 });/*
11350  * Based on:
11351  * Ext JS Library 1.1.1
11352  * Copyright(c) 2006-2007, Ext JS, LLC.
11353  *
11354  * Originally Released Under LGPL - original licence link has changed is not relivant.
11355  *
11356  * Fork - LGPL
11357  * <script type="text/javascript">
11358  */
11359
11360 /**
11361  * @class Roo.data.ArrayReader
11362  * @extends Roo.data.DataReader
11363  * Data reader class to create an Array of Roo.data.Record objects from an Array.
11364  * Each element of that Array represents a row of data fields. The
11365  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11366  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11367  * <p>
11368  * Example code:.
11369  * <pre><code>
11370 var RecordDef = Roo.data.Record.create([
11371     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
11372     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
11373 ]);
11374 var myReader = new Roo.data.ArrayReader({
11375     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
11376 }, RecordDef);
11377 </code></pre>
11378  * <p>
11379  * This would consume an Array like this:
11380  * <pre><code>
11381 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11382   </code></pre>
11383  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11384  * @constructor
11385  * Create a new JsonReader
11386  * @param {Object} meta Metadata configuration options.
11387  * @param {Object} recordType Either an Array of field definition objects
11388  * as specified to {@link Roo.data.Record#create},
11389  * or an {@link Roo.data.Record} object
11390  * created using {@link Roo.data.Record#create}.
11391  */
11392 Roo.data.ArrayReader = function(meta, recordType){
11393     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11394 };
11395
11396 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11397     /**
11398      * Create a data block containing Roo.data.Records from an XML document.
11399      * @param {Object} o An Array of row objects which represents the dataset.
11400      * @return {Object} data A data block which is used by an Roo.data.Store object as
11401      * a cache of Roo.data.Records.
11402      */
11403     readRecords : function(o){
11404         var sid = this.meta ? this.meta.id : null;
11405         var recordType = this.recordType, fields = recordType.prototype.fields;
11406         var records = [];
11407         var root = o;
11408             for(var i = 0; i < root.length; i++){
11409                     var n = root[i];
11410                 var values = {};
11411                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11412                 for(var j = 0, jlen = fields.length; j < jlen; j++){
11413                 var f = fields.items[j];
11414                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11415                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11416                 v = f.convert(v);
11417                 values[f.name] = v;
11418             }
11419                 var record = new recordType(values, id);
11420                 record.json = n;
11421                 records[records.length] = record;
11422             }
11423             return {
11424                 records : records,
11425                 totalRecords : records.length
11426             };
11427     }
11428 });/*
11429  * - LGPL
11430  * * 
11431  */
11432
11433 /**
11434  * @class Roo.bootstrap.ComboBox
11435  * @extends Roo.bootstrap.TriggerField
11436  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11437  * @cfg {Boolean} append (true|false) default false
11438  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11439  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11440  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11441  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11442  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11443  * @cfg {Boolean} animate default true
11444  * @cfg {Boolean} emptyResultText only for touch device
11445  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11446  * @constructor
11447  * Create a new ComboBox.
11448  * @param {Object} config Configuration options
11449  */
11450 Roo.bootstrap.ComboBox = function(config){
11451     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11452     this.addEvents({
11453         /**
11454          * @event expand
11455          * Fires when the dropdown list is expanded
11456              * @param {Roo.bootstrap.ComboBox} combo This combo box
11457              */
11458         'expand' : true,
11459         /**
11460          * @event collapse
11461          * Fires when the dropdown list is collapsed
11462              * @param {Roo.bootstrap.ComboBox} combo This combo box
11463              */
11464         'collapse' : true,
11465         /**
11466          * @event beforeselect
11467          * Fires before a list item is selected. Return false to cancel the selection.
11468              * @param {Roo.bootstrap.ComboBox} combo This combo box
11469              * @param {Roo.data.Record} record The data record returned from the underlying store
11470              * @param {Number} index The index of the selected item in the dropdown list
11471              */
11472         'beforeselect' : true,
11473         /**
11474          * @event select
11475          * Fires when a list item is selected
11476              * @param {Roo.bootstrap.ComboBox} combo This combo box
11477              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11478              * @param {Number} index The index of the selected item in the dropdown list
11479              */
11480         'select' : true,
11481         /**
11482          * @event beforequery
11483          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11484          * The event object passed has these properties:
11485              * @param {Roo.bootstrap.ComboBox} combo This combo box
11486              * @param {String} query The query
11487              * @param {Boolean} forceAll true to force "all" query
11488              * @param {Boolean} cancel true to cancel the query
11489              * @param {Object} e The query event object
11490              */
11491         'beforequery': true,
11492          /**
11493          * @event add
11494          * Fires when the 'add' icon is pressed (add a listener to enable add button)
11495              * @param {Roo.bootstrap.ComboBox} combo This combo box
11496              */
11497         'add' : true,
11498         /**
11499          * @event edit
11500          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11501              * @param {Roo.bootstrap.ComboBox} combo This combo box
11502              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11503              */
11504         'edit' : true,
11505         /**
11506          * @event remove
11507          * Fires when the remove value from the combobox array
11508              * @param {Roo.bootstrap.ComboBox} combo This combo box
11509              */
11510         'remove' : true,
11511         /**
11512          * @event specialfilter
11513          * Fires when specialfilter
11514             * @param {Roo.bootstrap.ComboBox} combo This combo box
11515             */
11516         'specialfilter' : true,
11517         /**
11518          * @event tick
11519          * Fires when tick the element
11520             * @param {Roo.bootstrap.ComboBox} combo This combo box
11521             */
11522         'tick' : true,
11523         /**
11524          * @event touchviewdisplay
11525          * Fires when touch view require special display (default is using displayField)
11526             * @param {Roo.bootstrap.ComboBox} combo This combo box
11527             * @param {Object} cfg set html .
11528             */
11529         'touchviewdisplay' : true
11530         
11531     });
11532     
11533     this.item = [];
11534     this.tickItems = [];
11535     
11536     this.selectedIndex = -1;
11537     if(this.mode == 'local'){
11538         if(config.queryDelay === undefined){
11539             this.queryDelay = 10;
11540         }
11541         if(config.minChars === undefined){
11542             this.minChars = 0;
11543         }
11544     }
11545 };
11546
11547 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11548      
11549     /**
11550      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11551      * rendering into an Roo.Editor, defaults to false)
11552      */
11553     /**
11554      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11555      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11556      */
11557     /**
11558      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11559      */
11560     /**
11561      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11562      * the dropdown list (defaults to undefined, with no header element)
11563      */
11564
11565      /**
11566      * @cfg {String/Roo.Template} tpl The template to use to render the output
11567      */
11568      
11569      /**
11570      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11571      */
11572     listWidth: undefined,
11573     /**
11574      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11575      * mode = 'remote' or 'text' if mode = 'local')
11576      */
11577     displayField: undefined,
11578     
11579     /**
11580      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11581      * mode = 'remote' or 'value' if mode = 'local'). 
11582      * Note: use of a valueField requires the user make a selection
11583      * in order for a value to be mapped.
11584      */
11585     valueField: undefined,
11586     
11587     
11588     /**
11589      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11590      * field's data value (defaults to the underlying DOM element's name)
11591      */
11592     hiddenName: undefined,
11593     /**
11594      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11595      */
11596     listClass: '',
11597     /**
11598      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11599      */
11600     selectedClass: 'active',
11601     
11602     /**
11603      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11604      */
11605     shadow:'sides',
11606     /**
11607      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11608      * anchor positions (defaults to 'tl-bl')
11609      */
11610     listAlign: 'tl-bl?',
11611     /**
11612      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11613      */
11614     maxHeight: 300,
11615     /**
11616      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
11617      * query specified by the allQuery config option (defaults to 'query')
11618      */
11619     triggerAction: 'query',
11620     /**
11621      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11622      * (defaults to 4, does not apply if editable = false)
11623      */
11624     minChars : 4,
11625     /**
11626      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11627      * delay (typeAheadDelay) if it matches a known value (defaults to false)
11628      */
11629     typeAhead: false,
11630     /**
11631      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11632      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11633      */
11634     queryDelay: 500,
11635     /**
11636      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11637      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
11638      */
11639     pageSize: 0,
11640     /**
11641      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
11642      * when editable = true (defaults to false)
11643      */
11644     selectOnFocus:false,
11645     /**
11646      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11647      */
11648     queryParam: 'query',
11649     /**
11650      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
11651      * when mode = 'remote' (defaults to 'Loading...')
11652      */
11653     loadingText: 'Loading...',
11654     /**
11655      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11656      */
11657     resizable: false,
11658     /**
11659      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11660      */
11661     handleHeight : 8,
11662     /**
11663      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11664      * traditional select (defaults to true)
11665      */
11666     editable: true,
11667     /**
11668      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11669      */
11670     allQuery: '',
11671     /**
11672      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11673      */
11674     mode: 'remote',
11675     /**
11676      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11677      * listWidth has a higher value)
11678      */
11679     minListWidth : 70,
11680     /**
11681      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11682      * allow the user to set arbitrary text into the field (defaults to false)
11683      */
11684     forceSelection:false,
11685     /**
11686      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11687      * if typeAhead = true (defaults to 250)
11688      */
11689     typeAheadDelay : 250,
11690     /**
11691      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11692      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11693      */
11694     valueNotFoundText : undefined,
11695     /**
11696      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11697      */
11698     blockFocus : false,
11699     
11700     /**
11701      * @cfg {Boolean} disableClear Disable showing of clear button.
11702      */
11703     disableClear : false,
11704     /**
11705      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
11706      */
11707     alwaysQuery : false,
11708     
11709     /**
11710      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
11711      */
11712     multiple : false,
11713     
11714     /**
11715      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11716      */
11717     invalidClass : "has-warning",
11718     
11719     /**
11720      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11721      */
11722     validClass : "has-success",
11723     
11724     /**
11725      * @cfg {Boolean} specialFilter (true|false) special filter default false
11726      */
11727     specialFilter : false,
11728     
11729     /**
11730      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
11731      */
11732     mobileTouchView : true,
11733     
11734     //private
11735     addicon : false,
11736     editicon: false,
11737     
11738     page: 0,
11739     hasQuery: false,
11740     append: false,
11741     loadNext: false,
11742     autoFocus : true,
11743     tickable : false,
11744     btnPosition : 'right',
11745     triggerList : true,
11746     showToggleBtn : true,
11747     animate : true,
11748     emptyResultText: 'Empty',
11749     triggerText : 'Select',
11750     
11751     // element that contains real text value.. (when hidden is used..)
11752     
11753     getAutoCreate : function()
11754     {
11755         var cfg = false;
11756         
11757         /*
11758          * Touch Devices
11759          */
11760         
11761         if(Roo.isTouch && this.mobileTouchView){
11762             cfg = this.getAutoCreateTouchView();
11763             return cfg;;
11764         }
11765         
11766         /*
11767          *  Normal ComboBox
11768          */
11769         if(!this.tickable){
11770             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11771             return cfg;
11772         }
11773         
11774         /*
11775          *  ComboBox with tickable selections
11776          */
11777              
11778         var align = this.labelAlign || this.parentLabelAlign();
11779         
11780         cfg = {
11781             cls : 'form-group roo-combobox-tickable' //input-group
11782         };
11783         
11784         var buttons = {
11785             tag : 'div',
11786             cls : 'tickable-buttons',
11787             cn : [
11788                 {
11789                     tag : 'button',
11790                     type : 'button',
11791                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11792                     html : this.triggerText
11793                 },
11794                 {
11795                     tag : 'button',
11796                     type : 'button',
11797                     name : 'ok',
11798                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11799                     html : 'Done'
11800                 },
11801                 {
11802                     tag : 'button',
11803                     type : 'button',
11804                     name : 'cancel',
11805                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11806                     html : 'Cancel'
11807                 }
11808             ]
11809         };
11810         
11811         if(this.editable){
11812             buttons.cn.unshift({
11813                 tag: 'input',
11814                 cls: 'select2-search-field-input'
11815             });
11816         }
11817         
11818         var _this = this;
11819         
11820         Roo.each(buttons.cn, function(c){
11821             if (_this.size) {
11822                 c.cls += ' btn-' + _this.size;
11823             }
11824
11825             if (_this.disabled) {
11826                 c.disabled = true;
11827             }
11828         });
11829         
11830         var box = {
11831             tag: 'div',
11832             cn: [
11833                 {
11834                     tag: 'input',
11835                     type : 'hidden',
11836                     cls: 'form-hidden-field'
11837                 },
11838                 {
11839                     tag: 'ul',
11840                     cls: 'select2-choices',
11841                     cn:[
11842                         {
11843                             tag: 'li',
11844                             cls: 'select2-search-field',
11845                             cn: [
11846
11847                                 buttons
11848                             ]
11849                         }
11850                     ]
11851                 }
11852             ]
11853         };
11854         
11855         var combobox = {
11856             cls: 'select2-container input-group select2-container-multi',
11857             cn: [
11858                 box
11859 //                {
11860 //                    tag: 'ul',
11861 //                    cls: 'typeahead typeahead-long dropdown-menu',
11862 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
11863 //                }
11864             ]
11865         };
11866         
11867         if(this.hasFeedback && !this.allowBlank){
11868             
11869             var feedback = {
11870                 tag: 'span',
11871                 cls: 'glyphicon form-control-feedback'
11872             };
11873
11874             combobox.cn.push(feedback);
11875         }
11876         
11877         if (align ==='left' && this.fieldLabel.length) {
11878             
11879 //                Roo.log("left and has label");
11880                 cfg.cn = [
11881                     
11882                     {
11883                         tag: 'label',
11884                         'for' :  id,
11885                         cls : 'control-label col-sm-' + this.labelWidth,
11886                         html : this.fieldLabel
11887                         
11888                     },
11889                     {
11890                         cls : "col-sm-" + (12 - this.labelWidth), 
11891                         cn: [
11892                             combobox
11893                         ]
11894                     }
11895                     
11896                 ];
11897         } else if ( this.fieldLabel.length) {
11898 //                Roo.log(" label");
11899                  cfg.cn = [
11900                    
11901                     {
11902                         tag: 'label',
11903                         //cls : 'input-group-addon',
11904                         html : this.fieldLabel
11905                         
11906                     },
11907                     
11908                     combobox
11909                     
11910                 ];
11911
11912         } else {
11913             
11914 //                Roo.log(" no label && no align");
11915                 cfg = combobox
11916                      
11917                 
11918         }
11919          
11920         var settings=this;
11921         ['xs','sm','md','lg'].map(function(size){
11922             if (settings[size]) {
11923                 cfg.cls += ' col-' + size + '-' + settings[size];
11924             }
11925         });
11926         
11927         return cfg;
11928         
11929     },
11930     
11931     _initEventsCalled : false,
11932     
11933     // private
11934     initEvents: function()
11935     {
11936         
11937         if (this._initEventsCalled) { // as we call render... prevent looping...
11938             return;
11939         }
11940         this._initEventsCalled = true;
11941         
11942         if (!this.store) {
11943             throw "can not find store for combo";
11944         }
11945         
11946         this.store = Roo.factory(this.store, Roo.data);
11947         
11948         // if we are building from html. then this element is so complex, that we can not really
11949         // use the rendered HTML.
11950         // so we have to trash and replace the previous code.
11951         if (Roo.XComponent.build_from_html) {
11952             
11953             // remove this element....
11954             var e = this.el.dom, k=0;
11955             while (e ) { e = e.previousSibling;  ++k;}
11956
11957             this.el.remove();
11958             
11959             this.el=false;
11960             this.rendered = false;
11961             
11962             this.render(this.parent().getChildContainer(true), k);
11963             
11964             
11965             
11966         }
11967         
11968         
11969         /*
11970          * Touch Devices
11971          */
11972         
11973         if(Roo.isTouch && this.mobileTouchView){
11974             this.initTouchView();
11975             return;
11976         }
11977         
11978         if(this.tickable){
11979             this.initTickableEvents();
11980             return;
11981         }
11982         
11983         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11984         
11985         if(this.hiddenName){
11986             
11987             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11988             
11989             this.hiddenField.dom.value =
11990                 this.hiddenValue !== undefined ? this.hiddenValue :
11991                 this.value !== undefined ? this.value : '';
11992
11993             // prevent input submission
11994             this.el.dom.removeAttribute('name');
11995             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11996              
11997              
11998         }
11999         //if(Roo.isGecko){
12000         //    this.el.dom.setAttribute('autocomplete', 'off');
12001         //}
12002         
12003         var cls = 'x-combo-list';
12004         
12005         //this.list = new Roo.Layer({
12006         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12007         //});
12008         
12009         var _this = this;
12010         
12011         (function(){
12012             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12013             _this.list.setWidth(lw);
12014         }).defer(100);
12015         
12016         this.list.on('mouseover', this.onViewOver, this);
12017         this.list.on('mousemove', this.onViewMove, this);
12018         
12019         this.list.on('scroll', this.onViewScroll, this);
12020         
12021         /*
12022         this.list.swallowEvent('mousewheel');
12023         this.assetHeight = 0;
12024
12025         if(this.title){
12026             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12027             this.assetHeight += this.header.getHeight();
12028         }
12029
12030         this.innerList = this.list.createChild({cls:cls+'-inner'});
12031         this.innerList.on('mouseover', this.onViewOver, this);
12032         this.innerList.on('mousemove', this.onViewMove, this);
12033         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12034         
12035         if(this.allowBlank && !this.pageSize && !this.disableClear){
12036             this.footer = this.list.createChild({cls:cls+'-ft'});
12037             this.pageTb = new Roo.Toolbar(this.footer);
12038            
12039         }
12040         if(this.pageSize){
12041             this.footer = this.list.createChild({cls:cls+'-ft'});
12042             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12043                     {pageSize: this.pageSize});
12044             
12045         }
12046         
12047         if (this.pageTb && this.allowBlank && !this.disableClear) {
12048             var _this = this;
12049             this.pageTb.add(new Roo.Toolbar.Fill(), {
12050                 cls: 'x-btn-icon x-btn-clear',
12051                 text: '&#160;',
12052                 handler: function()
12053                 {
12054                     _this.collapse();
12055                     _this.clearValue();
12056                     _this.onSelect(false, -1);
12057                 }
12058             });
12059         }
12060         if (this.footer) {
12061             this.assetHeight += this.footer.getHeight();
12062         }
12063         */
12064             
12065         if(!this.tpl){
12066             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12067         }
12068
12069         this.view = new Roo.View(this.list, this.tpl, {
12070             singleSelect:true, store: this.store, selectedClass: this.selectedClass
12071         });
12072         //this.view.wrapEl.setDisplayed(false);
12073         this.view.on('click', this.onViewClick, this);
12074         
12075         
12076         
12077         this.store.on('beforeload', this.onBeforeLoad, this);
12078         this.store.on('load', this.onLoad, this);
12079         this.store.on('loadexception', this.onLoadException, this);
12080         /*
12081         if(this.resizable){
12082             this.resizer = new Roo.Resizable(this.list,  {
12083                pinned:true, handles:'se'
12084             });
12085             this.resizer.on('resize', function(r, w, h){
12086                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12087                 this.listWidth = w;
12088                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12089                 this.restrictHeight();
12090             }, this);
12091             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12092         }
12093         */
12094         if(!this.editable){
12095             this.editable = true;
12096             this.setEditable(false);
12097         }
12098         
12099         /*
12100         
12101         if (typeof(this.events.add.listeners) != 'undefined') {
12102             
12103             this.addicon = this.wrap.createChild(
12104                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
12105        
12106             this.addicon.on('click', function(e) {
12107                 this.fireEvent('add', this);
12108             }, this);
12109         }
12110         if (typeof(this.events.edit.listeners) != 'undefined') {
12111             
12112             this.editicon = this.wrap.createChild(
12113                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
12114             if (this.addicon) {
12115                 this.editicon.setStyle('margin-left', '40px');
12116             }
12117             this.editicon.on('click', function(e) {
12118                 
12119                 // we fire even  if inothing is selected..
12120                 this.fireEvent('edit', this, this.lastData );
12121                 
12122             }, this);
12123         }
12124         */
12125         
12126         this.keyNav = new Roo.KeyNav(this.inputEl(), {
12127             "up" : function(e){
12128                 this.inKeyMode = true;
12129                 this.selectPrev();
12130             },
12131
12132             "down" : function(e){
12133                 if(!this.isExpanded()){
12134                     this.onTriggerClick();
12135                 }else{
12136                     this.inKeyMode = true;
12137                     this.selectNext();
12138                 }
12139             },
12140
12141             "enter" : function(e){
12142 //                this.onViewClick();
12143                 //return true;
12144                 this.collapse();
12145                 
12146                 if(this.fireEvent("specialkey", this, e)){
12147                     this.onViewClick(false);
12148                 }
12149                 
12150                 return true;
12151             },
12152
12153             "esc" : function(e){
12154                 this.collapse();
12155             },
12156
12157             "tab" : function(e){
12158                 this.collapse();
12159                 
12160                 if(this.fireEvent("specialkey", this, e)){
12161                     this.onViewClick(false);
12162                 }
12163                 
12164                 return true;
12165             },
12166
12167             scope : this,
12168
12169             doRelay : function(foo, bar, hname){
12170                 if(hname == 'down' || this.scope.isExpanded()){
12171                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12172                 }
12173                 return true;
12174             },
12175
12176             forceKeyDown: true
12177         });
12178         
12179         
12180         this.queryDelay = Math.max(this.queryDelay || 10,
12181                 this.mode == 'local' ? 10 : 250);
12182         
12183         
12184         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12185         
12186         if(this.typeAhead){
12187             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12188         }
12189         if(this.editable !== false){
12190             this.inputEl().on("keyup", this.onKeyUp, this);
12191         }
12192         if(this.forceSelection){
12193             this.inputEl().on('blur', this.doForce, this);
12194         }
12195         
12196         if(this.multiple){
12197             this.choices = this.el.select('ul.select2-choices', true).first();
12198             this.searchField = this.el.select('ul li.select2-search-field', true).first();
12199         }
12200     },
12201     
12202     initTickableEvents: function()
12203     {   
12204         this.createList();
12205         
12206         if(this.hiddenName){
12207             
12208             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12209             
12210             this.hiddenField.dom.value =
12211                 this.hiddenValue !== undefined ? this.hiddenValue :
12212                 this.value !== undefined ? this.value : '';
12213
12214             // prevent input submission
12215             this.el.dom.removeAttribute('name');
12216             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12217              
12218              
12219         }
12220         
12221 //        this.list = this.el.select('ul.dropdown-menu',true).first();
12222         
12223         this.choices = this.el.select('ul.select2-choices', true).first();
12224         this.searchField = this.el.select('ul li.select2-search-field', true).first();
12225         if(this.triggerList){
12226             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12227         }
12228          
12229         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12230         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12231         
12232         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12233         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12234         
12235         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12236         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12237         
12238         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12239         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12240         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12241         
12242         this.okBtn.hide();
12243         this.cancelBtn.hide();
12244         
12245         var _this = this;
12246         
12247         (function(){
12248             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12249             _this.list.setWidth(lw);
12250         }).defer(100);
12251         
12252         this.list.on('mouseover', this.onViewOver, this);
12253         this.list.on('mousemove', this.onViewMove, this);
12254         
12255         this.list.on('scroll', this.onViewScroll, this);
12256         
12257         if(!this.tpl){
12258             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>';
12259         }
12260
12261         this.view = new Roo.View(this.list, this.tpl, {
12262             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12263         });
12264         
12265         //this.view.wrapEl.setDisplayed(false);
12266         this.view.on('click', this.onViewClick, this);
12267         
12268         
12269         
12270         this.store.on('beforeload', this.onBeforeLoad, this);
12271         this.store.on('load', this.onLoad, this);
12272         this.store.on('loadexception', this.onLoadException, this);
12273         
12274         if(this.editable){
12275             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12276                 "up" : function(e){
12277                     this.inKeyMode = true;
12278                     this.selectPrev();
12279                 },
12280
12281                 "down" : function(e){
12282                     this.inKeyMode = true;
12283                     this.selectNext();
12284                 },
12285
12286                 "enter" : function(e){
12287                     if(this.fireEvent("specialkey", this, e)){
12288                         this.onViewClick(false);
12289                     }
12290                     
12291                     return true;
12292                 },
12293
12294                 "esc" : function(e){
12295                     this.onTickableFooterButtonClick(e, false, false);
12296                 },
12297
12298                 "tab" : function(e){
12299                     this.fireEvent("specialkey", this, e);
12300                     
12301                     this.onTickableFooterButtonClick(e, false, false);
12302                     
12303                     return true;
12304                 },
12305
12306                 scope : this,
12307
12308                 doRelay : function(e, fn, key){
12309                     if(this.scope.isExpanded()){
12310                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12311                     }
12312                     return true;
12313                 },
12314
12315                 forceKeyDown: true
12316             });
12317         }
12318         
12319         this.queryDelay = Math.max(this.queryDelay || 10,
12320                 this.mode == 'local' ? 10 : 250);
12321         
12322         
12323         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12324         
12325         if(this.typeAhead){
12326             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12327         }
12328         
12329         if(this.editable !== false){
12330             this.tickableInputEl().on("keyup", this.onKeyUp, this);
12331         }
12332         
12333     },
12334
12335     onDestroy : function(){
12336         if(this.view){
12337             this.view.setStore(null);
12338             this.view.el.removeAllListeners();
12339             this.view.el.remove();
12340             this.view.purgeListeners();
12341         }
12342         if(this.list){
12343             this.list.dom.innerHTML  = '';
12344         }
12345         
12346         if(this.store){
12347             this.store.un('beforeload', this.onBeforeLoad, this);
12348             this.store.un('load', this.onLoad, this);
12349             this.store.un('loadexception', this.onLoadException, this);
12350         }
12351         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12352     },
12353
12354     // private
12355     fireKey : function(e){
12356         if(e.isNavKeyPress() && !this.list.isVisible()){
12357             this.fireEvent("specialkey", this, e);
12358         }
12359     },
12360
12361     // private
12362     onResize: function(w, h){
12363 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12364 //        
12365 //        if(typeof w != 'number'){
12366 //            // we do not handle it!?!?
12367 //            return;
12368 //        }
12369 //        var tw = this.trigger.getWidth();
12370 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
12371 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
12372 //        var x = w - tw;
12373 //        this.inputEl().setWidth( this.adjustWidth('input', x));
12374 //            
12375 //        //this.trigger.setStyle('left', x+'px');
12376 //        
12377 //        if(this.list && this.listWidth === undefined){
12378 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12379 //            this.list.setWidth(lw);
12380 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12381 //        }
12382         
12383     
12384         
12385     },
12386
12387     /**
12388      * Allow or prevent the user from directly editing the field text.  If false is passed,
12389      * the user will only be able to select from the items defined in the dropdown list.  This method
12390      * is the runtime equivalent of setting the 'editable' config option at config time.
12391      * @param {Boolean} value True to allow the user to directly edit the field text
12392      */
12393     setEditable : function(value){
12394         if(value == this.editable){
12395             return;
12396         }
12397         this.editable = value;
12398         if(!value){
12399             this.inputEl().dom.setAttribute('readOnly', true);
12400             this.inputEl().on('mousedown', this.onTriggerClick,  this);
12401             this.inputEl().addClass('x-combo-noedit');
12402         }else{
12403             this.inputEl().dom.setAttribute('readOnly', false);
12404             this.inputEl().un('mousedown', this.onTriggerClick,  this);
12405             this.inputEl().removeClass('x-combo-noedit');
12406         }
12407     },
12408
12409     // private
12410     
12411     onBeforeLoad : function(combo,opts){
12412         if(!this.hasFocus){
12413             return;
12414         }
12415          if (!opts.add) {
12416             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12417          }
12418         this.restrictHeight();
12419         this.selectedIndex = -1;
12420     },
12421
12422     // private
12423     onLoad : function(){
12424         
12425         this.hasQuery = false;
12426         
12427         if(!this.hasFocus){
12428             return;
12429         }
12430         
12431         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12432             this.loading.hide();
12433         }
12434              
12435         if(this.store.getCount() > 0){
12436             this.expand();
12437             this.restrictHeight();
12438             if(this.lastQuery == this.allQuery){
12439                 if(this.editable && !this.tickable){
12440                     this.inputEl().dom.select();
12441                 }
12442                 
12443                 if(
12444                     !this.selectByValue(this.value, true) &&
12445                     this.autoFocus && 
12446                     (
12447                         !this.store.lastOptions ||
12448                         typeof(this.store.lastOptions.add) == 'undefined' || 
12449                         this.store.lastOptions.add != true
12450                     )
12451                 ){
12452                     this.select(0, true);
12453                 }
12454             }else{
12455                 if(this.autoFocus){
12456                     this.selectNext();
12457                 }
12458                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12459                     this.taTask.delay(this.typeAheadDelay);
12460                 }
12461             }
12462         }else{
12463             this.onEmptyResults();
12464         }
12465         
12466         //this.el.focus();
12467     },
12468     // private
12469     onLoadException : function()
12470     {
12471         this.hasQuery = false;
12472         
12473         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12474             this.loading.hide();
12475         }
12476         
12477         if(this.tickable && this.editable){
12478             return;
12479         }
12480         
12481         this.collapse();
12482         // only causes errors at present
12483         //Roo.log(this.store.reader.jsonData);
12484         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12485             // fixme
12486             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12487         //}
12488         
12489         
12490     },
12491     // private
12492     onTypeAhead : function(){
12493         if(this.store.getCount() > 0){
12494             var r = this.store.getAt(0);
12495             var newValue = r.data[this.displayField];
12496             var len = newValue.length;
12497             var selStart = this.getRawValue().length;
12498             
12499             if(selStart != len){
12500                 this.setRawValue(newValue);
12501                 this.selectText(selStart, newValue.length);
12502             }
12503         }
12504     },
12505
12506     // private
12507     onSelect : function(record, index){
12508         
12509         if(this.fireEvent('beforeselect', this, record, index) !== false){
12510         
12511             this.setFromData(index > -1 ? record.data : false);
12512             
12513             this.collapse();
12514             this.fireEvent('select', this, record, index);
12515         }
12516     },
12517
12518     /**
12519      * Returns the currently selected field value or empty string if no value is set.
12520      * @return {String} value The selected value
12521      */
12522     getValue : function(){
12523         
12524         if(this.multiple){
12525             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12526         }
12527         
12528         if(this.valueField){
12529             return typeof this.value != 'undefined' ? this.value : '';
12530         }else{
12531             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12532         }
12533     },
12534
12535     /**
12536      * Clears any text/value currently set in the field
12537      */
12538     clearValue : function(){
12539         if(this.hiddenField){
12540             this.hiddenField.dom.value = '';
12541         }
12542         this.value = '';
12543         this.setRawValue('');
12544         this.lastSelectionText = '';
12545         this.lastData = false;
12546         
12547         var close = this.closeTriggerEl();
12548         
12549         if(close){
12550             close.hide();
12551         }
12552         
12553     },
12554
12555     /**
12556      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
12557      * will be displayed in the field.  If the value does not match the data value of an existing item,
12558      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12559      * Otherwise the field will be blank (although the value will still be set).
12560      * @param {String} value The value to match
12561      */
12562     setValue : function(v){
12563         if(this.multiple){
12564             this.syncValue();
12565             return;
12566         }
12567         
12568         var text = v;
12569         if(this.valueField){
12570             var r = this.findRecord(this.valueField, v);
12571             if(r){
12572                 text = r.data[this.displayField];
12573             }else if(this.valueNotFoundText !== undefined){
12574                 text = this.valueNotFoundText;
12575             }
12576         }
12577         this.lastSelectionText = text;
12578         if(this.hiddenField){
12579             this.hiddenField.dom.value = v;
12580         }
12581         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12582         this.value = v;
12583         
12584         var close = this.closeTriggerEl();
12585         
12586         if(close){
12587             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12588         }
12589     },
12590     /**
12591      * @property {Object} the last set data for the element
12592      */
12593     
12594     lastData : false,
12595     /**
12596      * Sets the value of the field based on a object which is related to the record format for the store.
12597      * @param {Object} value the value to set as. or false on reset?
12598      */
12599     setFromData : function(o){
12600         
12601         if(this.multiple){
12602             this.addItem(o);
12603             return;
12604         }
12605             
12606         var dv = ''; // display value
12607         var vv = ''; // value value..
12608         this.lastData = o;
12609         if (this.displayField) {
12610             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12611         } else {
12612             // this is an error condition!!!
12613             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12614         }
12615         
12616         if(this.valueField){
12617             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12618         }
12619         
12620         var close = this.closeTriggerEl();
12621         
12622         if(close){
12623             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12624         }
12625         
12626         if(this.hiddenField){
12627             this.hiddenField.dom.value = vv;
12628             
12629             this.lastSelectionText = dv;
12630             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12631             this.value = vv;
12632             return;
12633         }
12634         // no hidden field.. - we store the value in 'value', but still display
12635         // display field!!!!
12636         this.lastSelectionText = dv;
12637         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12638         this.value = vv;
12639         
12640         
12641         
12642     },
12643     // private
12644     reset : function(){
12645         // overridden so that last data is reset..
12646         
12647         if(this.multiple){
12648             this.clearItem();
12649             return;
12650         }
12651         
12652         this.setValue(this.originalValue);
12653         this.clearInvalid();
12654         this.lastData = false;
12655         if (this.view) {
12656             this.view.clearSelections();
12657         }
12658     },
12659     // private
12660     findRecord : function(prop, value){
12661         var record;
12662         if(this.store.getCount() > 0){
12663             this.store.each(function(r){
12664                 if(r.data[prop] == value){
12665                     record = r;
12666                     return false;
12667                 }
12668                 return true;
12669             });
12670         }
12671         return record;
12672     },
12673     
12674     getName: function()
12675     {
12676         // returns hidden if it's set..
12677         if (!this.rendered) {return ''};
12678         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
12679         
12680     },
12681     // private
12682     onViewMove : function(e, t){
12683         this.inKeyMode = false;
12684     },
12685
12686     // private
12687     onViewOver : function(e, t){
12688         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12689             return;
12690         }
12691         var item = this.view.findItemFromChild(t);
12692         
12693         if(item){
12694             var index = this.view.indexOf(item);
12695             this.select(index, false);
12696         }
12697     },
12698
12699     // private
12700     onViewClick : function(view, doFocus, el, e)
12701     {
12702         var index = this.view.getSelectedIndexes()[0];
12703         
12704         var r = this.store.getAt(index);
12705         
12706         if(this.tickable){
12707             
12708             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12709                 return;
12710             }
12711             
12712             var rm = false;
12713             var _this = this;
12714             
12715             Roo.each(this.tickItems, function(v,k){
12716                 
12717                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12718                     Roo.log(v);
12719                     _this.tickItems.splice(k, 1);
12720                     
12721                     if(typeof(e) == 'undefined' && view == false){
12722                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12723                     }
12724                     
12725                     rm = true;
12726                     return;
12727                 }
12728             });
12729             
12730             if(rm){
12731                 return;
12732             }
12733             
12734             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
12735                 this.tickItems.push(r.data);
12736             }
12737             
12738             if(typeof(e) == 'undefined' && view == false){
12739                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12740             }
12741                     
12742             return;
12743         }
12744         
12745         if(r){
12746             this.onSelect(r, index);
12747         }
12748         if(doFocus !== false && !this.blockFocus){
12749             this.inputEl().focus();
12750         }
12751     },
12752
12753     // private
12754     restrictHeight : function(){
12755         //this.innerList.dom.style.height = '';
12756         //var inner = this.innerList.dom;
12757         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12758         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12759         //this.list.beginUpdate();
12760         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12761         this.list.alignTo(this.inputEl(), this.listAlign);
12762         this.list.alignTo(this.inputEl(), this.listAlign);
12763         //this.list.endUpdate();
12764     },
12765
12766     // private
12767     onEmptyResults : function(){
12768         
12769         if(this.tickable && this.editable){
12770             this.restrictHeight();
12771             return;
12772         }
12773         
12774         this.collapse();
12775     },
12776
12777     /**
12778      * Returns true if the dropdown list is expanded, else false.
12779      */
12780     isExpanded : function(){
12781         return this.list.isVisible();
12782     },
12783
12784     /**
12785      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12786      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12787      * @param {String} value The data value of the item to select
12788      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12789      * selected item if it is not currently in view (defaults to true)
12790      * @return {Boolean} True if the value matched an item in the list, else false
12791      */
12792     selectByValue : function(v, scrollIntoView){
12793         if(v !== undefined && v !== null){
12794             var r = this.findRecord(this.valueField || this.displayField, v);
12795             if(r){
12796                 this.select(this.store.indexOf(r), scrollIntoView);
12797                 return true;
12798             }
12799         }
12800         return false;
12801     },
12802
12803     /**
12804      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12805      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12806      * @param {Number} index The zero-based index of the list item to select
12807      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12808      * selected item if it is not currently in view (defaults to true)
12809      */
12810     select : function(index, scrollIntoView){
12811         this.selectedIndex = index;
12812         this.view.select(index);
12813         if(scrollIntoView !== false){
12814             var el = this.view.getNode(index);
12815             /*
12816              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12817              */
12818             if(el){
12819                 this.list.scrollChildIntoView(el, false);
12820             }
12821         }
12822     },
12823
12824     // private
12825     selectNext : function(){
12826         var ct = this.store.getCount();
12827         if(ct > 0){
12828             if(this.selectedIndex == -1){
12829                 this.select(0);
12830             }else if(this.selectedIndex < ct-1){
12831                 this.select(this.selectedIndex+1);
12832             }
12833         }
12834     },
12835
12836     // private
12837     selectPrev : function(){
12838         var ct = this.store.getCount();
12839         if(ct > 0){
12840             if(this.selectedIndex == -1){
12841                 this.select(0);
12842             }else if(this.selectedIndex != 0){
12843                 this.select(this.selectedIndex-1);
12844             }
12845         }
12846     },
12847
12848     // private
12849     onKeyUp : function(e){
12850         if(this.editable !== false && !e.isSpecialKey()){
12851             this.lastKey = e.getKey();
12852             this.dqTask.delay(this.queryDelay);
12853         }
12854     },
12855
12856     // private
12857     validateBlur : function(){
12858         return !this.list || !this.list.isVisible();   
12859     },
12860
12861     // private
12862     initQuery : function(){
12863         
12864         var v = this.getRawValue();
12865         
12866         if(this.tickable && this.editable){
12867             v = this.tickableInputEl().getValue();
12868         }
12869         
12870         this.doQuery(v);
12871     },
12872
12873     // private
12874     doForce : function(){
12875         if(this.inputEl().dom.value.length > 0){
12876             this.inputEl().dom.value =
12877                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12878              
12879         }
12880     },
12881
12882     /**
12883      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
12884      * query allowing the query action to be canceled if needed.
12885      * @param {String} query The SQL query to execute
12886      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12887      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
12888      * saved in the current store (defaults to false)
12889      */
12890     doQuery : function(q, forceAll){
12891         
12892         if(q === undefined || q === null){
12893             q = '';
12894         }
12895         var qe = {
12896             query: q,
12897             forceAll: forceAll,
12898             combo: this,
12899             cancel:false
12900         };
12901         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12902             return false;
12903         }
12904         q = qe.query;
12905         
12906         forceAll = qe.forceAll;
12907         if(forceAll === true || (q.length >= this.minChars)){
12908             
12909             this.hasQuery = true;
12910             
12911             if(this.lastQuery != q || this.alwaysQuery){
12912                 this.lastQuery = q;
12913                 if(this.mode == 'local'){
12914                     this.selectedIndex = -1;
12915                     if(forceAll){
12916                         this.store.clearFilter();
12917                     }else{
12918                         
12919                         if(this.specialFilter){
12920                             this.fireEvent('specialfilter', this);
12921                             this.onLoad();
12922                             return;
12923                         }
12924                         
12925                         this.store.filter(this.displayField, q);
12926                     }
12927                     
12928                     this.store.fireEvent("datachanged", this.store);
12929                     
12930                     this.onLoad();
12931                     
12932                     
12933                 }else{
12934                     
12935                     this.store.baseParams[this.queryParam] = q;
12936                     
12937                     var options = {params : this.getParams(q)};
12938                     
12939                     if(this.loadNext){
12940                         options.add = true;
12941                         options.params.start = this.page * this.pageSize;
12942                     }
12943                     
12944                     this.store.load(options);
12945                     
12946                     /*
12947                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
12948                      *  we should expand the list on onLoad
12949                      *  so command out it
12950                      */
12951 //                    this.expand();
12952                 }
12953             }else{
12954                 this.selectedIndex = -1;
12955                 this.onLoad();   
12956             }
12957         }
12958         
12959         this.loadNext = false;
12960     },
12961     
12962     // private
12963     getParams : function(q){
12964         var p = {};
12965         //p[this.queryParam] = q;
12966         
12967         if(this.pageSize){
12968             p.start = 0;
12969             p.limit = this.pageSize;
12970         }
12971         return p;
12972     },
12973
12974     /**
12975      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12976      */
12977     collapse : function(){
12978         if(!this.isExpanded()){
12979             return;
12980         }
12981         
12982         this.list.hide();
12983         
12984         if(this.tickable){
12985             this.hasFocus = false;
12986             this.okBtn.hide();
12987             this.cancelBtn.hide();
12988             this.trigger.show();
12989             
12990             if(this.editable){
12991                 this.tickableInputEl().dom.value = '';
12992                 this.tickableInputEl().blur();
12993             }
12994             
12995         }
12996         
12997         Roo.get(document).un('mousedown', this.collapseIf, this);
12998         Roo.get(document).un('mousewheel', this.collapseIf, this);
12999         if (!this.editable) {
13000             Roo.get(document).un('keydown', this.listKeyPress, this);
13001         }
13002         this.fireEvent('collapse', this);
13003     },
13004
13005     // private
13006     collapseIf : function(e){
13007         var in_combo  = e.within(this.el);
13008         var in_list =  e.within(this.list);
13009         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13010         
13011         if (in_combo || in_list || is_list) {
13012             //e.stopPropagation();
13013             return;
13014         }
13015         
13016         if(this.tickable){
13017             this.onTickableFooterButtonClick(e, false, false);
13018         }
13019
13020         this.collapse();
13021         
13022     },
13023
13024     /**
13025      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13026      */
13027     expand : function(){
13028        
13029         if(this.isExpanded() || !this.hasFocus){
13030             return;
13031         }
13032         
13033         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13034         this.list.setWidth(lw);
13035         
13036         
13037          Roo.log('expand');
13038         
13039         this.list.show();
13040         
13041         this.restrictHeight();
13042         
13043         if(this.tickable){
13044             
13045             this.tickItems = Roo.apply([], this.item);
13046             
13047             this.okBtn.show();
13048             this.cancelBtn.show();
13049             this.trigger.hide();
13050             
13051             if(this.editable){
13052                 this.tickableInputEl().focus();
13053             }
13054             
13055         }
13056         
13057         Roo.get(document).on('mousedown', this.collapseIf, this);
13058         Roo.get(document).on('mousewheel', this.collapseIf, this);
13059         if (!this.editable) {
13060             Roo.get(document).on('keydown', this.listKeyPress, this);
13061         }
13062         
13063         this.fireEvent('expand', this);
13064     },
13065
13066     // private
13067     // Implements the default empty TriggerField.onTriggerClick function
13068     onTriggerClick : function(e)
13069     {
13070         Roo.log('trigger click');
13071         
13072         if(this.disabled || !this.triggerList){
13073             return;
13074         }
13075         
13076         this.page = 0;
13077         this.loadNext = false;
13078         
13079         if(this.isExpanded()){
13080             this.collapse();
13081             if (!this.blockFocus) {
13082                 this.inputEl().focus();
13083             }
13084             
13085         }else {
13086             this.hasFocus = true;
13087             if(this.triggerAction == 'all') {
13088                 this.doQuery(this.allQuery, true);
13089             } else {
13090                 this.doQuery(this.getRawValue());
13091             }
13092             if (!this.blockFocus) {
13093                 this.inputEl().focus();
13094             }
13095         }
13096     },
13097     
13098     onTickableTriggerClick : function(e)
13099     {
13100         if(this.disabled){
13101             return;
13102         }
13103         
13104         this.page = 0;
13105         this.loadNext = false;
13106         this.hasFocus = true;
13107         
13108         if(this.triggerAction == 'all') {
13109             this.doQuery(this.allQuery, true);
13110         } else {
13111             this.doQuery(this.getRawValue());
13112         }
13113     },
13114     
13115     onSearchFieldClick : function(e)
13116     {
13117         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13118             this.onTickableFooterButtonClick(e, false, false);
13119             return;
13120         }
13121         
13122         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13123             return;
13124         }
13125         
13126         this.page = 0;
13127         this.loadNext = false;
13128         this.hasFocus = true;
13129         
13130         if(this.triggerAction == 'all') {
13131             this.doQuery(this.allQuery, true);
13132         } else {
13133             this.doQuery(this.getRawValue());
13134         }
13135     },
13136     
13137     listKeyPress : function(e)
13138     {
13139         //Roo.log('listkeypress');
13140         // scroll to first matching element based on key pres..
13141         if (e.isSpecialKey()) {
13142             return false;
13143         }
13144         var k = String.fromCharCode(e.getKey()).toUpperCase();
13145         //Roo.log(k);
13146         var match  = false;
13147         var csel = this.view.getSelectedNodes();
13148         var cselitem = false;
13149         if (csel.length) {
13150             var ix = this.view.indexOf(csel[0]);
13151             cselitem  = this.store.getAt(ix);
13152             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13153                 cselitem = false;
13154             }
13155             
13156         }
13157         
13158         this.store.each(function(v) { 
13159             if (cselitem) {
13160                 // start at existing selection.
13161                 if (cselitem.id == v.id) {
13162                     cselitem = false;
13163                 }
13164                 return true;
13165             }
13166                 
13167             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13168                 match = this.store.indexOf(v);
13169                 return false;
13170             }
13171             return true;
13172         }, this);
13173         
13174         if (match === false) {
13175             return true; // no more action?
13176         }
13177         // scroll to?
13178         this.view.select(match);
13179         var sn = Roo.get(this.view.getSelectedNodes()[0]);
13180         sn.scrollIntoView(sn.dom.parentNode, false);
13181     },
13182     
13183     onViewScroll : function(e, t){
13184         
13185         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){
13186             return;
13187         }
13188         
13189         this.hasQuery = true;
13190         
13191         this.loading = this.list.select('.loading', true).first();
13192         
13193         if(this.loading === null){
13194             this.list.createChild({
13195                 tag: 'div',
13196                 cls: 'loading select2-more-results select2-active',
13197                 html: 'Loading more results...'
13198             });
13199             
13200             this.loading = this.list.select('.loading', true).first();
13201             
13202             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13203             
13204             this.loading.hide();
13205         }
13206         
13207         this.loading.show();
13208         
13209         var _combo = this;
13210         
13211         this.page++;
13212         this.loadNext = true;
13213         
13214         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13215         
13216         return;
13217     },
13218     
13219     addItem : function(o)
13220     {   
13221         var dv = ''; // display value
13222         
13223         if (this.displayField) {
13224             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13225         } else {
13226             // this is an error condition!!!
13227             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13228         }
13229         
13230         if(!dv.length){
13231             return;
13232         }
13233         
13234         var choice = this.choices.createChild({
13235             tag: 'li',
13236             cls: 'select2-search-choice',
13237             cn: [
13238                 {
13239                     tag: 'div',
13240                     html: dv
13241                 },
13242                 {
13243                     tag: 'a',
13244                     href: '#',
13245                     cls: 'select2-search-choice-close',
13246                     tabindex: '-1'
13247                 }
13248             ]
13249             
13250         }, this.searchField);
13251         
13252         var close = choice.select('a.select2-search-choice-close', true).first();
13253         
13254         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13255         
13256         this.item.push(o);
13257         
13258         this.lastData = o;
13259         
13260         this.syncValue();
13261         
13262         this.inputEl().dom.value = '';
13263         
13264         this.validate();
13265     },
13266     
13267     onRemoveItem : function(e, _self, o)
13268     {
13269         e.preventDefault();
13270         
13271         this.lastItem = Roo.apply([], this.item);
13272         
13273         var index = this.item.indexOf(o.data) * 1;
13274         
13275         if( index < 0){
13276             Roo.log('not this item?!');
13277             return;
13278         }
13279         
13280         this.item.splice(index, 1);
13281         o.item.remove();
13282         
13283         this.syncValue();
13284         
13285         this.fireEvent('remove', this, e);
13286         
13287         this.validate();
13288         
13289     },
13290     
13291     syncValue : function()
13292     {
13293         if(!this.item.length){
13294             this.clearValue();
13295             return;
13296         }
13297             
13298         var value = [];
13299         var _this = this;
13300         Roo.each(this.item, function(i){
13301             if(_this.valueField){
13302                 value.push(i[_this.valueField]);
13303                 return;
13304             }
13305
13306             value.push(i);
13307         });
13308
13309         this.value = value.join(',');
13310
13311         if(this.hiddenField){
13312             this.hiddenField.dom.value = this.value;
13313         }
13314         
13315         this.store.fireEvent("datachanged", this.store);
13316     },
13317     
13318     clearItem : function()
13319     {
13320         if(!this.multiple){
13321             return;
13322         }
13323         
13324         this.item = [];
13325         
13326         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
13327            c.remove();
13328         });
13329         
13330         this.syncValue();
13331         
13332         this.validate();
13333         
13334         if(this.tickable && !Roo.isTouch){
13335             this.view.refresh();
13336         }
13337     },
13338     
13339     inputEl: function ()
13340     {
13341         if(Roo.isTouch && this.mobileTouchView){
13342             return this.el.select('input.form-control',true).first();
13343         }
13344         
13345         if(this.tickable){
13346             return this.searchField;
13347         }
13348         
13349         return this.el.select('input.form-control',true).first();
13350     },
13351     
13352     
13353     onTickableFooterButtonClick : function(e, btn, el)
13354     {
13355         e.preventDefault();
13356         
13357         this.lastItem = Roo.apply([], this.item);
13358         
13359         if(btn && btn.name == 'cancel'){
13360             this.tickItems = Roo.apply([], this.item);
13361             this.collapse();
13362             return;
13363         }
13364         
13365         this.clearItem();
13366         
13367         var _this = this;
13368         
13369         Roo.each(this.tickItems, function(o){
13370             _this.addItem(o);
13371         });
13372         
13373         this.collapse();
13374         
13375     },
13376     
13377     validate : function()
13378     {
13379         var v = this.getRawValue();
13380         
13381         if(this.multiple){
13382             v = this.getValue();
13383         }
13384         
13385         if(this.disabled || this.allowBlank || v.length){
13386             this.markValid();
13387             return true;
13388         }
13389         
13390         this.markInvalid();
13391         return false;
13392     },
13393     
13394     tickableInputEl : function()
13395     {
13396         if(!this.tickable || !this.editable){
13397             return this.inputEl();
13398         }
13399         
13400         return this.inputEl().select('.select2-search-field-input', true).first();
13401     },
13402     
13403     
13404     getAutoCreateTouchView : function()
13405     {
13406         var id = Roo.id();
13407         
13408         var cfg = {
13409             cls: 'form-group' //input-group
13410         };
13411         
13412         var input =  {
13413             tag: 'input',
13414             id : id,
13415             type : this.inputType,
13416             cls : 'form-control x-combo-noedit',
13417             autocomplete: 'new-password',
13418             placeholder : this.placeholder || '',
13419             readonly : true
13420         };
13421         
13422         if (this.name) {
13423             input.name = this.name;
13424         }
13425         
13426         if (this.size) {
13427             input.cls += ' input-' + this.size;
13428         }
13429         
13430         if (this.disabled) {
13431             input.disabled = true;
13432         }
13433         
13434         var inputblock = {
13435             cls : '',
13436             cn : [
13437                 input
13438             ]
13439         };
13440         
13441         if(this.before){
13442             inputblock.cls += ' input-group';
13443             
13444             inputblock.cn.unshift({
13445                 tag :'span',
13446                 cls : 'input-group-addon',
13447                 html : this.before
13448             });
13449         }
13450         
13451         if(this.removable && !this.multiple){
13452             inputblock.cls += ' roo-removable';
13453             
13454             inputblock.cn.push({
13455                 tag: 'button',
13456                 html : 'x',
13457                 cls : 'roo-combo-removable-btn close'
13458             });
13459         }
13460
13461         if(this.hasFeedback && !this.allowBlank){
13462             
13463             inputblock.cls += ' has-feedback';
13464             
13465             inputblock.cn.push({
13466                 tag: 'span',
13467                 cls: 'glyphicon form-control-feedback'
13468             });
13469             
13470         }
13471         
13472         if (this.after) {
13473             
13474             inputblock.cls += (this.before) ? '' : ' input-group';
13475             
13476             inputblock.cn.push({
13477                 tag :'span',
13478                 cls : 'input-group-addon',
13479                 html : this.after
13480             });
13481         }
13482
13483         var box = {
13484             tag: 'div',
13485             cn: [
13486                 {
13487                     tag: 'input',
13488                     type : 'hidden',
13489                     cls: 'form-hidden-field'
13490                 },
13491                 inputblock
13492             ]
13493             
13494         };
13495         
13496         if(this.multiple){
13497             box = {
13498                 tag: 'div',
13499                 cn: [
13500                     {
13501                         tag: 'input',
13502                         type : 'hidden',
13503                         cls: 'form-hidden-field'
13504                     },
13505                     {
13506                         tag: 'ul',
13507                         cls: 'select2-choices',
13508                         cn:[
13509                             {
13510                                 tag: 'li',
13511                                 cls: 'select2-search-field',
13512                                 cn: [
13513
13514                                     inputblock
13515                                 ]
13516                             }
13517                         ]
13518                     }
13519                 ]
13520             }
13521         };
13522         
13523         var combobox = {
13524             cls: 'select2-container input-group',
13525             cn: [
13526                 box
13527             ]
13528         };
13529         
13530         if(this.multiple){
13531             combobox.cls += ' select2-container-multi';
13532         }
13533         
13534         var align = this.labelAlign || this.parentLabelAlign();
13535         
13536         cfg.cn = combobox;
13537         
13538         if(this.fieldLabel.length){
13539             
13540             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13541             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13542             
13543             cfg.cn = [
13544                 {
13545                     tag: 'label',
13546                     cls : 'control-label ' + lw,
13547                     html : this.fieldLabel
13548
13549                 },
13550                 {
13551                     cls : cw, 
13552                     cn: [
13553                         combobox
13554                     ]
13555                 }
13556             ];
13557         }
13558         
13559         var settings = this;
13560         
13561         ['xs','sm','md','lg'].map(function(size){
13562             if (settings[size]) {
13563                 cfg.cls += ' col-' + size + '-' + settings[size];
13564             }
13565         });
13566         
13567         return cfg;
13568     },
13569     
13570     initTouchView : function()
13571     {
13572         this.renderTouchView();
13573         
13574         this.touchViewEl.on('scroll', function(){
13575             this.el.dom.scrollTop = 0;
13576         }, this);
13577         
13578         this.originalValue = this.getValue();
13579         
13580         this.inputEl().on("click", this.showTouchView, this);
13581         
13582         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13583         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13584         
13585         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13586         
13587         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13588         this.store.on('load', this.onTouchViewLoad, this);
13589         this.store.on('loadexception', this.onTouchViewLoadException, this);
13590         
13591         if(this.hiddenName){
13592             
13593             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13594             
13595             this.hiddenField.dom.value =
13596                 this.hiddenValue !== undefined ? this.hiddenValue :
13597                 this.value !== undefined ? this.value : '';
13598         
13599             this.el.dom.removeAttribute('name');
13600             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13601         }
13602         
13603         if(this.multiple){
13604             this.choices = this.el.select('ul.select2-choices', true).first();
13605             this.searchField = this.el.select('ul li.select2-search-field', true).first();
13606         }
13607         
13608         if(this.removable && !this.multiple){
13609             var close = this.closeTriggerEl();
13610             if(close){
13611                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13612                 close.on('click', this.removeBtnClick, this, close);
13613             }
13614         }
13615         /*
13616          * fix the bug in Safari iOS8
13617          */
13618         this.inputEl().on("focus", function(e){
13619             document.activeElement.blur();
13620         }, this);
13621         
13622         return;
13623         
13624         
13625     },
13626     
13627     renderTouchView : function()
13628     {
13629         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13630         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13631         
13632         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13633         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13634         
13635         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13636         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13637         this.touchViewBodyEl.setStyle('overflow', 'auto');
13638         
13639         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13640         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13641         
13642         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13643         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13644         
13645     },
13646     
13647     showTouchView : function()
13648     {
13649         if(this.disabled){
13650             return;
13651         }
13652         
13653         this.touchViewHeaderEl.hide();
13654
13655         if(this.fieldLabel.length){
13656             this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13657             this.touchViewHeaderEl.show();
13658         }
13659
13660         this.touchViewEl.show();
13661
13662         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13663         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13664
13665         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13666
13667         if(this.fieldLabel.length){
13668             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13669         }
13670         
13671         this.touchViewBodyEl.setHeight(bodyHeight);
13672
13673         if(this.animate){
13674             var _this = this;
13675             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13676         }else{
13677             this.touchViewEl.addClass('in');
13678         }
13679
13680         this.doTouchViewQuery();
13681         
13682     },
13683     
13684     hideTouchView : function()
13685     {
13686         this.touchViewEl.removeClass('in');
13687
13688         if(this.animate){
13689             var _this = this;
13690             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13691         }else{
13692             this.touchViewEl.setStyle('display', 'none');
13693         }
13694         
13695     },
13696     
13697     setTouchViewValue : function()
13698     {
13699         if(this.multiple){
13700             this.clearItem();
13701         
13702             var _this = this;
13703
13704             Roo.each(this.tickItems, function(o){
13705                 this.addItem(o);
13706             }, this);
13707         }
13708         
13709         this.hideTouchView();
13710     },
13711     
13712     doTouchViewQuery : function()
13713     {
13714         var qe = {
13715             query: '',
13716             forceAll: true,
13717             combo: this,
13718             cancel:false
13719         };
13720         
13721         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13722             return false;
13723         }
13724         
13725         if(!this.alwaysQuery || this.mode == 'local'){
13726             this.onTouchViewLoad();
13727             return;
13728         }
13729         
13730         this.store.load();
13731     },
13732     
13733     onTouchViewBeforeLoad : function(combo,opts)
13734     {
13735         return;
13736     },
13737
13738     // private
13739     onTouchViewLoad : function()
13740     {
13741         if(this.store.getCount() < 1){
13742             this.onTouchViewEmptyResults();
13743             return;
13744         }
13745         
13746         this.clearTouchView();
13747         
13748         var rawValue = this.getRawValue();
13749         
13750         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13751         
13752         this.tickItems = [];
13753         
13754         this.store.data.each(function(d, rowIndex){
13755             var row = this.touchViewListGroup.createChild(template);
13756             
13757             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13758                 var cfg = {
13759                     data : d.data,
13760                     html : d.data[this.displayField]
13761                 };
13762                 
13763                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
13764                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
13765                 }
13766             }
13767             
13768             if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
13769                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13770             }
13771             
13772             if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
13773                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13774                 this.tickItems.push(d.data);
13775             }
13776             
13777             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
13778             
13779         }, this);
13780         
13781         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
13782         
13783         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13784
13785         if(this.fieldLabel.length){
13786             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13787         }
13788
13789         var listHeight = this.touchViewListGroup.getHeight();
13790         
13791         var _this = this;
13792         
13793         if(firstChecked && listHeight > bodyHeight){
13794             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
13795         }
13796         
13797     },
13798     
13799     onTouchViewLoadException : function()
13800     {
13801         this.hideTouchView();
13802     },
13803     
13804     onTouchViewEmptyResults : function()
13805     {
13806         this.clearTouchView();
13807         
13808         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
13809         
13810         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
13811         
13812     },
13813     
13814     clearTouchView : function()
13815     {
13816         this.touchViewListGroup.dom.innerHTML = '';
13817     },
13818     
13819     onTouchViewClick : function(e, el, o)
13820     {
13821         e.preventDefault();
13822         
13823         var row = o.row;
13824         var rowIndex = o.rowIndex;
13825         
13826         var r = this.store.getAt(rowIndex);
13827         
13828         if(!this.multiple){
13829             Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
13830                 c.dom.removeAttribute('checked');
13831             }, this);
13832             
13833             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13834         
13835             this.setFromData(r.data);
13836             
13837             var close = this.closeTriggerEl();
13838         
13839             if(close){
13840                 close.show();
13841             }
13842
13843             this.hideTouchView();
13844             
13845             this.fireEvent('select', this, r, rowIndex);
13846             
13847             return;
13848         }
13849         
13850         if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
13851             row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
13852             this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
13853             return;
13854         }
13855         
13856         row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13857         this.addItem(r.data);
13858         this.tickItems.push(r.data);
13859         
13860     }
13861     
13862
13863     /** 
13864     * @cfg {Boolean} grow 
13865     * @hide 
13866     */
13867     /** 
13868     * @cfg {Number} growMin 
13869     * @hide 
13870     */
13871     /** 
13872     * @cfg {Number} growMax 
13873     * @hide 
13874     */
13875     /**
13876      * @hide
13877      * @method autoSize
13878      */
13879 });
13880
13881 Roo.apply(Roo.bootstrap.ComboBox,  {
13882     
13883     header : {
13884         tag: 'div',
13885         cls: 'modal-header',
13886         cn: [
13887             {
13888                 tag: 'h4',
13889                 cls: 'modal-title'
13890             }
13891         ]
13892     },
13893     
13894     body : {
13895         tag: 'div',
13896         cls: 'modal-body',
13897         cn: [
13898             {
13899                 tag: 'ul',
13900                 cls: 'list-group'
13901             }
13902         ]
13903     },
13904     
13905     listItemRadio : {
13906         tag: 'li',
13907         cls: 'list-group-item',
13908         cn: [
13909             {
13910                 tag: 'span',
13911                 cls: 'roo-combobox-list-group-item-value'
13912             },
13913             {
13914                 tag: 'div',
13915                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
13916                 cn: [
13917                     {
13918                         tag: 'input',
13919                         type: 'radio'
13920                     },
13921                     {
13922                         tag: 'label'
13923                     }
13924                 ]
13925             }
13926         ]
13927     },
13928     
13929     listItemCheckbox : {
13930         tag: 'li',
13931         cls: 'list-group-item',
13932         cn: [
13933             {
13934                 tag: 'span',
13935                 cls: 'roo-combobox-list-group-item-value'
13936             },
13937             {
13938                 tag: 'div',
13939                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
13940                 cn: [
13941                     {
13942                         tag: 'input',
13943                         type: 'checkbox'
13944                     },
13945                     {
13946                         tag: 'label'
13947                     }
13948                 ]
13949             }
13950         ]
13951     },
13952     
13953     emptyResult : {
13954         tag: 'div',
13955         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
13956     },
13957     
13958     footer : {
13959         tag: 'div',
13960         cls: 'modal-footer',
13961         cn: [
13962             {
13963                 tag: 'div',
13964                 cls: 'row',
13965                 cn: [
13966                     {
13967                         tag: 'div',
13968                         cls: 'col-xs-6 text-left',
13969                         cn: {
13970                             tag: 'button',
13971                             cls: 'btn btn-danger roo-touch-view-cancel',
13972                             html: 'Cancel'
13973                         }
13974                     },
13975                     {
13976                         tag: 'div',
13977                         cls: 'col-xs-6 text-right',
13978                         cn: {
13979                             tag: 'button',
13980                             cls: 'btn btn-success roo-touch-view-ok',
13981                             html: 'OK'
13982                         }
13983                     }
13984                 ]
13985             }
13986         ]
13987         
13988     }
13989 });
13990
13991 Roo.apply(Roo.bootstrap.ComboBox,  {
13992     
13993     touchViewTemplate : {
13994         tag: 'div',
13995         cls: 'modal fade roo-combobox-touch-view',
13996         cn: [
13997             {
13998                 tag: 'div',
13999                 cls: 'modal-dialog',
14000                 style : 'position:fixed', // we have to fix position....
14001                 cn: [
14002                     {
14003                         tag: 'div',
14004                         cls: 'modal-content',
14005                         cn: [
14006                             Roo.bootstrap.ComboBox.header,
14007                             Roo.bootstrap.ComboBox.body,
14008                             Roo.bootstrap.ComboBox.footer
14009                         ]
14010                     }
14011                 ]
14012             }
14013         ]
14014     }
14015 });/*
14016  * Based on:
14017  * Ext JS Library 1.1.1
14018  * Copyright(c) 2006-2007, Ext JS, LLC.
14019  *
14020  * Originally Released Under LGPL - original licence link has changed is not relivant.
14021  *
14022  * Fork - LGPL
14023  * <script type="text/javascript">
14024  */
14025
14026 /**
14027  * @class Roo.View
14028  * @extends Roo.util.Observable
14029  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
14030  * This class also supports single and multi selection modes. <br>
14031  * Create a data model bound view:
14032  <pre><code>
14033  var store = new Roo.data.Store(...);
14034
14035  var view = new Roo.View({
14036     el : "my-element",
14037     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
14038  
14039     singleSelect: true,
14040     selectedClass: "ydataview-selected",
14041     store: store
14042  });
14043
14044  // listen for node click?
14045  view.on("click", function(vw, index, node, e){
14046  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14047  });
14048
14049  // load XML data
14050  dataModel.load("foobar.xml");
14051  </code></pre>
14052  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14053  * <br><br>
14054  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14055  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14056  * 
14057  * Note: old style constructor is still suported (container, template, config)
14058  * 
14059  * @constructor
14060  * Create a new View
14061  * @param {Object} config The config object
14062  * 
14063  */
14064 Roo.View = function(config, depreciated_tpl, depreciated_config){
14065     
14066     this.parent = false;
14067     
14068     if (typeof(depreciated_tpl) == 'undefined') {
14069         // new way.. - universal constructor.
14070         Roo.apply(this, config);
14071         this.el  = Roo.get(this.el);
14072     } else {
14073         // old format..
14074         this.el  = Roo.get(config);
14075         this.tpl = depreciated_tpl;
14076         Roo.apply(this, depreciated_config);
14077     }
14078     this.wrapEl  = this.el.wrap().wrap();
14079     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14080     
14081     
14082     if(typeof(this.tpl) == "string"){
14083         this.tpl = new Roo.Template(this.tpl);
14084     } else {
14085         // support xtype ctors..
14086         this.tpl = new Roo.factory(this.tpl, Roo);
14087     }
14088     
14089     
14090     this.tpl.compile();
14091     
14092     /** @private */
14093     this.addEvents({
14094         /**
14095          * @event beforeclick
14096          * Fires before a click is processed. Returns false to cancel the default action.
14097          * @param {Roo.View} this
14098          * @param {Number} index The index of the target node
14099          * @param {HTMLElement} node The target node
14100          * @param {Roo.EventObject} e The raw event object
14101          */
14102             "beforeclick" : true,
14103         /**
14104          * @event click
14105          * Fires when a template node is clicked.
14106          * @param {Roo.View} this
14107          * @param {Number} index The index of the target node
14108          * @param {HTMLElement} node The target node
14109          * @param {Roo.EventObject} e The raw event object
14110          */
14111             "click" : true,
14112         /**
14113          * @event dblclick
14114          * Fires when a template node is double clicked.
14115          * @param {Roo.View} this
14116          * @param {Number} index The index of the target node
14117          * @param {HTMLElement} node The target node
14118          * @param {Roo.EventObject} e The raw event object
14119          */
14120             "dblclick" : true,
14121         /**
14122          * @event contextmenu
14123          * Fires when a template node is right clicked.
14124          * @param {Roo.View} this
14125          * @param {Number} index The index of the target node
14126          * @param {HTMLElement} node The target node
14127          * @param {Roo.EventObject} e The raw event object
14128          */
14129             "contextmenu" : true,
14130         /**
14131          * @event selectionchange
14132          * Fires when the selected nodes change.
14133          * @param {Roo.View} this
14134          * @param {Array} selections Array of the selected nodes
14135          */
14136             "selectionchange" : true,
14137     
14138         /**
14139          * @event beforeselect
14140          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14141          * @param {Roo.View} this
14142          * @param {HTMLElement} node The node to be selected
14143          * @param {Array} selections Array of currently selected nodes
14144          */
14145             "beforeselect" : true,
14146         /**
14147          * @event preparedata
14148          * Fires on every row to render, to allow you to change the data.
14149          * @param {Roo.View} this
14150          * @param {Object} data to be rendered (change this)
14151          */
14152           "preparedata" : true
14153           
14154           
14155         });
14156
14157
14158
14159     this.el.on({
14160         "click": this.onClick,
14161         "dblclick": this.onDblClick,
14162         "contextmenu": this.onContextMenu,
14163         scope:this
14164     });
14165
14166     this.selections = [];
14167     this.nodes = [];
14168     this.cmp = new Roo.CompositeElementLite([]);
14169     if(this.store){
14170         this.store = Roo.factory(this.store, Roo.data);
14171         this.setStore(this.store, true);
14172     }
14173     
14174     if ( this.footer && this.footer.xtype) {
14175            
14176          var fctr = this.wrapEl.appendChild(document.createElement("div"));
14177         
14178         this.footer.dataSource = this.store;
14179         this.footer.container = fctr;
14180         this.footer = Roo.factory(this.footer, Roo);
14181         fctr.insertFirst(this.el);
14182         
14183         // this is a bit insane - as the paging toolbar seems to detach the el..
14184 //        dom.parentNode.parentNode.parentNode
14185          // they get detached?
14186     }
14187     
14188     
14189     Roo.View.superclass.constructor.call(this);
14190     
14191     
14192 };
14193
14194 Roo.extend(Roo.View, Roo.util.Observable, {
14195     
14196      /**
14197      * @cfg {Roo.data.Store} store Data store to load data from.
14198      */
14199     store : false,
14200     
14201     /**
14202      * @cfg {String|Roo.Element} el The container element.
14203      */
14204     el : '',
14205     
14206     /**
14207      * @cfg {String|Roo.Template} tpl The template used by this View 
14208      */
14209     tpl : false,
14210     /**
14211      * @cfg {String} dataName the named area of the template to use as the data area
14212      *                          Works with domtemplates roo-name="name"
14213      */
14214     dataName: false,
14215     /**
14216      * @cfg {String} selectedClass The css class to add to selected nodes
14217      */
14218     selectedClass : "x-view-selected",
14219      /**
14220      * @cfg {String} emptyText The empty text to show when nothing is loaded.
14221      */
14222     emptyText : "",
14223     
14224     /**
14225      * @cfg {String} text to display on mask (default Loading)
14226      */
14227     mask : false,
14228     /**
14229      * @cfg {Boolean} multiSelect Allow multiple selection
14230      */
14231     multiSelect : false,
14232     /**
14233      * @cfg {Boolean} singleSelect Allow single selection
14234      */
14235     singleSelect:  false,
14236     
14237     /**
14238      * @cfg {Boolean} toggleSelect - selecting 
14239      */
14240     toggleSelect : false,
14241     
14242     /**
14243      * @cfg {Boolean} tickable - selecting 
14244      */
14245     tickable : false,
14246     
14247     /**
14248      * Returns the element this view is bound to.
14249      * @return {Roo.Element}
14250      */
14251     getEl : function(){
14252         return this.wrapEl;
14253     },
14254     
14255     
14256
14257     /**
14258      * Refreshes the view. - called by datachanged on the store. - do not call directly.
14259      */
14260     refresh : function(){
14261         //Roo.log('refresh');
14262         var t = this.tpl;
14263         
14264         // if we are using something like 'domtemplate', then
14265         // the what gets used is:
14266         // t.applySubtemplate(NAME, data, wrapping data..)
14267         // the outer template then get' applied with
14268         //     the store 'extra data'
14269         // and the body get's added to the
14270         //      roo-name="data" node?
14271         //      <span class='roo-tpl-{name}'></span> ?????
14272         
14273         
14274         
14275         this.clearSelections();
14276         this.el.update("");
14277         var html = [];
14278         var records = this.store.getRange();
14279         if(records.length < 1) {
14280             
14281             // is this valid??  = should it render a template??
14282             
14283             this.el.update(this.emptyText);
14284             return;
14285         }
14286         var el = this.el;
14287         if (this.dataName) {
14288             this.el.update(t.apply(this.store.meta)); //????
14289             el = this.el.child('.roo-tpl-' + this.dataName);
14290         }
14291         
14292         for(var i = 0, len = records.length; i < len; i++){
14293             var data = this.prepareData(records[i].data, i, records[i]);
14294             this.fireEvent("preparedata", this, data, i, records[i]);
14295             
14296             var d = Roo.apply({}, data);
14297             
14298             if(this.tickable){
14299                 Roo.apply(d, {'roo-id' : Roo.id()});
14300                 
14301                 var _this = this;
14302             
14303                 Roo.each(this.parent.item, function(item){
14304                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14305                         return;
14306                     }
14307                     Roo.apply(d, {'roo-data-checked' : 'checked'});
14308                 });
14309             }
14310             
14311             html[html.length] = Roo.util.Format.trim(
14312                 this.dataName ?
14313                     t.applySubtemplate(this.dataName, d, this.store.meta) :
14314                     t.apply(d)
14315             );
14316         }
14317         
14318         
14319         
14320         el.update(html.join(""));
14321         this.nodes = el.dom.childNodes;
14322         this.updateIndexes(0);
14323     },
14324     
14325
14326     /**
14327      * Function to override to reformat the data that is sent to
14328      * the template for each node.
14329      * DEPRICATED - use the preparedata event handler.
14330      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14331      * a JSON object for an UpdateManager bound view).
14332      */
14333     prepareData : function(data, index, record)
14334     {
14335         this.fireEvent("preparedata", this, data, index, record);
14336         return data;
14337     },
14338
14339     onUpdate : function(ds, record){
14340         // Roo.log('on update');   
14341         this.clearSelections();
14342         var index = this.store.indexOf(record);
14343         var n = this.nodes[index];
14344         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14345         n.parentNode.removeChild(n);
14346         this.updateIndexes(index, index);
14347     },
14348
14349     
14350     
14351 // --------- FIXME     
14352     onAdd : function(ds, records, index)
14353     {
14354         //Roo.log(['on Add', ds, records, index] );        
14355         this.clearSelections();
14356         if(this.nodes.length == 0){
14357             this.refresh();
14358             return;
14359         }
14360         var n = this.nodes[index];
14361         for(var i = 0, len = records.length; i < len; i++){
14362             var d = this.prepareData(records[i].data, i, records[i]);
14363             if(n){
14364                 this.tpl.insertBefore(n, d);
14365             }else{
14366                 
14367                 this.tpl.append(this.el, d);
14368             }
14369         }
14370         this.updateIndexes(index);
14371     },
14372
14373     onRemove : function(ds, record, index){
14374        // Roo.log('onRemove');
14375         this.clearSelections();
14376         var el = this.dataName  ?
14377             this.el.child('.roo-tpl-' + this.dataName) :
14378             this.el; 
14379         
14380         el.dom.removeChild(this.nodes[index]);
14381         this.updateIndexes(index);
14382     },
14383
14384     /**
14385      * Refresh an individual node.
14386      * @param {Number} index
14387      */
14388     refreshNode : function(index){
14389         this.onUpdate(this.store, this.store.getAt(index));
14390     },
14391
14392     updateIndexes : function(startIndex, endIndex){
14393         var ns = this.nodes;
14394         startIndex = startIndex || 0;
14395         endIndex = endIndex || ns.length - 1;
14396         for(var i = startIndex; i <= endIndex; i++){
14397             ns[i].nodeIndex = i;
14398         }
14399     },
14400
14401     /**
14402      * Changes the data store this view uses and refresh the view.
14403      * @param {Store} store
14404      */
14405     setStore : function(store, initial){
14406         if(!initial && this.store){
14407             this.store.un("datachanged", this.refresh);
14408             this.store.un("add", this.onAdd);
14409             this.store.un("remove", this.onRemove);
14410             this.store.un("update", this.onUpdate);
14411             this.store.un("clear", this.refresh);
14412             this.store.un("beforeload", this.onBeforeLoad);
14413             this.store.un("load", this.onLoad);
14414             this.store.un("loadexception", this.onLoad);
14415         }
14416         if(store){
14417           
14418             store.on("datachanged", this.refresh, this);
14419             store.on("add", this.onAdd, this);
14420             store.on("remove", this.onRemove, this);
14421             store.on("update", this.onUpdate, this);
14422             store.on("clear", this.refresh, this);
14423             store.on("beforeload", this.onBeforeLoad, this);
14424             store.on("load", this.onLoad, this);
14425             store.on("loadexception", this.onLoad, this);
14426         }
14427         
14428         if(store){
14429             this.refresh();
14430         }
14431     },
14432     /**
14433      * onbeforeLoad - masks the loading area.
14434      *
14435      */
14436     onBeforeLoad : function(store,opts)
14437     {
14438          //Roo.log('onBeforeLoad');   
14439         if (!opts.add) {
14440             this.el.update("");
14441         }
14442         this.el.mask(this.mask ? this.mask : "Loading" ); 
14443     },
14444     onLoad : function ()
14445     {
14446         this.el.unmask();
14447     },
14448     
14449
14450     /**
14451      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14452      * @param {HTMLElement} node
14453      * @return {HTMLElement} The template node
14454      */
14455     findItemFromChild : function(node){
14456         var el = this.dataName  ?
14457             this.el.child('.roo-tpl-' + this.dataName,true) :
14458             this.el.dom; 
14459         
14460         if(!node || node.parentNode == el){
14461                     return node;
14462             }
14463             var p = node.parentNode;
14464             while(p && p != el){
14465             if(p.parentNode == el){
14466                 return p;
14467             }
14468             p = p.parentNode;
14469         }
14470             return null;
14471     },
14472
14473     /** @ignore */
14474     onClick : function(e){
14475         var item = this.findItemFromChild(e.getTarget());
14476         if(item){
14477             var index = this.indexOf(item);
14478             if(this.onItemClick(item, index, e) !== false){
14479                 this.fireEvent("click", this, index, item, e);
14480             }
14481         }else{
14482             this.clearSelections();
14483         }
14484     },
14485
14486     /** @ignore */
14487     onContextMenu : function(e){
14488         var item = this.findItemFromChild(e.getTarget());
14489         if(item){
14490             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14491         }
14492     },
14493
14494     /** @ignore */
14495     onDblClick : function(e){
14496         var item = this.findItemFromChild(e.getTarget());
14497         if(item){
14498             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14499         }
14500     },
14501
14502     onItemClick : function(item, index, e)
14503     {
14504         if(this.fireEvent("beforeclick", this, index, item, e) === false){
14505             return false;
14506         }
14507         if (this.toggleSelect) {
14508             var m = this.isSelected(item) ? 'unselect' : 'select';
14509             //Roo.log(m);
14510             var _t = this;
14511             _t[m](item, true, false);
14512             return true;
14513         }
14514         if(this.multiSelect || this.singleSelect){
14515             if(this.multiSelect && e.shiftKey && this.lastSelection){
14516                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14517             }else{
14518                 this.select(item, this.multiSelect && e.ctrlKey);
14519                 this.lastSelection = item;
14520             }
14521             
14522             if(!this.tickable){
14523                 e.preventDefault();
14524             }
14525             
14526         }
14527         return true;
14528     },
14529
14530     /**
14531      * Get the number of selected nodes.
14532      * @return {Number}
14533      */
14534     getSelectionCount : function(){
14535         return this.selections.length;
14536     },
14537
14538     /**
14539      * Get the currently selected nodes.
14540      * @return {Array} An array of HTMLElements
14541      */
14542     getSelectedNodes : function(){
14543         return this.selections;
14544     },
14545
14546     /**
14547      * Get the indexes of the selected nodes.
14548      * @return {Array}
14549      */
14550     getSelectedIndexes : function(){
14551         var indexes = [], s = this.selections;
14552         for(var i = 0, len = s.length; i < len; i++){
14553             indexes.push(s[i].nodeIndex);
14554         }
14555         return indexes;
14556     },
14557
14558     /**
14559      * Clear all selections
14560      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14561      */
14562     clearSelections : function(suppressEvent){
14563         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14564             this.cmp.elements = this.selections;
14565             this.cmp.removeClass(this.selectedClass);
14566             this.selections = [];
14567             if(!suppressEvent){
14568                 this.fireEvent("selectionchange", this, this.selections);
14569             }
14570         }
14571     },
14572
14573     /**
14574      * Returns true if the passed node is selected
14575      * @param {HTMLElement/Number} node The node or node index
14576      * @return {Boolean}
14577      */
14578     isSelected : function(node){
14579         var s = this.selections;
14580         if(s.length < 1){
14581             return false;
14582         }
14583         node = this.getNode(node);
14584         return s.indexOf(node) !== -1;
14585     },
14586
14587     /**
14588      * Selects nodes.
14589      * @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
14590      * @param {Boolean} keepExisting (optional) true to keep existing selections
14591      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14592      */
14593     select : function(nodeInfo, keepExisting, suppressEvent){
14594         if(nodeInfo instanceof Array){
14595             if(!keepExisting){
14596                 this.clearSelections(true);
14597             }
14598             for(var i = 0, len = nodeInfo.length; i < len; i++){
14599                 this.select(nodeInfo[i], true, true);
14600             }
14601             return;
14602         } 
14603         var node = this.getNode(nodeInfo);
14604         if(!node || this.isSelected(node)){
14605             return; // already selected.
14606         }
14607         if(!keepExisting){
14608             this.clearSelections(true);
14609         }
14610         
14611         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14612             Roo.fly(node).addClass(this.selectedClass);
14613             this.selections.push(node);
14614             if(!suppressEvent){
14615                 this.fireEvent("selectionchange", this, this.selections);
14616             }
14617         }
14618         
14619         
14620     },
14621       /**
14622      * Unselects nodes.
14623      * @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
14624      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14625      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14626      */
14627     unselect : function(nodeInfo, keepExisting, suppressEvent)
14628     {
14629         if(nodeInfo instanceof Array){
14630             Roo.each(this.selections, function(s) {
14631                 this.unselect(s, nodeInfo);
14632             }, this);
14633             return;
14634         }
14635         var node = this.getNode(nodeInfo);
14636         if(!node || !this.isSelected(node)){
14637             //Roo.log("not selected");
14638             return; // not selected.
14639         }
14640         // fireevent???
14641         var ns = [];
14642         Roo.each(this.selections, function(s) {
14643             if (s == node ) {
14644                 Roo.fly(node).removeClass(this.selectedClass);
14645
14646                 return;
14647             }
14648             ns.push(s);
14649         },this);
14650         
14651         this.selections= ns;
14652         this.fireEvent("selectionchange", this, this.selections);
14653     },
14654
14655     /**
14656      * Gets a template node.
14657      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14658      * @return {HTMLElement} The node or null if it wasn't found
14659      */
14660     getNode : function(nodeInfo){
14661         if(typeof nodeInfo == "string"){
14662             return document.getElementById(nodeInfo);
14663         }else if(typeof nodeInfo == "number"){
14664             return this.nodes[nodeInfo];
14665         }
14666         return nodeInfo;
14667     },
14668
14669     /**
14670      * Gets a range template nodes.
14671      * @param {Number} startIndex
14672      * @param {Number} endIndex
14673      * @return {Array} An array of nodes
14674      */
14675     getNodes : function(start, end){
14676         var ns = this.nodes;
14677         start = start || 0;
14678         end = typeof end == "undefined" ? ns.length - 1 : end;
14679         var nodes = [];
14680         if(start <= end){
14681             for(var i = start; i <= end; i++){
14682                 nodes.push(ns[i]);
14683             }
14684         } else{
14685             for(var i = start; i >= end; i--){
14686                 nodes.push(ns[i]);
14687             }
14688         }
14689         return nodes;
14690     },
14691
14692     /**
14693      * Finds the index of the passed node
14694      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14695      * @return {Number} The index of the node or -1
14696      */
14697     indexOf : function(node){
14698         node = this.getNode(node);
14699         if(typeof node.nodeIndex == "number"){
14700             return node.nodeIndex;
14701         }
14702         var ns = this.nodes;
14703         for(var i = 0, len = ns.length; i < len; i++){
14704             if(ns[i] == node){
14705                 return i;
14706             }
14707         }
14708         return -1;
14709     }
14710 });
14711 /*
14712  * - LGPL
14713  *
14714  * based on jquery fullcalendar
14715  * 
14716  */
14717
14718 Roo.bootstrap = Roo.bootstrap || {};
14719 /**
14720  * @class Roo.bootstrap.Calendar
14721  * @extends Roo.bootstrap.Component
14722  * Bootstrap Calendar class
14723  * @cfg {Boolean} loadMask (true|false) default false
14724  * @cfg {Object} header generate the user specific header of the calendar, default false
14725
14726  * @constructor
14727  * Create a new Container
14728  * @param {Object} config The config object
14729  */
14730
14731
14732
14733 Roo.bootstrap.Calendar = function(config){
14734     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14735      this.addEvents({
14736         /**
14737              * @event select
14738              * Fires when a date is selected
14739              * @param {DatePicker} this
14740              * @param {Date} date The selected date
14741              */
14742         'select': true,
14743         /**
14744              * @event monthchange
14745              * Fires when the displayed month changes 
14746              * @param {DatePicker} this
14747              * @param {Date} date The selected month
14748              */
14749         'monthchange': true,
14750         /**
14751              * @event evententer
14752              * Fires when mouse over an event
14753              * @param {Calendar} this
14754              * @param {event} Event
14755              */
14756         'evententer': true,
14757         /**
14758              * @event eventleave
14759              * Fires when the mouse leaves an
14760              * @param {Calendar} this
14761              * @param {event}
14762              */
14763         'eventleave': true,
14764         /**
14765              * @event eventclick
14766              * Fires when the mouse click an
14767              * @param {Calendar} this
14768              * @param {event}
14769              */
14770         'eventclick': true
14771         
14772     });
14773
14774 };
14775
14776 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
14777     
14778      /**
14779      * @cfg {Number} startDay
14780      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
14781      */
14782     startDay : 0,
14783     
14784     loadMask : false,
14785     
14786     header : false,
14787       
14788     getAutoCreate : function(){
14789         
14790         
14791         var fc_button = function(name, corner, style, content ) {
14792             return Roo.apply({},{
14793                 tag : 'span',
14794                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
14795                          (corner.length ?
14796                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
14797                             ''
14798                         ),
14799                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
14800                 unselectable: 'on'
14801             });
14802         };
14803         
14804         var header = {};
14805         
14806         if(!this.header){
14807             header = {
14808                 tag : 'table',
14809                 cls : 'fc-header',
14810                 style : 'width:100%',
14811                 cn : [
14812                     {
14813                         tag: 'tr',
14814                         cn : [
14815                             {
14816                                 tag : 'td',
14817                                 cls : 'fc-header-left',
14818                                 cn : [
14819                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
14820                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
14821                                     { tag: 'span', cls: 'fc-header-space' },
14822                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
14823
14824
14825                                 ]
14826                             },
14827
14828                             {
14829                                 tag : 'td',
14830                                 cls : 'fc-header-center',
14831                                 cn : [
14832                                     {
14833                                         tag: 'span',
14834                                         cls: 'fc-header-title',
14835                                         cn : {
14836                                             tag: 'H2',
14837                                             html : 'month / year'
14838                                         }
14839                                     }
14840
14841                                 ]
14842                             },
14843                             {
14844                                 tag : 'td',
14845                                 cls : 'fc-header-right',
14846                                 cn : [
14847                               /*      fc_button('month', 'left', '', 'month' ),
14848                                     fc_button('week', '', '', 'week' ),
14849                                     fc_button('day', 'right', '', 'day' )
14850                                 */    
14851
14852                                 ]
14853                             }
14854
14855                         ]
14856                     }
14857                 ]
14858             };
14859         }
14860         
14861         header = this.header;
14862         
14863        
14864         var cal_heads = function() {
14865             var ret = [];
14866             // fixme - handle this.
14867             
14868             for (var i =0; i < Date.dayNames.length; i++) {
14869                 var d = Date.dayNames[i];
14870                 ret.push({
14871                     tag: 'th',
14872                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
14873                     html : d.substring(0,3)
14874                 });
14875                 
14876             }
14877             ret[0].cls += ' fc-first';
14878             ret[6].cls += ' fc-last';
14879             return ret;
14880         };
14881         var cal_cell = function(n) {
14882             return  {
14883                 tag: 'td',
14884                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
14885                 cn : [
14886                     {
14887                         cn : [
14888                             {
14889                                 cls: 'fc-day-number',
14890                                 html: 'D'
14891                             },
14892                             {
14893                                 cls: 'fc-day-content',
14894                              
14895                                 cn : [
14896                                      {
14897                                         style: 'position: relative;' // height: 17px;
14898                                     }
14899                                 ]
14900                             }
14901                             
14902                             
14903                         ]
14904                     }
14905                 ]
14906                 
14907             }
14908         };
14909         var cal_rows = function() {
14910             
14911             var ret = [];
14912             for (var r = 0; r < 6; r++) {
14913                 var row= {
14914                     tag : 'tr',
14915                     cls : 'fc-week',
14916                     cn : []
14917                 };
14918                 
14919                 for (var i =0; i < Date.dayNames.length; i++) {
14920                     var d = Date.dayNames[i];
14921                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
14922
14923                 }
14924                 row.cn[0].cls+=' fc-first';
14925                 row.cn[0].cn[0].style = 'min-height:90px';
14926                 row.cn[6].cls+=' fc-last';
14927                 ret.push(row);
14928                 
14929             }
14930             ret[0].cls += ' fc-first';
14931             ret[4].cls += ' fc-prev-last';
14932             ret[5].cls += ' fc-last';
14933             return ret;
14934             
14935         };
14936         
14937         var cal_table = {
14938             tag: 'table',
14939             cls: 'fc-border-separate',
14940             style : 'width:100%',
14941             cellspacing  : 0,
14942             cn : [
14943                 { 
14944                     tag: 'thead',
14945                     cn : [
14946                         { 
14947                             tag: 'tr',
14948                             cls : 'fc-first fc-last',
14949                             cn : cal_heads()
14950                         }
14951                     ]
14952                 },
14953                 { 
14954                     tag: 'tbody',
14955                     cn : cal_rows()
14956                 }
14957                   
14958             ]
14959         };
14960          
14961          var cfg = {
14962             cls : 'fc fc-ltr',
14963             cn : [
14964                 header,
14965                 {
14966                     cls : 'fc-content',
14967                     style : "position: relative;",
14968                     cn : [
14969                         {
14970                             cls : 'fc-view fc-view-month fc-grid',
14971                             style : 'position: relative',
14972                             unselectable : 'on',
14973                             cn : [
14974                                 {
14975                                     cls : 'fc-event-container',
14976                                     style : 'position:absolute;z-index:8;top:0;left:0;'
14977                                 },
14978                                 cal_table
14979                             ]
14980                         }
14981                     ]
14982     
14983                 }
14984            ] 
14985             
14986         };
14987         
14988          
14989         
14990         return cfg;
14991     },
14992     
14993     
14994     initEvents : function()
14995     {
14996         if(!this.store){
14997             throw "can not find store for calendar";
14998         }
14999         
15000         var mark = {
15001             tag: "div",
15002             cls:"x-dlg-mask",
15003             style: "text-align:center",
15004             cn: [
15005                 {
15006                     tag: "div",
15007                     style: "background-color:white;width:50%;margin:250 auto",
15008                     cn: [
15009                         {
15010                             tag: "img",
15011                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
15012                         },
15013                         {
15014                             tag: "span",
15015                             html: "Loading"
15016                         }
15017                         
15018                     ]
15019                 }
15020             ]
15021         };
15022         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15023         
15024         var size = this.el.select('.fc-content', true).first().getSize();
15025         this.maskEl.setSize(size.width, size.height);
15026         this.maskEl.enableDisplayMode("block");
15027         if(!this.loadMask){
15028             this.maskEl.hide();
15029         }
15030         
15031         this.store = Roo.factory(this.store, Roo.data);
15032         this.store.on('load', this.onLoad, this);
15033         this.store.on('beforeload', this.onBeforeLoad, this);
15034         
15035         this.resize();
15036         
15037         this.cells = this.el.select('.fc-day',true);
15038         //Roo.log(this.cells);
15039         this.textNodes = this.el.query('.fc-day-number');
15040         this.cells.addClassOnOver('fc-state-hover');
15041         
15042         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15043         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15044         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15045         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15046         
15047         this.on('monthchange', this.onMonthChange, this);
15048         
15049         this.update(new Date().clearTime());
15050     },
15051     
15052     resize : function() {
15053         var sz  = this.el.getSize();
15054         
15055         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15056         this.el.select('.fc-day-content div',true).setHeight(34);
15057     },
15058     
15059     
15060     // private
15061     showPrevMonth : function(e){
15062         this.update(this.activeDate.add("mo", -1));
15063     },
15064     showToday : function(e){
15065         this.update(new Date().clearTime());
15066     },
15067     // private
15068     showNextMonth : function(e){
15069         this.update(this.activeDate.add("mo", 1));
15070     },
15071
15072     // private
15073     showPrevYear : function(){
15074         this.update(this.activeDate.add("y", -1));
15075     },
15076
15077     // private
15078     showNextYear : function(){
15079         this.update(this.activeDate.add("y", 1));
15080     },
15081
15082     
15083    // private
15084     update : function(date)
15085     {
15086         var vd = this.activeDate;
15087         this.activeDate = date;
15088 //        if(vd && this.el){
15089 //            var t = date.getTime();
15090 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15091 //                Roo.log('using add remove');
15092 //                
15093 //                this.fireEvent('monthchange', this, date);
15094 //                
15095 //                this.cells.removeClass("fc-state-highlight");
15096 //                this.cells.each(function(c){
15097 //                   if(c.dateValue == t){
15098 //                       c.addClass("fc-state-highlight");
15099 //                       setTimeout(function(){
15100 //                            try{c.dom.firstChild.focus();}catch(e){}
15101 //                       }, 50);
15102 //                       return false;
15103 //                   }
15104 //                   return true;
15105 //                });
15106 //                return;
15107 //            }
15108 //        }
15109         
15110         var days = date.getDaysInMonth();
15111         
15112         var firstOfMonth = date.getFirstDateOfMonth();
15113         var startingPos = firstOfMonth.getDay()-this.startDay;
15114         
15115         if(startingPos < this.startDay){
15116             startingPos += 7;
15117         }
15118         
15119         var pm = date.add(Date.MONTH, -1);
15120         var prevStart = pm.getDaysInMonth()-startingPos;
15121 //        
15122         this.cells = this.el.select('.fc-day',true);
15123         this.textNodes = this.el.query('.fc-day-number');
15124         this.cells.addClassOnOver('fc-state-hover');
15125         
15126         var cells = this.cells.elements;
15127         var textEls = this.textNodes;
15128         
15129         Roo.each(cells, function(cell){
15130             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15131         });
15132         
15133         days += startingPos;
15134
15135         // convert everything to numbers so it's fast
15136         var day = 86400000;
15137         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15138         //Roo.log(d);
15139         //Roo.log(pm);
15140         //Roo.log(prevStart);
15141         
15142         var today = new Date().clearTime().getTime();
15143         var sel = date.clearTime().getTime();
15144         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15145         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15146         var ddMatch = this.disabledDatesRE;
15147         var ddText = this.disabledDatesText;
15148         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15149         var ddaysText = this.disabledDaysText;
15150         var format = this.format;
15151         
15152         var setCellClass = function(cal, cell){
15153             cell.row = 0;
15154             cell.events = [];
15155             cell.more = [];
15156             //Roo.log('set Cell Class');
15157             cell.title = "";
15158             var t = d.getTime();
15159             
15160             //Roo.log(d);
15161             
15162             cell.dateValue = t;
15163             if(t == today){
15164                 cell.className += " fc-today";
15165                 cell.className += " fc-state-highlight";
15166                 cell.title = cal.todayText;
15167             }
15168             if(t == sel){
15169                 // disable highlight in other month..
15170                 //cell.className += " fc-state-highlight";
15171                 
15172             }
15173             // disabling
15174             if(t < min) {
15175                 cell.className = " fc-state-disabled";
15176                 cell.title = cal.minText;
15177                 return;
15178             }
15179             if(t > max) {
15180                 cell.className = " fc-state-disabled";
15181                 cell.title = cal.maxText;
15182                 return;
15183             }
15184             if(ddays){
15185                 if(ddays.indexOf(d.getDay()) != -1){
15186                     cell.title = ddaysText;
15187                     cell.className = " fc-state-disabled";
15188                 }
15189             }
15190             if(ddMatch && format){
15191                 var fvalue = d.dateFormat(format);
15192                 if(ddMatch.test(fvalue)){
15193                     cell.title = ddText.replace("%0", fvalue);
15194                     cell.className = " fc-state-disabled";
15195                 }
15196             }
15197             
15198             if (!cell.initialClassName) {
15199                 cell.initialClassName = cell.dom.className;
15200             }
15201             
15202             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
15203         };
15204
15205         var i = 0;
15206         
15207         for(; i < startingPos; i++) {
15208             textEls[i].innerHTML = (++prevStart);
15209             d.setDate(d.getDate()+1);
15210             
15211             cells[i].className = "fc-past fc-other-month";
15212             setCellClass(this, cells[i]);
15213         }
15214         
15215         var intDay = 0;
15216         
15217         for(; i < days; i++){
15218             intDay = i - startingPos + 1;
15219             textEls[i].innerHTML = (intDay);
15220             d.setDate(d.getDate()+1);
15221             
15222             cells[i].className = ''; // "x-date-active";
15223             setCellClass(this, cells[i]);
15224         }
15225         var extraDays = 0;
15226         
15227         for(; i < 42; i++) {
15228             textEls[i].innerHTML = (++extraDays);
15229             d.setDate(d.getDate()+1);
15230             
15231             cells[i].className = "fc-future fc-other-month";
15232             setCellClass(this, cells[i]);
15233         }
15234         
15235         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15236         
15237         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15238         
15239         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15240         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15241         
15242         if(totalRows != 6){
15243             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15244             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15245         }
15246         
15247         this.fireEvent('monthchange', this, date);
15248         
15249         
15250         /*
15251         if(!this.internalRender){
15252             var main = this.el.dom.firstChild;
15253             var w = main.offsetWidth;
15254             this.el.setWidth(w + this.el.getBorderWidth("lr"));
15255             Roo.fly(main).setWidth(w);
15256             this.internalRender = true;
15257             // opera does not respect the auto grow header center column
15258             // then, after it gets a width opera refuses to recalculate
15259             // without a second pass
15260             if(Roo.isOpera && !this.secondPass){
15261                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15262                 this.secondPass = true;
15263                 this.update.defer(10, this, [date]);
15264             }
15265         }
15266         */
15267         
15268     },
15269     
15270     findCell : function(dt) {
15271         dt = dt.clearTime().getTime();
15272         var ret = false;
15273         this.cells.each(function(c){
15274             //Roo.log("check " +c.dateValue + '?=' + dt);
15275             if(c.dateValue == dt){
15276                 ret = c;
15277                 return false;
15278             }
15279             return true;
15280         });
15281         
15282         return ret;
15283     },
15284     
15285     findCells : function(ev) {
15286         var s = ev.start.clone().clearTime().getTime();
15287        // Roo.log(s);
15288         var e= ev.end.clone().clearTime().getTime();
15289        // Roo.log(e);
15290         var ret = [];
15291         this.cells.each(function(c){
15292              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15293             
15294             if(c.dateValue > e){
15295                 return ;
15296             }
15297             if(c.dateValue < s){
15298                 return ;
15299             }
15300             ret.push(c);
15301         });
15302         
15303         return ret;    
15304     },
15305     
15306 //    findBestRow: function(cells)
15307 //    {
15308 //        var ret = 0;
15309 //        
15310 //        for (var i =0 ; i < cells.length;i++) {
15311 //            ret  = Math.max(cells[i].rows || 0,ret);
15312 //        }
15313 //        return ret;
15314 //        
15315 //    },
15316     
15317     
15318     addItem : function(ev)
15319     {
15320         // look for vertical location slot in
15321         var cells = this.findCells(ev);
15322         
15323 //        ev.row = this.findBestRow(cells);
15324         
15325         // work out the location.
15326         
15327         var crow = false;
15328         var rows = [];
15329         for(var i =0; i < cells.length; i++) {
15330             
15331             cells[i].row = cells[0].row;
15332             
15333             if(i == 0){
15334                 cells[i].row = cells[i].row + 1;
15335             }
15336             
15337             if (!crow) {
15338                 crow = {
15339                     start : cells[i],
15340                     end :  cells[i]
15341                 };
15342                 continue;
15343             }
15344             if (crow.start.getY() == cells[i].getY()) {
15345                 // on same row.
15346                 crow.end = cells[i];
15347                 continue;
15348             }
15349             // different row.
15350             rows.push(crow);
15351             crow = {
15352                 start: cells[i],
15353                 end : cells[i]
15354             };
15355             
15356         }
15357         
15358         rows.push(crow);
15359         ev.els = [];
15360         ev.rows = rows;
15361         ev.cells = cells;
15362         
15363         cells[0].events.push(ev);
15364         
15365         this.calevents.push(ev);
15366     },
15367     
15368     clearEvents: function() {
15369         
15370         if(!this.calevents){
15371             return;
15372         }
15373         
15374         Roo.each(this.cells.elements, function(c){
15375             c.row = 0;
15376             c.events = [];
15377             c.more = [];
15378         });
15379         
15380         Roo.each(this.calevents, function(e) {
15381             Roo.each(e.els, function(el) {
15382                 el.un('mouseenter' ,this.onEventEnter, this);
15383                 el.un('mouseleave' ,this.onEventLeave, this);
15384                 el.remove();
15385             },this);
15386         },this);
15387         
15388         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
15389             e.remove();
15390         });
15391         
15392     },
15393     
15394     renderEvents: function()
15395     {   
15396         var _this = this;
15397         
15398         this.cells.each(function(c) {
15399             
15400             if(c.row < 5){
15401                 return;
15402             }
15403             
15404             var ev = c.events;
15405             
15406             var r = 4;
15407             if(c.row != c.events.length){
15408                 r = 4 - (4 - (c.row - c.events.length));
15409             }
15410             
15411             c.events = ev.slice(0, r);
15412             c.more = ev.slice(r);
15413             
15414             if(c.more.length && c.more.length == 1){
15415                 c.events.push(c.more.pop());
15416             }
15417             
15418             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15419             
15420         });
15421             
15422         this.cells.each(function(c) {
15423             
15424             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15425             
15426             
15427             for (var e = 0; e < c.events.length; e++){
15428                 var ev = c.events[e];
15429                 var rows = ev.rows;
15430                 
15431                 for(var i = 0; i < rows.length; i++) {
15432                 
15433                     // how many rows should it span..
15434
15435                     var  cfg = {
15436                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15437                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15438
15439                         unselectable : "on",
15440                         cn : [
15441                             {
15442                                 cls: 'fc-event-inner',
15443                                 cn : [
15444     //                                {
15445     //                                  tag:'span',
15446     //                                  cls: 'fc-event-time',
15447     //                                  html : cells.length > 1 ? '' : ev.time
15448     //                                },
15449                                     {
15450                                       tag:'span',
15451                                       cls: 'fc-event-title',
15452                                       html : String.format('{0}', ev.title)
15453                                     }
15454
15455
15456                                 ]
15457                             },
15458                             {
15459                                 cls: 'ui-resizable-handle ui-resizable-e',
15460                                 html : '&nbsp;&nbsp;&nbsp'
15461                             }
15462
15463                         ]
15464                     };
15465
15466                     if (i == 0) {
15467                         cfg.cls += ' fc-event-start';
15468                     }
15469                     if ((i+1) == rows.length) {
15470                         cfg.cls += ' fc-event-end';
15471                     }
15472
15473                     var ctr = _this.el.select('.fc-event-container',true).first();
15474                     var cg = ctr.createChild(cfg);
15475
15476                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15477                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15478
15479                     var r = (c.more.length) ? 1 : 0;
15480                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
15481                     cg.setWidth(ebox.right - sbox.x -2);
15482
15483                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15484                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15485                     cg.on('click', _this.onEventClick, _this, ev);
15486
15487                     ev.els.push(cg);
15488                     
15489                 }
15490                 
15491             }
15492             
15493             
15494             if(c.more.length){
15495                 var  cfg = {
15496                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15497                     style : 'position: absolute',
15498                     unselectable : "on",
15499                     cn : [
15500                         {
15501                             cls: 'fc-event-inner',
15502                             cn : [
15503                                 {
15504                                   tag:'span',
15505                                   cls: 'fc-event-title',
15506                                   html : 'More'
15507                                 }
15508
15509
15510                             ]
15511                         },
15512                         {
15513                             cls: 'ui-resizable-handle ui-resizable-e',
15514                             html : '&nbsp;&nbsp;&nbsp'
15515                         }
15516
15517                     ]
15518                 };
15519
15520                 var ctr = _this.el.select('.fc-event-container',true).first();
15521                 var cg = ctr.createChild(cfg);
15522
15523                 var sbox = c.select('.fc-day-content',true).first().getBox();
15524                 var ebox = c.select('.fc-day-content',true).first().getBox();
15525                 //Roo.log(cg);
15526                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
15527                 cg.setWidth(ebox.right - sbox.x -2);
15528
15529                 cg.on('click', _this.onMoreEventClick, _this, c.more);
15530                 
15531             }
15532             
15533         });
15534         
15535         
15536         
15537     },
15538     
15539     onEventEnter: function (e, el,event,d) {
15540         this.fireEvent('evententer', this, el, event);
15541     },
15542     
15543     onEventLeave: function (e, el,event,d) {
15544         this.fireEvent('eventleave', this, el, event);
15545     },
15546     
15547     onEventClick: function (e, el,event,d) {
15548         this.fireEvent('eventclick', this, el, event);
15549     },
15550     
15551     onMonthChange: function () {
15552         this.store.load();
15553     },
15554     
15555     onMoreEventClick: function(e, el, more)
15556     {
15557         var _this = this;
15558         
15559         this.calpopover.placement = 'right';
15560         this.calpopover.setTitle('More');
15561         
15562         this.calpopover.setContent('');
15563         
15564         var ctr = this.calpopover.el.select('.popover-content', true).first();
15565         
15566         Roo.each(more, function(m){
15567             var cfg = {
15568                 cls : 'fc-event-hori fc-event-draggable',
15569                 html : m.title
15570             };
15571             var cg = ctr.createChild(cfg);
15572             
15573             cg.on('click', _this.onEventClick, _this, m);
15574         });
15575         
15576         this.calpopover.show(el);
15577         
15578         
15579     },
15580     
15581     onLoad: function () 
15582     {   
15583         this.calevents = [];
15584         var cal = this;
15585         
15586         if(this.store.getCount() > 0){
15587             this.store.data.each(function(d){
15588                cal.addItem({
15589                     id : d.data.id,
15590                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15591                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15592                     time : d.data.start_time,
15593                     title : d.data.title,
15594                     description : d.data.description,
15595                     venue : d.data.venue
15596                 });
15597             });
15598         }
15599         
15600         this.renderEvents();
15601         
15602         if(this.calevents.length && this.loadMask){
15603             this.maskEl.hide();
15604         }
15605     },
15606     
15607     onBeforeLoad: function()
15608     {
15609         this.clearEvents();
15610         if(this.loadMask){
15611             this.maskEl.show();
15612         }
15613     }
15614 });
15615
15616  
15617  /*
15618  * - LGPL
15619  *
15620  * element
15621  * 
15622  */
15623
15624 /**
15625  * @class Roo.bootstrap.Popover
15626  * @extends Roo.bootstrap.Component
15627  * Bootstrap Popover class
15628  * @cfg {String} html contents of the popover   (or false to use children..)
15629  * @cfg {String} title of popover (or false to hide)
15630  * @cfg {String} placement how it is placed
15631  * @cfg {String} trigger click || hover (or false to trigger manually)
15632  * @cfg {String} over what (parent or false to trigger manually.)
15633  * @cfg {Number} delay - delay before showing
15634  
15635  * @constructor
15636  * Create a new Popover
15637  * @param {Object} config The config object
15638  */
15639
15640 Roo.bootstrap.Popover = function(config){
15641     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15642 };
15643
15644 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
15645     
15646     title: 'Fill in a title',
15647     html: false,
15648     
15649     placement : 'right',
15650     trigger : 'hover', // hover
15651     
15652     delay : 0,
15653     
15654     over: 'parent',
15655     
15656     can_build_overlaid : false,
15657     
15658     getChildContainer : function()
15659     {
15660         return this.el.select('.popover-content',true).first();
15661     },
15662     
15663     getAutoCreate : function(){
15664          
15665         var cfg = {
15666            cls : 'popover roo-dynamic',
15667            style: 'display:block',
15668            cn : [
15669                 {
15670                     cls : 'arrow'
15671                 },
15672                 {
15673                     cls : 'popover-inner',
15674                     cn : [
15675                         {
15676                             tag: 'h3',
15677                             cls: 'popover-title',
15678                             html : this.title
15679                         },
15680                         {
15681                             cls : 'popover-content',
15682                             html : this.html
15683                         }
15684                     ]
15685                     
15686                 }
15687            ]
15688         };
15689         
15690         return cfg;
15691     },
15692     setTitle: function(str)
15693     {
15694         this.title = str;
15695         this.el.select('.popover-title',true).first().dom.innerHTML = str;
15696     },
15697     setContent: function(str)
15698     {
15699         this.html = str;
15700         this.el.select('.popover-content',true).first().dom.innerHTML = str;
15701     },
15702     // as it get's added to the bottom of the page.
15703     onRender : function(ct, position)
15704     {
15705         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15706         if(!this.el){
15707             var cfg = Roo.apply({},  this.getAutoCreate());
15708             cfg.id = Roo.id();
15709             
15710             if (this.cls) {
15711                 cfg.cls += ' ' + this.cls;
15712             }
15713             if (this.style) {
15714                 cfg.style = this.style;
15715             }
15716             //Roo.log("adding to ");
15717             this.el = Roo.get(document.body).createChild(cfg, position);
15718 //            Roo.log(this.el);
15719         }
15720         this.initEvents();
15721     },
15722     
15723     initEvents : function()
15724     {
15725         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15726         this.el.enableDisplayMode('block');
15727         this.el.hide();
15728         if (this.over === false) {
15729             return; 
15730         }
15731         if (this.triggers === false) {
15732             return;
15733         }
15734         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15735         var triggers = this.trigger ? this.trigger.split(' ') : [];
15736         Roo.each(triggers, function(trigger) {
15737         
15738             if (trigger == 'click') {
15739                 on_el.on('click', this.toggle, this);
15740             } else if (trigger != 'manual') {
15741                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
15742                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
15743       
15744                 on_el.on(eventIn  ,this.enter, this);
15745                 on_el.on(eventOut, this.leave, this);
15746             }
15747         }, this);
15748         
15749     },
15750     
15751     
15752     // private
15753     timeout : null,
15754     hoverState : null,
15755     
15756     toggle : function () {
15757         this.hoverState == 'in' ? this.leave() : this.enter();
15758     },
15759     
15760     enter : function () {
15761        
15762     
15763         clearTimeout(this.timeout);
15764     
15765         this.hoverState = 'in';
15766     
15767         if (!this.delay || !this.delay.show) {
15768             this.show();
15769             return;
15770         }
15771         var _t = this;
15772         this.timeout = setTimeout(function () {
15773             if (_t.hoverState == 'in') {
15774                 _t.show();
15775             }
15776         }, this.delay.show)
15777     },
15778     leave : function() {
15779         clearTimeout(this.timeout);
15780     
15781         this.hoverState = 'out';
15782     
15783         if (!this.delay || !this.delay.hide) {
15784             this.hide();
15785             return;
15786         }
15787         var _t = this;
15788         this.timeout = setTimeout(function () {
15789             if (_t.hoverState == 'out') {
15790                 _t.hide();
15791             }
15792         }, this.delay.hide)
15793     },
15794     
15795     show : function (on_el)
15796     {
15797         if (!on_el) {
15798             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15799         }
15800         // set content.
15801         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
15802         if (this.html !== false) {
15803             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
15804         }
15805         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
15806         if (!this.title.length) {
15807             this.el.select('.popover-title',true).hide();
15808         }
15809         
15810         var placement = typeof this.placement == 'function' ?
15811             this.placement.call(this, this.el, on_el) :
15812             this.placement;
15813             
15814         var autoToken = /\s?auto?\s?/i;
15815         var autoPlace = autoToken.test(placement);
15816         if (autoPlace) {
15817             placement = placement.replace(autoToken, '') || 'top';
15818         }
15819         
15820         //this.el.detach()
15821         //this.el.setXY([0,0]);
15822         this.el.show();
15823         this.el.dom.style.display='block';
15824         this.el.addClass(placement);
15825         
15826         //this.el.appendTo(on_el);
15827         
15828         var p = this.getPosition();
15829         var box = this.el.getBox();
15830         
15831         if (autoPlace) {
15832             // fixme..
15833         }
15834         var align = Roo.bootstrap.Popover.alignment[placement];
15835         this.el.alignTo(on_el, align[0],align[1]);
15836         //var arrow = this.el.select('.arrow',true).first();
15837         //arrow.set(align[2], 
15838         
15839         this.el.addClass('in');
15840         
15841         
15842         if (this.el.hasClass('fade')) {
15843             // fade it?
15844         }
15845         
15846     },
15847     hide : function()
15848     {
15849         this.el.setXY([0,0]);
15850         this.el.removeClass('in');
15851         this.el.hide();
15852         this.hoverState = null;
15853         
15854     }
15855     
15856 });
15857
15858 Roo.bootstrap.Popover.alignment = {
15859     'left' : ['r-l', [-10,0], 'right'],
15860     'right' : ['l-r', [10,0], 'left'],
15861     'bottom' : ['t-b', [0,10], 'top'],
15862     'top' : [ 'b-t', [0,-10], 'bottom']
15863 };
15864
15865  /*
15866  * - LGPL
15867  *
15868  * Progress
15869  * 
15870  */
15871
15872 /**
15873  * @class Roo.bootstrap.Progress
15874  * @extends Roo.bootstrap.Component
15875  * Bootstrap Progress class
15876  * @cfg {Boolean} striped striped of the progress bar
15877  * @cfg {Boolean} active animated of the progress bar
15878  * 
15879  * 
15880  * @constructor
15881  * Create a new Progress
15882  * @param {Object} config The config object
15883  */
15884
15885 Roo.bootstrap.Progress = function(config){
15886     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
15887 };
15888
15889 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
15890     
15891     striped : false,
15892     active: false,
15893     
15894     getAutoCreate : function(){
15895         var cfg = {
15896             tag: 'div',
15897             cls: 'progress'
15898         };
15899         
15900         
15901         if(this.striped){
15902             cfg.cls += ' progress-striped';
15903         }
15904       
15905         if(this.active){
15906             cfg.cls += ' active';
15907         }
15908         
15909         
15910         return cfg;
15911     }
15912    
15913 });
15914
15915  
15916
15917  /*
15918  * - LGPL
15919  *
15920  * ProgressBar
15921  * 
15922  */
15923
15924 /**
15925  * @class Roo.bootstrap.ProgressBar
15926  * @extends Roo.bootstrap.Component
15927  * Bootstrap ProgressBar class
15928  * @cfg {Number} aria_valuenow aria-value now
15929  * @cfg {Number} aria_valuemin aria-value min
15930  * @cfg {Number} aria_valuemax aria-value max
15931  * @cfg {String} label label for the progress bar
15932  * @cfg {String} panel (success | info | warning | danger )
15933  * @cfg {String} role role of the progress bar
15934  * @cfg {String} sr_only text
15935  * 
15936  * 
15937  * @constructor
15938  * Create a new ProgressBar
15939  * @param {Object} config The config object
15940  */
15941
15942 Roo.bootstrap.ProgressBar = function(config){
15943     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
15944 };
15945
15946 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
15947     
15948     aria_valuenow : 0,
15949     aria_valuemin : 0,
15950     aria_valuemax : 100,
15951     label : false,
15952     panel : false,
15953     role : false,
15954     sr_only: false,
15955     
15956     getAutoCreate : function()
15957     {
15958         
15959         var cfg = {
15960             tag: 'div',
15961             cls: 'progress-bar',
15962             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
15963         };
15964         
15965         if(this.sr_only){
15966             cfg.cn = {
15967                 tag: 'span',
15968                 cls: 'sr-only',
15969                 html: this.sr_only
15970             }
15971         }
15972         
15973         if(this.role){
15974             cfg.role = this.role;
15975         }
15976         
15977         if(this.aria_valuenow){
15978             cfg['aria-valuenow'] = this.aria_valuenow;
15979         }
15980         
15981         if(this.aria_valuemin){
15982             cfg['aria-valuemin'] = this.aria_valuemin;
15983         }
15984         
15985         if(this.aria_valuemax){
15986             cfg['aria-valuemax'] = this.aria_valuemax;
15987         }
15988         
15989         if(this.label && !this.sr_only){
15990             cfg.html = this.label;
15991         }
15992         
15993         if(this.panel){
15994             cfg.cls += ' progress-bar-' + this.panel;
15995         }
15996         
15997         return cfg;
15998     },
15999     
16000     update : function(aria_valuenow)
16001     {
16002         this.aria_valuenow = aria_valuenow;
16003         
16004         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16005     }
16006    
16007 });
16008
16009  
16010
16011  /*
16012  * - LGPL
16013  *
16014  * column
16015  * 
16016  */
16017
16018 /**
16019  * @class Roo.bootstrap.TabGroup
16020  * @extends Roo.bootstrap.Column
16021  * Bootstrap Column class
16022  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16023  * @cfg {Boolean} carousel true to make the group behave like a carousel
16024  * @cfg {Boolean} bullets show bullets for the panels
16025  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16026  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
16027  * @cfg {Number} timer auto slide timer .. default 0 millisecond
16028  * 
16029  * @constructor
16030  * Create a new TabGroup
16031  * @param {Object} config The config object
16032  */
16033
16034 Roo.bootstrap.TabGroup = function(config){
16035     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16036     if (!this.navId) {
16037         this.navId = Roo.id();
16038     }
16039     this.tabs = [];
16040     Roo.bootstrap.TabGroup.register(this);
16041     
16042 };
16043
16044 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
16045     
16046     carousel : false,
16047     transition : false,
16048     bullets : 0,
16049     timer : 0,
16050     autoslide : false,
16051     slideFn : false,
16052     slideOnTouch : false,
16053     
16054     getAutoCreate : function()
16055     {
16056         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16057         
16058         cfg.cls += ' tab-content';
16059         
16060         if (this.carousel) {
16061             cfg.cls += ' carousel slide';
16062             
16063             cfg.cn = [{
16064                cls : 'carousel-inner'
16065             }];
16066         
16067             if(this.bullets  && !Roo.isTouch){
16068                 
16069                 var bullets = {
16070                     cls : 'carousel-bullets',
16071                     cn : []
16072                 };
16073                
16074                 if(this.bullets_cls){
16075                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16076                 }
16077                  /*
16078                 for (var i = 0; i < this.bullets; i++){
16079                     bullets.cn.push({
16080                         cls : 'bullet bullet-' + i
16081                     });
16082                 }
16083                 */
16084                 bullets.cn.push({
16085                     cls : 'clear'
16086                 });
16087                 
16088                 cfg.cn[0].cn = bullets;
16089             }
16090         }
16091         
16092         return cfg;
16093     },
16094     
16095     initEvents:  function()
16096     {
16097         if(Roo.isTouch && this.slideOnTouch){
16098             this.el.on("touchstart", this.onTouchStart, this);
16099         }
16100         
16101         if(this.autoslide){
16102             var _this = this;
16103             
16104             this.slideFn = window.setInterval(function() {
16105                 _this.showPanelNext();
16106             }, this.timer);
16107         }
16108         
16109     },
16110     
16111     onTouchStart : function(e, el, o)
16112     {
16113         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
16114             return;
16115         }
16116         
16117         this.showPanelNext();
16118     },
16119     
16120     getChildContainer : function()
16121     {
16122         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
16123     },
16124     
16125     /**
16126     * register a Navigation item
16127     * @param {Roo.bootstrap.NavItem} the navitem to add
16128     */
16129     register : function(item)
16130     {
16131         this.tabs.push( item);
16132         item.navId = this.navId; // not really needed..
16133         this.addBullet();
16134     
16135     },
16136     
16137     getActivePanel : function()
16138     {
16139         var r = false;
16140         Roo.each(this.tabs, function(t) {
16141             if (t.active) {
16142                 r = t;
16143                 return false;
16144             }
16145             return null;
16146         });
16147         return r;
16148         
16149     },
16150     getPanelByName : function(n)
16151     {
16152         var r = false;
16153         Roo.each(this.tabs, function(t) {
16154             if (t.tabId == n) {
16155                 r = t;
16156                 return false;
16157             }
16158             return null;
16159         });
16160         return r;
16161     },
16162     indexOfPanel : function(p)
16163     {
16164         var r = false;
16165         Roo.each(this.tabs, function(t,i) {
16166             if (t.tabId == p.tabId) {
16167                 r = i;
16168                 return false;
16169             }
16170             return null;
16171         });
16172         return r;
16173     },
16174     /**
16175      * show a specific panel
16176      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16177      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16178      */
16179     showPanel : function (pan)
16180     {
16181         if(this.transition || typeof(pan) == 'undefined'){
16182             Roo.log("waiting for the transitionend");
16183             return;
16184         }
16185         
16186         if (typeof(pan) == 'number') {
16187             pan = this.tabs[pan];
16188         }
16189         
16190         if (typeof(pan) == 'string') {
16191             pan = this.getPanelByName(pan);
16192         }
16193         
16194         var cur = this.getActivePanel();
16195         
16196         if(!pan || !cur){
16197             Roo.log('pan or acitve pan is undefined');
16198             return false;
16199         }
16200         
16201         if (pan.tabId == this.getActivePanel().tabId) {
16202             return true;
16203         }
16204         
16205         if (false === cur.fireEvent('beforedeactivate')) {
16206             return false;
16207         }
16208         
16209         if(this.bullets > 0 && !Roo.isTouch){
16210             this.setActiveBullet(this.indexOfPanel(pan));
16211         }
16212         
16213         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16214             
16215             this.transition = true;
16216             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
16217             var lr = dir == 'next' ? 'left' : 'right';
16218             pan.el.addClass(dir); // or prev
16219             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16220             cur.el.addClass(lr); // or right
16221             pan.el.addClass(lr);
16222             
16223             var _this = this;
16224             cur.el.on('transitionend', function() {
16225                 Roo.log("trans end?");
16226                 
16227                 pan.el.removeClass([lr,dir]);
16228                 pan.setActive(true);
16229                 
16230                 cur.el.removeClass([lr]);
16231                 cur.setActive(false);
16232                 
16233                 _this.transition = false;
16234                 
16235             }, this, { single:  true } );
16236             
16237             return true;
16238         }
16239         
16240         cur.setActive(false);
16241         pan.setActive(true);
16242         
16243         return true;
16244         
16245     },
16246     showPanelNext : function()
16247     {
16248         var i = this.indexOfPanel(this.getActivePanel());
16249         
16250         if (i >= this.tabs.length - 1 && !this.autoslide) {
16251             return;
16252         }
16253         
16254         if (i >= this.tabs.length - 1 && this.autoslide) {
16255             i = -1;
16256         }
16257         
16258         this.showPanel(this.tabs[i+1]);
16259     },
16260     
16261     showPanelPrev : function()
16262     {
16263         var i = this.indexOfPanel(this.getActivePanel());
16264         
16265         if (i  < 1 && !this.autoslide) {
16266             return;
16267         }
16268         
16269         if (i < 1 && this.autoslide) {
16270             i = this.tabs.length;
16271         }
16272         
16273         this.showPanel(this.tabs[i-1]);
16274     },
16275     
16276     
16277     addBullet: function()
16278     {
16279         if(!this.bullets || Roo.isTouch){
16280             return;
16281         }
16282         var ctr = this.el.select('.carousel-bullets',true).first();
16283         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16284         var bullet = ctr.createChild({
16285             cls : 'bullet bullet-' + i
16286         },ctr.dom.lastChild);
16287         
16288         
16289         var _this = this;
16290         
16291         bullet.on('click', (function(e, el, o, ii, t){
16292
16293             e.preventDefault();
16294
16295             this.showPanel(ii);
16296
16297             if(this.autoslide && this.slideFn){
16298                 clearInterval(this.slideFn);
16299                 this.slideFn = window.setInterval(function() {
16300                     _this.showPanelNext();
16301                 }, this.timer);
16302             }
16303
16304         }).createDelegate(this, [i, bullet], true));
16305                 
16306         
16307     },
16308      
16309     setActiveBullet : function(i)
16310     {
16311         if(Roo.isTouch){
16312             return;
16313         }
16314         
16315         Roo.each(this.el.select('.bullet', true).elements, function(el){
16316             el.removeClass('selected');
16317         });
16318
16319         var bullet = this.el.select('.bullet-' + i, true).first();
16320         
16321         if(!bullet){
16322             return;
16323         }
16324         
16325         bullet.addClass('selected');
16326     }
16327     
16328     
16329   
16330 });
16331
16332  
16333
16334  
16335  
16336 Roo.apply(Roo.bootstrap.TabGroup, {
16337     
16338     groups: {},
16339      /**
16340     * register a Navigation Group
16341     * @param {Roo.bootstrap.NavGroup} the navgroup to add
16342     */
16343     register : function(navgrp)
16344     {
16345         this.groups[navgrp.navId] = navgrp;
16346         
16347     },
16348     /**
16349     * fetch a Navigation Group based on the navigation ID
16350     * if one does not exist , it will get created.
16351     * @param {string} the navgroup to add
16352     * @returns {Roo.bootstrap.NavGroup} the navgroup 
16353     */
16354     get: function(navId) {
16355         if (typeof(this.groups[navId]) == 'undefined') {
16356             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
16357         }
16358         return this.groups[navId] ;
16359     }
16360     
16361     
16362     
16363 });
16364
16365  /*
16366  * - LGPL
16367  *
16368  * TabPanel
16369  * 
16370  */
16371
16372 /**
16373  * @class Roo.bootstrap.TabPanel
16374  * @extends Roo.bootstrap.Component
16375  * Bootstrap TabPanel class
16376  * @cfg {Boolean} active panel active
16377  * @cfg {String} html panel content
16378  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16379  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16380  * 
16381  * 
16382  * @constructor
16383  * Create a new TabPanel
16384  * @param {Object} config The config object
16385  */
16386
16387 Roo.bootstrap.TabPanel = function(config){
16388     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16389     this.addEvents({
16390         /**
16391              * @event changed
16392              * Fires when the active status changes
16393              * @param {Roo.bootstrap.TabPanel} this
16394              * @param {Boolean} state the new state
16395             
16396          */
16397         'changed': true,
16398         /**
16399              * @event beforedeactivate
16400              * Fires before a tab is de-activated - can be used to do validation on a form.
16401              * @param {Roo.bootstrap.TabPanel} this
16402              * @return {Boolean} false if there is an error
16403             
16404          */
16405         'beforedeactivate': true
16406      });
16407     
16408     this.tabId = this.tabId || Roo.id();
16409   
16410 };
16411
16412 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
16413     
16414     active: false,
16415     html: false,
16416     tabId: false,
16417     navId : false,
16418     
16419     getAutoCreate : function(){
16420         var cfg = {
16421             tag: 'div',
16422             // item is needed for carousel - not sure if it has any effect otherwise
16423             cls: 'tab-pane item',
16424             html: this.html || ''
16425         };
16426         
16427         if(this.active){
16428             cfg.cls += ' active';
16429         }
16430         
16431         if(this.tabId){
16432             cfg.tabId = this.tabId;
16433         }
16434         
16435         
16436         return cfg;
16437     },
16438     
16439     initEvents:  function()
16440     {
16441         var p = this.parent();
16442         this.navId = this.navId || p.navId;
16443         
16444         if (typeof(this.navId) != 'undefined') {
16445             // not really needed.. but just in case.. parent should be a NavGroup.
16446             var tg = Roo.bootstrap.TabGroup.get(this.navId);
16447             
16448             tg.register(this);
16449             
16450             var i = tg.tabs.length - 1;
16451             
16452             if(this.active && tg.bullets > 0 && i < tg.bullets){
16453                 tg.setActiveBullet(i);
16454             }
16455         }
16456         
16457     },
16458     
16459     
16460     onRender : function(ct, position)
16461     {
16462        // Roo.log("Call onRender: " + this.xtype);
16463         
16464         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16465         
16466         
16467         
16468         
16469         
16470     },
16471     
16472     setActive: function(state)
16473     {
16474         Roo.log("panel - set active " + this.tabId + "=" + state);
16475         
16476         this.active = state;
16477         if (!state) {
16478             this.el.removeClass('active');
16479             
16480         } else  if (!this.el.hasClass('active')) {
16481             this.el.addClass('active');
16482         }
16483         
16484         this.fireEvent('changed', this, state);
16485     }
16486     
16487     
16488 });
16489  
16490
16491  
16492
16493  /*
16494  * - LGPL
16495  *
16496  * DateField
16497  * 
16498  */
16499
16500 /**
16501  * @class Roo.bootstrap.DateField
16502  * @extends Roo.bootstrap.Input
16503  * Bootstrap DateField class
16504  * @cfg {Number} weekStart default 0
16505  * @cfg {String} viewMode default empty, (months|years)
16506  * @cfg {String} minViewMode default empty, (months|years)
16507  * @cfg {Number} startDate default -Infinity
16508  * @cfg {Number} endDate default Infinity
16509  * @cfg {Boolean} todayHighlight default false
16510  * @cfg {Boolean} todayBtn default false
16511  * @cfg {Boolean} calendarWeeks default false
16512  * @cfg {Object} daysOfWeekDisabled default empty
16513  * @cfg {Boolean} singleMode default false (true | false)
16514  * 
16515  * @cfg {Boolean} keyboardNavigation default true
16516  * @cfg {String} language default en
16517  * 
16518  * @constructor
16519  * Create a new DateField
16520  * @param {Object} config The config object
16521  */
16522
16523 Roo.bootstrap.DateField = function(config){
16524     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16525      this.addEvents({
16526             /**
16527              * @event show
16528              * Fires when this field show.
16529              * @param {Roo.bootstrap.DateField} this
16530              * @param {Mixed} date The date value
16531              */
16532             show : true,
16533             /**
16534              * @event show
16535              * Fires when this field hide.
16536              * @param {Roo.bootstrap.DateField} this
16537              * @param {Mixed} date The date value
16538              */
16539             hide : true,
16540             /**
16541              * @event select
16542              * Fires when select a date.
16543              * @param {Roo.bootstrap.DateField} this
16544              * @param {Mixed} date The date value
16545              */
16546             select : true,
16547             /**
16548              * @event beforeselect
16549              * Fires when before select a date.
16550              * @param {Roo.bootstrap.DateField} this
16551              * @param {Mixed} date The date value
16552              */
16553             beforeselect : true
16554         });
16555 };
16556
16557 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
16558     
16559     /**
16560      * @cfg {String} format
16561      * The default date format string which can be overriden for localization support.  The format must be
16562      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16563      */
16564     format : "m/d/y",
16565     /**
16566      * @cfg {String} altFormats
16567      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16568      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16569      */
16570     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16571     
16572     weekStart : 0,
16573     
16574     viewMode : '',
16575     
16576     minViewMode : '',
16577     
16578     todayHighlight : false,
16579     
16580     todayBtn: false,
16581     
16582     language: 'en',
16583     
16584     keyboardNavigation: true,
16585     
16586     calendarWeeks: false,
16587     
16588     startDate: -Infinity,
16589     
16590     endDate: Infinity,
16591     
16592     daysOfWeekDisabled: [],
16593     
16594     _events: [],
16595     
16596     singleMode : false,
16597     
16598     UTCDate: function()
16599     {
16600         return new Date(Date.UTC.apply(Date, arguments));
16601     },
16602     
16603     UTCToday: function()
16604     {
16605         var today = new Date();
16606         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16607     },
16608     
16609     getDate: function() {
16610             var d = this.getUTCDate();
16611             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16612     },
16613     
16614     getUTCDate: function() {
16615             return this.date;
16616     },
16617     
16618     setDate: function(d) {
16619             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16620     },
16621     
16622     setUTCDate: function(d) {
16623             this.date = d;
16624             this.setValue(this.formatDate(this.date));
16625     },
16626         
16627     onRender: function(ct, position)
16628     {
16629         
16630         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16631         
16632         this.language = this.language || 'en';
16633         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16634         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16635         
16636         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16637         this.format = this.format || 'm/d/y';
16638         this.isInline = false;
16639         this.isInput = true;
16640         this.component = this.el.select('.add-on', true).first() || false;
16641         this.component = (this.component && this.component.length === 0) ? false : this.component;
16642         this.hasInput = this.component && this.inputEL().length;
16643         
16644         if (typeof(this.minViewMode === 'string')) {
16645             switch (this.minViewMode) {
16646                 case 'months':
16647                     this.minViewMode = 1;
16648                     break;
16649                 case 'years':
16650                     this.minViewMode = 2;
16651                     break;
16652                 default:
16653                     this.minViewMode = 0;
16654                     break;
16655             }
16656         }
16657         
16658         if (typeof(this.viewMode === 'string')) {
16659             switch (this.viewMode) {
16660                 case 'months':
16661                     this.viewMode = 1;
16662                     break;
16663                 case 'years':
16664                     this.viewMode = 2;
16665                     break;
16666                 default:
16667                     this.viewMode = 0;
16668                     break;
16669             }
16670         }
16671                 
16672         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16673         
16674 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16675         
16676         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16677         
16678         this.picker().on('mousedown', this.onMousedown, this);
16679         this.picker().on('click', this.onClick, this);
16680         
16681         this.picker().addClass('datepicker-dropdown');
16682         
16683         this.startViewMode = this.viewMode;
16684         
16685         if(this.singleMode){
16686             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16687                 v.setVisibilityMode(Roo.Element.DISPLAY);
16688                 v.hide();
16689             });
16690             
16691             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16692                 v.setStyle('width', '189px');
16693             });
16694         }
16695         
16696         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16697             if(!this.calendarWeeks){
16698                 v.remove();
16699                 return;
16700             }
16701             
16702             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16703             v.attr('colspan', function(i, val){
16704                 return parseInt(val) + 1;
16705             });
16706         });
16707                         
16708         
16709         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16710         
16711         this.setStartDate(this.startDate);
16712         this.setEndDate(this.endDate);
16713         
16714         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16715         
16716         this.fillDow();
16717         this.fillMonths();
16718         this.update();
16719         this.showMode();
16720         
16721         if(this.isInline) {
16722             this.show();
16723         }
16724     },
16725     
16726     picker : function()
16727     {
16728         return this.pickerEl;
16729 //        return this.el.select('.datepicker', true).first();
16730     },
16731     
16732     fillDow: function()
16733     {
16734         var dowCnt = this.weekStart;
16735         
16736         var dow = {
16737             tag: 'tr',
16738             cn: [
16739                 
16740             ]
16741         };
16742         
16743         if(this.calendarWeeks){
16744             dow.cn.push({
16745                 tag: 'th',
16746                 cls: 'cw',
16747                 html: '&nbsp;'
16748             })
16749         }
16750         
16751         while (dowCnt < this.weekStart + 7) {
16752             dow.cn.push({
16753                 tag: 'th',
16754                 cls: 'dow',
16755                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16756             });
16757         }
16758         
16759         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16760     },
16761     
16762     fillMonths: function()
16763     {    
16764         var i = 0;
16765         var months = this.picker().select('>.datepicker-months td', true).first();
16766         
16767         months.dom.innerHTML = '';
16768         
16769         while (i < 12) {
16770             var month = {
16771                 tag: 'span',
16772                 cls: 'month',
16773                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16774             };
16775             
16776             months.createChild(month);
16777         }
16778         
16779     },
16780     
16781     update: function()
16782     {
16783         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;
16784         
16785         if (this.date < this.startDate) {
16786             this.viewDate = new Date(this.startDate);
16787         } else if (this.date > this.endDate) {
16788             this.viewDate = new Date(this.endDate);
16789         } else {
16790             this.viewDate = new Date(this.date);
16791         }
16792         
16793         this.fill();
16794     },
16795     
16796     fill: function() 
16797     {
16798         var d = new Date(this.viewDate),
16799                 year = d.getUTCFullYear(),
16800                 month = d.getUTCMonth(),
16801                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16802                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16803                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16804                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16805                 currentDate = this.date && this.date.valueOf(),
16806                 today = this.UTCToday();
16807         
16808         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16809         
16810 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16811         
16812 //        this.picker.select('>tfoot th.today').
16813 //                                              .text(dates[this.language].today)
16814 //                                              .toggle(this.todayBtn !== false);
16815     
16816         this.updateNavArrows();
16817         this.fillMonths();
16818                                                 
16819         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16820         
16821         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16822          
16823         prevMonth.setUTCDate(day);
16824         
16825         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16826         
16827         var nextMonth = new Date(prevMonth);
16828         
16829         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16830         
16831         nextMonth = nextMonth.valueOf();
16832         
16833         var fillMonths = false;
16834         
16835         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16836         
16837         while(prevMonth.valueOf() < nextMonth) {
16838             var clsName = '';
16839             
16840             if (prevMonth.getUTCDay() === this.weekStart) {
16841                 if(fillMonths){
16842                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16843                 }
16844                     
16845                 fillMonths = {
16846                     tag: 'tr',
16847                     cn: []
16848                 };
16849                 
16850                 if(this.calendarWeeks){
16851                     // ISO 8601: First week contains first thursday.
16852                     // ISO also states week starts on Monday, but we can be more abstract here.
16853                     var
16854                     // Start of current week: based on weekstart/current date
16855                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16856                     // Thursday of this week
16857                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16858                     // First Thursday of year, year from thursday
16859                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16860                     // Calendar week: ms between thursdays, div ms per day, div 7 days
16861                     calWeek =  (th - yth) / 864e5 / 7 + 1;
16862                     
16863                     fillMonths.cn.push({
16864                         tag: 'td',
16865                         cls: 'cw',
16866                         html: calWeek
16867                     });
16868                 }
16869             }
16870             
16871             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16872                 clsName += ' old';
16873             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16874                 clsName += ' new';
16875             }
16876             if (this.todayHighlight &&
16877                 prevMonth.getUTCFullYear() == today.getFullYear() &&
16878                 prevMonth.getUTCMonth() == today.getMonth() &&
16879                 prevMonth.getUTCDate() == today.getDate()) {
16880                 clsName += ' today';
16881             }
16882             
16883             if (currentDate && prevMonth.valueOf() === currentDate) {
16884                 clsName += ' active';
16885             }
16886             
16887             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
16888                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
16889                     clsName += ' disabled';
16890             }
16891             
16892             fillMonths.cn.push({
16893                 tag: 'td',
16894                 cls: 'day ' + clsName,
16895                 html: prevMonth.getDate()
16896             });
16897             
16898             prevMonth.setDate(prevMonth.getDate()+1);
16899         }
16900           
16901         var currentYear = this.date && this.date.getUTCFullYear();
16902         var currentMonth = this.date && this.date.getUTCMonth();
16903         
16904         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
16905         
16906         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
16907             v.removeClass('active');
16908             
16909             if(currentYear === year && k === currentMonth){
16910                 v.addClass('active');
16911             }
16912             
16913             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
16914                 v.addClass('disabled');
16915             }
16916             
16917         });
16918         
16919         
16920         year = parseInt(year/10, 10) * 10;
16921         
16922         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
16923         
16924         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
16925         
16926         year -= 1;
16927         for (var i = -1; i < 11; i++) {
16928             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
16929                 tag: 'span',
16930                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
16931                 html: year
16932             });
16933             
16934             year += 1;
16935         }
16936     },
16937     
16938     showMode: function(dir) 
16939     {
16940         if (dir) {
16941             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
16942         }
16943         
16944         Roo.each(this.picker().select('>div',true).elements, function(v){
16945             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16946             v.hide();
16947         });
16948         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
16949     },
16950     
16951     place: function()
16952     {
16953         if(this.isInline) {
16954             return;
16955         }
16956         
16957         this.picker().removeClass(['bottom', 'top']);
16958         
16959         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16960             /*
16961              * place to the top of element!
16962              *
16963              */
16964             
16965             this.picker().addClass('top');
16966             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16967             
16968             return;
16969         }
16970         
16971         this.picker().addClass('bottom');
16972         
16973         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16974     },
16975     
16976     parseDate : function(value)
16977     {
16978         if(!value || value instanceof Date){
16979             return value;
16980         }
16981         var v = Date.parseDate(value, this.format);
16982         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
16983             v = Date.parseDate(value, 'Y-m-d');
16984         }
16985         if(!v && this.altFormats){
16986             if(!this.altFormatsArray){
16987                 this.altFormatsArray = this.altFormats.split("|");
16988             }
16989             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
16990                 v = Date.parseDate(value, this.altFormatsArray[i]);
16991             }
16992         }
16993         return v;
16994     },
16995     
16996     formatDate : function(date, fmt)
16997     {   
16998         return (!date || !(date instanceof Date)) ?
16999         date : date.dateFormat(fmt || this.format);
17000     },
17001     
17002     onFocus : function()
17003     {
17004         Roo.bootstrap.DateField.superclass.onFocus.call(this);
17005         this.show();
17006     },
17007     
17008     onBlur : function()
17009     {
17010         Roo.bootstrap.DateField.superclass.onBlur.call(this);
17011         
17012         var d = this.inputEl().getValue();
17013         
17014         this.setValue(d);
17015                 
17016         this.hide();
17017     },
17018     
17019     show : function()
17020     {
17021         this.picker().show();
17022         this.update();
17023         this.place();
17024         
17025         this.fireEvent('show', this, this.date);
17026     },
17027     
17028     hide : function()
17029     {
17030         if(this.isInline) {
17031             return;
17032         }
17033         this.picker().hide();
17034         this.viewMode = this.startViewMode;
17035         this.showMode();
17036         
17037         this.fireEvent('hide', this, this.date);
17038         
17039     },
17040     
17041     onMousedown: function(e)
17042     {
17043         e.stopPropagation();
17044         e.preventDefault();
17045     },
17046     
17047     keyup: function(e)
17048     {
17049         Roo.bootstrap.DateField.superclass.keyup.call(this);
17050         this.update();
17051     },
17052
17053     setValue: function(v)
17054     {
17055         if(this.fireEvent('beforeselect', this, v) !== false){
17056             var d = new Date(this.parseDate(v) ).clearTime();
17057         
17058             if(isNaN(d.getTime())){
17059                 this.date = this.viewDate = '';
17060                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17061                 return;
17062             }
17063
17064             v = this.formatDate(d);
17065
17066             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17067
17068             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17069
17070             this.update();
17071
17072             this.fireEvent('select', this, this.date);
17073         }
17074     },
17075     
17076     getValue: function()
17077     {
17078         return this.formatDate(this.date);
17079     },
17080     
17081     fireKey: function(e)
17082     {
17083         if (!this.picker().isVisible()){
17084             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17085                 this.show();
17086             }
17087             return;
17088         }
17089         
17090         var dateChanged = false,
17091         dir, day, month,
17092         newDate, newViewDate;
17093         
17094         switch(e.keyCode){
17095             case 27: // escape
17096                 this.hide();
17097                 e.preventDefault();
17098                 break;
17099             case 37: // left
17100             case 39: // right
17101                 if (!this.keyboardNavigation) {
17102                     break;
17103                 }
17104                 dir = e.keyCode == 37 ? -1 : 1;
17105                 
17106                 if (e.ctrlKey){
17107                     newDate = this.moveYear(this.date, dir);
17108                     newViewDate = this.moveYear(this.viewDate, dir);
17109                 } else if (e.shiftKey){
17110                     newDate = this.moveMonth(this.date, dir);
17111                     newViewDate = this.moveMonth(this.viewDate, dir);
17112                 } else {
17113                     newDate = new Date(this.date);
17114                     newDate.setUTCDate(this.date.getUTCDate() + dir);
17115                     newViewDate = new Date(this.viewDate);
17116                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
17117                 }
17118                 if (this.dateWithinRange(newDate)){
17119                     this.date = newDate;
17120                     this.viewDate = newViewDate;
17121                     this.setValue(this.formatDate(this.date));
17122 //                    this.update();
17123                     e.preventDefault();
17124                     dateChanged = true;
17125                 }
17126                 break;
17127             case 38: // up
17128             case 40: // down
17129                 if (!this.keyboardNavigation) {
17130                     break;
17131                 }
17132                 dir = e.keyCode == 38 ? -1 : 1;
17133                 if (e.ctrlKey){
17134                     newDate = this.moveYear(this.date, dir);
17135                     newViewDate = this.moveYear(this.viewDate, dir);
17136                 } else if (e.shiftKey){
17137                     newDate = this.moveMonth(this.date, dir);
17138                     newViewDate = this.moveMonth(this.viewDate, dir);
17139                 } else {
17140                     newDate = new Date(this.date);
17141                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
17142                     newViewDate = new Date(this.viewDate);
17143                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
17144                 }
17145                 if (this.dateWithinRange(newDate)){
17146                     this.date = newDate;
17147                     this.viewDate = newViewDate;
17148                     this.setValue(this.formatDate(this.date));
17149 //                    this.update();
17150                     e.preventDefault();
17151                     dateChanged = true;
17152                 }
17153                 break;
17154             case 13: // enter
17155                 this.setValue(this.formatDate(this.date));
17156                 this.hide();
17157                 e.preventDefault();
17158                 break;
17159             case 9: // tab
17160                 this.setValue(this.formatDate(this.date));
17161                 this.hide();
17162                 break;
17163             case 16: // shift
17164             case 17: // ctrl
17165             case 18: // alt
17166                 break;
17167             default :
17168                 this.hide();
17169                 
17170         }
17171     },
17172     
17173     
17174     onClick: function(e) 
17175     {
17176         e.stopPropagation();
17177         e.preventDefault();
17178         
17179         var target = e.getTarget();
17180         
17181         if(target.nodeName.toLowerCase() === 'i'){
17182             target = Roo.get(target).dom.parentNode;
17183         }
17184         
17185         var nodeName = target.nodeName;
17186         var className = target.className;
17187         var html = target.innerHTML;
17188         //Roo.log(nodeName);
17189         
17190         switch(nodeName.toLowerCase()) {
17191             case 'th':
17192                 switch(className) {
17193                     case 'switch':
17194                         this.showMode(1);
17195                         break;
17196                     case 'prev':
17197                     case 'next':
17198                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17199                         switch(this.viewMode){
17200                                 case 0:
17201                                         this.viewDate = this.moveMonth(this.viewDate, dir);
17202                                         break;
17203                                 case 1:
17204                                 case 2:
17205                                         this.viewDate = this.moveYear(this.viewDate, dir);
17206                                         break;
17207                         }
17208                         this.fill();
17209                         break;
17210                     case 'today':
17211                         var date = new Date();
17212                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17213 //                        this.fill()
17214                         this.setValue(this.formatDate(this.date));
17215                         
17216                         this.hide();
17217                         break;
17218                 }
17219                 break;
17220             case 'span':
17221                 if (className.indexOf('disabled') < 0) {
17222                     this.viewDate.setUTCDate(1);
17223                     if (className.indexOf('month') > -1) {
17224                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17225                     } else {
17226                         var year = parseInt(html, 10) || 0;
17227                         this.viewDate.setUTCFullYear(year);
17228                         
17229                     }
17230                     
17231                     if(this.singleMode){
17232                         this.setValue(this.formatDate(this.viewDate));
17233                         this.hide();
17234                         return;
17235                     }
17236                     
17237                     this.showMode(-1);
17238                     this.fill();
17239                 }
17240                 break;
17241                 
17242             case 'td':
17243                 //Roo.log(className);
17244                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17245                     var day = parseInt(html, 10) || 1;
17246                     var year = this.viewDate.getUTCFullYear(),
17247                         month = this.viewDate.getUTCMonth();
17248
17249                     if (className.indexOf('old') > -1) {
17250                         if(month === 0 ){
17251                             month = 11;
17252                             year -= 1;
17253                         }else{
17254                             month -= 1;
17255                         }
17256                     } else if (className.indexOf('new') > -1) {
17257                         if (month == 11) {
17258                             month = 0;
17259                             year += 1;
17260                         } else {
17261                             month += 1;
17262                         }
17263                     }
17264                     //Roo.log([year,month,day]);
17265                     this.date = this.UTCDate(year, month, day,0,0,0,0);
17266                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17267 //                    this.fill();
17268                     //Roo.log(this.formatDate(this.date));
17269                     this.setValue(this.formatDate(this.date));
17270                     this.hide();
17271                 }
17272                 break;
17273         }
17274     },
17275     
17276     setStartDate: function(startDate)
17277     {
17278         this.startDate = startDate || -Infinity;
17279         if (this.startDate !== -Infinity) {
17280             this.startDate = this.parseDate(this.startDate);
17281         }
17282         this.update();
17283         this.updateNavArrows();
17284     },
17285
17286     setEndDate: function(endDate)
17287     {
17288         this.endDate = endDate || Infinity;
17289         if (this.endDate !== Infinity) {
17290             this.endDate = this.parseDate(this.endDate);
17291         }
17292         this.update();
17293         this.updateNavArrows();
17294     },
17295     
17296     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17297     {
17298         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17299         if (typeof(this.daysOfWeekDisabled) !== 'object') {
17300             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17301         }
17302         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17303             return parseInt(d, 10);
17304         });
17305         this.update();
17306         this.updateNavArrows();
17307     },
17308     
17309     updateNavArrows: function() 
17310     {
17311         if(this.singleMode){
17312             return;
17313         }
17314         
17315         var d = new Date(this.viewDate),
17316         year = d.getUTCFullYear(),
17317         month = d.getUTCMonth();
17318         
17319         Roo.each(this.picker().select('.prev', true).elements, function(v){
17320             v.show();
17321             switch (this.viewMode) {
17322                 case 0:
17323
17324                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
17325                         v.hide();
17326                     }
17327                     break;
17328                 case 1:
17329                 case 2:
17330                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
17331                         v.hide();
17332                     }
17333                     break;
17334             }
17335         });
17336         
17337         Roo.each(this.picker().select('.next', true).elements, function(v){
17338             v.show();
17339             switch (this.viewMode) {
17340                 case 0:
17341
17342                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
17343                         v.hide();
17344                     }
17345                     break;
17346                 case 1:
17347                 case 2:
17348                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
17349                         v.hide();
17350                     }
17351                     break;
17352             }
17353         })
17354     },
17355     
17356     moveMonth: function(date, dir)
17357     {
17358         if (!dir) {
17359             return date;
17360         }
17361         var new_date = new Date(date.valueOf()),
17362         day = new_date.getUTCDate(),
17363         month = new_date.getUTCMonth(),
17364         mag = Math.abs(dir),
17365         new_month, test;
17366         dir = dir > 0 ? 1 : -1;
17367         if (mag == 1){
17368             test = dir == -1
17369             // If going back one month, make sure month is not current month
17370             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
17371             ? function(){
17372                 return new_date.getUTCMonth() == month;
17373             }
17374             // If going forward one month, make sure month is as expected
17375             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
17376             : function(){
17377                 return new_date.getUTCMonth() != new_month;
17378             };
17379             new_month = month + dir;
17380             new_date.setUTCMonth(new_month);
17381             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
17382             if (new_month < 0 || new_month > 11) {
17383                 new_month = (new_month + 12) % 12;
17384             }
17385         } else {
17386             // For magnitudes >1, move one month at a time...
17387             for (var i=0; i<mag; i++) {
17388                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
17389                 new_date = this.moveMonth(new_date, dir);
17390             }
17391             // ...then reset the day, keeping it in the new month
17392             new_month = new_date.getUTCMonth();
17393             new_date.setUTCDate(day);
17394             test = function(){
17395                 return new_month != new_date.getUTCMonth();
17396             };
17397         }
17398         // Common date-resetting loop -- if date is beyond end of month, make it
17399         // end of month
17400         while (test()){
17401             new_date.setUTCDate(--day);
17402             new_date.setUTCMonth(new_month);
17403         }
17404         return new_date;
17405     },
17406
17407     moveYear: function(date, dir)
17408     {
17409         return this.moveMonth(date, dir*12);
17410     },
17411
17412     dateWithinRange: function(date)
17413     {
17414         return date >= this.startDate && date <= this.endDate;
17415     },
17416
17417     
17418     remove: function() 
17419     {
17420         this.picker().remove();
17421     }
17422    
17423 });
17424
17425 Roo.apply(Roo.bootstrap.DateField,  {
17426     
17427     head : {
17428         tag: 'thead',
17429         cn: [
17430         {
17431             tag: 'tr',
17432             cn: [
17433             {
17434                 tag: 'th',
17435                 cls: 'prev',
17436                 html: '<i class="fa fa-arrow-left"/>'
17437             },
17438             {
17439                 tag: 'th',
17440                 cls: 'switch',
17441                 colspan: '5'
17442             },
17443             {
17444                 tag: 'th',
17445                 cls: 'next',
17446                 html: '<i class="fa fa-arrow-right"/>'
17447             }
17448
17449             ]
17450         }
17451         ]
17452     },
17453     
17454     content : {
17455         tag: 'tbody',
17456         cn: [
17457         {
17458             tag: 'tr',
17459             cn: [
17460             {
17461                 tag: 'td',
17462                 colspan: '7'
17463             }
17464             ]
17465         }
17466         ]
17467     },
17468     
17469     footer : {
17470         tag: 'tfoot',
17471         cn: [
17472         {
17473             tag: 'tr',
17474             cn: [
17475             {
17476                 tag: 'th',
17477                 colspan: '7',
17478                 cls: 'today'
17479             }
17480                     
17481             ]
17482         }
17483         ]
17484     },
17485     
17486     dates:{
17487         en: {
17488             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17489             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17490             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17491             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17492             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17493             today: "Today"
17494         }
17495     },
17496     
17497     modes: [
17498     {
17499         clsName: 'days',
17500         navFnc: 'Month',
17501         navStep: 1
17502     },
17503     {
17504         clsName: 'months',
17505         navFnc: 'FullYear',
17506         navStep: 1
17507     },
17508     {
17509         clsName: 'years',
17510         navFnc: 'FullYear',
17511         navStep: 10
17512     }]
17513 });
17514
17515 Roo.apply(Roo.bootstrap.DateField,  {
17516   
17517     template : {
17518         tag: 'div',
17519         cls: 'datepicker dropdown-menu roo-dynamic',
17520         cn: [
17521         {
17522             tag: 'div',
17523             cls: 'datepicker-days',
17524             cn: [
17525             {
17526                 tag: 'table',
17527                 cls: 'table-condensed',
17528                 cn:[
17529                 Roo.bootstrap.DateField.head,
17530                 {
17531                     tag: 'tbody'
17532                 },
17533                 Roo.bootstrap.DateField.footer
17534                 ]
17535             }
17536             ]
17537         },
17538         {
17539             tag: 'div',
17540             cls: 'datepicker-months',
17541             cn: [
17542             {
17543                 tag: 'table',
17544                 cls: 'table-condensed',
17545                 cn:[
17546                 Roo.bootstrap.DateField.head,
17547                 Roo.bootstrap.DateField.content,
17548                 Roo.bootstrap.DateField.footer
17549                 ]
17550             }
17551             ]
17552         },
17553         {
17554             tag: 'div',
17555             cls: 'datepicker-years',
17556             cn: [
17557             {
17558                 tag: 'table',
17559                 cls: 'table-condensed',
17560                 cn:[
17561                 Roo.bootstrap.DateField.head,
17562                 Roo.bootstrap.DateField.content,
17563                 Roo.bootstrap.DateField.footer
17564                 ]
17565             }
17566             ]
17567         }
17568         ]
17569     }
17570 });
17571
17572  
17573
17574  /*
17575  * - LGPL
17576  *
17577  * TimeField
17578  * 
17579  */
17580
17581 /**
17582  * @class Roo.bootstrap.TimeField
17583  * @extends Roo.bootstrap.Input
17584  * Bootstrap DateField class
17585  * 
17586  * 
17587  * @constructor
17588  * Create a new TimeField
17589  * @param {Object} config The config object
17590  */
17591
17592 Roo.bootstrap.TimeField = function(config){
17593     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17594     this.addEvents({
17595             /**
17596              * @event show
17597              * Fires when this field show.
17598              * @param {Roo.bootstrap.DateField} thisthis
17599              * @param {Mixed} date The date value
17600              */
17601             show : true,
17602             /**
17603              * @event show
17604              * Fires when this field hide.
17605              * @param {Roo.bootstrap.DateField} this
17606              * @param {Mixed} date The date value
17607              */
17608             hide : true,
17609             /**
17610              * @event select
17611              * Fires when select a date.
17612              * @param {Roo.bootstrap.DateField} this
17613              * @param {Mixed} date The date value
17614              */
17615             select : true
17616         });
17617 };
17618
17619 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
17620     
17621     /**
17622      * @cfg {String} format
17623      * The default time format string which can be overriden for localization support.  The format must be
17624      * valid according to {@link Date#parseDate} (defaults to 'H:i').
17625      */
17626     format : "H:i",
17627        
17628     onRender: function(ct, position)
17629     {
17630         
17631         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17632                 
17633         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17634         
17635         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17636         
17637         this.pop = this.picker().select('>.datepicker-time',true).first();
17638         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17639         
17640         this.picker().on('mousedown', this.onMousedown, this);
17641         this.picker().on('click', this.onClick, this);
17642         
17643         this.picker().addClass('datepicker-dropdown');
17644     
17645         this.fillTime();
17646         this.update();
17647             
17648         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17649         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17650         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17651         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17652         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17653         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17654
17655     },
17656     
17657     fireKey: function(e){
17658         if (!this.picker().isVisible()){
17659             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17660                 this.show();
17661             }
17662             return;
17663         }
17664
17665         e.preventDefault();
17666         
17667         switch(e.keyCode){
17668             case 27: // escape
17669                 this.hide();
17670                 break;
17671             case 37: // left
17672             case 39: // right
17673                 this.onTogglePeriod();
17674                 break;
17675             case 38: // up
17676                 this.onIncrementMinutes();
17677                 break;
17678             case 40: // down
17679                 this.onDecrementMinutes();
17680                 break;
17681             case 13: // enter
17682             case 9: // tab
17683                 this.setTime();
17684                 break;
17685         }
17686     },
17687     
17688     onClick: function(e) {
17689         e.stopPropagation();
17690         e.preventDefault();
17691     },
17692     
17693     picker : function()
17694     {
17695         return this.el.select('.datepicker', true).first();
17696     },
17697     
17698     fillTime: function()
17699     {    
17700         var time = this.pop.select('tbody', true).first();
17701         
17702         time.dom.innerHTML = '';
17703         
17704         time.createChild({
17705             tag: 'tr',
17706             cn: [
17707                 {
17708                     tag: 'td',
17709                     cn: [
17710                         {
17711                             tag: 'a',
17712                             href: '#',
17713                             cls: 'btn',
17714                             cn: [
17715                                 {
17716                                     tag: 'span',
17717                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
17718                                 }
17719                             ]
17720                         } 
17721                     ]
17722                 },
17723                 {
17724                     tag: 'td',
17725                     cls: 'separator'
17726                 },
17727                 {
17728                     tag: 'td',
17729                     cn: [
17730                         {
17731                             tag: 'a',
17732                             href: '#',
17733                             cls: 'btn',
17734                             cn: [
17735                                 {
17736                                     tag: 'span',
17737                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
17738                                 }
17739                             ]
17740                         }
17741                     ]
17742                 },
17743                 {
17744                     tag: 'td',
17745                     cls: 'separator'
17746                 }
17747             ]
17748         });
17749         
17750         time.createChild({
17751             tag: 'tr',
17752             cn: [
17753                 {
17754                     tag: 'td',
17755                     cn: [
17756                         {
17757                             tag: 'span',
17758                             cls: 'timepicker-hour',
17759                             html: '00'
17760                         }  
17761                     ]
17762                 },
17763                 {
17764                     tag: 'td',
17765                     cls: 'separator',
17766                     html: ':'
17767                 },
17768                 {
17769                     tag: 'td',
17770                     cn: [
17771                         {
17772                             tag: 'span',
17773                             cls: 'timepicker-minute',
17774                             html: '00'
17775                         }  
17776                     ]
17777                 },
17778                 {
17779                     tag: 'td',
17780                     cls: 'separator'
17781                 },
17782                 {
17783                     tag: 'td',
17784                     cn: [
17785                         {
17786                             tag: 'button',
17787                             type: 'button',
17788                             cls: 'btn btn-primary period',
17789                             html: 'AM'
17790                             
17791                         }
17792                     ]
17793                 }
17794             ]
17795         });
17796         
17797         time.createChild({
17798             tag: 'tr',
17799             cn: [
17800                 {
17801                     tag: 'td',
17802                     cn: [
17803                         {
17804                             tag: 'a',
17805                             href: '#',
17806                             cls: 'btn',
17807                             cn: [
17808                                 {
17809                                     tag: 'span',
17810                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
17811                                 }
17812                             ]
17813                         }
17814                     ]
17815                 },
17816                 {
17817                     tag: 'td',
17818                     cls: 'separator'
17819                 },
17820                 {
17821                     tag: 'td',
17822                     cn: [
17823                         {
17824                             tag: 'a',
17825                             href: '#',
17826                             cls: 'btn',
17827                             cn: [
17828                                 {
17829                                     tag: 'span',
17830                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
17831                                 }
17832                             ]
17833                         }
17834                     ]
17835                 },
17836                 {
17837                     tag: 'td',
17838                     cls: 'separator'
17839                 }
17840             ]
17841         });
17842         
17843     },
17844     
17845     update: function()
17846     {
17847         
17848         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17849         
17850         this.fill();
17851     },
17852     
17853     fill: function() 
17854     {
17855         var hours = this.time.getHours();
17856         var minutes = this.time.getMinutes();
17857         var period = 'AM';
17858         
17859         if(hours > 11){
17860             period = 'PM';
17861         }
17862         
17863         if(hours == 0){
17864             hours = 12;
17865         }
17866         
17867         
17868         if(hours > 12){
17869             hours = hours - 12;
17870         }
17871         
17872         if(hours < 10){
17873             hours = '0' + hours;
17874         }
17875         
17876         if(minutes < 10){
17877             minutes = '0' + minutes;
17878         }
17879         
17880         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
17881         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
17882         this.pop.select('button', true).first().dom.innerHTML = period;
17883         
17884     },
17885     
17886     place: function()
17887     {   
17888         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
17889         
17890         var cls = ['bottom'];
17891         
17892         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
17893             cls.pop();
17894             cls.push('top');
17895         }
17896         
17897         cls.push('right');
17898         
17899         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
17900             cls.pop();
17901             cls.push('left');
17902         }
17903         
17904         this.picker().addClass(cls.join('-'));
17905         
17906         var _this = this;
17907         
17908         Roo.each(cls, function(c){
17909             if(c == 'bottom'){
17910                 _this.picker().setTop(_this.inputEl().getHeight());
17911                 return;
17912             }
17913             if(c == 'top'){
17914                 _this.picker().setTop(0 - _this.picker().getHeight());
17915                 return;
17916             }
17917             
17918             if(c == 'left'){
17919                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
17920                 return;
17921             }
17922             if(c == 'right'){
17923                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
17924                 return;
17925             }
17926         });
17927         
17928     },
17929   
17930     onFocus : function()
17931     {
17932         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
17933         this.show();
17934     },
17935     
17936     onBlur : function()
17937     {
17938         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
17939         this.hide();
17940     },
17941     
17942     show : function()
17943     {
17944         this.picker().show();
17945         this.pop.show();
17946         this.update();
17947         this.place();
17948         
17949         this.fireEvent('show', this, this.date);
17950     },
17951     
17952     hide : function()
17953     {
17954         this.picker().hide();
17955         this.pop.hide();
17956         
17957         this.fireEvent('hide', this, this.date);
17958     },
17959     
17960     setTime : function()
17961     {
17962         this.hide();
17963         this.setValue(this.time.format(this.format));
17964         
17965         this.fireEvent('select', this, this.date);
17966         
17967         
17968     },
17969     
17970     onMousedown: function(e){
17971         e.stopPropagation();
17972         e.preventDefault();
17973     },
17974     
17975     onIncrementHours: function()
17976     {
17977         Roo.log('onIncrementHours');
17978         this.time = this.time.add(Date.HOUR, 1);
17979         this.update();
17980         
17981     },
17982     
17983     onDecrementHours: function()
17984     {
17985         Roo.log('onDecrementHours');
17986         this.time = this.time.add(Date.HOUR, -1);
17987         this.update();
17988     },
17989     
17990     onIncrementMinutes: function()
17991     {
17992         Roo.log('onIncrementMinutes');
17993         this.time = this.time.add(Date.MINUTE, 1);
17994         this.update();
17995     },
17996     
17997     onDecrementMinutes: function()
17998     {
17999         Roo.log('onDecrementMinutes');
18000         this.time = this.time.add(Date.MINUTE, -1);
18001         this.update();
18002     },
18003     
18004     onTogglePeriod: function()
18005     {
18006         Roo.log('onTogglePeriod');
18007         this.time = this.time.add(Date.HOUR, 12);
18008         this.update();
18009     }
18010     
18011    
18012 });
18013
18014 Roo.apply(Roo.bootstrap.TimeField,  {
18015     
18016     content : {
18017         tag: 'tbody',
18018         cn: [
18019             {
18020                 tag: 'tr',
18021                 cn: [
18022                 {
18023                     tag: 'td',
18024                     colspan: '7'
18025                 }
18026                 ]
18027             }
18028         ]
18029     },
18030     
18031     footer : {
18032         tag: 'tfoot',
18033         cn: [
18034             {
18035                 tag: 'tr',
18036                 cn: [
18037                 {
18038                     tag: 'th',
18039                     colspan: '7',
18040                     cls: '',
18041                     cn: [
18042                         {
18043                             tag: 'button',
18044                             cls: 'btn btn-info ok',
18045                             html: 'OK'
18046                         }
18047                     ]
18048                 }
18049
18050                 ]
18051             }
18052         ]
18053     }
18054 });
18055
18056 Roo.apply(Roo.bootstrap.TimeField,  {
18057   
18058     template : {
18059         tag: 'div',
18060         cls: 'datepicker dropdown-menu',
18061         cn: [
18062             {
18063                 tag: 'div',
18064                 cls: 'datepicker-time',
18065                 cn: [
18066                 {
18067                     tag: 'table',
18068                     cls: 'table-condensed',
18069                     cn:[
18070                     Roo.bootstrap.TimeField.content,
18071                     Roo.bootstrap.TimeField.footer
18072                     ]
18073                 }
18074                 ]
18075             }
18076         ]
18077     }
18078 });
18079
18080  
18081
18082  /*
18083  * - LGPL
18084  *
18085  * MonthField
18086  * 
18087  */
18088
18089 /**
18090  * @class Roo.bootstrap.MonthField
18091  * @extends Roo.bootstrap.Input
18092  * Bootstrap MonthField class
18093  * 
18094  * @cfg {String} language default en
18095  * 
18096  * @constructor
18097  * Create a new MonthField
18098  * @param {Object} config The config object
18099  */
18100
18101 Roo.bootstrap.MonthField = function(config){
18102     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
18103     
18104     this.addEvents({
18105         /**
18106          * @event show
18107          * Fires when this field show.
18108          * @param {Roo.bootstrap.MonthField} this
18109          * @param {Mixed} date The date value
18110          */
18111         show : true,
18112         /**
18113          * @event show
18114          * Fires when this field hide.
18115          * @param {Roo.bootstrap.MonthField} this
18116          * @param {Mixed} date The date value
18117          */
18118         hide : true,
18119         /**
18120          * @event select
18121          * Fires when select a date.
18122          * @param {Roo.bootstrap.MonthField} this
18123          * @param {String} oldvalue The old value
18124          * @param {String} newvalue The new value
18125          */
18126         select : true
18127     });
18128 };
18129
18130 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
18131     
18132     onRender: function(ct, position)
18133     {
18134         
18135         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
18136         
18137         this.language = this.language || 'en';
18138         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
18139         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
18140         
18141         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
18142         this.isInline = false;
18143         this.isInput = true;
18144         this.component = this.el.select('.add-on', true).first() || false;
18145         this.component = (this.component && this.component.length === 0) ? false : this.component;
18146         this.hasInput = this.component && this.inputEL().length;
18147         
18148         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
18149         
18150         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18151         
18152         this.picker().on('mousedown', this.onMousedown, this);
18153         this.picker().on('click', this.onClick, this);
18154         
18155         this.picker().addClass('datepicker-dropdown');
18156         
18157         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18158             v.setStyle('width', '189px');
18159         });
18160         
18161         this.fillMonths();
18162         
18163         this.update();
18164         
18165         if(this.isInline) {
18166             this.show();
18167         }
18168         
18169     },
18170     
18171     setValue: function(v, suppressEvent)
18172     {   
18173         var o = this.getValue();
18174         
18175         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
18176         
18177         this.update();
18178
18179         if(suppressEvent !== true){
18180             this.fireEvent('select', this, o, v);
18181         }
18182         
18183     },
18184     
18185     getValue: function()
18186     {
18187         return this.value;
18188     },
18189     
18190     onClick: function(e) 
18191     {
18192         e.stopPropagation();
18193         e.preventDefault();
18194         
18195         var target = e.getTarget();
18196         
18197         if(target.nodeName.toLowerCase() === 'i'){
18198             target = Roo.get(target).dom.parentNode;
18199         }
18200         
18201         var nodeName = target.nodeName;
18202         var className = target.className;
18203         var html = target.innerHTML;
18204         
18205         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18206             return;
18207         }
18208         
18209         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18210         
18211         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18212         
18213         this.hide();
18214                         
18215     },
18216     
18217     picker : function()
18218     {
18219         return this.pickerEl;
18220     },
18221     
18222     fillMonths: function()
18223     {    
18224         var i = 0;
18225         var months = this.picker().select('>.datepicker-months td', true).first();
18226         
18227         months.dom.innerHTML = '';
18228         
18229         while (i < 12) {
18230             var month = {
18231                 tag: 'span',
18232                 cls: 'month',
18233                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18234             };
18235             
18236             months.createChild(month);
18237         }
18238         
18239     },
18240     
18241     update: function()
18242     {
18243         var _this = this;
18244         
18245         if(typeof(this.vIndex) == 'undefined' && this.value.length){
18246             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18247         }
18248         
18249         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18250             e.removeClass('active');
18251             
18252             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18253                 e.addClass('active');
18254             }
18255         })
18256     },
18257     
18258     place: function()
18259     {
18260         if(this.isInline) {
18261             return;
18262         }
18263         
18264         this.picker().removeClass(['bottom', 'top']);
18265         
18266         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18267             /*
18268              * place to the top of element!
18269              *
18270              */
18271             
18272             this.picker().addClass('top');
18273             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18274             
18275             return;
18276         }
18277         
18278         this.picker().addClass('bottom');
18279         
18280         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18281     },
18282     
18283     onFocus : function()
18284     {
18285         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18286         this.show();
18287     },
18288     
18289     onBlur : function()
18290     {
18291         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18292         
18293         var d = this.inputEl().getValue();
18294         
18295         this.setValue(d);
18296                 
18297         this.hide();
18298     },
18299     
18300     show : function()
18301     {
18302         this.picker().show();
18303         this.picker().select('>.datepicker-months', true).first().show();
18304         this.update();
18305         this.place();
18306         
18307         this.fireEvent('show', this, this.date);
18308     },
18309     
18310     hide : function()
18311     {
18312         if(this.isInline) {
18313             return;
18314         }
18315         this.picker().hide();
18316         this.fireEvent('hide', this, this.date);
18317         
18318     },
18319     
18320     onMousedown: function(e)
18321     {
18322         e.stopPropagation();
18323         e.preventDefault();
18324     },
18325     
18326     keyup: function(e)
18327     {
18328         Roo.bootstrap.MonthField.superclass.keyup.call(this);
18329         this.update();
18330     },
18331
18332     fireKey: function(e)
18333     {
18334         if (!this.picker().isVisible()){
18335             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
18336                 this.show();
18337             }
18338             return;
18339         }
18340         
18341         var dir;
18342         
18343         switch(e.keyCode){
18344             case 27: // escape
18345                 this.hide();
18346                 e.preventDefault();
18347                 break;
18348             case 37: // left
18349             case 39: // right
18350                 dir = e.keyCode == 37 ? -1 : 1;
18351                 
18352                 this.vIndex = this.vIndex + dir;
18353                 
18354                 if(this.vIndex < 0){
18355                     this.vIndex = 0;
18356                 }
18357                 
18358                 if(this.vIndex > 11){
18359                     this.vIndex = 11;
18360                 }
18361                 
18362                 if(isNaN(this.vIndex)){
18363                     this.vIndex = 0;
18364                 }
18365                 
18366                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18367                 
18368                 break;
18369             case 38: // up
18370             case 40: // down
18371                 
18372                 dir = e.keyCode == 38 ? -1 : 1;
18373                 
18374                 this.vIndex = this.vIndex + dir * 4;
18375                 
18376                 if(this.vIndex < 0){
18377                     this.vIndex = 0;
18378                 }
18379                 
18380                 if(this.vIndex > 11){
18381                     this.vIndex = 11;
18382                 }
18383                 
18384                 if(isNaN(this.vIndex)){
18385                     this.vIndex = 0;
18386                 }
18387                 
18388                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18389                 break;
18390                 
18391             case 13: // enter
18392                 
18393                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18394                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18395                 }
18396                 
18397                 this.hide();
18398                 e.preventDefault();
18399                 break;
18400             case 9: // tab
18401                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18402                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18403                 }
18404                 this.hide();
18405                 break;
18406             case 16: // shift
18407             case 17: // ctrl
18408             case 18: // alt
18409                 break;
18410             default :
18411                 this.hide();
18412                 
18413         }
18414     },
18415     
18416     remove: function() 
18417     {
18418         this.picker().remove();
18419     }
18420    
18421 });
18422
18423 Roo.apply(Roo.bootstrap.MonthField,  {
18424     
18425     content : {
18426         tag: 'tbody',
18427         cn: [
18428         {
18429             tag: 'tr',
18430             cn: [
18431             {
18432                 tag: 'td',
18433                 colspan: '7'
18434             }
18435             ]
18436         }
18437         ]
18438     },
18439     
18440     dates:{
18441         en: {
18442             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18443             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18444         }
18445     }
18446 });
18447
18448 Roo.apply(Roo.bootstrap.MonthField,  {
18449   
18450     template : {
18451         tag: 'div',
18452         cls: 'datepicker dropdown-menu roo-dynamic',
18453         cn: [
18454             {
18455                 tag: 'div',
18456                 cls: 'datepicker-months',
18457                 cn: [
18458                 {
18459                     tag: 'table',
18460                     cls: 'table-condensed',
18461                     cn:[
18462                         Roo.bootstrap.DateField.content
18463                     ]
18464                 }
18465                 ]
18466             }
18467         ]
18468     }
18469 });
18470
18471  
18472
18473  
18474  /*
18475  * - LGPL
18476  *
18477  * CheckBox
18478  * 
18479  */
18480
18481 /**
18482  * @class Roo.bootstrap.CheckBox
18483  * @extends Roo.bootstrap.Input
18484  * Bootstrap CheckBox class
18485  * 
18486  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18487  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18488  * @cfg {String} boxLabel The text that appears beside the checkbox
18489  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18490  * @cfg {Boolean} checked initnal the element
18491  * @cfg {Boolean} inline inline the element (default false)
18492  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18493  * 
18494  * @constructor
18495  * Create a new CheckBox
18496  * @param {Object} config The config object
18497  */
18498
18499 Roo.bootstrap.CheckBox = function(config){
18500     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18501    
18502     this.addEvents({
18503         /**
18504         * @event check
18505         * Fires when the element is checked or unchecked.
18506         * @param {Roo.bootstrap.CheckBox} this This input
18507         * @param {Boolean} checked The new checked value
18508         */
18509        check : true
18510     });
18511     
18512 };
18513
18514 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
18515   
18516     inputType: 'checkbox',
18517     inputValue: 1,
18518     valueOff: 0,
18519     boxLabel: false,
18520     checked: false,
18521     weight : false,
18522     inline: false,
18523     
18524     getAutoCreate : function()
18525     {
18526         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18527         
18528         var id = Roo.id();
18529         
18530         var cfg = {};
18531         
18532         cfg.cls = 'form-group ' + this.inputType; //input-group
18533         
18534         if(this.inline){
18535             cfg.cls += ' ' + this.inputType + '-inline';
18536         }
18537         
18538         var input =  {
18539             tag: 'input',
18540             id : id,
18541             type : this.inputType,
18542             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18543             cls : 'roo-' + this.inputType, //'form-box',
18544             placeholder : this.placeholder || ''
18545             
18546         };
18547         
18548         if (this.weight) { // Validity check?
18549             cfg.cls += " " + this.inputType + "-" + this.weight;
18550         }
18551         
18552         if (this.disabled) {
18553             input.disabled=true;
18554         }
18555         
18556         if(this.checked){
18557             input.checked = this.checked;
18558         }
18559         
18560         if (this.name) {
18561             input.name = this.name;
18562         }
18563         
18564         if (this.size) {
18565             input.cls += ' input-' + this.size;
18566         }
18567         
18568         var settings=this;
18569         
18570         ['xs','sm','md','lg'].map(function(size){
18571             if (settings[size]) {
18572                 cfg.cls += ' col-' + size + '-' + settings[size];
18573             }
18574         });
18575         
18576         var inputblock = input;
18577          
18578         if (this.before || this.after) {
18579             
18580             inputblock = {
18581                 cls : 'input-group',
18582                 cn :  [] 
18583             };
18584             
18585             if (this.before) {
18586                 inputblock.cn.push({
18587                     tag :'span',
18588                     cls : 'input-group-addon',
18589                     html : this.before
18590                 });
18591             }
18592             
18593             inputblock.cn.push(input);
18594             
18595             if (this.after) {
18596                 inputblock.cn.push({
18597                     tag :'span',
18598                     cls : 'input-group-addon',
18599                     html : this.after
18600                 });
18601             }
18602             
18603         }
18604         
18605         if (align ==='left' && this.fieldLabel.length) {
18606 //                Roo.log("left and has label");
18607                 cfg.cn = [
18608                     
18609                     {
18610                         tag: 'label',
18611                         'for' :  id,
18612                         cls : 'control-label col-md-' + this.labelWidth,
18613                         html : this.fieldLabel
18614                         
18615                     },
18616                     {
18617                         cls : "col-md-" + (12 - this.labelWidth), 
18618                         cn: [
18619                             inputblock
18620                         ]
18621                     }
18622                     
18623                 ];
18624         } else if ( this.fieldLabel.length) {
18625 //                Roo.log(" label");
18626                 cfg.cn = [
18627                    
18628                     {
18629                         tag: this.boxLabel ? 'span' : 'label',
18630                         'for': id,
18631                         cls: 'control-label box-input-label',
18632                         //cls : 'input-group-addon',
18633                         html : this.fieldLabel
18634                         
18635                     },
18636                     
18637                     inputblock
18638                     
18639                 ];
18640
18641         } else {
18642             
18643 //                Roo.log(" no label && no align");
18644                 cfg.cn = [  inputblock ] ;
18645                 
18646                 
18647         }
18648         if(this.boxLabel){
18649              var boxLabelCfg = {
18650                 tag: 'label',
18651                 //'for': id, // box label is handled by onclick - so no for...
18652                 cls: 'box-label',
18653                 html: this.boxLabel
18654             };
18655             
18656             if(this.tooltip){
18657                 boxLabelCfg.tooltip = this.tooltip;
18658             }
18659              
18660             cfg.cn.push(boxLabelCfg);
18661         }
18662         
18663         
18664        
18665         return cfg;
18666         
18667     },
18668     
18669     /**
18670      * return the real input element.
18671      */
18672     inputEl: function ()
18673     {
18674         return this.el.select('input.roo-' + this.inputType,true).first();
18675     },
18676     
18677     labelEl: function()
18678     {
18679         return this.el.select('label.control-label',true).first();
18680     },
18681     /* depricated... */
18682     
18683     label: function()
18684     {
18685         return this.labelEl();
18686     },
18687     
18688     initEvents : function()
18689     {
18690 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18691         
18692         this.inputEl().on('click', this.onClick,  this);
18693         
18694         if (this.boxLabel) { 
18695             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
18696         }
18697         
18698         this.startValue = this.getValue();
18699         
18700         if(this.groupId){
18701             Roo.bootstrap.CheckBox.register(this);
18702         }
18703     },
18704     
18705     onClick : function()
18706     {   
18707         this.setChecked(!this.checked);
18708     },
18709     
18710     setChecked : function(state,suppressEvent)
18711     {
18712         this.startValue = this.getValue();
18713         
18714         if(this.inputType == 'radio'){
18715             
18716             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18717                 e.dom.checked = false;
18718             });
18719             
18720             this.inputEl().dom.checked = true;
18721             
18722             this.inputEl().dom.value = this.inputValue;
18723             
18724             if(suppressEvent !== true){
18725                 this.fireEvent('check', this, true);
18726             }
18727             
18728             this.validate();
18729             
18730             return;
18731         }
18732         
18733         this.checked = state;
18734         
18735         this.inputEl().dom.checked = state;
18736         
18737         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18738         
18739         if(suppressEvent !== true){
18740             this.fireEvent('check', this, state);
18741         }
18742         
18743         this.validate();
18744     },
18745     
18746     getValue : function()
18747     {
18748         if(this.inputType == 'radio'){
18749             return this.getGroupValue();
18750         }
18751         
18752         return this.inputEl().getValue();
18753         
18754     },
18755     
18756     getGroupValue : function()
18757     {
18758         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18759             return '';
18760         }
18761         
18762         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18763     },
18764     
18765     setValue : function(v,suppressEvent)
18766     {
18767         if(this.inputType == 'radio'){
18768             this.setGroupValue(v, suppressEvent);
18769             return;
18770         }
18771         
18772         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18773         
18774         this.validate();
18775     },
18776     
18777     setGroupValue : function(v, suppressEvent)
18778     {
18779         this.startValue = this.getValue();
18780         
18781         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18782             e.dom.checked = false;
18783             
18784             if(e.dom.value == v){
18785                 e.dom.checked = true;
18786             }
18787         });
18788         
18789         if(suppressEvent !== true){
18790             this.fireEvent('check', this, true);
18791         }
18792
18793         this.validate();
18794         
18795         return;
18796     },
18797     
18798     validate : function()
18799     {
18800         if(
18801                 this.disabled || 
18802                 (this.inputType == 'radio' && this.validateRadio()) ||
18803                 (this.inputType == 'checkbox' && this.validateCheckbox())
18804         ){
18805             this.markValid();
18806             return true;
18807         }
18808         
18809         this.markInvalid();
18810         return false;
18811     },
18812     
18813     validateRadio : function()
18814     {
18815         var valid = false;
18816         
18817         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18818             if(!e.dom.checked){
18819                 return;
18820             }
18821             
18822             valid = true;
18823             
18824             return false;
18825         });
18826         
18827         return valid;
18828     },
18829     
18830     validateCheckbox : function()
18831     {
18832         if(!this.groupId){
18833             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18834         }
18835         
18836         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18837         
18838         if(!group){
18839             return false;
18840         }
18841         
18842         var r = false;
18843         
18844         for(var i in group){
18845             if(r){
18846                 break;
18847             }
18848             
18849             r = (group[i].getValue() == group[i].inputValue) ? true : false;
18850         }
18851         
18852         return r;
18853     },
18854     
18855     /**
18856      * Mark this field as valid
18857      */
18858     markValid : function()
18859     {
18860         if(this.allowBlank){
18861             return;
18862         }
18863         
18864         var _this = this;
18865         
18866         this.fireEvent('valid', this);
18867         
18868         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18869         
18870         if(this.groupId){
18871             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18872         }
18873         
18874         if(label){
18875             label.markValid();
18876         }
18877         
18878         if(this.inputType == 'radio'){
18879             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18880                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18881                 e.findParent('.form-group', false, true).addClass(_this.validClass);
18882             });
18883             
18884             return;
18885         }
18886         
18887         if(!this.groupId){
18888             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18889             this.el.findParent('.form-group', false, true).addClass(this.validClass);
18890             return;
18891         }
18892         
18893         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18894             
18895         if(!group){
18896             return;
18897         }
18898         
18899         for(var i in group){
18900             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18901             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
18902         }
18903     },
18904     
18905      /**
18906      * Mark this field as invalid
18907      * @param {String} msg The validation message
18908      */
18909     markInvalid : function(msg)
18910     {
18911         if(this.allowBlank){
18912             return;
18913         }
18914         
18915         var _this = this;
18916         
18917         this.fireEvent('invalid', this, msg);
18918         
18919         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18920         
18921         if(this.groupId){
18922             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18923         }
18924         
18925         if(label){
18926             label.markInvalid();
18927         }
18928             
18929         if(this.inputType == 'radio'){
18930             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18931                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18932                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
18933             });
18934             
18935             return;
18936         }
18937         
18938         if(!this.groupId){
18939             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18940             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
18941             return;
18942         }
18943         
18944         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18945         
18946         if(!group){
18947             return;
18948         }
18949         
18950         for(var i in group){
18951             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18952             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
18953         }
18954         
18955     }
18956     
18957 });
18958
18959 Roo.apply(Roo.bootstrap.CheckBox, {
18960     
18961     groups: {},
18962     
18963      /**
18964     * register a CheckBox Group
18965     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
18966     */
18967     register : function(checkbox)
18968     {
18969         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
18970             this.groups[checkbox.groupId] = {};
18971         }
18972         
18973         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
18974             return;
18975         }
18976         
18977         this.groups[checkbox.groupId][checkbox.name] = checkbox;
18978         
18979     },
18980     /**
18981     * fetch a CheckBox Group based on the group ID
18982     * @param {string} the group ID
18983     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
18984     */
18985     get: function(groupId) {
18986         if (typeof(this.groups[groupId]) == 'undefined') {
18987             return false;
18988         }
18989         
18990         return this.groups[groupId] ;
18991     }
18992     
18993     
18994 });
18995 /*
18996  * - LGPL
18997  *
18998  * Radio
18999  *
19000  *
19001  * not inline
19002  *<div class="radio">
19003   <label>
19004     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
19005     Option one is this and that&mdash;be sure to include why it's great
19006   </label>
19007 </div>
19008  *
19009  *
19010  *inline
19011  *<span>
19012  *<label class="radio-inline">fieldLabel</label>
19013  *<label class="radio-inline">
19014   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
19015 </label>
19016 <span>
19017  * 
19018  * 
19019  */
19020
19021 /**
19022  * @class Roo.bootstrap.Radio
19023  * @extends Roo.bootstrap.CheckBox
19024  * Bootstrap Radio class
19025
19026  * @constructor
19027  * Create a new Radio
19028  * @param {Object} config The config object
19029  */
19030
19031 Roo.bootstrap.Radio = function(config){
19032     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
19033    
19034 };
19035
19036 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
19037     
19038     inputType: 'radio',
19039     inputValue: '',
19040     valueOff: '',
19041     
19042     getAutoCreate : function()
19043     {
19044         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19045         align = align || 'left'; // default...
19046         
19047         
19048         
19049         var id = Roo.id();
19050         
19051         var cfg = {
19052                 tag : this.inline ? 'span' : 'div',
19053                 cls : '',
19054                 cn : []
19055         };
19056         
19057         var inline = this.inline ? ' radio-inline' : '';
19058         
19059         var lbl = {
19060                 tag: 'label' ,
19061                 // does not need for, as we wrap the input with it..
19062                 'for' : id,
19063                 cls : 'control-label box-label' + inline,
19064                 cn : []
19065         };
19066         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
19067         
19068         var fieldLabel = {
19069             tag: 'label' ,
19070             //cls : 'control-label' + inline,
19071             html : this.fieldLabel,
19072             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
19073         };
19074         
19075  
19076         
19077         
19078         var input =  {
19079             tag: 'input',
19080             id : id,
19081             type : this.inputType,
19082             //value : (!this.checked) ? this.valueOff : this.inputValue,
19083             value : this.inputValue,
19084             cls : 'roo-radio',
19085             placeholder : this.placeholder || '' // ?? needed????
19086             
19087         };
19088         if (this.weight) { // Validity check?
19089             input.cls += " radio-" + this.weight;
19090         }
19091         if (this.disabled) {
19092             input.disabled=true;
19093         }
19094         
19095         if(this.checked){
19096             input.checked = this.checked;
19097         }
19098         
19099         if (this.name) {
19100             input.name = this.name;
19101         }
19102         
19103         if (this.size) {
19104             input.cls += ' input-' + this.size;
19105         }
19106         
19107         //?? can span's inline have a width??
19108         
19109         var settings=this;
19110         ['xs','sm','md','lg'].map(function(size){
19111             if (settings[size]) {
19112                 cfg.cls += ' col-' + size + '-' + settings[size];
19113             }
19114         });
19115         
19116         var inputblock = input;
19117         
19118         if (this.before || this.after) {
19119             
19120             inputblock = {
19121                 cls : 'input-group',
19122                 tag : 'span',
19123                 cn :  [] 
19124             };
19125             if (this.before) {
19126                 inputblock.cn.push({
19127                     tag :'span',
19128                     cls : 'input-group-addon',
19129                     html : this.before
19130                 });
19131             }
19132             inputblock.cn.push(input);
19133             if (this.after) {
19134                 inputblock.cn.push({
19135                     tag :'span',
19136                     cls : 'input-group-addon',
19137                     html : this.after
19138                 });
19139             }
19140             
19141         };
19142         
19143         
19144         if (this.fieldLabel && this.fieldLabel.length) {
19145             cfg.cn.push(fieldLabel);
19146         }
19147        
19148         // normal bootstrap puts the input inside the label.
19149         // however with our styled version - it has to go after the input.
19150        
19151         //lbl.cn.push(inputblock);
19152         
19153         var lblwrap =  {
19154             tag: 'span',
19155             cls: 'radio' + inline,
19156             cn: [
19157                 inputblock,
19158                 lbl
19159             ]
19160         };
19161         
19162         cfg.cn.push( lblwrap);
19163         
19164         if(this.boxLabel){
19165             lbl.cn.push({
19166                 tag: 'span',
19167                 html: this.boxLabel
19168             })
19169         }
19170          
19171         
19172         return cfg;
19173         
19174     },
19175     
19176     initEvents : function()
19177     {
19178 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19179         
19180         this.inputEl().on('click', this.onClick,  this);
19181         if (this.boxLabel) {
19182             //Roo.log('find label');
19183             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
19184         }
19185         
19186     },
19187     
19188     inputEl: function ()
19189     {
19190         return this.el.select('input.roo-radio',true).first();
19191     },
19192     onClick : function()
19193     {   
19194         Roo.log("click");
19195         this.setChecked(true);
19196     },
19197     
19198     setChecked : function(state,suppressEvent)
19199     {
19200         if(state){
19201             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19202                 v.dom.checked = false;
19203             });
19204         }
19205         Roo.log(this.inputEl().dom);
19206         this.checked = state;
19207         this.inputEl().dom.checked = state;
19208         
19209         if(suppressEvent !== true){
19210             this.fireEvent('check', this, state);
19211         }
19212         
19213         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19214         
19215     },
19216     
19217     getGroupValue : function()
19218     {
19219         var value = '';
19220         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19221             if(v.dom.checked == true){
19222                 value = v.dom.value;
19223             }
19224         });
19225         
19226         return value;
19227     },
19228     
19229     /**
19230      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
19231      * @return {Mixed} value The field value
19232      */
19233     getValue : function(){
19234         return this.getGroupValue();
19235     }
19236     
19237 });
19238
19239  
19240 //<script type="text/javascript">
19241
19242 /*
19243  * Based  Ext JS Library 1.1.1
19244  * Copyright(c) 2006-2007, Ext JS, LLC.
19245  * LGPL
19246  *
19247  */
19248  
19249 /**
19250  * @class Roo.HtmlEditorCore
19251  * @extends Roo.Component
19252  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
19253  *
19254  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
19255  */
19256
19257 Roo.HtmlEditorCore = function(config){
19258     
19259     
19260     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
19261     
19262     
19263     this.addEvents({
19264         /**
19265          * @event initialize
19266          * Fires when the editor is fully initialized (including the iframe)
19267          * @param {Roo.HtmlEditorCore} this
19268          */
19269         initialize: true,
19270         /**
19271          * @event activate
19272          * Fires when the editor is first receives the focus. Any insertion must wait
19273          * until after this event.
19274          * @param {Roo.HtmlEditorCore} this
19275          */
19276         activate: true,
19277          /**
19278          * @event beforesync
19279          * Fires before the textarea is updated with content from the editor iframe. Return false
19280          * to cancel the sync.
19281          * @param {Roo.HtmlEditorCore} this
19282          * @param {String} html
19283          */
19284         beforesync: true,
19285          /**
19286          * @event beforepush
19287          * Fires before the iframe editor is updated with content from the textarea. Return false
19288          * to cancel the push.
19289          * @param {Roo.HtmlEditorCore} this
19290          * @param {String} html
19291          */
19292         beforepush: true,
19293          /**
19294          * @event sync
19295          * Fires when the textarea is updated with content from the editor iframe.
19296          * @param {Roo.HtmlEditorCore} this
19297          * @param {String} html
19298          */
19299         sync: true,
19300          /**
19301          * @event push
19302          * Fires when the iframe editor is updated with content from the textarea.
19303          * @param {Roo.HtmlEditorCore} this
19304          * @param {String} html
19305          */
19306         push: true,
19307         
19308         /**
19309          * @event editorevent
19310          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19311          * @param {Roo.HtmlEditorCore} this
19312          */
19313         editorevent: true
19314         
19315     });
19316     
19317     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
19318     
19319     // defaults : white / black...
19320     this.applyBlacklists();
19321     
19322     
19323     
19324 };
19325
19326
19327 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
19328
19329
19330      /**
19331      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
19332      */
19333     
19334     owner : false,
19335     
19336      /**
19337      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
19338      *                        Roo.resizable.
19339      */
19340     resizable : false,
19341      /**
19342      * @cfg {Number} height (in pixels)
19343      */   
19344     height: 300,
19345    /**
19346      * @cfg {Number} width (in pixels)
19347      */   
19348     width: 500,
19349     
19350     /**
19351      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19352      * 
19353      */
19354     stylesheets: false,
19355     
19356     // id of frame..
19357     frameId: false,
19358     
19359     // private properties
19360     validationEvent : false,
19361     deferHeight: true,
19362     initialized : false,
19363     activated : false,
19364     sourceEditMode : false,
19365     onFocus : Roo.emptyFn,
19366     iframePad:3,
19367     hideMode:'offsets',
19368     
19369     clearUp: true,
19370     
19371     // blacklist + whitelisted elements..
19372     black: false,
19373     white: false,
19374      
19375     
19376
19377     /**
19378      * Protected method that will not generally be called directly. It
19379      * is called when the editor initializes the iframe with HTML contents. Override this method if you
19380      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
19381      */
19382     getDocMarkup : function(){
19383         // body styles..
19384         var st = '';
19385         
19386         // inherit styels from page...?? 
19387         if (this.stylesheets === false) {
19388             
19389             Roo.get(document.head).select('style').each(function(node) {
19390                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19391             });
19392             
19393             Roo.get(document.head).select('link').each(function(node) { 
19394                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19395             });
19396             
19397         } else if (!this.stylesheets.length) {
19398                 // simple..
19399                 st = '<style type="text/css">' +
19400                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19401                    '</style>';
19402         } else { 
19403             
19404         }
19405         
19406         st +=  '<style type="text/css">' +
19407             'IMG { cursor: pointer } ' +
19408         '</style>';
19409
19410         
19411         return '<html><head>' + st  +
19412             //<style type="text/css">' +
19413             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19414             //'</style>' +
19415             ' </head><body class="roo-htmleditor-body"></body></html>';
19416     },
19417
19418     // private
19419     onRender : function(ct, position)
19420     {
19421         var _t = this;
19422         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19423         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19424         
19425         
19426         this.el.dom.style.border = '0 none';
19427         this.el.dom.setAttribute('tabIndex', -1);
19428         this.el.addClass('x-hidden hide');
19429         
19430         
19431         
19432         if(Roo.isIE){ // fix IE 1px bogus margin
19433             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19434         }
19435        
19436         
19437         this.frameId = Roo.id();
19438         
19439          
19440         
19441         var iframe = this.owner.wrap.createChild({
19442             tag: 'iframe',
19443             cls: 'form-control', // bootstrap..
19444             id: this.frameId,
19445             name: this.frameId,
19446             frameBorder : 'no',
19447             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
19448         }, this.el
19449         );
19450         
19451         
19452         this.iframe = iframe.dom;
19453
19454          this.assignDocWin();
19455         
19456         this.doc.designMode = 'on';
19457        
19458         this.doc.open();
19459         this.doc.write(this.getDocMarkup());
19460         this.doc.close();
19461
19462         
19463         var task = { // must defer to wait for browser to be ready
19464             run : function(){
19465                 //console.log("run task?" + this.doc.readyState);
19466                 this.assignDocWin();
19467                 if(this.doc.body || this.doc.readyState == 'complete'){
19468                     try {
19469                         this.doc.designMode="on";
19470                     } catch (e) {
19471                         return;
19472                     }
19473                     Roo.TaskMgr.stop(task);
19474                     this.initEditor.defer(10, this);
19475                 }
19476             },
19477             interval : 10,
19478             duration: 10000,
19479             scope: this
19480         };
19481         Roo.TaskMgr.start(task);
19482
19483     },
19484
19485     // private
19486     onResize : function(w, h)
19487     {
19488          Roo.log('resize: ' +w + ',' + h );
19489         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19490         if(!this.iframe){
19491             return;
19492         }
19493         if(typeof w == 'number'){
19494             
19495             this.iframe.style.width = w + 'px';
19496         }
19497         if(typeof h == 'number'){
19498             
19499             this.iframe.style.height = h + 'px';
19500             if(this.doc){
19501                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19502             }
19503         }
19504         
19505     },
19506
19507     /**
19508      * Toggles the editor between standard and source edit mode.
19509      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19510      */
19511     toggleSourceEdit : function(sourceEditMode){
19512         
19513         this.sourceEditMode = sourceEditMode === true;
19514         
19515         if(this.sourceEditMode){
19516  
19517             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
19518             
19519         }else{
19520             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19521             //this.iframe.className = '';
19522             this.deferFocus();
19523         }
19524         //this.setSize(this.owner.wrap.getSize());
19525         //this.fireEvent('editmodechange', this, this.sourceEditMode);
19526     },
19527
19528     
19529   
19530
19531     /**
19532      * Protected method that will not generally be called directly. If you need/want
19533      * custom HTML cleanup, this is the method you should override.
19534      * @param {String} html The HTML to be cleaned
19535      * return {String} The cleaned HTML
19536      */
19537     cleanHtml : function(html){
19538         html = String(html);
19539         if(html.length > 5){
19540             if(Roo.isSafari){ // strip safari nonsense
19541                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19542             }
19543         }
19544         if(html == '&nbsp;'){
19545             html = '';
19546         }
19547         return html;
19548     },
19549
19550     /**
19551      * HTML Editor -> Textarea
19552      * Protected method that will not generally be called directly. Syncs the contents
19553      * of the editor iframe with the textarea.
19554      */
19555     syncValue : function(){
19556         if(this.initialized){
19557             var bd = (this.doc.body || this.doc.documentElement);
19558             //this.cleanUpPaste(); -- this is done else where and causes havoc..
19559             var html = bd.innerHTML;
19560             if(Roo.isSafari){
19561                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19562                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19563                 if(m && m[1]){
19564                     html = '<div style="'+m[0]+'">' + html + '</div>';
19565                 }
19566             }
19567             html = this.cleanHtml(html);
19568             // fix up the special chars.. normaly like back quotes in word...
19569             // however we do not want to do this with chinese..
19570             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19571                 var cc = b.charCodeAt();
19572                 if (
19573                     (cc >= 0x4E00 && cc < 0xA000 ) ||
19574                     (cc >= 0x3400 && cc < 0x4E00 ) ||
19575                     (cc >= 0xf900 && cc < 0xfb00 )
19576                 ) {
19577                         return b;
19578                 }
19579                 return "&#"+cc+";" 
19580             });
19581             if(this.owner.fireEvent('beforesync', this, html) !== false){
19582                 this.el.dom.value = html;
19583                 this.owner.fireEvent('sync', this, html);
19584             }
19585         }
19586     },
19587
19588     /**
19589      * Protected method that will not generally be called directly. Pushes the value of the textarea
19590      * into the iframe editor.
19591      */
19592     pushValue : function(){
19593         if(this.initialized){
19594             var v = this.el.dom.value.trim();
19595             
19596 //            if(v.length < 1){
19597 //                v = '&#160;';
19598 //            }
19599             
19600             if(this.owner.fireEvent('beforepush', this, v) !== false){
19601                 var d = (this.doc.body || this.doc.documentElement);
19602                 d.innerHTML = v;
19603                 this.cleanUpPaste();
19604                 this.el.dom.value = d.innerHTML;
19605                 this.owner.fireEvent('push', this, v);
19606             }
19607         }
19608     },
19609
19610     // private
19611     deferFocus : function(){
19612         this.focus.defer(10, this);
19613     },
19614
19615     // doc'ed in Field
19616     focus : function(){
19617         if(this.win && !this.sourceEditMode){
19618             this.win.focus();
19619         }else{
19620             this.el.focus();
19621         }
19622     },
19623     
19624     assignDocWin: function()
19625     {
19626         var iframe = this.iframe;
19627         
19628          if(Roo.isIE){
19629             this.doc = iframe.contentWindow.document;
19630             this.win = iframe.contentWindow;
19631         } else {
19632 //            if (!Roo.get(this.frameId)) {
19633 //                return;
19634 //            }
19635 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19636 //            this.win = Roo.get(this.frameId).dom.contentWindow;
19637             
19638             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19639                 return;
19640             }
19641             
19642             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19643             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19644         }
19645     },
19646     
19647     // private
19648     initEditor : function(){
19649         //console.log("INIT EDITOR");
19650         this.assignDocWin();
19651         
19652         
19653         
19654         this.doc.designMode="on";
19655         this.doc.open();
19656         this.doc.write(this.getDocMarkup());
19657         this.doc.close();
19658         
19659         var dbody = (this.doc.body || this.doc.documentElement);
19660         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19661         // this copies styles from the containing element into thsi one..
19662         // not sure why we need all of this..
19663         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19664         
19665         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19666         //ss['background-attachment'] = 'fixed'; // w3c
19667         dbody.bgProperties = 'fixed'; // ie
19668         //Roo.DomHelper.applyStyles(dbody, ss);
19669         Roo.EventManager.on(this.doc, {
19670             //'mousedown': this.onEditorEvent,
19671             'mouseup': this.onEditorEvent,
19672             'dblclick': this.onEditorEvent,
19673             'click': this.onEditorEvent,
19674             'keyup': this.onEditorEvent,
19675             buffer:100,
19676             scope: this
19677         });
19678         if(Roo.isGecko){
19679             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19680         }
19681         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19682             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19683         }
19684         this.initialized = true;
19685
19686         this.owner.fireEvent('initialize', this);
19687         this.pushValue();
19688     },
19689
19690     // private
19691     onDestroy : function(){
19692         
19693         
19694         
19695         if(this.rendered){
19696             
19697             //for (var i =0; i < this.toolbars.length;i++) {
19698             //    // fixme - ask toolbars for heights?
19699             //    this.toolbars[i].onDestroy();
19700            // }
19701             
19702             //this.wrap.dom.innerHTML = '';
19703             //this.wrap.remove();
19704         }
19705     },
19706
19707     // private
19708     onFirstFocus : function(){
19709         
19710         this.assignDocWin();
19711         
19712         
19713         this.activated = true;
19714          
19715     
19716         if(Roo.isGecko){ // prevent silly gecko errors
19717             this.win.focus();
19718             var s = this.win.getSelection();
19719             if(!s.focusNode || s.focusNode.nodeType != 3){
19720                 var r = s.getRangeAt(0);
19721                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19722                 r.collapse(true);
19723                 this.deferFocus();
19724             }
19725             try{
19726                 this.execCmd('useCSS', true);
19727                 this.execCmd('styleWithCSS', false);
19728             }catch(e){}
19729         }
19730         this.owner.fireEvent('activate', this);
19731     },
19732
19733     // private
19734     adjustFont: function(btn){
19735         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19736         //if(Roo.isSafari){ // safari
19737         //    adjust *= 2;
19738        // }
19739         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19740         if(Roo.isSafari){ // safari
19741             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19742             v =  (v < 10) ? 10 : v;
19743             v =  (v > 48) ? 48 : v;
19744             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19745             
19746         }
19747         
19748         
19749         v = Math.max(1, v+adjust);
19750         
19751         this.execCmd('FontSize', v  );
19752     },
19753
19754     onEditorEvent : function(e)
19755     {
19756         this.owner.fireEvent('editorevent', this, e);
19757       //  this.updateToolbar();
19758         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19759     },
19760
19761     insertTag : function(tg)
19762     {
19763         // could be a bit smarter... -> wrap the current selected tRoo..
19764         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19765             
19766             range = this.createRange(this.getSelection());
19767             var wrappingNode = this.doc.createElement(tg.toLowerCase());
19768             wrappingNode.appendChild(range.extractContents());
19769             range.insertNode(wrappingNode);
19770
19771             return;
19772             
19773             
19774             
19775         }
19776         this.execCmd("formatblock",   tg);
19777         
19778     },
19779     
19780     insertText : function(txt)
19781     {
19782         
19783         
19784         var range = this.createRange();
19785         range.deleteContents();
19786                //alert(Sender.getAttribute('label'));
19787                
19788         range.insertNode(this.doc.createTextNode(txt));
19789     } ,
19790     
19791      
19792
19793     /**
19794      * Executes a Midas editor command on the editor document and performs necessary focus and
19795      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19796      * @param {String} cmd The Midas command
19797      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19798      */
19799     relayCmd : function(cmd, value){
19800         this.win.focus();
19801         this.execCmd(cmd, value);
19802         this.owner.fireEvent('editorevent', this);
19803         //this.updateToolbar();
19804         this.owner.deferFocus();
19805     },
19806
19807     /**
19808      * Executes a Midas editor command directly on the editor document.
19809      * For visual commands, you should use {@link #relayCmd} instead.
19810      * <b>This should only be called after the editor is initialized.</b>
19811      * @param {String} cmd The Midas command
19812      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19813      */
19814     execCmd : function(cmd, value){
19815         this.doc.execCommand(cmd, false, value === undefined ? null : value);
19816         this.syncValue();
19817     },
19818  
19819  
19820    
19821     /**
19822      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19823      * to insert tRoo.
19824      * @param {String} text | dom node.. 
19825      */
19826     insertAtCursor : function(text)
19827     {
19828         
19829         
19830         
19831         if(!this.activated){
19832             return;
19833         }
19834         /*
19835         if(Roo.isIE){
19836             this.win.focus();
19837             var r = this.doc.selection.createRange();
19838             if(r){
19839                 r.collapse(true);
19840                 r.pasteHTML(text);
19841                 this.syncValue();
19842                 this.deferFocus();
19843             
19844             }
19845             return;
19846         }
19847         */
19848         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19849             this.win.focus();
19850             
19851             
19852             // from jquery ui (MIT licenced)
19853             var range, node;
19854             var win = this.win;
19855             
19856             if (win.getSelection && win.getSelection().getRangeAt) {
19857                 range = win.getSelection().getRangeAt(0);
19858                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19859                 range.insertNode(node);
19860             } else if (win.document.selection && win.document.selection.createRange) {
19861                 // no firefox support
19862                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19863                 win.document.selection.createRange().pasteHTML(txt);
19864             } else {
19865                 // no firefox support
19866                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19867                 this.execCmd('InsertHTML', txt);
19868             } 
19869             
19870             this.syncValue();
19871             
19872             this.deferFocus();
19873         }
19874     },
19875  // private
19876     mozKeyPress : function(e){
19877         if(e.ctrlKey){
19878             var c = e.getCharCode(), cmd;
19879           
19880             if(c > 0){
19881                 c = String.fromCharCode(c).toLowerCase();
19882                 switch(c){
19883                     case 'b':
19884                         cmd = 'bold';
19885                         break;
19886                     case 'i':
19887                         cmd = 'italic';
19888                         break;
19889                     
19890                     case 'u':
19891                         cmd = 'underline';
19892                         break;
19893                     
19894                     case 'v':
19895                         this.cleanUpPaste.defer(100, this);
19896                         return;
19897                         
19898                 }
19899                 if(cmd){
19900                     this.win.focus();
19901                     this.execCmd(cmd);
19902                     this.deferFocus();
19903                     e.preventDefault();
19904                 }
19905                 
19906             }
19907         }
19908     },
19909
19910     // private
19911     fixKeys : function(){ // load time branching for fastest keydown performance
19912         if(Roo.isIE){
19913             return function(e){
19914                 var k = e.getKey(), r;
19915                 if(k == e.TAB){
19916                     e.stopEvent();
19917                     r = this.doc.selection.createRange();
19918                     if(r){
19919                         r.collapse(true);
19920                         r.pasteHTML('&#160;&#160;&#160;&#160;');
19921                         this.deferFocus();
19922                     }
19923                     return;
19924                 }
19925                 
19926                 if(k == e.ENTER){
19927                     r = this.doc.selection.createRange();
19928                     if(r){
19929                         var target = r.parentElement();
19930                         if(!target || target.tagName.toLowerCase() != 'li'){
19931                             e.stopEvent();
19932                             r.pasteHTML('<br />');
19933                             r.collapse(false);
19934                             r.select();
19935                         }
19936                     }
19937                 }
19938                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19939                     this.cleanUpPaste.defer(100, this);
19940                     return;
19941                 }
19942                 
19943                 
19944             };
19945         }else if(Roo.isOpera){
19946             return function(e){
19947                 var k = e.getKey();
19948                 if(k == e.TAB){
19949                     e.stopEvent();
19950                     this.win.focus();
19951                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
19952                     this.deferFocus();
19953                 }
19954                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19955                     this.cleanUpPaste.defer(100, this);
19956                     return;
19957                 }
19958                 
19959             };
19960         }else if(Roo.isSafari){
19961             return function(e){
19962                 var k = e.getKey();
19963                 
19964                 if(k == e.TAB){
19965                     e.stopEvent();
19966                     this.execCmd('InsertText','\t');
19967                     this.deferFocus();
19968                     return;
19969                 }
19970                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19971                     this.cleanUpPaste.defer(100, this);
19972                     return;
19973                 }
19974                 
19975              };
19976         }
19977     }(),
19978     
19979     getAllAncestors: function()
19980     {
19981         var p = this.getSelectedNode();
19982         var a = [];
19983         if (!p) {
19984             a.push(p); // push blank onto stack..
19985             p = this.getParentElement();
19986         }
19987         
19988         
19989         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
19990             a.push(p);
19991             p = p.parentNode;
19992         }
19993         a.push(this.doc.body);
19994         return a;
19995     },
19996     lastSel : false,
19997     lastSelNode : false,
19998     
19999     
20000     getSelection : function() 
20001     {
20002         this.assignDocWin();
20003         return Roo.isIE ? this.doc.selection : this.win.getSelection();
20004     },
20005     
20006     getSelectedNode: function() 
20007     {
20008         // this may only work on Gecko!!!
20009         
20010         // should we cache this!!!!
20011         
20012         
20013         
20014          
20015         var range = this.createRange(this.getSelection()).cloneRange();
20016         
20017         if (Roo.isIE) {
20018             var parent = range.parentElement();
20019             while (true) {
20020                 var testRange = range.duplicate();
20021                 testRange.moveToElementText(parent);
20022                 if (testRange.inRange(range)) {
20023                     break;
20024                 }
20025                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
20026                     break;
20027                 }
20028                 parent = parent.parentElement;
20029             }
20030             return parent;
20031         }
20032         
20033         // is ancestor a text element.
20034         var ac =  range.commonAncestorContainer;
20035         if (ac.nodeType == 3) {
20036             ac = ac.parentNode;
20037         }
20038         
20039         var ar = ac.childNodes;
20040          
20041         var nodes = [];
20042         var other_nodes = [];
20043         var has_other_nodes = false;
20044         for (var i=0;i<ar.length;i++) {
20045             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
20046                 continue;
20047             }
20048             // fullly contained node.
20049             
20050             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
20051                 nodes.push(ar[i]);
20052                 continue;
20053             }
20054             
20055             // probably selected..
20056             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
20057                 other_nodes.push(ar[i]);
20058                 continue;
20059             }
20060             // outer..
20061             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
20062                 continue;
20063             }
20064             
20065             
20066             has_other_nodes = true;
20067         }
20068         if (!nodes.length && other_nodes.length) {
20069             nodes= other_nodes;
20070         }
20071         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
20072             return false;
20073         }
20074         
20075         return nodes[0];
20076     },
20077     createRange: function(sel)
20078     {
20079         // this has strange effects when using with 
20080         // top toolbar - not sure if it's a great idea.
20081         //this.editor.contentWindow.focus();
20082         if (typeof sel != "undefined") {
20083             try {
20084                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
20085             } catch(e) {
20086                 return this.doc.createRange();
20087             }
20088         } else {
20089             return this.doc.createRange();
20090         }
20091     },
20092     getParentElement: function()
20093     {
20094         
20095         this.assignDocWin();
20096         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
20097         
20098         var range = this.createRange(sel);
20099          
20100         try {
20101             var p = range.commonAncestorContainer;
20102             while (p.nodeType == 3) { // text node
20103                 p = p.parentNode;
20104             }
20105             return p;
20106         } catch (e) {
20107             return null;
20108         }
20109     
20110     },
20111     /***
20112      *
20113      * Range intersection.. the hard stuff...
20114      *  '-1' = before
20115      *  '0' = hits..
20116      *  '1' = after.
20117      *         [ -- selected range --- ]
20118      *   [fail]                        [fail]
20119      *
20120      *    basically..
20121      *      if end is before start or  hits it. fail.
20122      *      if start is after end or hits it fail.
20123      *
20124      *   if either hits (but other is outside. - then it's not 
20125      *   
20126      *    
20127      **/
20128     
20129     
20130     // @see http://www.thismuchiknow.co.uk/?p=64.
20131     rangeIntersectsNode : function(range, node)
20132     {
20133         var nodeRange = node.ownerDocument.createRange();
20134         try {
20135             nodeRange.selectNode(node);
20136         } catch (e) {
20137             nodeRange.selectNodeContents(node);
20138         }
20139     
20140         var rangeStartRange = range.cloneRange();
20141         rangeStartRange.collapse(true);
20142     
20143         var rangeEndRange = range.cloneRange();
20144         rangeEndRange.collapse(false);
20145     
20146         var nodeStartRange = nodeRange.cloneRange();
20147         nodeStartRange.collapse(true);
20148     
20149         var nodeEndRange = nodeRange.cloneRange();
20150         nodeEndRange.collapse(false);
20151     
20152         return rangeStartRange.compareBoundaryPoints(
20153                  Range.START_TO_START, nodeEndRange) == -1 &&
20154                rangeEndRange.compareBoundaryPoints(
20155                  Range.START_TO_START, nodeStartRange) == 1;
20156         
20157          
20158     },
20159     rangeCompareNode : function(range, node)
20160     {
20161         var nodeRange = node.ownerDocument.createRange();
20162         try {
20163             nodeRange.selectNode(node);
20164         } catch (e) {
20165             nodeRange.selectNodeContents(node);
20166         }
20167         
20168         
20169         range.collapse(true);
20170     
20171         nodeRange.collapse(true);
20172      
20173         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
20174         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
20175          
20176         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
20177         
20178         var nodeIsBefore   =  ss == 1;
20179         var nodeIsAfter    = ee == -1;
20180         
20181         if (nodeIsBefore && nodeIsAfter) {
20182             return 0; // outer
20183         }
20184         if (!nodeIsBefore && nodeIsAfter) {
20185             return 1; //right trailed.
20186         }
20187         
20188         if (nodeIsBefore && !nodeIsAfter) {
20189             return 2;  // left trailed.
20190         }
20191         // fully contined.
20192         return 3;
20193     },
20194
20195     // private? - in a new class?
20196     cleanUpPaste :  function()
20197     {
20198         // cleans up the whole document..
20199         Roo.log('cleanuppaste');
20200         
20201         this.cleanUpChildren(this.doc.body);
20202         var clean = this.cleanWordChars(this.doc.body.innerHTML);
20203         if (clean != this.doc.body.innerHTML) {
20204             this.doc.body.innerHTML = clean;
20205         }
20206         
20207     },
20208     
20209     cleanWordChars : function(input) {// change the chars to hex code
20210         var he = Roo.HtmlEditorCore;
20211         
20212         var output = input;
20213         Roo.each(he.swapCodes, function(sw) { 
20214             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
20215             
20216             output = output.replace(swapper, sw[1]);
20217         });
20218         
20219         return output;
20220     },
20221     
20222     
20223     cleanUpChildren : function (n)
20224     {
20225         if (!n.childNodes.length) {
20226             return;
20227         }
20228         for (var i = n.childNodes.length-1; i > -1 ; i--) {
20229            this.cleanUpChild(n.childNodes[i]);
20230         }
20231     },
20232     
20233     
20234         
20235     
20236     cleanUpChild : function (node)
20237     {
20238         var ed = this;
20239         //console.log(node);
20240         if (node.nodeName == "#text") {
20241             // clean up silly Windows -- stuff?
20242             return; 
20243         }
20244         if (node.nodeName == "#comment") {
20245             node.parentNode.removeChild(node);
20246             // clean up silly Windows -- stuff?
20247             return; 
20248         }
20249         var lcname = node.tagName.toLowerCase();
20250         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
20251         // whitelist of tags..
20252         
20253         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
20254             // remove node.
20255             node.parentNode.removeChild(node);
20256             return;
20257             
20258         }
20259         
20260         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
20261         
20262         // remove <a name=....> as rendering on yahoo mailer is borked with this.
20263         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
20264         
20265         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
20266         //    remove_keep_children = true;
20267         //}
20268         
20269         if (remove_keep_children) {
20270             this.cleanUpChildren(node);
20271             // inserts everything just before this node...
20272             while (node.childNodes.length) {
20273                 var cn = node.childNodes[0];
20274                 node.removeChild(cn);
20275                 node.parentNode.insertBefore(cn, node);
20276             }
20277             node.parentNode.removeChild(node);
20278             return;
20279         }
20280         
20281         if (!node.attributes || !node.attributes.length) {
20282             this.cleanUpChildren(node);
20283             return;
20284         }
20285         
20286         function cleanAttr(n,v)
20287         {
20288             
20289             if (v.match(/^\./) || v.match(/^\//)) {
20290                 return;
20291             }
20292             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
20293                 return;
20294             }
20295             if (v.match(/^#/)) {
20296                 return;
20297             }
20298 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
20299             node.removeAttribute(n);
20300             
20301         }
20302         
20303         var cwhite = this.cwhite;
20304         var cblack = this.cblack;
20305             
20306         function cleanStyle(n,v)
20307         {
20308             if (v.match(/expression/)) { //XSS?? should we even bother..
20309                 node.removeAttribute(n);
20310                 return;
20311             }
20312             
20313             var parts = v.split(/;/);
20314             var clean = [];
20315             
20316             Roo.each(parts, function(p) {
20317                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
20318                 if (!p.length) {
20319                     return true;
20320                 }
20321                 var l = p.split(':').shift().replace(/\s+/g,'');
20322                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
20323                 
20324                 if ( cwhite.length && cblack.indexOf(l) > -1) {
20325 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20326                     //node.removeAttribute(n);
20327                     return true;
20328                 }
20329                 //Roo.log()
20330                 // only allow 'c whitelisted system attributes'
20331                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
20332 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20333                     //node.removeAttribute(n);
20334                     return true;
20335                 }
20336                 
20337                 
20338                  
20339                 
20340                 clean.push(p);
20341                 return true;
20342             });
20343             if (clean.length) { 
20344                 node.setAttribute(n, clean.join(';'));
20345             } else {
20346                 node.removeAttribute(n);
20347             }
20348             
20349         }
20350         
20351         
20352         for (var i = node.attributes.length-1; i > -1 ; i--) {
20353             var a = node.attributes[i];
20354             //console.log(a);
20355             
20356             if (a.name.toLowerCase().substr(0,2)=='on')  {
20357                 node.removeAttribute(a.name);
20358                 continue;
20359             }
20360             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
20361                 node.removeAttribute(a.name);
20362                 continue;
20363             }
20364             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
20365                 cleanAttr(a.name,a.value); // fixme..
20366                 continue;
20367             }
20368             if (a.name == 'style') {
20369                 cleanStyle(a.name,a.value);
20370                 continue;
20371             }
20372             /// clean up MS crap..
20373             // tecnically this should be a list of valid class'es..
20374             
20375             
20376             if (a.name == 'class') {
20377                 if (a.value.match(/^Mso/)) {
20378                     node.className = '';
20379                 }
20380                 
20381                 if (a.value.match(/body/)) {
20382                     node.className = '';
20383                 }
20384                 continue;
20385             }
20386             
20387             // style cleanup!?
20388             // class cleanup?
20389             
20390         }
20391         
20392         
20393         this.cleanUpChildren(node);
20394         
20395         
20396     },
20397     
20398     /**
20399      * Clean up MS wordisms...
20400      */
20401     cleanWord : function(node)
20402     {
20403         
20404         
20405         if (!node) {
20406             this.cleanWord(this.doc.body);
20407             return;
20408         }
20409         if (node.nodeName == "#text") {
20410             // clean up silly Windows -- stuff?
20411             return; 
20412         }
20413         if (node.nodeName == "#comment") {
20414             node.parentNode.removeChild(node);
20415             // clean up silly Windows -- stuff?
20416             return; 
20417         }
20418         
20419         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
20420             node.parentNode.removeChild(node);
20421             return;
20422         }
20423         
20424         // remove - but keep children..
20425         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20426             while (node.childNodes.length) {
20427                 var cn = node.childNodes[0];
20428                 node.removeChild(cn);
20429                 node.parentNode.insertBefore(cn, node);
20430             }
20431             node.parentNode.removeChild(node);
20432             this.iterateChildren(node, this.cleanWord);
20433             return;
20434         }
20435         // clean styles
20436         if (node.className.length) {
20437             
20438             var cn = node.className.split(/\W+/);
20439             var cna = [];
20440             Roo.each(cn, function(cls) {
20441                 if (cls.match(/Mso[a-zA-Z]+/)) {
20442                     return;
20443                 }
20444                 cna.push(cls);
20445             });
20446             node.className = cna.length ? cna.join(' ') : '';
20447             if (!cna.length) {
20448                 node.removeAttribute("class");
20449             }
20450         }
20451         
20452         if (node.hasAttribute("lang")) {
20453             node.removeAttribute("lang");
20454         }
20455         
20456         if (node.hasAttribute("style")) {
20457             
20458             var styles = node.getAttribute("style").split(";");
20459             var nstyle = [];
20460             Roo.each(styles, function(s) {
20461                 if (!s.match(/:/)) {
20462                     return;
20463                 }
20464                 var kv = s.split(":");
20465                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20466                     return;
20467                 }
20468                 // what ever is left... we allow.
20469                 nstyle.push(s);
20470             });
20471             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20472             if (!nstyle.length) {
20473                 node.removeAttribute('style');
20474             }
20475         }
20476         this.iterateChildren(node, this.cleanWord);
20477         
20478         
20479         
20480     },
20481     /**
20482      * iterateChildren of a Node, calling fn each time, using this as the scole..
20483      * @param {DomNode} node node to iterate children of.
20484      * @param {Function} fn method of this class to call on each item.
20485      */
20486     iterateChildren : function(node, fn)
20487     {
20488         if (!node.childNodes.length) {
20489                 return;
20490         }
20491         for (var i = node.childNodes.length-1; i > -1 ; i--) {
20492            fn.call(this, node.childNodes[i])
20493         }
20494     },
20495     
20496     
20497     /**
20498      * cleanTableWidths.
20499      *
20500      * Quite often pasting from word etc.. results in tables with column and widths.
20501      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20502      *
20503      */
20504     cleanTableWidths : function(node)
20505     {
20506          
20507          
20508         if (!node) {
20509             this.cleanTableWidths(this.doc.body);
20510             return;
20511         }
20512         
20513         // ignore list...
20514         if (node.nodeName == "#text" || node.nodeName == "#comment") {
20515             return; 
20516         }
20517         Roo.log(node.tagName);
20518         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20519             this.iterateChildren(node, this.cleanTableWidths);
20520             return;
20521         }
20522         if (node.hasAttribute('width')) {
20523             node.removeAttribute('width');
20524         }
20525         
20526          
20527         if (node.hasAttribute("style")) {
20528             // pretty basic...
20529             
20530             var styles = node.getAttribute("style").split(";");
20531             var nstyle = [];
20532             Roo.each(styles, function(s) {
20533                 if (!s.match(/:/)) {
20534                     return;
20535                 }
20536                 var kv = s.split(":");
20537                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20538                     return;
20539                 }
20540                 // what ever is left... we allow.
20541                 nstyle.push(s);
20542             });
20543             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20544             if (!nstyle.length) {
20545                 node.removeAttribute('style');
20546             }
20547         }
20548         
20549         this.iterateChildren(node, this.cleanTableWidths);
20550         
20551         
20552     },
20553     
20554     
20555     
20556     
20557     domToHTML : function(currentElement, depth, nopadtext) {
20558         
20559         depth = depth || 0;
20560         nopadtext = nopadtext || false;
20561     
20562         if (!currentElement) {
20563             return this.domToHTML(this.doc.body);
20564         }
20565         
20566         //Roo.log(currentElement);
20567         var j;
20568         var allText = false;
20569         var nodeName = currentElement.nodeName;
20570         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20571         
20572         if  (nodeName == '#text') {
20573             
20574             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20575         }
20576         
20577         
20578         var ret = '';
20579         if (nodeName != 'BODY') {
20580              
20581             var i = 0;
20582             // Prints the node tagName, such as <A>, <IMG>, etc
20583             if (tagName) {
20584                 var attr = [];
20585                 for(i = 0; i < currentElement.attributes.length;i++) {
20586                     // quoting?
20587                     var aname = currentElement.attributes.item(i).name;
20588                     if (!currentElement.attributes.item(i).value.length) {
20589                         continue;
20590                     }
20591                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20592                 }
20593                 
20594                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20595             } 
20596             else {
20597                 
20598                 // eack
20599             }
20600         } else {
20601             tagName = false;
20602         }
20603         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20604             return ret;
20605         }
20606         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20607             nopadtext = true;
20608         }
20609         
20610         
20611         // Traverse the tree
20612         i = 0;
20613         var currentElementChild = currentElement.childNodes.item(i);
20614         var allText = true;
20615         var innerHTML  = '';
20616         lastnode = '';
20617         while (currentElementChild) {
20618             // Formatting code (indent the tree so it looks nice on the screen)
20619             var nopad = nopadtext;
20620             if (lastnode == 'SPAN') {
20621                 nopad  = true;
20622             }
20623             // text
20624             if  (currentElementChild.nodeName == '#text') {
20625                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20626                 toadd = nopadtext ? toadd : toadd.trim();
20627                 if (!nopad && toadd.length > 80) {
20628                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
20629                 }
20630                 innerHTML  += toadd;
20631                 
20632                 i++;
20633                 currentElementChild = currentElement.childNodes.item(i);
20634                 lastNode = '';
20635                 continue;
20636             }
20637             allText = false;
20638             
20639             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
20640                 
20641             // Recursively traverse the tree structure of the child node
20642             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
20643             lastnode = currentElementChild.nodeName;
20644             i++;
20645             currentElementChild=currentElement.childNodes.item(i);
20646         }
20647         
20648         ret += innerHTML;
20649         
20650         if (!allText) {
20651                 // The remaining code is mostly for formatting the tree
20652             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
20653         }
20654         
20655         
20656         if (tagName) {
20657             ret+= "</"+tagName+">";
20658         }
20659         return ret;
20660         
20661     },
20662         
20663     applyBlacklists : function()
20664     {
20665         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
20666         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
20667         
20668         this.white = [];
20669         this.black = [];
20670         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20671             if (b.indexOf(tag) > -1) {
20672                 return;
20673             }
20674             this.white.push(tag);
20675             
20676         }, this);
20677         
20678         Roo.each(w, function(tag) {
20679             if (b.indexOf(tag) > -1) {
20680                 return;
20681             }
20682             if (this.white.indexOf(tag) > -1) {
20683                 return;
20684             }
20685             this.white.push(tag);
20686             
20687         }, this);
20688         
20689         
20690         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20691             if (w.indexOf(tag) > -1) {
20692                 return;
20693             }
20694             this.black.push(tag);
20695             
20696         }, this);
20697         
20698         Roo.each(b, function(tag) {
20699             if (w.indexOf(tag) > -1) {
20700                 return;
20701             }
20702             if (this.black.indexOf(tag) > -1) {
20703                 return;
20704             }
20705             this.black.push(tag);
20706             
20707         }, this);
20708         
20709         
20710         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
20711         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
20712         
20713         this.cwhite = [];
20714         this.cblack = [];
20715         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20716             if (b.indexOf(tag) > -1) {
20717                 return;
20718             }
20719             this.cwhite.push(tag);
20720             
20721         }, this);
20722         
20723         Roo.each(w, function(tag) {
20724             if (b.indexOf(tag) > -1) {
20725                 return;
20726             }
20727             if (this.cwhite.indexOf(tag) > -1) {
20728                 return;
20729             }
20730             this.cwhite.push(tag);
20731             
20732         }, this);
20733         
20734         
20735         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20736             if (w.indexOf(tag) > -1) {
20737                 return;
20738             }
20739             this.cblack.push(tag);
20740             
20741         }, this);
20742         
20743         Roo.each(b, function(tag) {
20744             if (w.indexOf(tag) > -1) {
20745                 return;
20746             }
20747             if (this.cblack.indexOf(tag) > -1) {
20748                 return;
20749             }
20750             this.cblack.push(tag);
20751             
20752         }, this);
20753     },
20754     
20755     setStylesheets : function(stylesheets)
20756     {
20757         if(typeof(stylesheets) == 'string'){
20758             Roo.get(this.iframe.contentDocument.head).createChild({
20759                 tag : 'link',
20760                 rel : 'stylesheet',
20761                 type : 'text/css',
20762                 href : stylesheets
20763             });
20764             
20765             return;
20766         }
20767         var _this = this;
20768      
20769         Roo.each(stylesheets, function(s) {
20770             if(!s.length){
20771                 return;
20772             }
20773             
20774             Roo.get(_this.iframe.contentDocument.head).createChild({
20775                 tag : 'link',
20776                 rel : 'stylesheet',
20777                 type : 'text/css',
20778                 href : s
20779             });
20780         });
20781
20782         
20783     },
20784     
20785     removeStylesheets : function()
20786     {
20787         var _this = this;
20788         
20789         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20790             s.remove();
20791         });
20792     }
20793     
20794     // hide stuff that is not compatible
20795     /**
20796      * @event blur
20797      * @hide
20798      */
20799     /**
20800      * @event change
20801      * @hide
20802      */
20803     /**
20804      * @event focus
20805      * @hide
20806      */
20807     /**
20808      * @event specialkey
20809      * @hide
20810      */
20811     /**
20812      * @cfg {String} fieldClass @hide
20813      */
20814     /**
20815      * @cfg {String} focusClass @hide
20816      */
20817     /**
20818      * @cfg {String} autoCreate @hide
20819      */
20820     /**
20821      * @cfg {String} inputType @hide
20822      */
20823     /**
20824      * @cfg {String} invalidClass @hide
20825      */
20826     /**
20827      * @cfg {String} invalidText @hide
20828      */
20829     /**
20830      * @cfg {String} msgFx @hide
20831      */
20832     /**
20833      * @cfg {String} validateOnBlur @hide
20834      */
20835 });
20836
20837 Roo.HtmlEditorCore.white = [
20838         'area', 'br', 'img', 'input', 'hr', 'wbr',
20839         
20840        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
20841        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
20842        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
20843        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
20844        'table',   'ul',         'xmp', 
20845        
20846        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
20847       'thead',   'tr', 
20848      
20849       'dir', 'menu', 'ol', 'ul', 'dl',
20850        
20851       'embed',  'object'
20852 ];
20853
20854
20855 Roo.HtmlEditorCore.black = [
20856     //    'embed',  'object', // enable - backend responsiblity to clean thiese
20857         'applet', // 
20858         'base',   'basefont', 'bgsound', 'blink',  'body', 
20859         'frame',  'frameset', 'head',    'html',   'ilayer', 
20860         'iframe', 'layer',  'link',     'meta',    'object',   
20861         'script', 'style' ,'title',  'xml' // clean later..
20862 ];
20863 Roo.HtmlEditorCore.clean = [
20864     'script', 'style', 'title', 'xml'
20865 ];
20866 Roo.HtmlEditorCore.remove = [
20867     'font'
20868 ];
20869 // attributes..
20870
20871 Roo.HtmlEditorCore.ablack = [
20872     'on'
20873 ];
20874     
20875 Roo.HtmlEditorCore.aclean = [ 
20876     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
20877 ];
20878
20879 // protocols..
20880 Roo.HtmlEditorCore.pwhite= [
20881         'http',  'https',  'mailto'
20882 ];
20883
20884 // white listed style attributes.
20885 Roo.HtmlEditorCore.cwhite= [
20886       //  'text-align', /// default is to allow most things..
20887       
20888          
20889 //        'font-size'//??
20890 ];
20891
20892 // black listed style attributes.
20893 Roo.HtmlEditorCore.cblack= [
20894       //  'font-size' -- this can be set by the project 
20895 ];
20896
20897
20898 Roo.HtmlEditorCore.swapCodes   =[ 
20899     [    8211, "--" ], 
20900     [    8212, "--" ], 
20901     [    8216,  "'" ],  
20902     [    8217, "'" ],  
20903     [    8220, '"' ],  
20904     [    8221, '"' ],  
20905     [    8226, "*" ],  
20906     [    8230, "..." ]
20907 ]; 
20908
20909     /*
20910  * - LGPL
20911  *
20912  * HtmlEditor
20913  * 
20914  */
20915
20916 /**
20917  * @class Roo.bootstrap.HtmlEditor
20918  * @extends Roo.bootstrap.TextArea
20919  * Bootstrap HtmlEditor class
20920
20921  * @constructor
20922  * Create a new HtmlEditor
20923  * @param {Object} config The config object
20924  */
20925
20926 Roo.bootstrap.HtmlEditor = function(config){
20927     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
20928     if (!this.toolbars) {
20929         this.toolbars = [];
20930     }
20931     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
20932     this.addEvents({
20933             /**
20934              * @event initialize
20935              * Fires when the editor is fully initialized (including the iframe)
20936              * @param {HtmlEditor} this
20937              */
20938             initialize: true,
20939             /**
20940              * @event activate
20941              * Fires when the editor is first receives the focus. Any insertion must wait
20942              * until after this event.
20943              * @param {HtmlEditor} this
20944              */
20945             activate: true,
20946              /**
20947              * @event beforesync
20948              * Fires before the textarea is updated with content from the editor iframe. Return false
20949              * to cancel the sync.
20950              * @param {HtmlEditor} this
20951              * @param {String} html
20952              */
20953             beforesync: true,
20954              /**
20955              * @event beforepush
20956              * Fires before the iframe editor is updated with content from the textarea. Return false
20957              * to cancel the push.
20958              * @param {HtmlEditor} this
20959              * @param {String} html
20960              */
20961             beforepush: true,
20962              /**
20963              * @event sync
20964              * Fires when the textarea is updated with content from the editor iframe.
20965              * @param {HtmlEditor} this
20966              * @param {String} html
20967              */
20968             sync: true,
20969              /**
20970              * @event push
20971              * Fires when the iframe editor is updated with content from the textarea.
20972              * @param {HtmlEditor} this
20973              * @param {String} html
20974              */
20975             push: true,
20976              /**
20977              * @event editmodechange
20978              * Fires when the editor switches edit modes
20979              * @param {HtmlEditor} this
20980              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
20981              */
20982             editmodechange: true,
20983             /**
20984              * @event editorevent
20985              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20986              * @param {HtmlEditor} this
20987              */
20988             editorevent: true,
20989             /**
20990              * @event firstfocus
20991              * Fires when on first focus - needed by toolbars..
20992              * @param {HtmlEditor} this
20993              */
20994             firstfocus: true,
20995             /**
20996              * @event autosave
20997              * Auto save the htmlEditor value as a file into Events
20998              * @param {HtmlEditor} this
20999              */
21000             autosave: true,
21001             /**
21002              * @event savedpreview
21003              * preview the saved version of htmlEditor
21004              * @param {HtmlEditor} this
21005              */
21006             savedpreview: true
21007         });
21008 };
21009
21010
21011 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
21012     
21013     
21014       /**
21015      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
21016      */
21017     toolbars : false,
21018    
21019      /**
21020      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21021      *                        Roo.resizable.
21022      */
21023     resizable : false,
21024      /**
21025      * @cfg {Number} height (in pixels)
21026      */   
21027     height: 300,
21028    /**
21029      * @cfg {Number} width (in pixels)
21030      */   
21031     width: false,
21032     
21033     /**
21034      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21035      * 
21036      */
21037     stylesheets: false,
21038     
21039     // id of frame..
21040     frameId: false,
21041     
21042     // private properties
21043     validationEvent : false,
21044     deferHeight: true,
21045     initialized : false,
21046     activated : false,
21047     
21048     onFocus : Roo.emptyFn,
21049     iframePad:3,
21050     hideMode:'offsets',
21051     
21052     
21053     tbContainer : false,
21054     
21055     toolbarContainer :function() {
21056         return this.wrap.select('.x-html-editor-tb',true).first();
21057     },
21058
21059     /**
21060      * Protected method that will not generally be called directly. It
21061      * is called when the editor creates its toolbar. Override this method if you need to
21062      * add custom toolbar buttons.
21063      * @param {HtmlEditor} editor
21064      */
21065     createToolbar : function(){
21066         
21067         Roo.log("create toolbars");
21068         
21069         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
21070         this.toolbars[0].render(this.toolbarContainer());
21071         
21072         return;
21073         
21074 //        if (!editor.toolbars || !editor.toolbars.length) {
21075 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
21076 //        }
21077 //        
21078 //        for (var i =0 ; i < editor.toolbars.length;i++) {
21079 //            editor.toolbars[i] = Roo.factory(
21080 //                    typeof(editor.toolbars[i]) == 'string' ?
21081 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
21082 //                Roo.bootstrap.HtmlEditor);
21083 //            editor.toolbars[i].init(editor);
21084 //        }
21085     },
21086
21087      
21088     // private
21089     onRender : function(ct, position)
21090     {
21091        // Roo.log("Call onRender: " + this.xtype);
21092         var _t = this;
21093         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
21094       
21095         this.wrap = this.inputEl().wrap({
21096             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
21097         });
21098         
21099         this.editorcore.onRender(ct, position);
21100          
21101         if (this.resizable) {
21102             this.resizeEl = new Roo.Resizable(this.wrap, {
21103                 pinned : true,
21104                 wrap: true,
21105                 dynamic : true,
21106                 minHeight : this.height,
21107                 height: this.height,
21108                 handles : this.resizable,
21109                 width: this.width,
21110                 listeners : {
21111                     resize : function(r, w, h) {
21112                         _t.onResize(w,h); // -something
21113                     }
21114                 }
21115             });
21116             
21117         }
21118         this.createToolbar(this);
21119        
21120         
21121         if(!this.width && this.resizable){
21122             this.setSize(this.wrap.getSize());
21123         }
21124         if (this.resizeEl) {
21125             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
21126             // should trigger onReize..
21127         }
21128         
21129     },
21130
21131     // private
21132     onResize : function(w, h)
21133     {
21134         Roo.log('resize: ' +w + ',' + h );
21135         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
21136         var ew = false;
21137         var eh = false;
21138         
21139         if(this.inputEl() ){
21140             if(typeof w == 'number'){
21141                 var aw = w - this.wrap.getFrameWidth('lr');
21142                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
21143                 ew = aw;
21144             }
21145             if(typeof h == 'number'){
21146                  var tbh = -11;  // fixme it needs to tool bar size!
21147                 for (var i =0; i < this.toolbars.length;i++) {
21148                     // fixme - ask toolbars for heights?
21149                     tbh += this.toolbars[i].el.getHeight();
21150                     //if (this.toolbars[i].footer) {
21151                     //    tbh += this.toolbars[i].footer.el.getHeight();
21152                     //}
21153                 }
21154               
21155                 
21156                 
21157                 
21158                 
21159                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
21160                 ah -= 5; // knock a few pixes off for look..
21161                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
21162                 var eh = ah;
21163             }
21164         }
21165         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
21166         this.editorcore.onResize(ew,eh);
21167         
21168     },
21169
21170     /**
21171      * Toggles the editor between standard and source edit mode.
21172      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21173      */
21174     toggleSourceEdit : function(sourceEditMode)
21175     {
21176         this.editorcore.toggleSourceEdit(sourceEditMode);
21177         
21178         if(this.editorcore.sourceEditMode){
21179             Roo.log('editor - showing textarea');
21180             
21181 //            Roo.log('in');
21182 //            Roo.log(this.syncValue());
21183             this.syncValue();
21184             this.inputEl().removeClass(['hide', 'x-hidden']);
21185             this.inputEl().dom.removeAttribute('tabIndex');
21186             this.inputEl().focus();
21187         }else{
21188             Roo.log('editor - hiding textarea');
21189 //            Roo.log('out')
21190 //            Roo.log(this.pushValue()); 
21191             this.pushValue();
21192             
21193             this.inputEl().addClass(['hide', 'x-hidden']);
21194             this.inputEl().dom.setAttribute('tabIndex', -1);
21195             //this.deferFocus();
21196         }
21197          
21198         if(this.resizable){
21199             this.setSize(this.wrap.getSize());
21200         }
21201         
21202         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
21203     },
21204  
21205     // private (for BoxComponent)
21206     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21207
21208     // private (for BoxComponent)
21209     getResizeEl : function(){
21210         return this.wrap;
21211     },
21212
21213     // private (for BoxComponent)
21214     getPositionEl : function(){
21215         return this.wrap;
21216     },
21217
21218     // private
21219     initEvents : function(){
21220         this.originalValue = this.getValue();
21221     },
21222
21223 //    /**
21224 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21225 //     * @method
21226 //     */
21227 //    markInvalid : Roo.emptyFn,
21228 //    /**
21229 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21230 //     * @method
21231 //     */
21232 //    clearInvalid : Roo.emptyFn,
21233
21234     setValue : function(v){
21235         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
21236         this.editorcore.pushValue();
21237     },
21238
21239      
21240     // private
21241     deferFocus : function(){
21242         this.focus.defer(10, this);
21243     },
21244
21245     // doc'ed in Field
21246     focus : function(){
21247         this.editorcore.focus();
21248         
21249     },
21250       
21251
21252     // private
21253     onDestroy : function(){
21254         
21255         
21256         
21257         if(this.rendered){
21258             
21259             for (var i =0; i < this.toolbars.length;i++) {
21260                 // fixme - ask toolbars for heights?
21261                 this.toolbars[i].onDestroy();
21262             }
21263             
21264             this.wrap.dom.innerHTML = '';
21265             this.wrap.remove();
21266         }
21267     },
21268
21269     // private
21270     onFirstFocus : function(){
21271         //Roo.log("onFirstFocus");
21272         this.editorcore.onFirstFocus();
21273          for (var i =0; i < this.toolbars.length;i++) {
21274             this.toolbars[i].onFirstFocus();
21275         }
21276         
21277     },
21278     
21279     // private
21280     syncValue : function()
21281     {   
21282         this.editorcore.syncValue();
21283     },
21284     
21285     pushValue : function()
21286     {   
21287         this.editorcore.pushValue();
21288     }
21289      
21290     
21291     // hide stuff that is not compatible
21292     /**
21293      * @event blur
21294      * @hide
21295      */
21296     /**
21297      * @event change
21298      * @hide
21299      */
21300     /**
21301      * @event focus
21302      * @hide
21303      */
21304     /**
21305      * @event specialkey
21306      * @hide
21307      */
21308     /**
21309      * @cfg {String} fieldClass @hide
21310      */
21311     /**
21312      * @cfg {String} focusClass @hide
21313      */
21314     /**
21315      * @cfg {String} autoCreate @hide
21316      */
21317     /**
21318      * @cfg {String} inputType @hide
21319      */
21320     /**
21321      * @cfg {String} invalidClass @hide
21322      */
21323     /**
21324      * @cfg {String} invalidText @hide
21325      */
21326     /**
21327      * @cfg {String} msgFx @hide
21328      */
21329     /**
21330      * @cfg {String} validateOnBlur @hide
21331      */
21332 });
21333  
21334     
21335    
21336    
21337    
21338       
21339 Roo.namespace('Roo.bootstrap.htmleditor');
21340 /**
21341  * @class Roo.bootstrap.HtmlEditorToolbar1
21342  * Basic Toolbar
21343  * 
21344  * Usage:
21345  *
21346  new Roo.bootstrap.HtmlEditor({
21347     ....
21348     toolbars : [
21349         new Roo.bootstrap.HtmlEditorToolbar1({
21350             disable : { fonts: 1 , format: 1, ..., ... , ...],
21351             btns : [ .... ]
21352         })
21353     }
21354      
21355  * 
21356  * @cfg {Object} disable List of elements to disable..
21357  * @cfg {Array} btns List of additional buttons.
21358  * 
21359  * 
21360  * NEEDS Extra CSS? 
21361  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
21362  */
21363  
21364 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
21365 {
21366     
21367     Roo.apply(this, config);
21368     
21369     // default disabled, based on 'good practice'..
21370     this.disable = this.disable || {};
21371     Roo.applyIf(this.disable, {
21372         fontSize : true,
21373         colors : true,
21374         specialElements : true
21375     });
21376     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
21377     
21378     this.editor = config.editor;
21379     this.editorcore = config.editor.editorcore;
21380     
21381     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
21382     
21383     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
21384     // dont call parent... till later.
21385 }
21386 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
21387      
21388     bar : true,
21389     
21390     editor : false,
21391     editorcore : false,
21392     
21393     
21394     formats : [
21395         "p" ,  
21396         "h1","h2","h3","h4","h5","h6", 
21397         "pre", "code", 
21398         "abbr", "acronym", "address", "cite", "samp", "var",
21399         'div','span'
21400     ],
21401     
21402     onRender : function(ct, position)
21403     {
21404        // Roo.log("Call onRender: " + this.xtype);
21405         
21406        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
21407        Roo.log(this.el);
21408        this.el.dom.style.marginBottom = '0';
21409        var _this = this;
21410        var editorcore = this.editorcore;
21411        var editor= this.editor;
21412        
21413        var children = [];
21414        var btn = function(id,cmd , toggle, handler){
21415        
21416             var  event = toggle ? 'toggle' : 'click';
21417        
21418             var a = {
21419                 size : 'sm',
21420                 xtype: 'Button',
21421                 xns: Roo.bootstrap,
21422                 glyphicon : id,
21423                 cmd : id || cmd,
21424                 enableToggle:toggle !== false,
21425                 //html : 'submit'
21426                 pressed : toggle ? false : null,
21427                 listeners : {}
21428             };
21429             a.listeners[toggle ? 'toggle' : 'click'] = function() {
21430                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
21431             };
21432             children.push(a);
21433             return a;
21434        }
21435         
21436         var style = {
21437                 xtype: 'Button',
21438                 size : 'sm',
21439                 xns: Roo.bootstrap,
21440                 glyphicon : 'font',
21441                 //html : 'submit'
21442                 menu : {
21443                     xtype: 'Menu',
21444                     xns: Roo.bootstrap,
21445                     items:  []
21446                 }
21447         };
21448         Roo.each(this.formats, function(f) {
21449             style.menu.items.push({
21450                 xtype :'MenuItem',
21451                 xns: Roo.bootstrap,
21452                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21453                 tagname : f,
21454                 listeners : {
21455                     click : function()
21456                     {
21457                         editorcore.insertTag(this.tagname);
21458                         editor.focus();
21459                     }
21460                 }
21461                 
21462             });
21463         });
21464          children.push(style);   
21465             
21466             
21467         btn('bold',false,true);
21468         btn('italic',false,true);
21469         btn('align-left', 'justifyleft',true);
21470         btn('align-center', 'justifycenter',true);
21471         btn('align-right' , 'justifyright',true);
21472         btn('link', false, false, function(btn) {
21473             //Roo.log("create link?");
21474             var url = prompt(this.createLinkText, this.defaultLinkValue);
21475             if(url && url != 'http:/'+'/'){
21476                 this.editorcore.relayCmd('createlink', url);
21477             }
21478         }),
21479         btn('list','insertunorderedlist',true);
21480         btn('pencil', false,true, function(btn){
21481                 Roo.log(this);
21482                 
21483                 this.toggleSourceEdit(btn.pressed);
21484         });
21485         /*
21486         var cog = {
21487                 xtype: 'Button',
21488                 size : 'sm',
21489                 xns: Roo.bootstrap,
21490                 glyphicon : 'cog',
21491                 //html : 'submit'
21492                 menu : {
21493                     xtype: 'Menu',
21494                     xns: Roo.bootstrap,
21495                     items:  []
21496                 }
21497         };
21498         
21499         cog.menu.items.push({
21500             xtype :'MenuItem',
21501             xns: Roo.bootstrap,
21502             html : Clean styles,
21503             tagname : f,
21504             listeners : {
21505                 click : function()
21506                 {
21507                     editorcore.insertTag(this.tagname);
21508                     editor.focus();
21509                 }
21510             }
21511             
21512         });
21513        */
21514         
21515          
21516        this.xtype = 'NavSimplebar';
21517         
21518         for(var i=0;i< children.length;i++) {
21519             
21520             this.buttons.add(this.addxtypeChild(children[i]));
21521             
21522         }
21523         
21524         editor.on('editorevent', this.updateToolbar, this);
21525     },
21526     onBtnClick : function(id)
21527     {
21528        this.editorcore.relayCmd(id);
21529        this.editorcore.focus();
21530     },
21531     
21532     /**
21533      * Protected method that will not generally be called directly. It triggers
21534      * a toolbar update by reading the markup state of the current selection in the editor.
21535      */
21536     updateToolbar: function(){
21537
21538         if(!this.editorcore.activated){
21539             this.editor.onFirstFocus(); // is this neeed?
21540             return;
21541         }
21542
21543         var btns = this.buttons; 
21544         var doc = this.editorcore.doc;
21545         btns.get('bold').setActive(doc.queryCommandState('bold'));
21546         btns.get('italic').setActive(doc.queryCommandState('italic'));
21547         //btns.get('underline').setActive(doc.queryCommandState('underline'));
21548         
21549         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21550         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21551         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21552         
21553         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21554         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21555          /*
21556         
21557         var ans = this.editorcore.getAllAncestors();
21558         if (this.formatCombo) {
21559             
21560             
21561             var store = this.formatCombo.store;
21562             this.formatCombo.setValue("");
21563             for (var i =0; i < ans.length;i++) {
21564                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21565                     // select it..
21566                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21567                     break;
21568                 }
21569             }
21570         }
21571         
21572         
21573         
21574         // hides menus... - so this cant be on a menu...
21575         Roo.bootstrap.MenuMgr.hideAll();
21576         */
21577         Roo.bootstrap.MenuMgr.hideAll();
21578         //this.editorsyncValue();
21579     },
21580     onFirstFocus: function() {
21581         this.buttons.each(function(item){
21582            item.enable();
21583         });
21584     },
21585     toggleSourceEdit : function(sourceEditMode){
21586         
21587           
21588         if(sourceEditMode){
21589             Roo.log("disabling buttons");
21590            this.buttons.each( function(item){
21591                 if(item.cmd != 'pencil'){
21592                     item.disable();
21593                 }
21594             });
21595           
21596         }else{
21597             Roo.log("enabling buttons");
21598             if(this.editorcore.initialized){
21599                 this.buttons.each( function(item){
21600                     item.enable();
21601                 });
21602             }
21603             
21604         }
21605         Roo.log("calling toggole on editor");
21606         // tell the editor that it's been pressed..
21607         this.editor.toggleSourceEdit(sourceEditMode);
21608        
21609     }
21610 });
21611
21612
21613
21614
21615
21616 /**
21617  * @class Roo.bootstrap.Table.AbstractSelectionModel
21618  * @extends Roo.util.Observable
21619  * Abstract base class for grid SelectionModels.  It provides the interface that should be
21620  * implemented by descendant classes.  This class should not be directly instantiated.
21621  * @constructor
21622  */
21623 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21624     this.locked = false;
21625     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21626 };
21627
21628
21629 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
21630     /** @ignore Called by the grid automatically. Do not call directly. */
21631     init : function(grid){
21632         this.grid = grid;
21633         this.initEvents();
21634     },
21635
21636     /**
21637      * Locks the selections.
21638      */
21639     lock : function(){
21640         this.locked = true;
21641     },
21642
21643     /**
21644      * Unlocks the selections.
21645      */
21646     unlock : function(){
21647         this.locked = false;
21648     },
21649
21650     /**
21651      * Returns true if the selections are locked.
21652      * @return {Boolean}
21653      */
21654     isLocked : function(){
21655         return this.locked;
21656     }
21657 });
21658 /**
21659  * @extends Roo.bootstrap.Table.AbstractSelectionModel
21660  * @class Roo.bootstrap.Table.RowSelectionModel
21661  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21662  * It supports multiple selections and keyboard selection/navigation. 
21663  * @constructor
21664  * @param {Object} config
21665  */
21666
21667 Roo.bootstrap.Table.RowSelectionModel = function(config){
21668     Roo.apply(this, config);
21669     this.selections = new Roo.util.MixedCollection(false, function(o){
21670         return o.id;
21671     });
21672
21673     this.last = false;
21674     this.lastActive = false;
21675
21676     this.addEvents({
21677         /**
21678              * @event selectionchange
21679              * Fires when the selection changes
21680              * @param {SelectionModel} this
21681              */
21682             "selectionchange" : true,
21683         /**
21684              * @event afterselectionchange
21685              * Fires after the selection changes (eg. by key press or clicking)
21686              * @param {SelectionModel} this
21687              */
21688             "afterselectionchange" : true,
21689         /**
21690              * @event beforerowselect
21691              * Fires when a row is selected being selected, return false to cancel.
21692              * @param {SelectionModel} this
21693              * @param {Number} rowIndex The selected index
21694              * @param {Boolean} keepExisting False if other selections will be cleared
21695              */
21696             "beforerowselect" : true,
21697         /**
21698              * @event rowselect
21699              * Fires when a row is selected.
21700              * @param {SelectionModel} this
21701              * @param {Number} rowIndex The selected index
21702              * @param {Roo.data.Record} r The record
21703              */
21704             "rowselect" : true,
21705         /**
21706              * @event rowdeselect
21707              * Fires when a row is deselected.
21708              * @param {SelectionModel} this
21709              * @param {Number} rowIndex The selected index
21710              */
21711         "rowdeselect" : true
21712     });
21713     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21714     this.locked = false;
21715 };
21716
21717 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
21718     /**
21719      * @cfg {Boolean} singleSelect
21720      * True to allow selection of only one row at a time (defaults to false)
21721      */
21722     singleSelect : false,
21723
21724     // private
21725     initEvents : function(){
21726
21727         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21728             this.grid.on("mousedown", this.handleMouseDown, this);
21729         }else{ // allow click to work like normal
21730             this.grid.on("rowclick", this.handleDragableRowClick, this);
21731         }
21732
21733         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21734             "up" : function(e){
21735                 if(!e.shiftKey){
21736                     this.selectPrevious(e.shiftKey);
21737                 }else if(this.last !== false && this.lastActive !== false){
21738                     var last = this.last;
21739                     this.selectRange(this.last,  this.lastActive-1);
21740                     this.grid.getView().focusRow(this.lastActive);
21741                     if(last !== false){
21742                         this.last = last;
21743                     }
21744                 }else{
21745                     this.selectFirstRow();
21746                 }
21747                 this.fireEvent("afterselectionchange", this);
21748             },
21749             "down" : function(e){
21750                 if(!e.shiftKey){
21751                     this.selectNext(e.shiftKey);
21752                 }else if(this.last !== false && this.lastActive !== false){
21753                     var last = this.last;
21754                     this.selectRange(this.last,  this.lastActive+1);
21755                     this.grid.getView().focusRow(this.lastActive);
21756                     if(last !== false){
21757                         this.last = last;
21758                     }
21759                 }else{
21760                     this.selectFirstRow();
21761                 }
21762                 this.fireEvent("afterselectionchange", this);
21763             },
21764             scope: this
21765         });
21766
21767         var view = this.grid.view;
21768         view.on("refresh", this.onRefresh, this);
21769         view.on("rowupdated", this.onRowUpdated, this);
21770         view.on("rowremoved", this.onRemove, this);
21771     },
21772
21773     // private
21774     onRefresh : function(){
21775         var ds = this.grid.dataSource, i, v = this.grid.view;
21776         var s = this.selections;
21777         s.each(function(r){
21778             if((i = ds.indexOfId(r.id)) != -1){
21779                 v.onRowSelect(i);
21780             }else{
21781                 s.remove(r);
21782             }
21783         });
21784     },
21785
21786     // private
21787     onRemove : function(v, index, r){
21788         this.selections.remove(r);
21789     },
21790
21791     // private
21792     onRowUpdated : function(v, index, r){
21793         if(this.isSelected(r)){
21794             v.onRowSelect(index);
21795         }
21796     },
21797
21798     /**
21799      * Select records.
21800      * @param {Array} records The records to select
21801      * @param {Boolean} keepExisting (optional) True to keep existing selections
21802      */
21803     selectRecords : function(records, keepExisting){
21804         if(!keepExisting){
21805             this.clearSelections();
21806         }
21807         var ds = this.grid.dataSource;
21808         for(var i = 0, len = records.length; i < len; i++){
21809             this.selectRow(ds.indexOf(records[i]), true);
21810         }
21811     },
21812
21813     /**
21814      * Gets the number of selected rows.
21815      * @return {Number}
21816      */
21817     getCount : function(){
21818         return this.selections.length;
21819     },
21820
21821     /**
21822      * Selects the first row in the grid.
21823      */
21824     selectFirstRow : function(){
21825         this.selectRow(0);
21826     },
21827
21828     /**
21829      * Select the last row.
21830      * @param {Boolean} keepExisting (optional) True to keep existing selections
21831      */
21832     selectLastRow : function(keepExisting){
21833         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21834     },
21835
21836     /**
21837      * Selects the row immediately following the last selected row.
21838      * @param {Boolean} keepExisting (optional) True to keep existing selections
21839      */
21840     selectNext : function(keepExisting){
21841         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21842             this.selectRow(this.last+1, keepExisting);
21843             this.grid.getView().focusRow(this.last);
21844         }
21845     },
21846
21847     /**
21848      * Selects the row that precedes the last selected row.
21849      * @param {Boolean} keepExisting (optional) True to keep existing selections
21850      */
21851     selectPrevious : function(keepExisting){
21852         if(this.last){
21853             this.selectRow(this.last-1, keepExisting);
21854             this.grid.getView().focusRow(this.last);
21855         }
21856     },
21857
21858     /**
21859      * Returns the selected records
21860      * @return {Array} Array of selected records
21861      */
21862     getSelections : function(){
21863         return [].concat(this.selections.items);
21864     },
21865
21866     /**
21867      * Returns the first selected record.
21868      * @return {Record}
21869      */
21870     getSelected : function(){
21871         return this.selections.itemAt(0);
21872     },
21873
21874
21875     /**
21876      * Clears all selections.
21877      */
21878     clearSelections : function(fast){
21879         if(this.locked) {
21880             return;
21881         }
21882         if(fast !== true){
21883             var ds = this.grid.dataSource;
21884             var s = this.selections;
21885             s.each(function(r){
21886                 this.deselectRow(ds.indexOfId(r.id));
21887             }, this);
21888             s.clear();
21889         }else{
21890             this.selections.clear();
21891         }
21892         this.last = false;
21893     },
21894
21895
21896     /**
21897      * Selects all rows.
21898      */
21899     selectAll : function(){
21900         if(this.locked) {
21901             return;
21902         }
21903         this.selections.clear();
21904         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
21905             this.selectRow(i, true);
21906         }
21907     },
21908
21909     /**
21910      * Returns True if there is a selection.
21911      * @return {Boolean}
21912      */
21913     hasSelection : function(){
21914         return this.selections.length > 0;
21915     },
21916
21917     /**
21918      * Returns True if the specified row is selected.
21919      * @param {Number/Record} record The record or index of the record to check
21920      * @return {Boolean}
21921      */
21922     isSelected : function(index){
21923         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
21924         return (r && this.selections.key(r.id) ? true : false);
21925     },
21926
21927     /**
21928      * Returns True if the specified record id is selected.
21929      * @param {String} id The id of record to check
21930      * @return {Boolean}
21931      */
21932     isIdSelected : function(id){
21933         return (this.selections.key(id) ? true : false);
21934     },
21935
21936     // private
21937     handleMouseDown : function(e, t){
21938         var view = this.grid.getView(), rowIndex;
21939         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
21940             return;
21941         };
21942         if(e.shiftKey && this.last !== false){
21943             var last = this.last;
21944             this.selectRange(last, rowIndex, e.ctrlKey);
21945             this.last = last; // reset the last
21946             view.focusRow(rowIndex);
21947         }else{
21948             var isSelected = this.isSelected(rowIndex);
21949             if(e.button !== 0 && isSelected){
21950                 view.focusRow(rowIndex);
21951             }else if(e.ctrlKey && isSelected){
21952                 this.deselectRow(rowIndex);
21953             }else if(!isSelected){
21954                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
21955                 view.focusRow(rowIndex);
21956             }
21957         }
21958         this.fireEvent("afterselectionchange", this);
21959     },
21960     // private
21961     handleDragableRowClick :  function(grid, rowIndex, e) 
21962     {
21963         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
21964             this.selectRow(rowIndex, false);
21965             grid.view.focusRow(rowIndex);
21966              this.fireEvent("afterselectionchange", this);
21967         }
21968     },
21969     
21970     /**
21971      * Selects multiple rows.
21972      * @param {Array} rows Array of the indexes of the row to select
21973      * @param {Boolean} keepExisting (optional) True to keep existing selections
21974      */
21975     selectRows : function(rows, keepExisting){
21976         if(!keepExisting){
21977             this.clearSelections();
21978         }
21979         for(var i = 0, len = rows.length; i < len; i++){
21980             this.selectRow(rows[i], true);
21981         }
21982     },
21983
21984     /**
21985      * Selects a range of rows. All rows in between startRow and endRow are also selected.
21986      * @param {Number} startRow The index of the first row in the range
21987      * @param {Number} endRow The index of the last row in the range
21988      * @param {Boolean} keepExisting (optional) True to retain existing selections
21989      */
21990     selectRange : function(startRow, endRow, keepExisting){
21991         if(this.locked) {
21992             return;
21993         }
21994         if(!keepExisting){
21995             this.clearSelections();
21996         }
21997         if(startRow <= endRow){
21998             for(var i = startRow; i <= endRow; i++){
21999                 this.selectRow(i, true);
22000             }
22001         }else{
22002             for(var i = startRow; i >= endRow; i--){
22003                 this.selectRow(i, true);
22004             }
22005         }
22006     },
22007
22008     /**
22009      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
22010      * @param {Number} startRow The index of the first row in the range
22011      * @param {Number} endRow The index of the last row in the range
22012      */
22013     deselectRange : function(startRow, endRow, preventViewNotify){
22014         if(this.locked) {
22015             return;
22016         }
22017         for(var i = startRow; i <= endRow; i++){
22018             this.deselectRow(i, preventViewNotify);
22019         }
22020     },
22021
22022     /**
22023      * Selects a row.
22024      * @param {Number} row The index of the row to select
22025      * @param {Boolean} keepExisting (optional) True to keep existing selections
22026      */
22027     selectRow : function(index, keepExisting, preventViewNotify){
22028         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
22029             return;
22030         }
22031         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
22032             if(!keepExisting || this.singleSelect){
22033                 this.clearSelections();
22034             }
22035             var r = this.grid.dataSource.getAt(index);
22036             this.selections.add(r);
22037             this.last = this.lastActive = index;
22038             if(!preventViewNotify){
22039                 this.grid.getView().onRowSelect(index);
22040             }
22041             this.fireEvent("rowselect", this, index, r);
22042             this.fireEvent("selectionchange", this);
22043         }
22044     },
22045
22046     /**
22047      * Deselects a row.
22048      * @param {Number} row The index of the row to deselect
22049      */
22050     deselectRow : function(index, preventViewNotify){
22051         if(this.locked) {
22052             return;
22053         }
22054         if(this.last == index){
22055             this.last = false;
22056         }
22057         if(this.lastActive == index){
22058             this.lastActive = false;
22059         }
22060         var r = this.grid.dataSource.getAt(index);
22061         this.selections.remove(r);
22062         if(!preventViewNotify){
22063             this.grid.getView().onRowDeselect(index);
22064         }
22065         this.fireEvent("rowdeselect", this, index);
22066         this.fireEvent("selectionchange", this);
22067     },
22068
22069     // private
22070     restoreLast : function(){
22071         if(this._last){
22072             this.last = this._last;
22073         }
22074     },
22075
22076     // private
22077     acceptsNav : function(row, col, cm){
22078         return !cm.isHidden(col) && cm.isCellEditable(col, row);
22079     },
22080
22081     // private
22082     onEditorKey : function(field, e){
22083         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22084         if(k == e.TAB){
22085             e.stopEvent();
22086             ed.completeEdit();
22087             if(e.shiftKey){
22088                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22089             }else{
22090                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22091             }
22092         }else if(k == e.ENTER && !e.ctrlKey){
22093             e.stopEvent();
22094             ed.completeEdit();
22095             if(e.shiftKey){
22096                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22097             }else{
22098                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22099             }
22100         }else if(k == e.ESC){
22101             ed.cancelEdit();
22102         }
22103         if(newCell){
22104             g.startEditing(newCell[0], newCell[1]);
22105         }
22106     }
22107 });/*
22108  * Based on:
22109  * Ext JS Library 1.1.1
22110  * Copyright(c) 2006-2007, Ext JS, LLC.
22111  *
22112  * Originally Released Under LGPL - original licence link has changed is not relivant.
22113  *
22114  * Fork - LGPL
22115  * <script type="text/javascript">
22116  */
22117  
22118 /**
22119  * @class Roo.bootstrap.PagingToolbar
22120  * @extends Roo.bootstrap.NavSimplebar
22121  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
22122  * @constructor
22123  * Create a new PagingToolbar
22124  * @param {Object} config The config object
22125  * @param {Roo.data.Store} store
22126  */
22127 Roo.bootstrap.PagingToolbar = function(config)
22128 {
22129     // old args format still supported... - xtype is prefered..
22130         // created from xtype...
22131     
22132     this.ds = config.dataSource;
22133     
22134     if (config.store && !this.ds) {
22135         this.store= Roo.factory(config.store, Roo.data);
22136         this.ds = this.store;
22137         this.ds.xmodule = this.xmodule || false;
22138     }
22139     
22140     this.toolbarItems = [];
22141     if (config.items) {
22142         this.toolbarItems = config.items;
22143     }
22144     
22145     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
22146     
22147     this.cursor = 0;
22148     
22149     if (this.ds) { 
22150         this.bind(this.ds);
22151     }
22152     
22153     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
22154     
22155 };
22156
22157 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
22158     /**
22159      * @cfg {Roo.data.Store} dataSource
22160      * The underlying data store providing the paged data
22161      */
22162     /**
22163      * @cfg {String/HTMLElement/Element} container
22164      * container The id or element that will contain the toolbar
22165      */
22166     /**
22167      * @cfg {Boolean} displayInfo
22168      * True to display the displayMsg (defaults to false)
22169      */
22170     /**
22171      * @cfg {Number} pageSize
22172      * The number of records to display per page (defaults to 20)
22173      */
22174     pageSize: 20,
22175     /**
22176      * @cfg {String} displayMsg
22177      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
22178      */
22179     displayMsg : 'Displaying {0} - {1} of {2}',
22180     /**
22181      * @cfg {String} emptyMsg
22182      * The message to display when no records are found (defaults to "No data to display")
22183      */
22184     emptyMsg : 'No data to display',
22185     /**
22186      * Customizable piece of the default paging text (defaults to "Page")
22187      * @type String
22188      */
22189     beforePageText : "Page",
22190     /**
22191      * Customizable piece of the default paging text (defaults to "of %0")
22192      * @type String
22193      */
22194     afterPageText : "of {0}",
22195     /**
22196      * Customizable piece of the default paging text (defaults to "First Page")
22197      * @type String
22198      */
22199     firstText : "First Page",
22200     /**
22201      * Customizable piece of the default paging text (defaults to "Previous Page")
22202      * @type String
22203      */
22204     prevText : "Previous Page",
22205     /**
22206      * Customizable piece of the default paging text (defaults to "Next Page")
22207      * @type String
22208      */
22209     nextText : "Next Page",
22210     /**
22211      * Customizable piece of the default paging text (defaults to "Last Page")
22212      * @type String
22213      */
22214     lastText : "Last Page",
22215     /**
22216      * Customizable piece of the default paging text (defaults to "Refresh")
22217      * @type String
22218      */
22219     refreshText : "Refresh",
22220
22221     buttons : false,
22222     // private
22223     onRender : function(ct, position) 
22224     {
22225         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
22226         this.navgroup.parentId = this.id;
22227         this.navgroup.onRender(this.el, null);
22228         // add the buttons to the navgroup
22229         
22230         if(this.displayInfo){
22231             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
22232             this.displayEl = this.el.select('.x-paging-info', true).first();
22233 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
22234 //            this.displayEl = navel.el.select('span',true).first();
22235         }
22236         
22237         var _this = this;
22238         
22239         if(this.buttons){
22240             Roo.each(_this.buttons, function(e){ // this might need to use render????
22241                Roo.factory(e).onRender(_this.el, null);
22242             });
22243         }
22244             
22245         Roo.each(_this.toolbarItems, function(e) {
22246             _this.navgroup.addItem(e);
22247         });
22248         
22249         
22250         this.first = this.navgroup.addItem({
22251             tooltip: this.firstText,
22252             cls: "prev",
22253             icon : 'fa fa-backward',
22254             disabled: true,
22255             preventDefault: true,
22256             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
22257         });
22258         
22259         this.prev =  this.navgroup.addItem({
22260             tooltip: this.prevText,
22261             cls: "prev",
22262             icon : 'fa fa-step-backward',
22263             disabled: true,
22264             preventDefault: true,
22265             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
22266         });
22267     //this.addSeparator();
22268         
22269         
22270         var field = this.navgroup.addItem( {
22271             tagtype : 'span',
22272             cls : 'x-paging-position',
22273             
22274             html : this.beforePageText  +
22275                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22276                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
22277          } ); //?? escaped?
22278         
22279         this.field = field.el.select('input', true).first();
22280         this.field.on("keydown", this.onPagingKeydown, this);
22281         this.field.on("focus", function(){this.dom.select();});
22282     
22283     
22284         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
22285         //this.field.setHeight(18);
22286         //this.addSeparator();
22287         this.next = this.navgroup.addItem({
22288             tooltip: this.nextText,
22289             cls: "next",
22290             html : ' <i class="fa fa-step-forward">',
22291             disabled: true,
22292             preventDefault: true,
22293             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
22294         });
22295         this.last = this.navgroup.addItem({
22296             tooltip: this.lastText,
22297             icon : 'fa fa-forward',
22298             cls: "next",
22299             disabled: true,
22300             preventDefault: true,
22301             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
22302         });
22303     //this.addSeparator();
22304         this.loading = this.navgroup.addItem({
22305             tooltip: this.refreshText,
22306             icon: 'fa fa-refresh',
22307             preventDefault: true,
22308             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
22309         });
22310         
22311     },
22312
22313     // private
22314     updateInfo : function(){
22315         if(this.displayEl){
22316             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
22317             var msg = count == 0 ?
22318                 this.emptyMsg :
22319                 String.format(
22320                     this.displayMsg,
22321                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
22322                 );
22323             this.displayEl.update(msg);
22324         }
22325     },
22326
22327     // private
22328     onLoad : function(ds, r, o){
22329        this.cursor = o.params ? o.params.start : 0;
22330        var d = this.getPageData(),
22331             ap = d.activePage,
22332             ps = d.pages;
22333         
22334        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
22335        this.field.dom.value = ap;
22336        this.first.setDisabled(ap == 1);
22337        this.prev.setDisabled(ap == 1);
22338        this.next.setDisabled(ap == ps);
22339        this.last.setDisabled(ap == ps);
22340        this.loading.enable();
22341        this.updateInfo();
22342     },
22343
22344     // private
22345     getPageData : function(){
22346         var total = this.ds.getTotalCount();
22347         return {
22348             total : total,
22349             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
22350             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
22351         };
22352     },
22353
22354     // private
22355     onLoadError : function(){
22356         this.loading.enable();
22357     },
22358
22359     // private
22360     onPagingKeydown : function(e){
22361         var k = e.getKey();
22362         var d = this.getPageData();
22363         if(k == e.RETURN){
22364             var v = this.field.dom.value, pageNum;
22365             if(!v || isNaN(pageNum = parseInt(v, 10))){
22366                 this.field.dom.value = d.activePage;
22367                 return;
22368             }
22369             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
22370             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22371             e.stopEvent();
22372         }
22373         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))
22374         {
22375           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
22376           this.field.dom.value = pageNum;
22377           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
22378           e.stopEvent();
22379         }
22380         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22381         {
22382           var v = this.field.dom.value, pageNum; 
22383           var increment = (e.shiftKey) ? 10 : 1;
22384           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
22385                 increment *= -1;
22386           }
22387           if(!v || isNaN(pageNum = parseInt(v, 10))) {
22388             this.field.dom.value = d.activePage;
22389             return;
22390           }
22391           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
22392           {
22393             this.field.dom.value = parseInt(v, 10) + increment;
22394             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
22395             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22396           }
22397           e.stopEvent();
22398         }
22399     },
22400
22401     // private
22402     beforeLoad : function(){
22403         if(this.loading){
22404             this.loading.disable();
22405         }
22406     },
22407
22408     // private
22409     onClick : function(which){
22410         
22411         var ds = this.ds;
22412         if (!ds) {
22413             return;
22414         }
22415         
22416         switch(which){
22417             case "first":
22418                 ds.load({params:{start: 0, limit: this.pageSize}});
22419             break;
22420             case "prev":
22421                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
22422             break;
22423             case "next":
22424                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
22425             break;
22426             case "last":
22427                 var total = ds.getTotalCount();
22428                 var extra = total % this.pageSize;
22429                 var lastStart = extra ? (total - extra) : total-this.pageSize;
22430                 ds.load({params:{start: lastStart, limit: this.pageSize}});
22431             break;
22432             case "refresh":
22433                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22434             break;
22435         }
22436     },
22437
22438     /**
22439      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22440      * @param {Roo.data.Store} store The data store to unbind
22441      */
22442     unbind : function(ds){
22443         ds.un("beforeload", this.beforeLoad, this);
22444         ds.un("load", this.onLoad, this);
22445         ds.un("loadexception", this.onLoadError, this);
22446         ds.un("remove", this.updateInfo, this);
22447         ds.un("add", this.updateInfo, this);
22448         this.ds = undefined;
22449     },
22450
22451     /**
22452      * Binds the paging toolbar to the specified {@link Roo.data.Store}
22453      * @param {Roo.data.Store} store The data store to bind
22454      */
22455     bind : function(ds){
22456         ds.on("beforeload", this.beforeLoad, this);
22457         ds.on("load", this.onLoad, this);
22458         ds.on("loadexception", this.onLoadError, this);
22459         ds.on("remove", this.updateInfo, this);
22460         ds.on("add", this.updateInfo, this);
22461         this.ds = ds;
22462     }
22463 });/*
22464  * - LGPL
22465  *
22466  * element
22467  * 
22468  */
22469
22470 /**
22471  * @class Roo.bootstrap.MessageBar
22472  * @extends Roo.bootstrap.Component
22473  * Bootstrap MessageBar class
22474  * @cfg {String} html contents of the MessageBar
22475  * @cfg {String} weight (info | success | warning | danger) default info
22476  * @cfg {String} beforeClass insert the bar before the given class
22477  * @cfg {Boolean} closable (true | false) default false
22478  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22479  * 
22480  * @constructor
22481  * Create a new Element
22482  * @param {Object} config The config object
22483  */
22484
22485 Roo.bootstrap.MessageBar = function(config){
22486     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22487 };
22488
22489 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
22490     
22491     html: '',
22492     weight: 'info',
22493     closable: false,
22494     fixed: false,
22495     beforeClass: 'bootstrap-sticky-wrap',
22496     
22497     getAutoCreate : function(){
22498         
22499         var cfg = {
22500             tag: 'div',
22501             cls: 'alert alert-dismissable alert-' + this.weight,
22502             cn: [
22503                 {
22504                     tag: 'span',
22505                     cls: 'message',
22506                     html: this.html || ''
22507                 }
22508             ]
22509         };
22510         
22511         if(this.fixed){
22512             cfg.cls += ' alert-messages-fixed';
22513         }
22514         
22515         if(this.closable){
22516             cfg.cn.push({
22517                 tag: 'button',
22518                 cls: 'close',
22519                 html: 'x'
22520             });
22521         }
22522         
22523         return cfg;
22524     },
22525     
22526     onRender : function(ct, position)
22527     {
22528         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22529         
22530         if(!this.el){
22531             var cfg = Roo.apply({},  this.getAutoCreate());
22532             cfg.id = Roo.id();
22533             
22534             if (this.cls) {
22535                 cfg.cls += ' ' + this.cls;
22536             }
22537             if (this.style) {
22538                 cfg.style = this.style;
22539             }
22540             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22541             
22542             this.el.setVisibilityMode(Roo.Element.DISPLAY);
22543         }
22544         
22545         this.el.select('>button.close').on('click', this.hide, this);
22546         
22547     },
22548     
22549     show : function()
22550     {
22551         if (!this.rendered) {
22552             this.render();
22553         }
22554         
22555         this.el.show();
22556         
22557         this.fireEvent('show', this);
22558         
22559     },
22560     
22561     hide : function()
22562     {
22563         if (!this.rendered) {
22564             this.render();
22565         }
22566         
22567         this.el.hide();
22568         
22569         this.fireEvent('hide', this);
22570     },
22571     
22572     update : function()
22573     {
22574 //        var e = this.el.dom.firstChild;
22575 //        
22576 //        if(this.closable){
22577 //            e = e.nextSibling;
22578 //        }
22579 //        
22580 //        e.data = this.html || '';
22581
22582         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22583     }
22584    
22585 });
22586
22587  
22588
22589      /*
22590  * - LGPL
22591  *
22592  * Graph
22593  * 
22594  */
22595
22596
22597 /**
22598  * @class Roo.bootstrap.Graph
22599  * @extends Roo.bootstrap.Component
22600  * Bootstrap Graph class
22601 > Prameters
22602  -sm {number} sm 4
22603  -md {number} md 5
22604  @cfg {String} graphtype  bar | vbar | pie
22605  @cfg {number} g_x coodinator | centre x (pie)
22606  @cfg {number} g_y coodinator | centre y (pie)
22607  @cfg {number} g_r radius (pie)
22608  @cfg {number} g_height height of the chart (respected by all elements in the set)
22609  @cfg {number} g_width width of the chart (respected by all elements in the set)
22610  @cfg {Object} title The title of the chart
22611     
22612  -{Array}  values
22613  -opts (object) options for the chart 
22614      o {
22615      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22616      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22617      o vgutter (number)
22618      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.
22619      o stacked (boolean) whether or not to tread values as in a stacked bar chart
22620      o to
22621      o stretch (boolean)
22622      o }
22623  -opts (object) options for the pie
22624      o{
22625      o cut
22626      o startAngle (number)
22627      o endAngle (number)
22628      } 
22629  *
22630  * @constructor
22631  * Create a new Input
22632  * @param {Object} config The config object
22633  */
22634
22635 Roo.bootstrap.Graph = function(config){
22636     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22637     
22638     this.addEvents({
22639         // img events
22640         /**
22641          * @event click
22642          * The img click event for the img.
22643          * @param {Roo.EventObject} e
22644          */
22645         "click" : true
22646     });
22647 };
22648
22649 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
22650     
22651     sm: 4,
22652     md: 5,
22653     graphtype: 'bar',
22654     g_height: 250,
22655     g_width: 400,
22656     g_x: 50,
22657     g_y: 50,
22658     g_r: 30,
22659     opts:{
22660         //g_colors: this.colors,
22661         g_type: 'soft',
22662         g_gutter: '20%'
22663
22664     },
22665     title : false,
22666
22667     getAutoCreate : function(){
22668         
22669         var cfg = {
22670             tag: 'div',
22671             html : null
22672         };
22673         
22674         
22675         return  cfg;
22676     },
22677
22678     onRender : function(ct,position){
22679         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22680         this.raphael = Raphael(this.el.dom);
22681         
22682                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22683                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22684                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22685                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22686                 /*
22687                 r.text(160, 10, "Single Series Chart").attr(txtattr);
22688                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22689                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22690                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22691                 
22692                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22693                 r.barchart(330, 10, 300, 220, data1);
22694                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22695                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22696                 */
22697                 
22698                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22699                 // r.barchart(30, 30, 560, 250,  xdata, {
22700                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22701                 //     axis : "0 0 1 1",
22702                 //     axisxlabels :  xdata
22703                 //     //yvalues : cols,
22704                    
22705                 // });
22706 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22707 //        
22708 //        this.load(null,xdata,{
22709 //                axis : "0 0 1 1",
22710 //                axisxlabels :  xdata
22711 //                });
22712
22713     },
22714
22715     load : function(graphtype,xdata,opts){
22716         this.raphael.clear();
22717         if(!graphtype) {
22718             graphtype = this.graphtype;
22719         }
22720         if(!opts){
22721             opts = this.opts;
22722         }
22723         var r = this.raphael,
22724             fin = function () {
22725                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22726             },
22727             fout = function () {
22728                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22729             },
22730             pfin = function() {
22731                 this.sector.stop();
22732                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22733
22734                 if (this.label) {
22735                     this.label[0].stop();
22736                     this.label[0].attr({ r: 7.5 });
22737                     this.label[1].attr({ "font-weight": 800 });
22738                 }
22739             },
22740             pfout = function() {
22741                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22742
22743                 if (this.label) {
22744                     this.label[0].animate({ r: 5 }, 500, "bounce");
22745                     this.label[1].attr({ "font-weight": 400 });
22746                 }
22747             };
22748
22749         switch(graphtype){
22750             case 'bar':
22751                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22752                 break;
22753             case 'hbar':
22754                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22755                 break;
22756             case 'pie':
22757 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
22758 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22759 //            
22760                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22761                 
22762                 break;
22763
22764         }
22765         
22766         if(this.title){
22767             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22768         }
22769         
22770     },
22771     
22772     setTitle: function(o)
22773     {
22774         this.title = o;
22775     },
22776     
22777     initEvents: function() {
22778         
22779         if(!this.href){
22780             this.el.on('click', this.onClick, this);
22781         }
22782     },
22783     
22784     onClick : function(e)
22785     {
22786         Roo.log('img onclick');
22787         this.fireEvent('click', this, e);
22788     }
22789    
22790 });
22791
22792  
22793 /*
22794  * - LGPL
22795  *
22796  * numberBox
22797  * 
22798  */
22799 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22800
22801 /**
22802  * @class Roo.bootstrap.dash.NumberBox
22803  * @extends Roo.bootstrap.Component
22804  * Bootstrap NumberBox class
22805  * @cfg {String} headline Box headline
22806  * @cfg {String} content Box content
22807  * @cfg {String} icon Box icon
22808  * @cfg {String} footer Footer text
22809  * @cfg {String} fhref Footer href
22810  * 
22811  * @constructor
22812  * Create a new NumberBox
22813  * @param {Object} config The config object
22814  */
22815
22816
22817 Roo.bootstrap.dash.NumberBox = function(config){
22818     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22819     
22820 };
22821
22822 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
22823     
22824     headline : '',
22825     content : '',
22826     icon : '',
22827     footer : '',
22828     fhref : '',
22829     ficon : '',
22830     
22831     getAutoCreate : function(){
22832         
22833         var cfg = {
22834             tag : 'div',
22835             cls : 'small-box ',
22836             cn : [
22837                 {
22838                     tag : 'div',
22839                     cls : 'inner',
22840                     cn :[
22841                         {
22842                             tag : 'h3',
22843                             cls : 'roo-headline',
22844                             html : this.headline
22845                         },
22846                         {
22847                             tag : 'p',
22848                             cls : 'roo-content',
22849                             html : this.content
22850                         }
22851                     ]
22852                 }
22853             ]
22854         };
22855         
22856         if(this.icon){
22857             cfg.cn.push({
22858                 tag : 'div',
22859                 cls : 'icon',
22860                 cn :[
22861                     {
22862                         tag : 'i',
22863                         cls : 'ion ' + this.icon
22864                     }
22865                 ]
22866             });
22867         }
22868         
22869         if(this.footer){
22870             var footer = {
22871                 tag : 'a',
22872                 cls : 'small-box-footer',
22873                 href : this.fhref || '#',
22874                 html : this.footer
22875             };
22876             
22877             cfg.cn.push(footer);
22878             
22879         }
22880         
22881         return  cfg;
22882     },
22883
22884     onRender : function(ct,position){
22885         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
22886
22887
22888        
22889                 
22890     },
22891
22892     setHeadline: function (value)
22893     {
22894         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
22895     },
22896     
22897     setFooter: function (value, href)
22898     {
22899         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
22900         
22901         if(href){
22902             this.el.select('a.small-box-footer',true).first().attr('href', href);
22903         }
22904         
22905     },
22906
22907     setContent: function (value)
22908     {
22909         this.el.select('.roo-content',true).first().dom.innerHTML = value;
22910     },
22911
22912     initEvents: function() 
22913     {   
22914         
22915     }
22916     
22917 });
22918
22919  
22920 /*
22921  * - LGPL
22922  *
22923  * TabBox
22924  * 
22925  */
22926 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22927
22928 /**
22929  * @class Roo.bootstrap.dash.TabBox
22930  * @extends Roo.bootstrap.Component
22931  * Bootstrap TabBox class
22932  * @cfg {String} title Title of the TabBox
22933  * @cfg {String} icon Icon of the TabBox
22934  * @cfg {Boolean} showtabs (true|false) show the tabs default true
22935  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
22936  * 
22937  * @constructor
22938  * Create a new TabBox
22939  * @param {Object} config The config object
22940  */
22941
22942
22943 Roo.bootstrap.dash.TabBox = function(config){
22944     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
22945     this.addEvents({
22946         // raw events
22947         /**
22948          * @event addpane
22949          * When a pane is added
22950          * @param {Roo.bootstrap.dash.TabPane} pane
22951          */
22952         "addpane" : true,
22953         /**
22954          * @event activatepane
22955          * When a pane is activated
22956          * @param {Roo.bootstrap.dash.TabPane} pane
22957          */
22958         "activatepane" : true
22959         
22960          
22961     });
22962     
22963     this.panes = [];
22964 };
22965
22966 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
22967
22968     title : '',
22969     icon : false,
22970     showtabs : true,
22971     tabScrollable : false,
22972     
22973     getChildContainer : function()
22974     {
22975         return this.el.select('.tab-content', true).first();
22976     },
22977     
22978     getAutoCreate : function(){
22979         
22980         var header = {
22981             tag: 'li',
22982             cls: 'pull-left header',
22983             html: this.title,
22984             cn : []
22985         };
22986         
22987         if(this.icon){
22988             header.cn.push({
22989                 tag: 'i',
22990                 cls: 'fa ' + this.icon
22991             });
22992         }
22993         
22994         var h = {
22995             tag: 'ul',
22996             cls: 'nav nav-tabs pull-right',
22997             cn: [
22998                 header
22999             ]
23000         };
23001         
23002         if(this.tabScrollable){
23003             h = {
23004                 tag: 'div',
23005                 cls: 'tab-header',
23006                 cn: [
23007                     {
23008                         tag: 'ul',
23009                         cls: 'nav nav-tabs pull-right',
23010                         cn: [
23011                             header
23012                         ]
23013                     }
23014                 ]
23015             };
23016         }
23017         
23018         var cfg = {
23019             tag: 'div',
23020             cls: 'nav-tabs-custom',
23021             cn: [
23022                 h,
23023                 {
23024                     tag: 'div',
23025                     cls: 'tab-content no-padding',
23026                     cn: []
23027                 }
23028             ]
23029         };
23030
23031         return  cfg;
23032     },
23033     initEvents : function()
23034     {
23035         //Roo.log('add add pane handler');
23036         this.on('addpane', this.onAddPane, this);
23037     },
23038      /**
23039      * Updates the box title
23040      * @param {String} html to set the title to.
23041      */
23042     setTitle : function(value)
23043     {
23044         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23045     },
23046     onAddPane : function(pane)
23047     {
23048         this.panes.push(pane);
23049         //Roo.log('addpane');
23050         //Roo.log(pane);
23051         // tabs are rendere left to right..
23052         if(!this.showtabs){
23053             return;
23054         }
23055         
23056         var ctr = this.el.select('.nav-tabs', true).first();
23057          
23058          
23059         var existing = ctr.select('.nav-tab',true);
23060         var qty = existing.getCount();;
23061         
23062         
23063         var tab = ctr.createChild({
23064             tag : 'li',
23065             cls : 'nav-tab' + (qty ? '' : ' active'),
23066             cn : [
23067                 {
23068                     tag : 'a',
23069                     href:'#',
23070                     html : pane.title
23071                 }
23072             ]
23073         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23074         pane.tab = tab;
23075         
23076         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23077         if (!qty) {
23078             pane.el.addClass('active');
23079         }
23080         
23081                 
23082     },
23083     onTabClick : function(ev,un,ob,pane)
23084     {
23085         //Roo.log('tab - prev default');
23086         ev.preventDefault();
23087         
23088         
23089         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23090         pane.tab.addClass('active');
23091         //Roo.log(pane.title);
23092         this.getChildContainer().select('.tab-pane',true).removeClass('active');
23093         // technically we should have a deactivate event.. but maybe add later.
23094         // and it should not de-activate the selected tab...
23095         this.fireEvent('activatepane', pane);
23096         pane.el.addClass('active');
23097         pane.fireEvent('activate');
23098         
23099         
23100     },
23101     
23102     getActivePane : function()
23103     {
23104         var r = false;
23105         Roo.each(this.panes, function(p) {
23106             if(p.el.hasClass('active')){
23107                 r = p;
23108                 return false;
23109             }
23110             
23111             return;
23112         });
23113         
23114         return r;
23115     }
23116     
23117     
23118 });
23119
23120  
23121 /*
23122  * - LGPL
23123  *
23124  * Tab pane
23125  * 
23126  */
23127 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23128 /**
23129  * @class Roo.bootstrap.TabPane
23130  * @extends Roo.bootstrap.Component
23131  * Bootstrap TabPane class
23132  * @cfg {Boolean} active (false | true) Default false
23133  * @cfg {String} title title of panel
23134
23135  * 
23136  * @constructor
23137  * Create a new TabPane
23138  * @param {Object} config The config object
23139  */
23140
23141 Roo.bootstrap.dash.TabPane = function(config){
23142     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
23143     
23144     this.addEvents({
23145         // raw events
23146         /**
23147          * @event activate
23148          * When a pane is activated
23149          * @param {Roo.bootstrap.dash.TabPane} pane
23150          */
23151         "activate" : true
23152          
23153     });
23154 };
23155
23156 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
23157     
23158     active : false,
23159     title : '',
23160     
23161     // the tabBox that this is attached to.
23162     tab : false,
23163      
23164     getAutoCreate : function() 
23165     {
23166         var cfg = {
23167             tag: 'div',
23168             cls: 'tab-pane'
23169         };
23170         
23171         if(this.active){
23172             cfg.cls += ' active';
23173         }
23174         
23175         return cfg;
23176     },
23177     initEvents  : function()
23178     {
23179         //Roo.log('trigger add pane handler');
23180         this.parent().fireEvent('addpane', this)
23181     },
23182     
23183      /**
23184      * Updates the tab title 
23185      * @param {String} html to set the title to.
23186      */
23187     setTitle: function(str)
23188     {
23189         if (!this.tab) {
23190             return;
23191         }
23192         this.title = str;
23193         this.tab.select('a', true).first().dom.innerHTML = str;
23194         
23195     }
23196     
23197     
23198     
23199 });
23200
23201  
23202
23203
23204  /*
23205  * - LGPL
23206  *
23207  * menu
23208  * 
23209  */
23210 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23211
23212 /**
23213  * @class Roo.bootstrap.menu.Menu
23214  * @extends Roo.bootstrap.Component
23215  * Bootstrap Menu class - container for Menu
23216  * @cfg {String} html Text of the menu
23217  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
23218  * @cfg {String} icon Font awesome icon
23219  * @cfg {String} pos Menu align to (top | bottom) default bottom
23220  * 
23221  * 
23222  * @constructor
23223  * Create a new Menu
23224  * @param {Object} config The config object
23225  */
23226
23227
23228 Roo.bootstrap.menu.Menu = function(config){
23229     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
23230     
23231     this.addEvents({
23232         /**
23233          * @event beforeshow
23234          * Fires before this menu is displayed
23235          * @param {Roo.bootstrap.menu.Menu} this
23236          */
23237         beforeshow : true,
23238         /**
23239          * @event beforehide
23240          * Fires before this menu is hidden
23241          * @param {Roo.bootstrap.menu.Menu} this
23242          */
23243         beforehide : true,
23244         /**
23245          * @event show
23246          * Fires after this menu is displayed
23247          * @param {Roo.bootstrap.menu.Menu} this
23248          */
23249         show : true,
23250         /**
23251          * @event hide
23252          * Fires after this menu is hidden
23253          * @param {Roo.bootstrap.menu.Menu} this
23254          */
23255         hide : true,
23256         /**
23257          * @event click
23258          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
23259          * @param {Roo.bootstrap.menu.Menu} this
23260          * @param {Roo.EventObject} e
23261          */
23262         click : true
23263     });
23264     
23265 };
23266
23267 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
23268     
23269     submenu : false,
23270     html : '',
23271     weight : 'default',
23272     icon : false,
23273     pos : 'bottom',
23274     
23275     
23276     getChildContainer : function() {
23277         if(this.isSubMenu){
23278             return this.el;
23279         }
23280         
23281         return this.el.select('ul.dropdown-menu', true).first();  
23282     },
23283     
23284     getAutoCreate : function()
23285     {
23286         var text = [
23287             {
23288                 tag : 'span',
23289                 cls : 'roo-menu-text',
23290                 html : this.html
23291             }
23292         ];
23293         
23294         if(this.icon){
23295             text.unshift({
23296                 tag : 'i',
23297                 cls : 'fa ' + this.icon
23298             })
23299         }
23300         
23301         
23302         var cfg = {
23303             tag : 'div',
23304             cls : 'btn-group',
23305             cn : [
23306                 {
23307                     tag : 'button',
23308                     cls : 'dropdown-button btn btn-' + this.weight,
23309                     cn : text
23310                 },
23311                 {
23312                     tag : 'button',
23313                     cls : 'dropdown-toggle btn btn-' + this.weight,
23314                     cn : [
23315                         {
23316                             tag : 'span',
23317                             cls : 'caret'
23318                         }
23319                     ]
23320                 },
23321                 {
23322                     tag : 'ul',
23323                     cls : 'dropdown-menu'
23324                 }
23325             ]
23326             
23327         };
23328         
23329         if(this.pos == 'top'){
23330             cfg.cls += ' dropup';
23331         }
23332         
23333         if(this.isSubMenu){
23334             cfg = {
23335                 tag : 'ul',
23336                 cls : 'dropdown-menu'
23337             }
23338         }
23339         
23340         return cfg;
23341     },
23342     
23343     onRender : function(ct, position)
23344     {
23345         this.isSubMenu = ct.hasClass('dropdown-submenu');
23346         
23347         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
23348     },
23349     
23350     initEvents : function() 
23351     {
23352         if(this.isSubMenu){
23353             return;
23354         }
23355         
23356         this.hidden = true;
23357         
23358         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
23359         this.triggerEl.on('click', this.onTriggerPress, this);
23360         
23361         this.buttonEl = this.el.select('button.dropdown-button', true).first();
23362         this.buttonEl.on('click', this.onClick, this);
23363         
23364     },
23365     
23366     list : function()
23367     {
23368         if(this.isSubMenu){
23369             return this.el;
23370         }
23371         
23372         return this.el.select('ul.dropdown-menu', true).first();
23373     },
23374     
23375     onClick : function(e)
23376     {
23377         this.fireEvent("click", this, e);
23378     },
23379     
23380     onTriggerPress  : function(e)
23381     {   
23382         if (this.isVisible()) {
23383             this.hide();
23384         } else {
23385             this.show();
23386         }
23387     },
23388     
23389     isVisible : function(){
23390         return !this.hidden;
23391     },
23392     
23393     show : function()
23394     {
23395         this.fireEvent("beforeshow", this);
23396         
23397         this.hidden = false;
23398         this.el.addClass('open');
23399         
23400         Roo.get(document).on("mouseup", this.onMouseUp, this);
23401         
23402         this.fireEvent("show", this);
23403         
23404         
23405     },
23406     
23407     hide : function()
23408     {
23409         this.fireEvent("beforehide", this);
23410         
23411         this.hidden = true;
23412         this.el.removeClass('open');
23413         
23414         Roo.get(document).un("mouseup", this.onMouseUp);
23415         
23416         this.fireEvent("hide", this);
23417     },
23418     
23419     onMouseUp : function()
23420     {
23421         this.hide();
23422     }
23423     
23424 });
23425
23426  
23427  /*
23428  * - LGPL
23429  *
23430  * menu item
23431  * 
23432  */
23433 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23434
23435 /**
23436  * @class Roo.bootstrap.menu.Item
23437  * @extends Roo.bootstrap.Component
23438  * Bootstrap MenuItem class
23439  * @cfg {Boolean} submenu (true | false) default false
23440  * @cfg {String} html text of the item
23441  * @cfg {String} href the link
23442  * @cfg {Boolean} disable (true | false) default false
23443  * @cfg {Boolean} preventDefault (true | false) default true
23444  * @cfg {String} icon Font awesome icon
23445  * @cfg {String} pos Submenu align to (left | right) default right 
23446  * 
23447  * 
23448  * @constructor
23449  * Create a new Item
23450  * @param {Object} config The config object
23451  */
23452
23453
23454 Roo.bootstrap.menu.Item = function(config){
23455     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23456     this.addEvents({
23457         /**
23458          * @event mouseover
23459          * Fires when the mouse is hovering over this menu
23460          * @param {Roo.bootstrap.menu.Item} this
23461          * @param {Roo.EventObject} e
23462          */
23463         mouseover : true,
23464         /**
23465          * @event mouseout
23466          * Fires when the mouse exits this menu
23467          * @param {Roo.bootstrap.menu.Item} this
23468          * @param {Roo.EventObject} e
23469          */
23470         mouseout : true,
23471         // raw events
23472         /**
23473          * @event click
23474          * The raw click event for the entire grid.
23475          * @param {Roo.EventObject} e
23476          */
23477         click : true
23478     });
23479 };
23480
23481 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
23482     
23483     submenu : false,
23484     href : '',
23485     html : '',
23486     preventDefault: true,
23487     disable : false,
23488     icon : false,
23489     pos : 'right',
23490     
23491     getAutoCreate : function()
23492     {
23493         var text = [
23494             {
23495                 tag : 'span',
23496                 cls : 'roo-menu-item-text',
23497                 html : this.html
23498             }
23499         ];
23500         
23501         if(this.icon){
23502             text.unshift({
23503                 tag : 'i',
23504                 cls : 'fa ' + this.icon
23505             })
23506         }
23507         
23508         var cfg = {
23509             tag : 'li',
23510             cn : [
23511                 {
23512                     tag : 'a',
23513                     href : this.href || '#',
23514                     cn : text
23515                 }
23516             ]
23517         };
23518         
23519         if(this.disable){
23520             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23521         }
23522         
23523         if(this.submenu){
23524             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23525             
23526             if(this.pos == 'left'){
23527                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23528             }
23529         }
23530         
23531         return cfg;
23532     },
23533     
23534     initEvents : function() 
23535     {
23536         this.el.on('mouseover', this.onMouseOver, this);
23537         this.el.on('mouseout', this.onMouseOut, this);
23538         
23539         this.el.select('a', true).first().on('click', this.onClick, this);
23540         
23541     },
23542     
23543     onClick : function(e)
23544     {
23545         if(this.preventDefault){
23546             e.preventDefault();
23547         }
23548         
23549         this.fireEvent("click", this, e);
23550     },
23551     
23552     onMouseOver : function(e)
23553     {
23554         if(this.submenu && this.pos == 'left'){
23555             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23556         }
23557         
23558         this.fireEvent("mouseover", this, e);
23559     },
23560     
23561     onMouseOut : function(e)
23562     {
23563         this.fireEvent("mouseout", this, e);
23564     }
23565 });
23566
23567  
23568
23569  /*
23570  * - LGPL
23571  *
23572  * menu separator
23573  * 
23574  */
23575 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23576
23577 /**
23578  * @class Roo.bootstrap.menu.Separator
23579  * @extends Roo.bootstrap.Component
23580  * Bootstrap Separator class
23581  * 
23582  * @constructor
23583  * Create a new Separator
23584  * @param {Object} config The config object
23585  */
23586
23587
23588 Roo.bootstrap.menu.Separator = function(config){
23589     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23590 };
23591
23592 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
23593     
23594     getAutoCreate : function(){
23595         var cfg = {
23596             tag : 'li',
23597             cls: 'divider'
23598         };
23599         
23600         return cfg;
23601     }
23602    
23603 });
23604
23605  
23606
23607  /*
23608  * - LGPL
23609  *
23610  * Tooltip
23611  * 
23612  */
23613
23614 /**
23615  * @class Roo.bootstrap.Tooltip
23616  * Bootstrap Tooltip class
23617  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23618  * to determine which dom element triggers the tooltip.
23619  * 
23620  * It needs to add support for additional attributes like tooltip-position
23621  * 
23622  * @constructor
23623  * Create a new Toolti
23624  * @param {Object} config The config object
23625  */
23626
23627 Roo.bootstrap.Tooltip = function(config){
23628     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23629 };
23630
23631 Roo.apply(Roo.bootstrap.Tooltip, {
23632     /**
23633      * @function init initialize tooltip monitoring.
23634      * @static
23635      */
23636     currentEl : false,
23637     currentTip : false,
23638     currentRegion : false,
23639     
23640     //  init : delay?
23641     
23642     init : function()
23643     {
23644         Roo.get(document).on('mouseover', this.enter ,this);
23645         Roo.get(document).on('mouseout', this.leave, this);
23646          
23647         
23648         this.currentTip = new Roo.bootstrap.Tooltip();
23649     },
23650     
23651     enter : function(ev)
23652     {
23653         var dom = ev.getTarget();
23654         
23655         //Roo.log(['enter',dom]);
23656         var el = Roo.fly(dom);
23657         if (this.currentEl) {
23658             //Roo.log(dom);
23659             //Roo.log(this.currentEl);
23660             //Roo.log(this.currentEl.contains(dom));
23661             if (this.currentEl == el) {
23662                 return;
23663             }
23664             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23665                 return;
23666             }
23667
23668         }
23669         
23670         if (this.currentTip.el) {
23671             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
23672         }    
23673         //Roo.log(ev);
23674         var bindEl = el;
23675         
23676         // you can not look for children, as if el is the body.. then everythign is the child..
23677         if (!el.attr('tooltip')) { //
23678             if (!el.select("[tooltip]").elements.length) {
23679                 return;
23680             }
23681             // is the mouse over this child...?
23682             bindEl = el.select("[tooltip]").first();
23683             var xy = ev.getXY();
23684             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23685                 //Roo.log("not in region.");
23686                 return;
23687             }
23688             //Roo.log("child element over..");
23689             
23690         }
23691         this.currentEl = bindEl;
23692         this.currentTip.bind(bindEl);
23693         this.currentRegion = Roo.lib.Region.getRegion(dom);
23694         this.currentTip.enter();
23695         
23696     },
23697     leave : function(ev)
23698     {
23699         var dom = ev.getTarget();
23700         //Roo.log(['leave',dom]);
23701         if (!this.currentEl) {
23702             return;
23703         }
23704         
23705         
23706         if (dom != this.currentEl.dom) {
23707             return;
23708         }
23709         var xy = ev.getXY();
23710         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
23711             return;
23712         }
23713         // only activate leave if mouse cursor is outside... bounding box..
23714         
23715         
23716         
23717         
23718         if (this.currentTip) {
23719             this.currentTip.leave();
23720         }
23721         //Roo.log('clear currentEl');
23722         this.currentEl = false;
23723         
23724         
23725     },
23726     alignment : {
23727         'left' : ['r-l', [-2,0], 'right'],
23728         'right' : ['l-r', [2,0], 'left'],
23729         'bottom' : ['t-b', [0,2], 'top'],
23730         'top' : [ 'b-t', [0,-2], 'bottom']
23731     }
23732     
23733 });
23734
23735
23736 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
23737     
23738     
23739     bindEl : false,
23740     
23741     delay : null, // can be { show : 300 , hide: 500}
23742     
23743     timeout : null,
23744     
23745     hoverState : null, //???
23746     
23747     placement : 'bottom', 
23748     
23749     getAutoCreate : function(){
23750     
23751         var cfg = {
23752            cls : 'tooltip',
23753            role : 'tooltip',
23754            cn : [
23755                 {
23756                     cls : 'tooltip-arrow'
23757                 },
23758                 {
23759                     cls : 'tooltip-inner'
23760                 }
23761            ]
23762         };
23763         
23764         return cfg;
23765     },
23766     bind : function(el)
23767     {
23768         this.bindEl = el;
23769     },
23770       
23771     
23772     enter : function () {
23773        
23774         if (this.timeout != null) {
23775             clearTimeout(this.timeout);
23776         }
23777         
23778         this.hoverState = 'in';
23779          //Roo.log("enter - show");
23780         if (!this.delay || !this.delay.show) {
23781             this.show();
23782             return;
23783         }
23784         var _t = this;
23785         this.timeout = setTimeout(function () {
23786             if (_t.hoverState == 'in') {
23787                 _t.show();
23788             }
23789         }, this.delay.show);
23790     },
23791     leave : function()
23792     {
23793         clearTimeout(this.timeout);
23794     
23795         this.hoverState = 'out';
23796          if (!this.delay || !this.delay.hide) {
23797             this.hide();
23798             return;
23799         }
23800        
23801         var _t = this;
23802         this.timeout = setTimeout(function () {
23803             //Roo.log("leave - timeout");
23804             
23805             if (_t.hoverState == 'out') {
23806                 _t.hide();
23807                 Roo.bootstrap.Tooltip.currentEl = false;
23808             }
23809         }, delay);
23810     },
23811     
23812     show : function ()
23813     {
23814         if (!this.el) {
23815             this.render(document.body);
23816         }
23817         // set content.
23818         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23819         
23820         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23821         
23822         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23823         
23824         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23825         
23826         var placement = typeof this.placement == 'function' ?
23827             this.placement.call(this, this.el, on_el) :
23828             this.placement;
23829             
23830         var autoToken = /\s?auto?\s?/i;
23831         var autoPlace = autoToken.test(placement);
23832         if (autoPlace) {
23833             placement = placement.replace(autoToken, '') || 'top';
23834         }
23835         
23836         //this.el.detach()
23837         //this.el.setXY([0,0]);
23838         this.el.show();
23839         //this.el.dom.style.display='block';
23840         
23841         //this.el.appendTo(on_el);
23842         
23843         var p = this.getPosition();
23844         var box = this.el.getBox();
23845         
23846         if (autoPlace) {
23847             // fixme..
23848         }
23849         
23850         var align = Roo.bootstrap.Tooltip.alignment[placement];
23851         
23852         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
23853         
23854         if(placement == 'top' || placement == 'bottom'){
23855             if(xy[0] < 0){
23856                 placement = 'right';
23857             }
23858             
23859             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
23860                 placement = 'left';
23861             }
23862         }
23863         
23864         align = Roo.bootstrap.Tooltip.alignment[placement];
23865         
23866         this.el.alignTo(this.bindEl, align[0],align[1]);
23867         //var arrow = this.el.select('.arrow',true).first();
23868         //arrow.set(align[2], 
23869         
23870         this.el.addClass(placement);
23871         
23872         this.el.addClass('in fade');
23873         
23874         this.hoverState = null;
23875         
23876         if (this.el.hasClass('fade')) {
23877             // fade it?
23878         }
23879         
23880     },
23881     hide : function()
23882     {
23883          
23884         if (!this.el) {
23885             return;
23886         }
23887         //this.el.setXY([0,0]);
23888         this.el.removeClass('in');
23889         //this.el.hide();
23890         
23891     }
23892     
23893 });
23894  
23895
23896  /*
23897  * - LGPL
23898  *
23899  * Location Picker
23900  * 
23901  */
23902
23903 /**
23904  * @class Roo.bootstrap.LocationPicker
23905  * @extends Roo.bootstrap.Component
23906  * Bootstrap LocationPicker class
23907  * @cfg {Number} latitude Position when init default 0
23908  * @cfg {Number} longitude Position when init default 0
23909  * @cfg {Number} zoom default 15
23910  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
23911  * @cfg {Boolean} mapTypeControl default false
23912  * @cfg {Boolean} disableDoubleClickZoom default false
23913  * @cfg {Boolean} scrollwheel default true
23914  * @cfg {Boolean} streetViewControl default false
23915  * @cfg {Number} radius default 0
23916  * @cfg {String} locationName
23917  * @cfg {Boolean} draggable default true
23918  * @cfg {Boolean} enableAutocomplete default false
23919  * @cfg {Boolean} enableReverseGeocode default true
23920  * @cfg {String} markerTitle
23921  * 
23922  * @constructor
23923  * Create a new LocationPicker
23924  * @param {Object} config The config object
23925  */
23926
23927
23928 Roo.bootstrap.LocationPicker = function(config){
23929     
23930     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
23931     
23932     this.addEvents({
23933         /**
23934          * @event initial
23935          * Fires when the picker initialized.
23936          * @param {Roo.bootstrap.LocationPicker} this
23937          * @param {Google Location} location
23938          */
23939         initial : true,
23940         /**
23941          * @event positionchanged
23942          * Fires when the picker position changed.
23943          * @param {Roo.bootstrap.LocationPicker} this
23944          * @param {Google Location} location
23945          */
23946         positionchanged : true,
23947         /**
23948          * @event resize
23949          * Fires when the map resize.
23950          * @param {Roo.bootstrap.LocationPicker} this
23951          */
23952         resize : true,
23953         /**
23954          * @event show
23955          * Fires when the map show.
23956          * @param {Roo.bootstrap.LocationPicker} this
23957          */
23958         show : true,
23959         /**
23960          * @event hide
23961          * Fires when the map hide.
23962          * @param {Roo.bootstrap.LocationPicker} this
23963          */
23964         hide : true,
23965         /**
23966          * @event mapClick
23967          * Fires when click the map.
23968          * @param {Roo.bootstrap.LocationPicker} this
23969          * @param {Map event} e
23970          */
23971         mapClick : true,
23972         /**
23973          * @event mapRightClick
23974          * Fires when right click the map.
23975          * @param {Roo.bootstrap.LocationPicker} this
23976          * @param {Map event} e
23977          */
23978         mapRightClick : true,
23979         /**
23980          * @event markerClick
23981          * Fires when click the marker.
23982          * @param {Roo.bootstrap.LocationPicker} this
23983          * @param {Map event} e
23984          */
23985         markerClick : true,
23986         /**
23987          * @event markerRightClick
23988          * Fires when right click the marker.
23989          * @param {Roo.bootstrap.LocationPicker} this
23990          * @param {Map event} e
23991          */
23992         markerRightClick : true,
23993         /**
23994          * @event OverlayViewDraw
23995          * Fires when OverlayView Draw
23996          * @param {Roo.bootstrap.LocationPicker} this
23997          */
23998         OverlayViewDraw : true,
23999         /**
24000          * @event OverlayViewOnAdd
24001          * Fires when OverlayView Draw
24002          * @param {Roo.bootstrap.LocationPicker} this
24003          */
24004         OverlayViewOnAdd : true,
24005         /**
24006          * @event OverlayViewOnRemove
24007          * Fires when OverlayView Draw
24008          * @param {Roo.bootstrap.LocationPicker} this
24009          */
24010         OverlayViewOnRemove : true,
24011         /**
24012          * @event OverlayViewShow
24013          * Fires when OverlayView Draw
24014          * @param {Roo.bootstrap.LocationPicker} this
24015          * @param {Pixel} cpx
24016          */
24017         OverlayViewShow : true,
24018         /**
24019          * @event OverlayViewHide
24020          * Fires when OverlayView Draw
24021          * @param {Roo.bootstrap.LocationPicker} this
24022          */
24023         OverlayViewHide : true,
24024         /**
24025          * @event loadexception
24026          * Fires when load google lib failed.
24027          * @param {Roo.bootstrap.LocationPicker} this
24028          */
24029         loadexception : true
24030     });
24031         
24032 };
24033
24034 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
24035     
24036     gMapContext: false,
24037     
24038     latitude: 0,
24039     longitude: 0,
24040     zoom: 15,
24041     mapTypeId: false,
24042     mapTypeControl: false,
24043     disableDoubleClickZoom: false,
24044     scrollwheel: true,
24045     streetViewControl: false,
24046     radius: 0,
24047     locationName: '',
24048     draggable: true,
24049     enableAutocomplete: false,
24050     enableReverseGeocode: true,
24051     markerTitle: '',
24052     
24053     getAutoCreate: function()
24054     {
24055
24056         var cfg = {
24057             tag: 'div',
24058             cls: 'roo-location-picker'
24059         };
24060         
24061         return cfg
24062     },
24063     
24064     initEvents: function(ct, position)
24065     {       
24066         if(!this.el.getWidth() || this.isApplied()){
24067             return;
24068         }
24069         
24070         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24071         
24072         this.initial();
24073     },
24074     
24075     initial: function()
24076     {
24077         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24078             this.fireEvent('loadexception', this);
24079             return;
24080         }
24081         
24082         if(!this.mapTypeId){
24083             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24084         }
24085         
24086         this.gMapContext = this.GMapContext();
24087         
24088         this.initOverlayView();
24089         
24090         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24091         
24092         var _this = this;
24093                 
24094         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24095             _this.setPosition(_this.gMapContext.marker.position);
24096         });
24097         
24098         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
24099             _this.fireEvent('mapClick', this, event);
24100             
24101         });
24102
24103         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
24104             _this.fireEvent('mapRightClick', this, event);
24105             
24106         });
24107         
24108         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
24109             _this.fireEvent('markerClick', this, event);
24110             
24111         });
24112
24113         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
24114             _this.fireEvent('markerRightClick', this, event);
24115             
24116         });
24117         
24118         this.setPosition(this.gMapContext.location);
24119         
24120         this.fireEvent('initial', this, this.gMapContext.location);
24121     },
24122     
24123     initOverlayView: function()
24124     {
24125         var _this = this;
24126         
24127         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
24128             
24129             draw: function()
24130             {
24131                 _this.fireEvent('OverlayViewDraw', _this);
24132             },
24133             
24134             onAdd: function()
24135             {
24136                 _this.fireEvent('OverlayViewOnAdd', _this);
24137             },
24138             
24139             onRemove: function()
24140             {
24141                 _this.fireEvent('OverlayViewOnRemove', _this);
24142             },
24143             
24144             show: function(cpx)
24145             {
24146                 _this.fireEvent('OverlayViewShow', _this, cpx);
24147             },
24148             
24149             hide: function()
24150             {
24151                 _this.fireEvent('OverlayViewHide', _this);
24152             }
24153             
24154         });
24155     },
24156     
24157     fromLatLngToContainerPixel: function(event)
24158     {
24159         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
24160     },
24161     
24162     isApplied: function() 
24163     {
24164         return this.getGmapContext() == false ? false : true;
24165     },
24166     
24167     getGmapContext: function() 
24168     {
24169         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
24170     },
24171     
24172     GMapContext: function() 
24173     {
24174         var position = new google.maps.LatLng(this.latitude, this.longitude);
24175         
24176         var _map = new google.maps.Map(this.el.dom, {
24177             center: position,
24178             zoom: this.zoom,
24179             mapTypeId: this.mapTypeId,
24180             mapTypeControl: this.mapTypeControl,
24181             disableDoubleClickZoom: this.disableDoubleClickZoom,
24182             scrollwheel: this.scrollwheel,
24183             streetViewControl: this.streetViewControl,
24184             locationName: this.locationName,
24185             draggable: this.draggable,
24186             enableAutocomplete: this.enableAutocomplete,
24187             enableReverseGeocode: this.enableReverseGeocode
24188         });
24189         
24190         var _marker = new google.maps.Marker({
24191             position: position,
24192             map: _map,
24193             title: this.markerTitle,
24194             draggable: this.draggable
24195         });
24196         
24197         return {
24198             map: _map,
24199             marker: _marker,
24200             circle: null,
24201             location: position,
24202             radius: this.radius,
24203             locationName: this.locationName,
24204             addressComponents: {
24205                 formatted_address: null,
24206                 addressLine1: null,
24207                 addressLine2: null,
24208                 streetName: null,
24209                 streetNumber: null,
24210                 city: null,
24211                 district: null,
24212                 state: null,
24213                 stateOrProvince: null
24214             },
24215             settings: this,
24216             domContainer: this.el.dom,
24217             geodecoder: new google.maps.Geocoder()
24218         };
24219     },
24220     
24221     drawCircle: function(center, radius, options) 
24222     {
24223         if (this.gMapContext.circle != null) {
24224             this.gMapContext.circle.setMap(null);
24225         }
24226         if (radius > 0) {
24227             radius *= 1;
24228             options = Roo.apply({}, options, {
24229                 strokeColor: "#0000FF",
24230                 strokeOpacity: .35,
24231                 strokeWeight: 2,
24232                 fillColor: "#0000FF",
24233                 fillOpacity: .2
24234             });
24235             
24236             options.map = this.gMapContext.map;
24237             options.radius = radius;
24238             options.center = center;
24239             this.gMapContext.circle = new google.maps.Circle(options);
24240             return this.gMapContext.circle;
24241         }
24242         
24243         return null;
24244     },
24245     
24246     setPosition: function(location) 
24247     {
24248         this.gMapContext.location = location;
24249         this.gMapContext.marker.setPosition(location);
24250         this.gMapContext.map.panTo(location);
24251         this.drawCircle(location, this.gMapContext.radius, {});
24252         
24253         var _this = this;
24254         
24255         if (this.gMapContext.settings.enableReverseGeocode) {
24256             this.gMapContext.geodecoder.geocode({
24257                 latLng: this.gMapContext.location
24258             }, function(results, status) {
24259                 
24260                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
24261                     _this.gMapContext.locationName = results[0].formatted_address;
24262                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
24263                     
24264                     _this.fireEvent('positionchanged', this, location);
24265                 }
24266             });
24267             
24268             return;
24269         }
24270         
24271         this.fireEvent('positionchanged', this, location);
24272     },
24273     
24274     resize: function()
24275     {
24276         google.maps.event.trigger(this.gMapContext.map, "resize");
24277         
24278         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24279         
24280         this.fireEvent('resize', this);
24281     },
24282     
24283     setPositionByLatLng: function(latitude, longitude)
24284     {
24285         this.setPosition(new google.maps.LatLng(latitude, longitude));
24286     },
24287     
24288     getCurrentPosition: function() 
24289     {
24290         return {
24291             latitude: this.gMapContext.location.lat(),
24292             longitude: this.gMapContext.location.lng()
24293         };
24294     },
24295     
24296     getAddressName: function() 
24297     {
24298         return this.gMapContext.locationName;
24299     },
24300     
24301     getAddressComponents: function() 
24302     {
24303         return this.gMapContext.addressComponents;
24304     },
24305     
24306     address_component_from_google_geocode: function(address_components) 
24307     {
24308         var result = {};
24309         
24310         for (var i = 0; i < address_components.length; i++) {
24311             var component = address_components[i];
24312             if (component.types.indexOf("postal_code") >= 0) {
24313                 result.postalCode = component.short_name;
24314             } else if (component.types.indexOf("street_number") >= 0) {
24315                 result.streetNumber = component.short_name;
24316             } else if (component.types.indexOf("route") >= 0) {
24317                 result.streetName = component.short_name;
24318             } else if (component.types.indexOf("neighborhood") >= 0) {
24319                 result.city = component.short_name;
24320             } else if (component.types.indexOf("locality") >= 0) {
24321                 result.city = component.short_name;
24322             } else if (component.types.indexOf("sublocality") >= 0) {
24323                 result.district = component.short_name;
24324             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
24325                 result.stateOrProvince = component.short_name;
24326             } else if (component.types.indexOf("country") >= 0) {
24327                 result.country = component.short_name;
24328             }
24329         }
24330         
24331         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
24332         result.addressLine2 = "";
24333         return result;
24334     },
24335     
24336     setZoomLevel: function(zoom)
24337     {
24338         this.gMapContext.map.setZoom(zoom);
24339     },
24340     
24341     show: function()
24342     {
24343         if(!this.el){
24344             return;
24345         }
24346         
24347         this.el.show();
24348         
24349         this.resize();
24350         
24351         this.fireEvent('show', this);
24352     },
24353     
24354     hide: function()
24355     {
24356         if(!this.el){
24357             return;
24358         }
24359         
24360         this.el.hide();
24361         
24362         this.fireEvent('hide', this);
24363     }
24364     
24365 });
24366
24367 Roo.apply(Roo.bootstrap.LocationPicker, {
24368     
24369     OverlayView : function(map, options)
24370     {
24371         options = options || {};
24372         
24373         this.setMap(map);
24374     }
24375     
24376     
24377 });/*
24378  * - LGPL
24379  *
24380  * Alert
24381  * 
24382  */
24383
24384 /**
24385  * @class Roo.bootstrap.Alert
24386  * @extends Roo.bootstrap.Component
24387  * Bootstrap Alert class
24388  * @cfg {String} title The title of alert
24389  * @cfg {String} html The content of alert
24390  * @cfg {String} weight (  success | info | warning | danger )
24391  * @cfg {String} faicon font-awesomeicon
24392  * 
24393  * @constructor
24394  * Create a new alert
24395  * @param {Object} config The config object
24396  */
24397
24398
24399 Roo.bootstrap.Alert = function(config){
24400     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
24401     
24402 };
24403
24404 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
24405     
24406     title: '',
24407     html: '',
24408     weight: false,
24409     faicon: false,
24410     
24411     getAutoCreate : function()
24412     {
24413         
24414         var cfg = {
24415             tag : 'div',
24416             cls : 'alert',
24417             cn : [
24418                 {
24419                     tag : 'i',
24420                     cls : 'roo-alert-icon'
24421                     
24422                 },
24423                 {
24424                     tag : 'b',
24425                     cls : 'roo-alert-title',
24426                     html : this.title
24427                 },
24428                 {
24429                     tag : 'span',
24430                     cls : 'roo-alert-text',
24431                     html : this.html
24432                 }
24433             ]
24434         };
24435         
24436         if(this.faicon){
24437             cfg.cn[0].cls += ' fa ' + this.faicon;
24438         }
24439         
24440         if(this.weight){
24441             cfg.cls += ' alert-' + this.weight;
24442         }
24443         
24444         return cfg;
24445     },
24446     
24447     initEvents: function() 
24448     {
24449         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24450     },
24451     
24452     setTitle : function(str)
24453     {
24454         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24455     },
24456     
24457     setText : function(str)
24458     {
24459         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24460     },
24461     
24462     setWeight : function(weight)
24463     {
24464         if(this.weight){
24465             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24466         }
24467         
24468         this.weight = weight;
24469         
24470         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24471     },
24472     
24473     setIcon : function(icon)
24474     {
24475         if(this.faicon){
24476             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24477         }
24478         
24479         this.faicon = icon;
24480         
24481         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24482     },
24483     
24484     hide: function() 
24485     {
24486         this.el.hide();   
24487     },
24488     
24489     show: function() 
24490     {  
24491         this.el.show();   
24492     }
24493     
24494 });
24495
24496  
24497 /*
24498 * Licence: LGPL
24499 */
24500
24501 /**
24502  * @class Roo.bootstrap.UploadCropbox
24503  * @extends Roo.bootstrap.Component
24504  * Bootstrap UploadCropbox class
24505  * @cfg {String} emptyText show when image has been loaded
24506  * @cfg {String} rotateNotify show when image too small to rotate
24507  * @cfg {Number} errorTimeout default 3000
24508  * @cfg {Number} minWidth default 300
24509  * @cfg {Number} minHeight default 300
24510  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24511  * @cfg {Boolean} isDocument (true|false) default false
24512  * @cfg {String} url action url
24513  * @cfg {String} paramName default 'imageUpload'
24514  * @cfg {String} method default POST
24515  * @cfg {Boolean} loadMask (true|false) default true
24516  * @cfg {Boolean} loadingText default 'Loading...'
24517  * 
24518  * @constructor
24519  * Create a new UploadCropbox
24520  * @param {Object} config The config object
24521  */
24522
24523 Roo.bootstrap.UploadCropbox = function(config){
24524     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
24525     
24526     this.addEvents({
24527         /**
24528          * @event beforeselectfile
24529          * Fire before select file
24530          * @param {Roo.bootstrap.UploadCropbox} this
24531          */
24532         "beforeselectfile" : true,
24533         /**
24534          * @event initial
24535          * Fire after initEvent
24536          * @param {Roo.bootstrap.UploadCropbox} this
24537          */
24538         "initial" : true,
24539         /**
24540          * @event crop
24541          * Fire after initEvent
24542          * @param {Roo.bootstrap.UploadCropbox} this
24543          * @param {String} data
24544          */
24545         "crop" : true,
24546         /**
24547          * @event prepare
24548          * Fire when preparing the file data
24549          * @param {Roo.bootstrap.UploadCropbox} this
24550          * @param {Object} file
24551          */
24552         "prepare" : true,
24553         /**
24554          * @event exception
24555          * Fire when get exception
24556          * @param {Roo.bootstrap.UploadCropbox} this
24557          * @param {XMLHttpRequest} xhr
24558          */
24559         "exception" : true,
24560         /**
24561          * @event beforeloadcanvas
24562          * Fire before load the canvas
24563          * @param {Roo.bootstrap.UploadCropbox} this
24564          * @param {String} src
24565          */
24566         "beforeloadcanvas" : true,
24567         /**
24568          * @event trash
24569          * Fire when trash image
24570          * @param {Roo.bootstrap.UploadCropbox} this
24571          */
24572         "trash" : true,
24573         /**
24574          * @event download
24575          * Fire when download the image
24576          * @param {Roo.bootstrap.UploadCropbox} this
24577          */
24578         "download" : true,
24579         /**
24580          * @event footerbuttonclick
24581          * Fire when footerbuttonclick
24582          * @param {Roo.bootstrap.UploadCropbox} this
24583          * @param {String} type
24584          */
24585         "footerbuttonclick" : true,
24586         /**
24587          * @event resize
24588          * Fire when resize
24589          * @param {Roo.bootstrap.UploadCropbox} this
24590          */
24591         "resize" : true,
24592         /**
24593          * @event rotate
24594          * Fire when rotate the image
24595          * @param {Roo.bootstrap.UploadCropbox} this
24596          * @param {String} pos
24597          */
24598         "rotate" : true,
24599         /**
24600          * @event inspect
24601          * Fire when inspect the file
24602          * @param {Roo.bootstrap.UploadCropbox} this
24603          * @param {Object} file
24604          */
24605         "inspect" : true,
24606         /**
24607          * @event upload
24608          * Fire when xhr upload the file
24609          * @param {Roo.bootstrap.UploadCropbox} this
24610          * @param {Object} data
24611          */
24612         "upload" : true,
24613         /**
24614          * @event arrange
24615          * Fire when arrange the file data
24616          * @param {Roo.bootstrap.UploadCropbox} this
24617          * @param {Object} formData
24618          */
24619         "arrange" : true
24620     });
24621     
24622     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24623 };
24624
24625 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
24626     
24627     emptyText : 'Click to upload image',
24628     rotateNotify : 'Image is too small to rotate',
24629     errorTimeout : 3000,
24630     scale : 0,
24631     baseScale : 1,
24632     rotate : 0,
24633     dragable : false,
24634     pinching : false,
24635     mouseX : 0,
24636     mouseY : 0,
24637     cropData : false,
24638     minWidth : 300,
24639     minHeight : 300,
24640     file : false,
24641     exif : {},
24642     baseRotate : 1,
24643     cropType : 'image/jpeg',
24644     buttons : false,
24645     canvasLoaded : false,
24646     isDocument : false,
24647     method : 'POST',
24648     paramName : 'imageUpload',
24649     loadMask : true,
24650     loadingText : 'Loading...',
24651     maskEl : false,
24652     
24653     getAutoCreate : function()
24654     {
24655         var cfg = {
24656             tag : 'div',
24657             cls : 'roo-upload-cropbox',
24658             cn : [
24659                 {
24660                     tag : 'input',
24661                     cls : 'roo-upload-cropbox-selector',
24662                     type : 'file'
24663                 },
24664                 {
24665                     tag : 'div',
24666                     cls : 'roo-upload-cropbox-body',
24667                     style : 'cursor:pointer',
24668                     cn : [
24669                         {
24670                             tag : 'div',
24671                             cls : 'roo-upload-cropbox-preview'
24672                         },
24673                         {
24674                             tag : 'div',
24675                             cls : 'roo-upload-cropbox-thumb'
24676                         },
24677                         {
24678                             tag : 'div',
24679                             cls : 'roo-upload-cropbox-empty-notify',
24680                             html : this.emptyText
24681                         },
24682                         {
24683                             tag : 'div',
24684                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
24685                             html : this.rotateNotify
24686                         }
24687                     ]
24688                 },
24689                 {
24690                     tag : 'div',
24691                     cls : 'roo-upload-cropbox-footer',
24692                     cn : {
24693                         tag : 'div',
24694                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24695                         cn : []
24696                     }
24697                 }
24698             ]
24699         };
24700         
24701         return cfg;
24702     },
24703     
24704     onRender : function(ct, position)
24705     {
24706         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
24707         
24708         if (this.buttons.length) {
24709             
24710             Roo.each(this.buttons, function(bb) {
24711                 
24712                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
24713                 
24714                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
24715                 
24716             }, this);
24717         }
24718         
24719         if(this.loadMask){
24720             this.maskEl = this.el;
24721         }
24722     },
24723     
24724     initEvents : function()
24725     {
24726         this.urlAPI = (window.createObjectURL && window) || 
24727                                 (window.URL && URL.revokeObjectURL && URL) || 
24728                                 (window.webkitURL && webkitURL);
24729                         
24730         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
24731         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24732         
24733         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
24734         this.selectorEl.hide();
24735         
24736         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
24737         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24738         
24739         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
24740         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24741         this.thumbEl.hide();
24742         
24743         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
24744         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24745         
24746         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
24747         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24748         this.errorEl.hide();
24749         
24750         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
24751         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24752         this.footerEl.hide();
24753         
24754         this.setThumbBoxSize();
24755         
24756         this.bind();
24757         
24758         this.resize();
24759         
24760         this.fireEvent('initial', this);
24761     },
24762
24763     bind : function()
24764     {
24765         var _this = this;
24766         
24767         window.addEventListener("resize", function() { _this.resize(); } );
24768         
24769         this.bodyEl.on('click', this.beforeSelectFile, this);
24770         
24771         if(Roo.isTouch){
24772             this.bodyEl.on('touchstart', this.onTouchStart, this);
24773             this.bodyEl.on('touchmove', this.onTouchMove, this);
24774             this.bodyEl.on('touchend', this.onTouchEnd, this);
24775         }
24776         
24777         if(!Roo.isTouch){
24778             this.bodyEl.on('mousedown', this.onMouseDown, this);
24779             this.bodyEl.on('mousemove', this.onMouseMove, this);
24780             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
24781             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
24782             Roo.get(document).on('mouseup', this.onMouseUp, this);
24783         }
24784         
24785         this.selectorEl.on('change', this.onFileSelected, this);
24786     },
24787     
24788     reset : function()
24789     {    
24790         this.scale = 0;
24791         this.baseScale = 1;
24792         this.rotate = 0;
24793         this.baseRotate = 1;
24794         this.dragable = false;
24795         this.pinching = false;
24796         this.mouseX = 0;
24797         this.mouseY = 0;
24798         this.cropData = false;
24799         this.notifyEl.dom.innerHTML = this.emptyText;
24800         
24801         this.selectorEl.dom.value = '';
24802         
24803     },
24804     
24805     resize : function()
24806     {
24807         if(this.fireEvent('resize', this) != false){
24808             this.setThumbBoxPosition();
24809             this.setCanvasPosition();
24810         }
24811     },
24812     
24813     onFooterButtonClick : function(e, el, o, type)
24814     {
24815         switch (type) {
24816             case 'rotate-left' :
24817                 this.onRotateLeft(e);
24818                 break;
24819             case 'rotate-right' :
24820                 this.onRotateRight(e);
24821                 break;
24822             case 'picture' :
24823                 this.beforeSelectFile(e);
24824                 break;
24825             case 'trash' :
24826                 this.trash(e);
24827                 break;
24828             case 'crop' :
24829                 this.crop(e);
24830                 break;
24831             case 'download' :
24832                 this.download(e);
24833                 break;
24834             default :
24835                 break;
24836         }
24837         
24838         this.fireEvent('footerbuttonclick', this, type);
24839     },
24840     
24841     beforeSelectFile : function(e)
24842     {
24843         e.preventDefault();
24844         
24845         if(this.fireEvent('beforeselectfile', this) != false){
24846             this.selectorEl.dom.click();
24847         }
24848     },
24849     
24850     onFileSelected : function(e)
24851     {
24852         e.preventDefault();
24853         
24854         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
24855             return;
24856         }
24857         
24858         var file = this.selectorEl.dom.files[0];
24859         
24860         if(this.fireEvent('inspect', this, file) != false){
24861             this.prepare(file);
24862         }
24863         
24864     },
24865     
24866     trash : function(e)
24867     {
24868         this.fireEvent('trash', this);
24869     },
24870     
24871     download : function(e)
24872     {
24873         this.fireEvent('download', this);
24874     },
24875     
24876     loadCanvas : function(src)
24877     {   
24878         if(this.fireEvent('beforeloadcanvas', this, src) != false){
24879             
24880             this.reset();
24881             
24882             this.imageEl = document.createElement('img');
24883             
24884             var _this = this;
24885             
24886             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
24887             
24888             this.imageEl.src = src;
24889         }
24890     },
24891     
24892     onLoadCanvas : function()
24893     {   
24894         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
24895         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
24896         
24897         this.bodyEl.un('click', this.beforeSelectFile, this);
24898         
24899         this.notifyEl.hide();
24900         this.thumbEl.show();
24901         this.footerEl.show();
24902         
24903         this.baseRotateLevel();
24904         
24905         if(this.isDocument){
24906             this.setThumbBoxSize();
24907         }
24908         
24909         this.setThumbBoxPosition();
24910         
24911         this.baseScaleLevel();
24912         
24913         this.draw();
24914         
24915         this.resize();
24916         
24917         this.canvasLoaded = true;
24918         
24919         if(this.loadMask){
24920             this.maskEl.unmask();
24921         }
24922         
24923     },
24924     
24925     setCanvasPosition : function()
24926     {   
24927         if(!this.canvasEl){
24928             return;
24929         }
24930         
24931         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
24932         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
24933         
24934         this.previewEl.setLeft(pw);
24935         this.previewEl.setTop(ph);
24936         
24937     },
24938     
24939     onMouseDown : function(e)
24940     {   
24941         e.stopEvent();
24942         
24943         this.dragable = true;
24944         this.pinching = false;
24945         
24946         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
24947             this.dragable = false;
24948             return;
24949         }
24950         
24951         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24952         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24953         
24954     },
24955     
24956     onMouseMove : function(e)
24957     {   
24958         e.stopEvent();
24959         
24960         if(!this.canvasLoaded){
24961             return;
24962         }
24963         
24964         if (!this.dragable){
24965             return;
24966         }
24967         
24968         var minX = Math.ceil(this.thumbEl.getLeft(true));
24969         var minY = Math.ceil(this.thumbEl.getTop(true));
24970         
24971         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
24972         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
24973         
24974         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24975         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24976         
24977         x = x - this.mouseX;
24978         y = y - this.mouseY;
24979         
24980         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
24981         var bgY = Math.ceil(y + this.previewEl.getTop(true));
24982         
24983         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
24984         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
24985         
24986         this.previewEl.setLeft(bgX);
24987         this.previewEl.setTop(bgY);
24988         
24989         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24990         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24991     },
24992     
24993     onMouseUp : function(e)
24994     {   
24995         e.stopEvent();
24996         
24997         this.dragable = false;
24998     },
24999     
25000     onMouseWheel : function(e)
25001     {   
25002         e.stopEvent();
25003         
25004         this.startScale = this.scale;
25005         
25006         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
25007         
25008         if(!this.zoomable()){
25009             this.scale = this.startScale;
25010             return;
25011         }
25012         
25013         this.draw();
25014         
25015         return;
25016     },
25017     
25018     zoomable : function()
25019     {
25020         var minScale = this.thumbEl.getWidth() / this.minWidth;
25021         
25022         if(this.minWidth < this.minHeight){
25023             minScale = this.thumbEl.getHeight() / this.minHeight;
25024         }
25025         
25026         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
25027         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
25028         
25029         if(
25030                 this.isDocument &&
25031                 (this.rotate == 0 || this.rotate == 180) && 
25032                 (
25033                     width > this.imageEl.OriginWidth || 
25034                     height > this.imageEl.OriginHeight ||
25035                     (width < this.minWidth && height < this.minHeight)
25036                 )
25037         ){
25038             return false;
25039         }
25040         
25041         if(
25042                 this.isDocument &&
25043                 (this.rotate == 90 || this.rotate == 270) && 
25044                 (
25045                     width > this.imageEl.OriginWidth || 
25046                     height > this.imageEl.OriginHeight ||
25047                     (width < this.minHeight && height < this.minWidth)
25048                 )
25049         ){
25050             return false;
25051         }
25052         
25053         if(
25054                 !this.isDocument &&
25055                 (this.rotate == 0 || this.rotate == 180) && 
25056                 (
25057                     width < this.minWidth || 
25058                     width > this.imageEl.OriginWidth || 
25059                     height < this.minHeight || 
25060                     height > this.imageEl.OriginHeight
25061                 )
25062         ){
25063             return false;
25064         }
25065         
25066         if(
25067                 !this.isDocument &&
25068                 (this.rotate == 90 || this.rotate == 270) && 
25069                 (
25070                     width < this.minHeight || 
25071                     width > this.imageEl.OriginWidth || 
25072                     height < this.minWidth || 
25073                     height > this.imageEl.OriginHeight
25074                 )
25075         ){
25076             return false;
25077         }
25078         
25079         return true;
25080         
25081     },
25082     
25083     onRotateLeft : function(e)
25084     {   
25085         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25086             
25087             var minScale = this.thumbEl.getWidth() / this.minWidth;
25088             
25089             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25090             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25091             
25092             this.startScale = this.scale;
25093             
25094             while (this.getScaleLevel() < minScale){
25095             
25096                 this.scale = this.scale + 1;
25097                 
25098                 if(!this.zoomable()){
25099                     break;
25100                 }
25101                 
25102                 if(
25103                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25104                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25105                 ){
25106                     continue;
25107                 }
25108                 
25109                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25110
25111                 this.draw();
25112                 
25113                 return;
25114             }
25115             
25116             this.scale = this.startScale;
25117             
25118             this.onRotateFail();
25119             
25120             return false;
25121         }
25122         
25123         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25124
25125         if(this.isDocument){
25126             this.setThumbBoxSize();
25127             this.setThumbBoxPosition();
25128             this.setCanvasPosition();
25129         }
25130         
25131         this.draw();
25132         
25133         this.fireEvent('rotate', this, 'left');
25134         
25135     },
25136     
25137     onRotateRight : function(e)
25138     {
25139         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25140             
25141             var minScale = this.thumbEl.getWidth() / this.minWidth;
25142         
25143             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25144             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25145             
25146             this.startScale = this.scale;
25147             
25148             while (this.getScaleLevel() < minScale){
25149             
25150                 this.scale = this.scale + 1;
25151                 
25152                 if(!this.zoomable()){
25153                     break;
25154                 }
25155                 
25156                 if(
25157                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25158                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25159                 ){
25160                     continue;
25161                 }
25162                 
25163                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25164
25165                 this.draw();
25166                 
25167                 return;
25168             }
25169             
25170             this.scale = this.startScale;
25171             
25172             this.onRotateFail();
25173             
25174             return false;
25175         }
25176         
25177         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25178
25179         if(this.isDocument){
25180             this.setThumbBoxSize();
25181             this.setThumbBoxPosition();
25182             this.setCanvasPosition();
25183         }
25184         
25185         this.draw();
25186         
25187         this.fireEvent('rotate', this, 'right');
25188     },
25189     
25190     onRotateFail : function()
25191     {
25192         this.errorEl.show(true);
25193         
25194         var _this = this;
25195         
25196         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
25197     },
25198     
25199     draw : function()
25200     {
25201         this.previewEl.dom.innerHTML = '';
25202         
25203         var canvasEl = document.createElement("canvas");
25204         
25205         var contextEl = canvasEl.getContext("2d");
25206         
25207         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25208         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25209         var center = this.imageEl.OriginWidth / 2;
25210         
25211         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
25212             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25213             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25214             center = this.imageEl.OriginHeight / 2;
25215         }
25216         
25217         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
25218         
25219         contextEl.translate(center, center);
25220         contextEl.rotate(this.rotate * Math.PI / 180);
25221
25222         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25223         
25224         this.canvasEl = document.createElement("canvas");
25225         
25226         this.contextEl = this.canvasEl.getContext("2d");
25227         
25228         switch (this.rotate) {
25229             case 0 :
25230                 
25231                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25232                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25233                 
25234                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25235                 
25236                 break;
25237             case 90 : 
25238                 
25239                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25240                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25241                 
25242                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25243                     this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25244                     break;
25245                 }
25246                 
25247                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25248                 
25249                 break;
25250             case 180 :
25251                 
25252                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25253                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25254                 
25255                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25256                     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);
25257                     break;
25258                 }
25259                 
25260                 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);
25261                 
25262                 break;
25263             case 270 :
25264                 
25265                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25266                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25267         
25268                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25269                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25270                     break;
25271                 }
25272                 
25273                 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);
25274                 
25275                 break;
25276             default : 
25277                 break;
25278         }
25279         
25280         this.previewEl.appendChild(this.canvasEl);
25281         
25282         this.setCanvasPosition();
25283     },
25284     
25285     crop : function()
25286     {
25287         if(!this.canvasLoaded){
25288             return;
25289         }
25290         
25291         var imageCanvas = document.createElement("canvas");
25292         
25293         var imageContext = imageCanvas.getContext("2d");
25294         
25295         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25296         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25297         
25298         var center = imageCanvas.width / 2;
25299         
25300         imageContext.translate(center, center);
25301         
25302         imageContext.rotate(this.rotate * Math.PI / 180);
25303         
25304         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25305         
25306         var canvas = document.createElement("canvas");
25307         
25308         var context = canvas.getContext("2d");
25309                 
25310         canvas.width = this.minWidth;
25311         canvas.height = this.minHeight;
25312
25313         switch (this.rotate) {
25314             case 0 :
25315                 
25316                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25317                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25318                 
25319                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25320                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25321                 
25322                 var targetWidth = this.minWidth - 2 * x;
25323                 var targetHeight = this.minHeight - 2 * y;
25324                 
25325                 var scale = 1;
25326                 
25327                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25328                     scale = targetWidth / width;
25329                 }
25330                 
25331                 if(x > 0 && y == 0){
25332                     scale = targetHeight / height;
25333                 }
25334                 
25335                 if(x > 0 && y > 0){
25336                     scale = targetWidth / width;
25337                     
25338                     if(width < height){
25339                         scale = targetHeight / height;
25340                     }
25341                 }
25342                 
25343                 context.scale(scale, scale);
25344                 
25345                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25346                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25347
25348                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25349                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25350
25351                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25352                 
25353                 break;
25354             case 90 : 
25355                 
25356                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25357                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25358                 
25359                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25360                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25361                 
25362                 var targetWidth = this.minWidth - 2 * x;
25363                 var targetHeight = this.minHeight - 2 * y;
25364                 
25365                 var scale = 1;
25366                 
25367                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25368                     scale = targetWidth / width;
25369                 }
25370                 
25371                 if(x > 0 && y == 0){
25372                     scale = targetHeight / height;
25373                 }
25374                 
25375                 if(x > 0 && y > 0){
25376                     scale = targetWidth / width;
25377                     
25378                     if(width < height){
25379                         scale = targetHeight / height;
25380                     }
25381                 }
25382                 
25383                 context.scale(scale, scale);
25384                 
25385                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25386                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25387
25388                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25389                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25390                 
25391                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25392                 
25393                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25394                 
25395                 break;
25396             case 180 :
25397                 
25398                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25399                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25400                 
25401                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25402                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25403                 
25404                 var targetWidth = this.minWidth - 2 * x;
25405                 var targetHeight = this.minHeight - 2 * y;
25406                 
25407                 var scale = 1;
25408                 
25409                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25410                     scale = targetWidth / width;
25411                 }
25412                 
25413                 if(x > 0 && y == 0){
25414                     scale = targetHeight / height;
25415                 }
25416                 
25417                 if(x > 0 && y > 0){
25418                     scale = targetWidth / width;
25419                     
25420                     if(width < height){
25421                         scale = targetHeight / height;
25422                     }
25423                 }
25424                 
25425                 context.scale(scale, scale);
25426                 
25427                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25428                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25429
25430                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25431                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25432
25433                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25434                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25435                 
25436                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25437                 
25438                 break;
25439             case 270 :
25440                 
25441                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25442                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25443                 
25444                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25445                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25446                 
25447                 var targetWidth = this.minWidth - 2 * x;
25448                 var targetHeight = this.minHeight - 2 * y;
25449                 
25450                 var scale = 1;
25451                 
25452                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25453                     scale = targetWidth / width;
25454                 }
25455                 
25456                 if(x > 0 && y == 0){
25457                     scale = targetHeight / height;
25458                 }
25459                 
25460                 if(x > 0 && y > 0){
25461                     scale = targetWidth / width;
25462                     
25463                     if(width < height){
25464                         scale = targetHeight / height;
25465                     }
25466                 }
25467                 
25468                 context.scale(scale, scale);
25469                 
25470                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25471                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25472
25473                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25474                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25475                 
25476                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25477                 
25478                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25479                 
25480                 break;
25481             default : 
25482                 break;
25483         }
25484         
25485         this.cropData = canvas.toDataURL(this.cropType);
25486         
25487         if(this.fireEvent('crop', this, this.cropData) !== false){
25488             this.process(this.file, this.cropData);
25489         }
25490         
25491         return;
25492         
25493     },
25494     
25495     setThumbBoxSize : function()
25496     {
25497         var width, height;
25498         
25499         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
25500             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
25501             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
25502             
25503             this.minWidth = width;
25504             this.minHeight = height;
25505             
25506             if(this.rotate == 90 || this.rotate == 270){
25507                 this.minWidth = height;
25508                 this.minHeight = width;
25509             }
25510         }
25511         
25512         height = 300;
25513         width = Math.ceil(this.minWidth * height / this.minHeight);
25514         
25515         if(this.minWidth > this.minHeight){
25516             width = 300;
25517             height = Math.ceil(this.minHeight * width / this.minWidth);
25518         }
25519         
25520         this.thumbEl.setStyle({
25521             width : width + 'px',
25522             height : height + 'px'
25523         });
25524
25525         return;
25526             
25527     },
25528     
25529     setThumbBoxPosition : function()
25530     {
25531         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
25532         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
25533         
25534         this.thumbEl.setLeft(x);
25535         this.thumbEl.setTop(y);
25536         
25537     },
25538     
25539     baseRotateLevel : function()
25540     {
25541         this.baseRotate = 1;
25542         
25543         if(
25544                 typeof(this.exif) != 'undefined' &&
25545                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
25546                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
25547         ){
25548             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
25549         }
25550         
25551         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
25552         
25553     },
25554     
25555     baseScaleLevel : function()
25556     {
25557         var width, height;
25558         
25559         if(this.isDocument){
25560             
25561             if(this.baseRotate == 6 || this.baseRotate == 8){
25562             
25563                 height = this.thumbEl.getHeight();
25564                 this.baseScale = height / this.imageEl.OriginWidth;
25565
25566                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
25567                     width = this.thumbEl.getWidth();
25568                     this.baseScale = width / this.imageEl.OriginHeight;
25569                 }
25570
25571                 return;
25572             }
25573
25574             height = this.thumbEl.getHeight();
25575             this.baseScale = height / this.imageEl.OriginHeight;
25576
25577             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
25578                 width = this.thumbEl.getWidth();
25579                 this.baseScale = width / this.imageEl.OriginWidth;
25580             }
25581
25582             return;
25583         }
25584         
25585         if(this.baseRotate == 6 || this.baseRotate == 8){
25586             
25587             width = this.thumbEl.getHeight();
25588             this.baseScale = width / this.imageEl.OriginHeight;
25589             
25590             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
25591                 height = this.thumbEl.getWidth();
25592                 this.baseScale = height / this.imageEl.OriginHeight;
25593             }
25594             
25595             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25596                 height = this.thumbEl.getWidth();
25597                 this.baseScale = height / this.imageEl.OriginHeight;
25598                 
25599                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
25600                     width = this.thumbEl.getHeight();
25601                     this.baseScale = width / this.imageEl.OriginWidth;
25602                 }
25603             }
25604             
25605             return;
25606         }
25607         
25608         width = this.thumbEl.getWidth();
25609         this.baseScale = width / this.imageEl.OriginWidth;
25610         
25611         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
25612             height = this.thumbEl.getHeight();
25613             this.baseScale = height / this.imageEl.OriginHeight;
25614         }
25615         
25616         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25617             
25618             height = this.thumbEl.getHeight();
25619             this.baseScale = height / this.imageEl.OriginHeight;
25620             
25621             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
25622                 width = this.thumbEl.getWidth();
25623                 this.baseScale = width / this.imageEl.OriginWidth;
25624             }
25625             
25626         }
25627         
25628         return;
25629     },
25630     
25631     getScaleLevel : function()
25632     {
25633         return this.baseScale * Math.pow(1.1, this.scale);
25634     },
25635     
25636     onTouchStart : function(e)
25637     {
25638         if(!this.canvasLoaded){
25639             this.beforeSelectFile(e);
25640             return;
25641         }
25642         
25643         var touches = e.browserEvent.touches;
25644         
25645         if(!touches){
25646             return;
25647         }
25648         
25649         if(touches.length == 1){
25650             this.onMouseDown(e);
25651             return;
25652         }
25653         
25654         if(touches.length != 2){
25655             return;
25656         }
25657         
25658         var coords = [];
25659         
25660         for(var i = 0, finger; finger = touches[i]; i++){
25661             coords.push(finger.pageX, finger.pageY);
25662         }
25663         
25664         var x = Math.pow(coords[0] - coords[2], 2);
25665         var y = Math.pow(coords[1] - coords[3], 2);
25666         
25667         this.startDistance = Math.sqrt(x + y);
25668         
25669         this.startScale = this.scale;
25670         
25671         this.pinching = true;
25672         this.dragable = false;
25673         
25674     },
25675     
25676     onTouchMove : function(e)
25677     {
25678         if(!this.pinching && !this.dragable){
25679             return;
25680         }
25681         
25682         var touches = e.browserEvent.touches;
25683         
25684         if(!touches){
25685             return;
25686         }
25687         
25688         if(this.dragable){
25689             this.onMouseMove(e);
25690             return;
25691         }
25692         
25693         var coords = [];
25694         
25695         for(var i = 0, finger; finger = touches[i]; i++){
25696             coords.push(finger.pageX, finger.pageY);
25697         }
25698         
25699         var x = Math.pow(coords[0] - coords[2], 2);
25700         var y = Math.pow(coords[1] - coords[3], 2);
25701         
25702         this.endDistance = Math.sqrt(x + y);
25703         
25704         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
25705         
25706         if(!this.zoomable()){
25707             this.scale = this.startScale;
25708             return;
25709         }
25710         
25711         this.draw();
25712         
25713     },
25714     
25715     onTouchEnd : function(e)
25716     {
25717         this.pinching = false;
25718         this.dragable = false;
25719         
25720     },
25721     
25722     process : function(file, crop)
25723     {
25724         if(this.loadMask){
25725             this.maskEl.mask(this.loadingText);
25726         }
25727         
25728         this.xhr = new XMLHttpRequest();
25729         
25730         file.xhr = this.xhr;
25731
25732         this.xhr.open(this.method, this.url, true);
25733         
25734         var headers = {
25735             "Accept": "application/json",
25736             "Cache-Control": "no-cache",
25737             "X-Requested-With": "XMLHttpRequest"
25738         };
25739         
25740         for (var headerName in headers) {
25741             var headerValue = headers[headerName];
25742             if (headerValue) {
25743                 this.xhr.setRequestHeader(headerName, headerValue);
25744             }
25745         }
25746         
25747         var _this = this;
25748         
25749         this.xhr.onload = function()
25750         {
25751             _this.xhrOnLoad(_this.xhr);
25752         }
25753         
25754         this.xhr.onerror = function()
25755         {
25756             _this.xhrOnError(_this.xhr);
25757         }
25758         
25759         var formData = new FormData();
25760
25761         formData.append('returnHTML', 'NO');
25762         
25763         if(crop){
25764             formData.append('crop', crop);
25765         }
25766         
25767         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
25768             formData.append(this.paramName, file, file.name);
25769         }
25770         
25771         if(typeof(file.filename) != 'undefined'){
25772             formData.append('filename', file.filename);
25773         }
25774         
25775         if(typeof(file.mimetype) != 'undefined'){
25776             formData.append('mimetype', file.mimetype);
25777         }
25778         
25779         if(this.fireEvent('arrange', this, formData) != false){
25780             this.xhr.send(formData);
25781         };
25782     },
25783     
25784     xhrOnLoad : function(xhr)
25785     {
25786         if(this.loadMask){
25787             this.maskEl.unmask();
25788         }
25789         
25790         if (xhr.readyState !== 4) {
25791             this.fireEvent('exception', this, xhr);
25792             return;
25793         }
25794
25795         var response = Roo.decode(xhr.responseText);
25796         
25797         if(!response.success){
25798             this.fireEvent('exception', this, xhr);
25799             return;
25800         }
25801         
25802         var response = Roo.decode(xhr.responseText);
25803         
25804         this.fireEvent('upload', this, response);
25805         
25806     },
25807     
25808     xhrOnError : function()
25809     {
25810         if(this.loadMask){
25811             this.maskEl.unmask();
25812         }
25813         
25814         Roo.log('xhr on error');
25815         
25816         var response = Roo.decode(xhr.responseText);
25817           
25818         Roo.log(response);
25819         
25820     },
25821     
25822     prepare : function(file)
25823     {   
25824         if(this.loadMask){
25825             this.maskEl.mask(this.loadingText);
25826         }
25827         
25828         this.file = false;
25829         this.exif = {};
25830         
25831         if(typeof(file) === 'string'){
25832             this.loadCanvas(file);
25833             return;
25834         }
25835         
25836         if(!file || !this.urlAPI){
25837             return;
25838         }
25839         
25840         this.file = file;
25841         this.cropType = file.type;
25842         
25843         var _this = this;
25844         
25845         if(this.fireEvent('prepare', this, this.file) != false){
25846             
25847             var reader = new FileReader();
25848             
25849             reader.onload = function (e) {
25850                 if (e.target.error) {
25851                     Roo.log(e.target.error);
25852                     return;
25853                 }
25854                 
25855                 var buffer = e.target.result,
25856                     dataView = new DataView(buffer),
25857                     offset = 2,
25858                     maxOffset = dataView.byteLength - 4,
25859                     markerBytes,
25860                     markerLength;
25861                 
25862                 if (dataView.getUint16(0) === 0xffd8) {
25863                     while (offset < maxOffset) {
25864                         markerBytes = dataView.getUint16(offset);
25865                         
25866                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
25867                             markerLength = dataView.getUint16(offset + 2) + 2;
25868                             if (offset + markerLength > dataView.byteLength) {
25869                                 Roo.log('Invalid meta data: Invalid segment size.');
25870                                 break;
25871                             }
25872                             
25873                             if(markerBytes == 0xffe1){
25874                                 _this.parseExifData(
25875                                     dataView,
25876                                     offset,
25877                                     markerLength
25878                                 );
25879                             }
25880                             
25881                             offset += markerLength;
25882                             
25883                             continue;
25884                         }
25885                         
25886                         break;
25887                     }
25888                     
25889                 }
25890                 
25891                 var url = _this.urlAPI.createObjectURL(_this.file);
25892                 
25893                 _this.loadCanvas(url);
25894                 
25895                 return;
25896             }
25897             
25898             reader.readAsArrayBuffer(this.file);
25899             
25900         }
25901         
25902     },
25903     
25904     parseExifData : function(dataView, offset, length)
25905     {
25906         var tiffOffset = offset + 10,
25907             littleEndian,
25908             dirOffset;
25909     
25910         if (dataView.getUint32(offset + 4) !== 0x45786966) {
25911             // No Exif data, might be XMP data instead
25912             return;
25913         }
25914         
25915         // Check for the ASCII code for "Exif" (0x45786966):
25916         if (dataView.getUint32(offset + 4) !== 0x45786966) {
25917             // No Exif data, might be XMP data instead
25918             return;
25919         }
25920         if (tiffOffset + 8 > dataView.byteLength) {
25921             Roo.log('Invalid Exif data: Invalid segment size.');
25922             return;
25923         }
25924         // Check for the two null bytes:
25925         if (dataView.getUint16(offset + 8) !== 0x0000) {
25926             Roo.log('Invalid Exif data: Missing byte alignment offset.');
25927             return;
25928         }
25929         // Check the byte alignment:
25930         switch (dataView.getUint16(tiffOffset)) {
25931         case 0x4949:
25932             littleEndian = true;
25933             break;
25934         case 0x4D4D:
25935             littleEndian = false;
25936             break;
25937         default:
25938             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
25939             return;
25940         }
25941         // Check for the TIFF tag marker (0x002A):
25942         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
25943             Roo.log('Invalid Exif data: Missing TIFF marker.');
25944             return;
25945         }
25946         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
25947         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
25948         
25949         this.parseExifTags(
25950             dataView,
25951             tiffOffset,
25952             tiffOffset + dirOffset,
25953             littleEndian
25954         );
25955     },
25956     
25957     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
25958     {
25959         var tagsNumber,
25960             dirEndOffset,
25961             i;
25962         if (dirOffset + 6 > dataView.byteLength) {
25963             Roo.log('Invalid Exif data: Invalid directory offset.');
25964             return;
25965         }
25966         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
25967         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
25968         if (dirEndOffset + 4 > dataView.byteLength) {
25969             Roo.log('Invalid Exif data: Invalid directory size.');
25970             return;
25971         }
25972         for (i = 0; i < tagsNumber; i += 1) {
25973             this.parseExifTag(
25974                 dataView,
25975                 tiffOffset,
25976                 dirOffset + 2 + 12 * i, // tag offset
25977                 littleEndian
25978             );
25979         }
25980         // Return the offset to the next directory:
25981         return dataView.getUint32(dirEndOffset, littleEndian);
25982     },
25983     
25984     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
25985     {
25986         var tag = dataView.getUint16(offset, littleEndian);
25987         
25988         this.exif[tag] = this.getExifValue(
25989             dataView,
25990             tiffOffset,
25991             offset,
25992             dataView.getUint16(offset + 2, littleEndian), // tag type
25993             dataView.getUint32(offset + 4, littleEndian), // tag length
25994             littleEndian
25995         );
25996     },
25997     
25998     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
25999     {
26000         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
26001             tagSize,
26002             dataOffset,
26003             values,
26004             i,
26005             str,
26006             c;
26007     
26008         if (!tagType) {
26009             Roo.log('Invalid Exif data: Invalid tag type.');
26010             return;
26011         }
26012         
26013         tagSize = tagType.size * length;
26014         // Determine if the value is contained in the dataOffset bytes,
26015         // or if the value at the dataOffset is a pointer to the actual data:
26016         dataOffset = tagSize > 4 ?
26017                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
26018         if (dataOffset + tagSize > dataView.byteLength) {
26019             Roo.log('Invalid Exif data: Invalid data offset.');
26020             return;
26021         }
26022         if (length === 1) {
26023             return tagType.getValue(dataView, dataOffset, littleEndian);
26024         }
26025         values = [];
26026         for (i = 0; i < length; i += 1) {
26027             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
26028         }
26029         
26030         if (tagType.ascii) {
26031             str = '';
26032             // Concatenate the chars:
26033             for (i = 0; i < values.length; i += 1) {
26034                 c = values[i];
26035                 // Ignore the terminating NULL byte(s):
26036                 if (c === '\u0000') {
26037                     break;
26038                 }
26039                 str += c;
26040             }
26041             return str;
26042         }
26043         return values;
26044     }
26045     
26046 });
26047
26048 Roo.apply(Roo.bootstrap.UploadCropbox, {
26049     tags : {
26050         'Orientation': 0x0112
26051     },
26052     
26053     Orientation: {
26054             1: 0, //'top-left',
26055 //            2: 'top-right',
26056             3: 180, //'bottom-right',
26057 //            4: 'bottom-left',
26058 //            5: 'left-top',
26059             6: 90, //'right-top',
26060 //            7: 'right-bottom',
26061             8: 270 //'left-bottom'
26062     },
26063     
26064     exifTagTypes : {
26065         // byte, 8-bit unsigned int:
26066         1: {
26067             getValue: function (dataView, dataOffset) {
26068                 return dataView.getUint8(dataOffset);
26069             },
26070             size: 1
26071         },
26072         // ascii, 8-bit byte:
26073         2: {
26074             getValue: function (dataView, dataOffset) {
26075                 return String.fromCharCode(dataView.getUint8(dataOffset));
26076             },
26077             size: 1,
26078             ascii: true
26079         },
26080         // short, 16 bit int:
26081         3: {
26082             getValue: function (dataView, dataOffset, littleEndian) {
26083                 return dataView.getUint16(dataOffset, littleEndian);
26084             },
26085             size: 2
26086         },
26087         // long, 32 bit int:
26088         4: {
26089             getValue: function (dataView, dataOffset, littleEndian) {
26090                 return dataView.getUint32(dataOffset, littleEndian);
26091             },
26092             size: 4
26093         },
26094         // rational = two long values, first is numerator, second is denominator:
26095         5: {
26096             getValue: function (dataView, dataOffset, littleEndian) {
26097                 return dataView.getUint32(dataOffset, littleEndian) /
26098                     dataView.getUint32(dataOffset + 4, littleEndian);
26099             },
26100             size: 8
26101         },
26102         // slong, 32 bit signed int:
26103         9: {
26104             getValue: function (dataView, dataOffset, littleEndian) {
26105                 return dataView.getInt32(dataOffset, littleEndian);
26106             },
26107             size: 4
26108         },
26109         // srational, two slongs, first is numerator, second is denominator:
26110         10: {
26111             getValue: function (dataView, dataOffset, littleEndian) {
26112                 return dataView.getInt32(dataOffset, littleEndian) /
26113                     dataView.getInt32(dataOffset + 4, littleEndian);
26114             },
26115             size: 8
26116         }
26117     },
26118     
26119     footer : {
26120         STANDARD : [
26121             {
26122                 tag : 'div',
26123                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26124                 action : 'rotate-left',
26125                 cn : [
26126                     {
26127                         tag : 'button',
26128                         cls : 'btn btn-default',
26129                         html : '<i class="fa fa-undo"></i>'
26130                     }
26131                 ]
26132             },
26133             {
26134                 tag : 'div',
26135                 cls : 'btn-group roo-upload-cropbox-picture',
26136                 action : 'picture',
26137                 cn : [
26138                     {
26139                         tag : 'button',
26140                         cls : 'btn btn-default',
26141                         html : '<i class="fa fa-picture-o"></i>'
26142                     }
26143                 ]
26144             },
26145             {
26146                 tag : 'div',
26147                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26148                 action : 'rotate-right',
26149                 cn : [
26150                     {
26151                         tag : 'button',
26152                         cls : 'btn btn-default',
26153                         html : '<i class="fa fa-repeat"></i>'
26154                     }
26155                 ]
26156             }
26157         ],
26158         DOCUMENT : [
26159             {
26160                 tag : 'div',
26161                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26162                 action : 'rotate-left',
26163                 cn : [
26164                     {
26165                         tag : 'button',
26166                         cls : 'btn btn-default',
26167                         html : '<i class="fa fa-undo"></i>'
26168                     }
26169                 ]
26170             },
26171             {
26172                 tag : 'div',
26173                 cls : 'btn-group roo-upload-cropbox-download',
26174                 action : 'download',
26175                 cn : [
26176                     {
26177                         tag : 'button',
26178                         cls : 'btn btn-default',
26179                         html : '<i class="fa fa-download"></i>'
26180                     }
26181                 ]
26182             },
26183             {
26184                 tag : 'div',
26185                 cls : 'btn-group roo-upload-cropbox-crop',
26186                 action : 'crop',
26187                 cn : [
26188                     {
26189                         tag : 'button',
26190                         cls : 'btn btn-default',
26191                         html : '<i class="fa fa-crop"></i>'
26192                     }
26193                 ]
26194             },
26195             {
26196                 tag : 'div',
26197                 cls : 'btn-group roo-upload-cropbox-trash',
26198                 action : 'trash',
26199                 cn : [
26200                     {
26201                         tag : 'button',
26202                         cls : 'btn btn-default',
26203                         html : '<i class="fa fa-trash"></i>'
26204                     }
26205                 ]
26206             },
26207             {
26208                 tag : 'div',
26209                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26210                 action : 'rotate-right',
26211                 cn : [
26212                     {
26213                         tag : 'button',
26214                         cls : 'btn btn-default',
26215                         html : '<i class="fa fa-repeat"></i>'
26216                     }
26217                 ]
26218             }
26219         ],
26220         ROTATOR : [
26221             {
26222                 tag : 'div',
26223                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26224                 action : 'rotate-left',
26225                 cn : [
26226                     {
26227                         tag : 'button',
26228                         cls : 'btn btn-default',
26229                         html : '<i class="fa fa-undo"></i>'
26230                     }
26231                 ]
26232             },
26233             {
26234                 tag : 'div',
26235                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26236                 action : 'rotate-right',
26237                 cn : [
26238                     {
26239                         tag : 'button',
26240                         cls : 'btn btn-default',
26241                         html : '<i class="fa fa-repeat"></i>'
26242                     }
26243                 ]
26244             }
26245         ]
26246     }
26247 });
26248
26249 /*
26250 * Licence: LGPL
26251 */
26252
26253 /**
26254  * @class Roo.bootstrap.DocumentManager
26255  * @extends Roo.bootstrap.Component
26256  * Bootstrap DocumentManager class
26257  * @cfg {String} paramName default 'imageUpload'
26258  * @cfg {String} method default POST
26259  * @cfg {String} url action url
26260  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
26261  * @cfg {Boolean} multiple multiple upload default true
26262  * @cfg {Number} thumbSize default 300
26263  * @cfg {String} fieldLabel
26264  * @cfg {Number} labelWidth default 4
26265  * @cfg {String} labelAlign (left|top) default left
26266  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
26267  * 
26268  * @constructor
26269  * Create a new DocumentManager
26270  * @param {Object} config The config object
26271  */
26272
26273 Roo.bootstrap.DocumentManager = function(config){
26274     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26275     
26276     this.addEvents({
26277         /**
26278          * @event initial
26279          * Fire when initial the DocumentManager
26280          * @param {Roo.bootstrap.DocumentManager} this
26281          */
26282         "initial" : true,
26283         /**
26284          * @event inspect
26285          * inspect selected file
26286          * @param {Roo.bootstrap.DocumentManager} this
26287          * @param {File} file
26288          */
26289         "inspect" : true,
26290         /**
26291          * @event exception
26292          * Fire when xhr load exception
26293          * @param {Roo.bootstrap.DocumentManager} this
26294          * @param {XMLHttpRequest} xhr
26295          */
26296         "exception" : true,
26297         /**
26298          * @event prepare
26299          * prepare the form data
26300          * @param {Roo.bootstrap.DocumentManager} this
26301          * @param {Object} formData
26302          */
26303         "prepare" : true,
26304         /**
26305          * @event remove
26306          * Fire when remove the file
26307          * @param {Roo.bootstrap.DocumentManager} this
26308          * @param {Object} file
26309          */
26310         "remove" : true,
26311         /**
26312          * @event refresh
26313          * Fire after refresh the file
26314          * @param {Roo.bootstrap.DocumentManager} this
26315          */
26316         "refresh" : true,
26317         /**
26318          * @event click
26319          * Fire after click the image
26320          * @param {Roo.bootstrap.DocumentManager} this
26321          * @param {Object} file
26322          */
26323         "click" : true,
26324         /**
26325          * @event edit
26326          * Fire when upload a image and editable set to true
26327          * @param {Roo.bootstrap.DocumentManager} this
26328          * @param {Object} file
26329          */
26330         "edit" : true,
26331         /**
26332          * @event beforeselectfile
26333          * Fire before select file
26334          * @param {Roo.bootstrap.DocumentManager} this
26335          */
26336         "beforeselectfile" : true,
26337         /**
26338          * @event process
26339          * Fire before process file
26340          * @param {Roo.bootstrap.DocumentManager} this
26341          * @param {Object} file
26342          */
26343         "process" : true
26344         
26345     });
26346 };
26347
26348 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
26349     
26350     boxes : 0,
26351     inputName : '',
26352     thumbSize : 300,
26353     multiple : true,
26354     files : [],
26355     method : 'POST',
26356     url : '',
26357     paramName : 'imageUpload',
26358     fieldLabel : '',
26359     labelWidth : 4,
26360     labelAlign : 'left',
26361     editable : true,
26362     delegates : [],
26363     
26364     
26365     xhr : false, 
26366     
26367     getAutoCreate : function()
26368     {   
26369         var managerWidget = {
26370             tag : 'div',
26371             cls : 'roo-document-manager',
26372             cn : [
26373                 {
26374                     tag : 'input',
26375                     cls : 'roo-document-manager-selector',
26376                     type : 'file'
26377                 },
26378                 {
26379                     tag : 'div',
26380                     cls : 'roo-document-manager-uploader',
26381                     cn : [
26382                         {
26383                             tag : 'div',
26384                             cls : 'roo-document-manager-upload-btn',
26385                             html : '<i class="fa fa-plus"></i>'
26386                         }
26387                     ]
26388                     
26389                 }
26390             ]
26391         };
26392         
26393         var content = [
26394             {
26395                 tag : 'div',
26396                 cls : 'column col-md-12',
26397                 cn : managerWidget
26398             }
26399         ];
26400         
26401         if(this.fieldLabel.length){
26402             
26403             content = [
26404                 {
26405                     tag : 'div',
26406                     cls : 'column col-md-12',
26407                     html : this.fieldLabel
26408                 },
26409                 {
26410                     tag : 'div',
26411                     cls : 'column col-md-12',
26412                     cn : managerWidget
26413                 }
26414             ];
26415
26416             if(this.labelAlign == 'left'){
26417                 content = [
26418                     {
26419                         tag : 'div',
26420                         cls : 'column col-md-' + this.labelWidth,
26421                         html : this.fieldLabel
26422                     },
26423                     {
26424                         tag : 'div',
26425                         cls : 'column col-md-' + (12 - this.labelWidth),
26426                         cn : managerWidget
26427                     }
26428                 ];
26429                 
26430             }
26431         }
26432         
26433         var cfg = {
26434             tag : 'div',
26435             cls : 'row clearfix',
26436             cn : content
26437         };
26438         
26439         return cfg;
26440         
26441     },
26442     
26443     initEvents : function()
26444     {
26445         this.managerEl = this.el.select('.roo-document-manager', true).first();
26446         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26447         
26448         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
26449         this.selectorEl.hide();
26450         
26451         if(this.multiple){
26452             this.selectorEl.attr('multiple', 'multiple');
26453         }
26454         
26455         this.selectorEl.on('change', this.onFileSelected, this);
26456         
26457         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
26458         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26459         
26460         this.uploader.on('click', this.onUploaderClick, this);
26461         
26462         this.renderProgressDialog();
26463         
26464         var _this = this;
26465         
26466         window.addEventListener("resize", function() { _this.refresh(); } );
26467         
26468         this.fireEvent('initial', this);
26469     },
26470     
26471     renderProgressDialog : function()
26472     {
26473         var _this = this;
26474         
26475         this.progressDialog = new Roo.bootstrap.Modal({
26476             cls : 'roo-document-manager-progress-dialog',
26477             allow_close : false,
26478             title : '',
26479             buttons : [
26480                 {
26481                     name  :'cancel',
26482                     weight : 'danger',
26483                     html : 'Cancel'
26484                 }
26485             ], 
26486             listeners : { 
26487                 btnclick : function() {
26488                     _this.uploadCancel();
26489                     this.hide();
26490                 }
26491             }
26492         });
26493          
26494         this.progressDialog.render(Roo.get(document.body));
26495          
26496         this.progress = new Roo.bootstrap.Progress({
26497             cls : 'roo-document-manager-progress',
26498             active : true,
26499             striped : true
26500         });
26501         
26502         this.progress.render(this.progressDialog.getChildContainer());
26503         
26504         this.progressBar = new Roo.bootstrap.ProgressBar({
26505             cls : 'roo-document-manager-progress-bar',
26506             aria_valuenow : 0,
26507             aria_valuemin : 0,
26508             aria_valuemax : 12,
26509             panel : 'success'
26510         });
26511         
26512         this.progressBar.render(this.progress.getChildContainer());
26513     },
26514     
26515     onUploaderClick : function(e)
26516     {
26517         e.preventDefault();
26518      
26519         if(this.fireEvent('beforeselectfile', this) != false){
26520             this.selectorEl.dom.click();
26521         }
26522         
26523     },
26524     
26525     onFileSelected : function(e)
26526     {
26527         e.preventDefault();
26528         
26529         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26530             return;
26531         }
26532         
26533         Roo.each(this.selectorEl.dom.files, function(file){
26534             if(this.fireEvent('inspect', this, file) != false){
26535                 this.files.push(file);
26536             }
26537         }, this);
26538         
26539         this.queue();
26540         
26541     },
26542     
26543     queue : function()
26544     {
26545         this.selectorEl.dom.value = '';
26546         
26547         if(!this.files.length){
26548             return;
26549         }
26550         
26551         if(this.boxes > 0 && this.files.length > this.boxes){
26552             this.files = this.files.slice(0, this.boxes);
26553         }
26554         
26555         this.uploader.show();
26556         
26557         if(this.boxes > 0 && this.files.length > this.boxes - 1){
26558             this.uploader.hide();
26559         }
26560         
26561         var _this = this;
26562         
26563         var files = [];
26564         
26565         var docs = [];
26566         
26567         Roo.each(this.files, function(file){
26568             
26569             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26570                 var f = this.renderPreview(file);
26571                 files.push(f);
26572                 return;
26573             }
26574             
26575             if(file.type.indexOf('image') != -1){
26576                 this.delegates.push(
26577                     (function(){
26578                         _this.process(file);
26579                     }).createDelegate(this)
26580                 );
26581         
26582                 return;
26583             }
26584             
26585             docs.push(
26586                 (function(){
26587                     _this.process(file);
26588                 }).createDelegate(this)
26589             );
26590             
26591         }, this);
26592         
26593         this.files = files;
26594         
26595         this.delegates = this.delegates.concat(docs);
26596         
26597         if(!this.delegates.length){
26598             this.refresh();
26599             return;
26600         }
26601         
26602         this.progressBar.aria_valuemax = this.delegates.length;
26603         
26604         this.arrange();
26605         
26606         return;
26607     },
26608     
26609     arrange : function()
26610     {
26611         if(!this.delegates.length){
26612             this.progressDialog.hide();
26613             this.refresh();
26614             return;
26615         }
26616         
26617         var delegate = this.delegates.shift();
26618         
26619         this.progressDialog.show();
26620         
26621         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
26622         
26623         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
26624         
26625         delegate();
26626     },
26627     
26628     refresh : function()
26629     {
26630         this.uploader.show();
26631         
26632         if(this.boxes > 0 && this.files.length > this.boxes - 1){
26633             this.uploader.hide();
26634         }
26635         
26636         Roo.isTouch ? this.closable(false) : this.closable(true);
26637         
26638         this.fireEvent('refresh', this);
26639     },
26640     
26641     onRemove : function(e, el, o)
26642     {
26643         e.preventDefault();
26644         
26645         this.fireEvent('remove', this, o);
26646         
26647     },
26648     
26649     remove : function(o)
26650     {
26651         var files = [];
26652         
26653         Roo.each(this.files, function(file){
26654             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
26655                 files.push(file);
26656                 return;
26657             }
26658
26659             o.target.remove();
26660
26661         }, this);
26662         
26663         this.files = files;
26664         
26665         this.refresh();
26666     },
26667     
26668     clear : function()
26669     {
26670         Roo.each(this.files, function(file){
26671             if(!file.target){
26672                 return;
26673             }
26674             
26675             file.target.remove();
26676
26677         }, this);
26678         
26679         this.files = [];
26680         
26681         this.refresh();
26682     },
26683     
26684     onClick : function(e, el, o)
26685     {
26686         e.preventDefault();
26687         
26688         this.fireEvent('click', this, o);
26689         
26690     },
26691     
26692     closable : function(closable)
26693     {
26694         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
26695             
26696             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26697             
26698             if(closable){
26699                 el.show();
26700                 return;
26701             }
26702             
26703             el.hide();
26704             
26705         }, this);
26706     },
26707     
26708     xhrOnLoad : function(xhr)
26709     {
26710         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26711             el.remove();
26712         }, this);
26713         
26714         if (xhr.readyState !== 4) {
26715             this.arrange();
26716             this.fireEvent('exception', this, xhr);
26717             return;
26718         }
26719
26720         var response = Roo.decode(xhr.responseText);
26721         
26722         if(!response.success){
26723             this.arrange();
26724             this.fireEvent('exception', this, xhr);
26725             return;
26726         }
26727         
26728         var file = this.renderPreview(response.data);
26729         
26730         this.files.push(file);
26731         
26732         this.arrange();
26733         
26734     },
26735     
26736     xhrOnError : function()
26737     {
26738         Roo.log('xhr on error');
26739         
26740         var response = Roo.decode(xhr.responseText);
26741           
26742         Roo.log(response);
26743         
26744         this.arrange();
26745     },
26746     
26747     process : function(file)
26748     {
26749         if(this.fireEvent('process', this, file) !== false){
26750             if(this.editable && file.type.indexOf('image') != -1){
26751                 this.fireEvent('edit', this, file);
26752                 return;
26753             }
26754
26755             this.uploadStart(file, false);
26756
26757             return;
26758         }
26759         
26760     },
26761     
26762     uploadStart : function(file, crop)
26763     {
26764         this.xhr = new XMLHttpRequest();
26765         
26766         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26767             this.arrange();
26768             return;
26769         }
26770         
26771         file.xhr = this.xhr;
26772             
26773         this.managerEl.createChild({
26774             tag : 'div',
26775             cls : 'roo-document-manager-loading',
26776             cn : [
26777                 {
26778                     tag : 'div',
26779                     tooltip : file.name,
26780                     cls : 'roo-document-manager-thumb',
26781                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26782                 }
26783             ]
26784
26785         });
26786
26787         this.xhr.open(this.method, this.url, true);
26788         
26789         var headers = {
26790             "Accept": "application/json",
26791             "Cache-Control": "no-cache",
26792             "X-Requested-With": "XMLHttpRequest"
26793         };
26794         
26795         for (var headerName in headers) {
26796             var headerValue = headers[headerName];
26797             if (headerValue) {
26798                 this.xhr.setRequestHeader(headerName, headerValue);
26799             }
26800         }
26801         
26802         var _this = this;
26803         
26804         this.xhr.onload = function()
26805         {
26806             _this.xhrOnLoad(_this.xhr);
26807         }
26808         
26809         this.xhr.onerror = function()
26810         {
26811             _this.xhrOnError(_this.xhr);
26812         }
26813         
26814         var formData = new FormData();
26815
26816         formData.append('returnHTML', 'NO');
26817         
26818         if(crop){
26819             formData.append('crop', crop);
26820         }
26821         
26822         formData.append(this.paramName, file, file.name);
26823         
26824         if(this.fireEvent('prepare', this, formData) != false){
26825             this.xhr.send(formData);
26826         };
26827     },
26828     
26829     uploadCancel : function()
26830     {
26831         if (this.xhr) {
26832             this.xhr.abort();
26833         }
26834         
26835         
26836         this.delegates = [];
26837         
26838         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26839             el.remove();
26840         }, this);
26841         
26842         this.arrange();
26843     },
26844     
26845     renderPreview : function(file)
26846     {
26847         if(typeof(file.target) != 'undefined' && file.target){
26848             return file;
26849         }
26850         
26851         var previewEl = this.managerEl.createChild({
26852             tag : 'div',
26853             cls : 'roo-document-manager-preview',
26854             cn : [
26855                 {
26856                     tag : 'div',
26857                     tooltip : file.filename,
26858                     cls : 'roo-document-manager-thumb',
26859                     html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
26860                 },
26861                 {
26862                     tag : 'button',
26863                     cls : 'close',
26864                     html : '<i class="fa fa-times-circle"></i>'
26865                 }
26866             ]
26867         });
26868
26869         var close = previewEl.select('button.close', true).first();
26870
26871         close.on('click', this.onRemove, this, file);
26872
26873         file.target = previewEl;
26874
26875         var image = previewEl.select('img', true).first();
26876         
26877         var _this = this;
26878         
26879         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
26880         
26881         image.on('click', this.onClick, this, file);
26882         
26883         return file;
26884         
26885     },
26886     
26887     onPreviewLoad : function(file, image)
26888     {
26889         if(typeof(file.target) == 'undefined' || !file.target){
26890             return;
26891         }
26892         
26893         var width = image.dom.naturalWidth || image.dom.width;
26894         var height = image.dom.naturalHeight || image.dom.height;
26895         
26896         if(width > height){
26897             file.target.addClass('wide');
26898             return;
26899         }
26900         
26901         file.target.addClass('tall');
26902         return;
26903         
26904     },
26905     
26906     uploadFromSource : function(file, crop)
26907     {
26908         this.xhr = new XMLHttpRequest();
26909         
26910         this.managerEl.createChild({
26911             tag : 'div',
26912             cls : 'roo-document-manager-loading',
26913             cn : [
26914                 {
26915                     tag : 'div',
26916                     tooltip : file.name,
26917                     cls : 'roo-document-manager-thumb',
26918                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26919                 }
26920             ]
26921
26922         });
26923
26924         this.xhr.open(this.method, this.url, true);
26925         
26926         var headers = {
26927             "Accept": "application/json",
26928             "Cache-Control": "no-cache",
26929             "X-Requested-With": "XMLHttpRequest"
26930         };
26931         
26932         for (var headerName in headers) {
26933             var headerValue = headers[headerName];
26934             if (headerValue) {
26935                 this.xhr.setRequestHeader(headerName, headerValue);
26936             }
26937         }
26938         
26939         var _this = this;
26940         
26941         this.xhr.onload = function()
26942         {
26943             _this.xhrOnLoad(_this.xhr);
26944         }
26945         
26946         this.xhr.onerror = function()
26947         {
26948             _this.xhrOnError(_this.xhr);
26949         }
26950         
26951         var formData = new FormData();
26952
26953         formData.append('returnHTML', 'NO');
26954         
26955         formData.append('crop', crop);
26956         
26957         if(typeof(file.filename) != 'undefined'){
26958             formData.append('filename', file.filename);
26959         }
26960         
26961         if(typeof(file.mimetype) != 'undefined'){
26962             formData.append('mimetype', file.mimetype);
26963         }
26964         
26965         if(this.fireEvent('prepare', this, formData) != false){
26966             this.xhr.send(formData);
26967         };
26968     }
26969 });
26970
26971 /*
26972 * Licence: LGPL
26973 */
26974
26975 /**
26976  * @class Roo.bootstrap.DocumentViewer
26977  * @extends Roo.bootstrap.Component
26978  * Bootstrap DocumentViewer class
26979  * 
26980  * @constructor
26981  * Create a new DocumentViewer
26982  * @param {Object} config The config object
26983  */
26984
26985 Roo.bootstrap.DocumentViewer = function(config){
26986     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
26987     
26988     this.addEvents({
26989         /**
26990          * @event initial
26991          * Fire after initEvent
26992          * @param {Roo.bootstrap.DocumentViewer} this
26993          */
26994         "initial" : true,
26995         /**
26996          * @event click
26997          * Fire after click
26998          * @param {Roo.bootstrap.DocumentViewer} this
26999          */
27000         "click" : true,
27001         /**
27002          * @event trash
27003          * Fire after trash button
27004          * @param {Roo.bootstrap.DocumentViewer} this
27005          */
27006         "trash" : true
27007         
27008     });
27009 };
27010
27011 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
27012     
27013     getAutoCreate : function()
27014     {
27015         var cfg = {
27016             tag : 'div',
27017             cls : 'roo-document-viewer',
27018             cn : [
27019                 {
27020                     tag : 'div',
27021                     cls : 'roo-document-viewer-body',
27022                     cn : [
27023                         {
27024                             tag : 'div',
27025                             cls : 'roo-document-viewer-thumb',
27026                             cn : [
27027                                 {
27028                                     tag : 'img',
27029                                     cls : 'roo-document-viewer-image'
27030                                 }
27031                             ]
27032                         }
27033                     ]
27034                 },
27035                 {
27036                     tag : 'div',
27037                     cls : 'roo-document-viewer-footer',
27038                     cn : {
27039                         tag : 'div',
27040                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
27041                         cn : [
27042                             {
27043                                 tag : 'div',
27044                                 cls : 'btn-group',
27045                                 cn : [
27046                                     {
27047                                         tag : 'button',
27048                                         cls : 'btn btn-default roo-document-viewer-trash',
27049                                         html : '<i class="fa fa-trash"></i>'
27050                                     }
27051                                 ]
27052                             }
27053                         ]
27054                     }
27055                 }
27056             ]
27057         };
27058         
27059         return cfg;
27060     },
27061     
27062     initEvents : function()
27063     {
27064         
27065         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27066         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27067         
27068         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27069         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27070         
27071         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27072         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27073         
27074         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27075         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27076         
27077         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27078         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27079         
27080         this.bodyEl.on('click', this.onClick, this);
27081         
27082         this.trashBtn.on('click', this.onTrash, this);
27083         
27084     },
27085     
27086     initial : function()
27087     {
27088 //        this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27089         
27090         
27091         this.fireEvent('initial', this);
27092         
27093     },
27094     
27095     onClick : function(e)
27096     {
27097         e.preventDefault();
27098         
27099         this.fireEvent('click', this);
27100     },
27101     
27102     onTrash : function(e)
27103     {
27104         e.preventDefault();
27105         
27106         this.fireEvent('trash', this);
27107     }
27108     
27109 });
27110 /*
27111  * - LGPL
27112  *
27113  * nav progress bar
27114  * 
27115  */
27116
27117 /**
27118  * @class Roo.bootstrap.NavProgressBar
27119  * @extends Roo.bootstrap.Component
27120  * Bootstrap NavProgressBar class
27121  * 
27122  * @constructor
27123  * Create a new nav progress bar
27124  * @param {Object} config The config object
27125  */
27126
27127 Roo.bootstrap.NavProgressBar = function(config){
27128     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
27129
27130     this.bullets = this.bullets || [];
27131    
27132 //    Roo.bootstrap.NavProgressBar.register(this);
27133      this.addEvents({
27134         /**
27135              * @event changed
27136              * Fires when the active item changes
27137              * @param {Roo.bootstrap.NavProgressBar} this
27138              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
27139              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
27140          */
27141         'changed': true
27142      });
27143     
27144 };
27145
27146 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
27147     
27148     bullets : [],
27149     barItems : [],
27150     
27151     getAutoCreate : function()
27152     {
27153         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
27154         
27155         cfg = {
27156             tag : 'div',
27157             cls : 'roo-navigation-bar-group',
27158             cn : [
27159                 {
27160                     tag : 'div',
27161                     cls : 'roo-navigation-top-bar'
27162                 },
27163                 {
27164                     tag : 'div',
27165                     cls : 'roo-navigation-bullets-bar',
27166                     cn : [
27167                         {
27168                             tag : 'ul',
27169                             cls : 'roo-navigation-bar'
27170                         }
27171                     ]
27172                 },
27173                 
27174                 {
27175                     tag : 'div',
27176                     cls : 'roo-navigation-bottom-bar'
27177                 }
27178             ]
27179             
27180         };
27181         
27182         return cfg;
27183         
27184     },
27185     
27186     initEvents: function() 
27187     {
27188         
27189     },
27190     
27191     onRender : function(ct, position) 
27192     {
27193         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27194         
27195         if(this.bullets.length){
27196             Roo.each(this.bullets, function(b){
27197                this.addItem(b);
27198             }, this);
27199         }
27200         
27201         this.format();
27202         
27203     },
27204     
27205     addItem : function(cfg)
27206     {
27207         var item = new Roo.bootstrap.NavProgressItem(cfg);
27208         
27209         item.parentId = this.id;
27210         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
27211         
27212         if(cfg.html){
27213             var top = new Roo.bootstrap.Element({
27214                 tag : 'div',
27215                 cls : 'roo-navigation-bar-text'
27216             });
27217             
27218             var bottom = new Roo.bootstrap.Element({
27219                 tag : 'div',
27220                 cls : 'roo-navigation-bar-text'
27221             });
27222             
27223             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
27224             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
27225             
27226             var topText = new Roo.bootstrap.Element({
27227                 tag : 'span',
27228                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
27229             });
27230             
27231             var bottomText = new Roo.bootstrap.Element({
27232                 tag : 'span',
27233                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
27234             });
27235             
27236             topText.onRender(top.el, null);
27237             bottomText.onRender(bottom.el, null);
27238             
27239             item.topEl = top;
27240             item.bottomEl = bottom;
27241         }
27242         
27243         this.barItems.push(item);
27244         
27245         return item;
27246     },
27247     
27248     getActive : function()
27249     {
27250         var active = false;
27251         
27252         Roo.each(this.barItems, function(v){
27253             
27254             if (!v.isActive()) {
27255                 return;
27256             }
27257             
27258             active = v;
27259             return false;
27260             
27261         });
27262         
27263         return active;
27264     },
27265     
27266     setActiveItem : function(item)
27267     {
27268         var prev = false;
27269         
27270         Roo.each(this.barItems, function(v){
27271             if (v.rid == item.rid) {
27272                 return ;
27273             }
27274             
27275             if (v.isActive()) {
27276                 v.setActive(false);
27277                 prev = v;
27278             }
27279         });
27280
27281         item.setActive(true);
27282         
27283         this.fireEvent('changed', this, item, prev);
27284     },
27285     
27286     getBarItem: function(rid)
27287     {
27288         var ret = false;
27289         
27290         Roo.each(this.barItems, function(e) {
27291             if (e.rid != rid) {
27292                 return;
27293             }
27294             
27295             ret =  e;
27296             return false;
27297         });
27298         
27299         return ret;
27300     },
27301     
27302     indexOfItem : function(item)
27303     {
27304         var index = false;
27305         
27306         Roo.each(this.barItems, function(v, i){
27307             
27308             if (v.rid != item.rid) {
27309                 return;
27310             }
27311             
27312             index = i;
27313             return false
27314         });
27315         
27316         return index;
27317     },
27318     
27319     setActiveNext : function()
27320     {
27321         var i = this.indexOfItem(this.getActive());
27322         
27323         if (i > this.barItems.length) {
27324             return;
27325         }
27326         
27327         this.setActiveItem(this.barItems[i+1]);
27328     },
27329     
27330     setActivePrev : function()
27331     {
27332         var i = this.indexOfItem(this.getActive());
27333         
27334         if (i  < 1) {
27335             return;
27336         }
27337         
27338         this.setActiveItem(this.barItems[i-1]);
27339     },
27340     
27341     format : function()
27342     {
27343         if(!this.barItems.length){
27344             return;
27345         }
27346      
27347         var width = 100 / this.barItems.length;
27348         
27349         Roo.each(this.barItems, function(i){
27350             i.el.setStyle('width', width + '%');
27351             i.topEl.el.setStyle('width', width + '%');
27352             i.bottomEl.el.setStyle('width', width + '%');
27353         }, this);
27354         
27355     }
27356     
27357 });
27358 /*
27359  * - LGPL
27360  *
27361  * Nav Progress Item
27362  * 
27363  */
27364
27365 /**
27366  * @class Roo.bootstrap.NavProgressItem
27367  * @extends Roo.bootstrap.Component
27368  * Bootstrap NavProgressItem class
27369  * @cfg {String} rid the reference id
27370  * @cfg {Boolean} active (true|false) Is item active default false
27371  * @cfg {Boolean} disabled (true|false) Is item active default false
27372  * @cfg {String} html
27373  * @cfg {String} position (top|bottom) text position default bottom
27374  * @cfg {String} icon show icon instead of number
27375  * 
27376  * @constructor
27377  * Create a new NavProgressItem
27378  * @param {Object} config The config object
27379  */
27380 Roo.bootstrap.NavProgressItem = function(config){
27381     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
27382     this.addEvents({
27383         // raw events
27384         /**
27385          * @event click
27386          * The raw click event for the entire grid.
27387          * @param {Roo.bootstrap.NavProgressItem} this
27388          * @param {Roo.EventObject} e
27389          */
27390         "click" : true
27391     });
27392    
27393 };
27394
27395 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
27396     
27397     rid : '',
27398     active : false,
27399     disabled : false,
27400     html : '',
27401     position : 'bottom',
27402     icon : false,
27403     
27404     getAutoCreate : function()
27405     {
27406         var iconCls = 'roo-navigation-bar-item-icon';
27407         
27408         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
27409         
27410         var cfg = {
27411             tag: 'li',
27412             cls: 'roo-navigation-bar-item',
27413             cn : [
27414                 {
27415                     tag : 'i',
27416                     cls : iconCls
27417                 }
27418             ]
27419         };
27420         
27421         if(this.active){
27422             cfg.cls += ' active';
27423         }
27424         if(this.disabled){
27425             cfg.cls += ' disabled';
27426         }
27427         
27428         return cfg;
27429     },
27430     
27431     disable : function()
27432     {
27433         this.setDisabled(true);
27434     },
27435     
27436     enable : function()
27437     {
27438         this.setDisabled(false);
27439     },
27440     
27441     initEvents: function() 
27442     {
27443         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
27444         
27445         this.iconEl.on('click', this.onClick, this);
27446     },
27447     
27448     onClick : function(e)
27449     {
27450         e.preventDefault();
27451         
27452         if(this.disabled){
27453             return;
27454         }
27455         
27456         if(this.fireEvent('click', this, e) === false){
27457             return;
27458         };
27459         
27460         this.parent().setActiveItem(this);
27461     },
27462     
27463     isActive: function () 
27464     {
27465         return this.active;
27466     },
27467     
27468     setActive : function(state)
27469     {
27470         if(this.active == state){
27471             return;
27472         }
27473         
27474         this.active = state;
27475         
27476         if (state) {
27477             this.el.addClass('active');
27478             return;
27479         }
27480         
27481         this.el.removeClass('active');
27482         
27483         return;
27484     },
27485     
27486     setDisabled : function(state)
27487     {
27488         if(this.disabled == state){
27489             return;
27490         }
27491         
27492         this.disabled = state;
27493         
27494         if (state) {
27495             this.el.addClass('disabled');
27496             return;
27497         }
27498         
27499         this.el.removeClass('disabled');
27500     },
27501     
27502     tooltipEl : function()
27503     {
27504         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
27505     }
27506 });
27507  
27508
27509  /*
27510  * - LGPL
27511  *
27512  * FieldLabel
27513  * 
27514  */
27515
27516 /**
27517  * @class Roo.bootstrap.FieldLabel
27518  * @extends Roo.bootstrap.Component
27519  * Bootstrap FieldLabel class
27520  * @cfg {String} html contents of the element
27521  * @cfg {String} tag tag of the element default label
27522  * @cfg {String} cls class of the element
27523  * @cfg {String} target label target 
27524  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
27525  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
27526  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
27527  * @cfg {String} iconTooltip default "This field is required"
27528  * 
27529  * @constructor
27530  * Create a new FieldLabel
27531  * @param {Object} config The config object
27532  */
27533
27534 Roo.bootstrap.FieldLabel = function(config){
27535     Roo.bootstrap.Element.superclass.constructor.call(this, config);
27536     
27537     this.addEvents({
27538             /**
27539              * @event invalid
27540              * Fires after the field has been marked as invalid.
27541              * @param {Roo.form.FieldLabel} this
27542              * @param {String} msg The validation message
27543              */
27544             invalid : true,
27545             /**
27546              * @event valid
27547              * Fires after the field has been validated with no errors.
27548              * @param {Roo.form.FieldLabel} this
27549              */
27550             valid : true
27551         });
27552 };
27553
27554 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
27555     
27556     tag: 'label',
27557     cls: '',
27558     html: '',
27559     target: '',
27560     allowBlank : true,
27561     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
27562     validClass : 'text-success fa fa-lg fa-check',
27563     iconTooltip : 'This field is required',
27564     
27565     getAutoCreate : function(){
27566         
27567         var cfg = {
27568             tag : this.tag,
27569             cls : 'roo-bootstrap-field-label ' + this.cls,
27570             for : this.target,
27571             cn : [
27572                 {
27573                     tag : 'i',
27574                     cls : '',
27575                     tooltip : this.iconTooltip
27576                 },
27577                 {
27578                     tag : 'span',
27579                     html : this.html
27580                 }
27581             ] 
27582         };
27583         
27584         return cfg;
27585     },
27586     
27587     initEvents: function() 
27588     {
27589         Roo.bootstrap.Element.superclass.initEvents.call(this);
27590         
27591         this.iconEl = this.el.select('i', true).first();
27592         
27593         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
27594         
27595         Roo.bootstrap.FieldLabel.register(this);
27596     },
27597     
27598     /**
27599      * Mark this field as valid
27600      */
27601     markValid : function()
27602     {
27603         this.iconEl.show();
27604         
27605         this.iconEl.removeClass(this.invalidClass);
27606         
27607         this.iconEl.addClass(this.validClass);
27608         
27609         this.fireEvent('valid', this);
27610     },
27611     
27612     /**
27613      * Mark this field as invalid
27614      * @param {String} msg The validation message
27615      */
27616     markInvalid : function(msg)
27617     {
27618         this.iconEl.show();
27619         
27620         this.iconEl.removeClass(this.validClass);
27621         
27622         this.iconEl.addClass(this.invalidClass);
27623         
27624         this.fireEvent('invalid', this, msg);
27625     }
27626     
27627    
27628 });
27629
27630 Roo.apply(Roo.bootstrap.FieldLabel, {
27631     
27632     groups: {},
27633     
27634      /**
27635     * register a FieldLabel Group
27636     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
27637     */
27638     register : function(label)
27639     {
27640         if(this.groups.hasOwnProperty(label.target)){
27641             return;
27642         }
27643      
27644         this.groups[label.target] = label;
27645         
27646     },
27647     /**
27648     * fetch a FieldLabel Group based on the target
27649     * @param {string} target
27650     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
27651     */
27652     get: function(target) {
27653         if (typeof(this.groups[target]) == 'undefined') {
27654             return false;
27655         }
27656         
27657         return this.groups[target] ;
27658     }
27659 });
27660
27661  
27662
27663  /*
27664  * - LGPL
27665  *
27666  * page DateSplitField.
27667  * 
27668  */
27669
27670
27671 /**
27672  * @class Roo.bootstrap.DateSplitField
27673  * @extends Roo.bootstrap.Component
27674  * Bootstrap DateSplitField class
27675  * @cfg {string} fieldLabel - the label associated
27676  * @cfg {Number} labelWidth set the width of label (0-12)
27677  * @cfg {String} labelAlign (top|left)
27678  * @cfg {Boolean} dayAllowBlank (true|false) default false
27679  * @cfg {Boolean} monthAllowBlank (true|false) default false
27680  * @cfg {Boolean} yearAllowBlank (true|false) default false
27681  * @cfg {string} dayPlaceholder 
27682  * @cfg {string} monthPlaceholder
27683  * @cfg {string} yearPlaceholder
27684  * @cfg {string} dayFormat default 'd'
27685  * @cfg {string} monthFormat default 'm'
27686  * @cfg {string} yearFormat default 'Y'
27687
27688  *     
27689  * @constructor
27690  * Create a new DateSplitField
27691  * @param {Object} config The config object
27692  */
27693
27694 Roo.bootstrap.DateSplitField = function(config){
27695     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
27696     
27697     this.addEvents({
27698         // raw events
27699          /**
27700          * @event years
27701          * getting the data of years
27702          * @param {Roo.bootstrap.DateSplitField} this
27703          * @param {Object} years
27704          */
27705         "years" : true,
27706         /**
27707          * @event days
27708          * getting the data of days
27709          * @param {Roo.bootstrap.DateSplitField} this
27710          * @param {Object} days
27711          */
27712         "days" : true,
27713         /**
27714          * @event invalid
27715          * Fires after the field has been marked as invalid.
27716          * @param {Roo.form.Field} this
27717          * @param {String} msg The validation message
27718          */
27719         invalid : true,
27720        /**
27721          * @event valid
27722          * Fires after the field has been validated with no errors.
27723          * @param {Roo.form.Field} this
27724          */
27725         valid : true
27726     });
27727 };
27728
27729 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
27730     
27731     fieldLabel : '',
27732     labelAlign : 'top',
27733     labelWidth : 3,
27734     dayAllowBlank : false,
27735     monthAllowBlank : false,
27736     yearAllowBlank : false,
27737     dayPlaceholder : '',
27738     monthPlaceholder : '',
27739     yearPlaceholder : '',
27740     dayFormat : 'd',
27741     monthFormat : 'm',
27742     yearFormat : 'Y',
27743     isFormField : true,
27744     
27745     getAutoCreate : function()
27746     {
27747         var cfg = {
27748             tag : 'div',
27749             cls : 'row roo-date-split-field-group',
27750             cn : [
27751                 {
27752                     tag : 'input',
27753                     type : 'hidden',
27754                     cls : 'form-hidden-field roo-date-split-field-group-value',
27755                     name : this.name
27756                 }
27757             ]
27758         };
27759         
27760         if(this.fieldLabel){
27761             cfg.cn.push({
27762                 tag : 'div',
27763                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
27764                 cn : [
27765                     {
27766                         tag : 'label',
27767                         html : this.fieldLabel
27768                     }
27769                 ]
27770             });
27771         }
27772         
27773         Roo.each(['day', 'month', 'year'], function(t){
27774             cfg.cn.push({
27775                 tag : 'div',
27776                 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
27777             });
27778         }, this);
27779         
27780         return cfg;
27781     },
27782     
27783     inputEl: function ()
27784     {
27785         return this.el.select('.roo-date-split-field-group-value', true).first();
27786     },
27787     
27788     onRender : function(ct, position) 
27789     {
27790         var _this = this;
27791         
27792         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27793         
27794         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
27795         
27796         this.dayField = new Roo.bootstrap.ComboBox({
27797             allowBlank : this.dayAllowBlank,
27798             alwaysQuery : true,
27799             displayField : 'value',
27800             editable : false,
27801             fieldLabel : '',
27802             forceSelection : true,
27803             mode : 'local',
27804             placeholder : this.dayPlaceholder,
27805             selectOnFocus : true,
27806             tpl : '<div class="select2-result"><b>{value}</b></div>',
27807             triggerAction : 'all',
27808             typeAhead : true,
27809             valueField : 'value',
27810             store : new Roo.data.SimpleStore({
27811                 data : (function() {    
27812                     var days = [];
27813                     _this.fireEvent('days', _this, days);
27814                     return days;
27815                 })(),
27816                 fields : [ 'value' ]
27817             }),
27818             listeners : {
27819                 select : function (_self, record, index)
27820                 {
27821                     _this.setValue(_this.getValue());
27822                 }
27823             }
27824         });
27825
27826         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
27827         
27828         this.monthField = new Roo.bootstrap.MonthField({
27829             after : '<i class=\"fa fa-calendar\"></i>',
27830             allowBlank : this.monthAllowBlank,
27831             placeholder : this.monthPlaceholder,
27832             readOnly : true,
27833             listeners : {
27834                 render : function (_self)
27835                 {
27836                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
27837                         e.preventDefault();
27838                         _self.focus();
27839                     });
27840                 },
27841                 select : function (_self, oldvalue, newvalue)
27842                 {
27843                     _this.setValue(_this.getValue());
27844                 }
27845             }
27846         });
27847         
27848         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
27849         
27850         this.yearField = new Roo.bootstrap.ComboBox({
27851             allowBlank : this.yearAllowBlank,
27852             alwaysQuery : true,
27853             displayField : 'value',
27854             editable : false,
27855             fieldLabel : '',
27856             forceSelection : true,
27857             mode : 'local',
27858             placeholder : this.yearPlaceholder,
27859             selectOnFocus : true,
27860             tpl : '<div class="select2-result"><b>{value}</b></div>',
27861             triggerAction : 'all',
27862             typeAhead : true,
27863             valueField : 'value',
27864             store : new Roo.data.SimpleStore({
27865                 data : (function() {
27866                     var years = [];
27867                     _this.fireEvent('years', _this, years);
27868                     return years;
27869                 })(),
27870                 fields : [ 'value' ]
27871             }),
27872             listeners : {
27873                 select : function (_self, record, index)
27874                 {
27875                     _this.setValue(_this.getValue());
27876                 }
27877             }
27878         });
27879
27880         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
27881     },
27882     
27883     setValue : function(v, format)
27884     {
27885         this.inputEl.dom.value = v;
27886         
27887         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
27888         
27889         var d = Date.parseDate(v, f);
27890         
27891         if(!d){
27892             this.validate();
27893             return;
27894         }
27895         
27896         this.setDay(d.format(this.dayFormat));
27897         this.setMonth(d.format(this.monthFormat));
27898         this.setYear(d.format(this.yearFormat));
27899         
27900         this.validate();
27901         
27902         return;
27903     },
27904     
27905     setDay : function(v)
27906     {
27907         this.dayField.setValue(v);
27908         this.inputEl.dom.value = this.getValue();
27909         this.validate();
27910         return;
27911     },
27912     
27913     setMonth : function(v)
27914     {
27915         this.monthField.setValue(v, true);
27916         this.inputEl.dom.value = this.getValue();
27917         this.validate();
27918         return;
27919     },
27920     
27921     setYear : function(v)
27922     {
27923         this.yearField.setValue(v);
27924         this.inputEl.dom.value = this.getValue();
27925         this.validate();
27926         return;
27927     },
27928     
27929     getDay : function()
27930     {
27931         return this.dayField.getValue();
27932     },
27933     
27934     getMonth : function()
27935     {
27936         return this.monthField.getValue();
27937     },
27938     
27939     getYear : function()
27940     {
27941         return this.yearField.getValue();
27942     },
27943     
27944     getValue : function()
27945     {
27946         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
27947         
27948         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
27949         
27950         return date;
27951     },
27952     
27953     reset : function()
27954     {
27955         this.setDay('');
27956         this.setMonth('');
27957         this.setYear('');
27958         this.inputEl.dom.value = '';
27959         this.validate();
27960         return;
27961     },
27962     
27963     validate : function()
27964     {
27965         var d = this.dayField.validate();
27966         var m = this.monthField.validate();
27967         var y = this.yearField.validate();
27968         
27969         var valid = true;
27970         
27971         if(
27972                 (!this.dayAllowBlank && !d) ||
27973                 (!this.monthAllowBlank && !m) ||
27974                 (!this.yearAllowBlank && !y)
27975         ){
27976             valid = false;
27977         }
27978         
27979         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
27980             return valid;
27981         }
27982         
27983         if(valid){
27984             this.markValid();
27985             return valid;
27986         }
27987         
27988         this.markInvalid();
27989         
27990         return valid;
27991     },
27992     
27993     markValid : function()
27994     {
27995         
27996         var label = this.el.select('label', true).first();
27997         var icon = this.el.select('i.fa-star', true).first();
27998
27999         if(label && icon){
28000             icon.remove();
28001         }
28002         
28003         this.fireEvent('valid', this);
28004     },
28005     
28006      /**
28007      * Mark this field as invalid
28008      * @param {String} msg The validation message
28009      */
28010     markInvalid : function(msg)
28011     {
28012         
28013         var label = this.el.select('label', true).first();
28014         var icon = this.el.select('i.fa-star', true).first();
28015
28016         if(label && !icon){
28017             this.el.select('.roo-date-split-field-label', true).createChild({
28018                 tag : 'i',
28019                 cls : 'text-danger fa fa-lg fa-star',
28020                 tooltip : 'This field is required',
28021                 style : 'margin-right:5px;'
28022             }, label, true);
28023         }
28024         
28025         this.fireEvent('invalid', this, msg);
28026     },
28027     
28028     clearInvalid : function()
28029     {
28030         var label = this.el.select('label', true).first();
28031         var icon = this.el.select('i.fa-star', true).first();
28032
28033         if(label && icon){
28034             icon.remove();
28035         }
28036         
28037         this.fireEvent('valid', this);
28038     },
28039     
28040     getName: function()
28041     {
28042         return this.name;
28043     }
28044     
28045 });
28046
28047