f0e10dd04c02a51fb45a4ace3e89ceb1238a46a8
[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         
140         this.initEvents();
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         //Roo.log(['addxtype', cn]);
166            
167         cn.parentType = this.xtype; //??
168         cn.parentId = this.id;
169         
170         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
171         if (typeof(cn.container_method) == 'string') {
172             cntr = cn.container_method;
173         }
174         
175         
176         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
177         
178         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
179         
180         var build_from_html =  Roo.XComponent.build_from_html;
181           
182         var is_body  = (tree.xtype == 'Body') ;
183           
184         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
185           
186         var self_cntr_el = Roo.get(this[cntr](false));
187         
188         // do not try and build conditional elements 
189         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
190             return false;
191         }
192         
193         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
194             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
195                 return this.addxtypeChild(tree,cntr, is_body);
196             }
197             
198             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
199                 
200             if(echild){
201                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
202             }
203             
204             Roo.log('skipping render');
205             return cn;
206             
207         }
208         
209         var ret = false;
210         if (!build_from_html) {
211             return false;
212         }
213         
214         // this i think handles overlaying multiple children of the same type
215         // with the sam eelement.. - which might be buggy..
216         while (true) {
217             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
218             
219             if (!echild) {
220                 break;
221             }
222             
223             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
224                 break;
225             }
226             
227             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
228         }
229        
230         return ret;
231     },
232     
233     
234     addxtypeChild : function (tree, cntr, is_body)
235     {
236         Roo.debug && Roo.log('addxtypeChild:' + cntr);
237         var cn = this;
238         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
239         
240         
241         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
242                     (typeof(tree['flexy:foreach']) != 'undefined');
243           
244     
245         
246         skip_children = false;
247         // render the element if it's not BODY.
248         if (!is_body) {
249             
250             // if parent was disabled, then do not try and create the children..
251             if(!this[cntr](true)){
252                 tree.items = [];
253                 return tree;
254             }
255            
256             cn = Roo.factory(tree);
257            
258             cn.parentType = this.xtype; //??
259             cn.parentId = this.id;
260             
261             var build_from_html =  Roo.XComponent.build_from_html;
262             
263             
264             // does the container contain child eleemnts with 'xtype' attributes.
265             // that match this xtype..
266             // note - when we render we create these as well..
267             // so we should check to see if body has xtype set.
268             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
269                
270                 var self_cntr_el = Roo.get(this[cntr](false));
271                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
272                 if (echild) { 
273                     //Roo.log(Roo.XComponent.build_from_html);
274                     //Roo.log("got echild:");
275                     //Roo.log(echild);
276                 }
277                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
278                 // and are not displayed -this causes this to use up the wrong element when matching.
279                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
280                 
281                 
282                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
283                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
284                   
285                   
286                   
287                     cn.el = echild;
288                   //  Roo.log("GOT");
289                     //echild.dom.removeAttribute('xtype');
290                 } else {
291                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
292                     Roo.debug && Roo.log(self_cntr_el);
293                     Roo.debug && Roo.log(echild);
294                     Roo.debug && Roo.log(cn);
295                 }
296             }
297            
298             
299            
300             // if object has flexy:if - then it may or may not be rendered.
301             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
302                 // skip a flexy if element.
303                 Roo.debug && Roo.log('skipping render');
304                 Roo.debug && Roo.log(tree);
305                 if (!cn.el) {
306                     Roo.debug && Roo.log('skipping all children');
307                     skip_children = true;
308                 }
309                 
310              } else {
311                  
312                 // actually if flexy:foreach is found, we really want to create 
313                 // multiple copies here...
314                 //Roo.log('render');
315                 //Roo.log(this[cntr]());
316                 // some elements do not have render methods.. like the layouts...
317                 /*
318                 if(this[cntr](true) === false){
319                     cn.items = [];
320                     return cn;
321                 }
322                 */
323                 cn.render && cn.render(this[cntr](true));
324                 
325              }
326             // then add the element..
327         }
328          
329         // handle the kids..
330         
331         var nitems = [];
332         /*
333         if (typeof (tree.menu) != 'undefined') {
334             tree.menu.parentType = cn.xtype;
335             tree.menu.triggerEl = cn.el;
336             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
337             
338         }
339         */
340         if (!tree.items || !tree.items.length) {
341             cn.items = nitems;
342             //Roo.log(["no children", this]);
343             
344             return cn;
345         }
346          
347         var items = tree.items;
348         delete tree.items;
349         
350         //Roo.log(items.length);
351             // add the items..
352         if (!skip_children) {    
353             for(var i =0;i < items.length;i++) {
354               //  Roo.log(['add child', items[i]]);
355                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
356             }
357         }
358         
359         cn.items = nitems;
360         
361         //Roo.log("fire childrenrendered");
362         
363         cn.fireEvent('childrenrendered', this);
364         
365         return cn;
366     },
367     /**
368      * Show a component - removes 'hidden' class
369      */
370     show : function()
371     {
372         if(!this.getEl()){
373             return;
374         }
375         
376         this.getEl().removeClass('hidden');
377         
378         if(!this.hideParent){
379             return;
380         }
381         
382         this.parent().getEl().removeClass('hidden');
383         
384     },
385     /**
386      * Hide a component - adds 'hidden' class
387      */
388     hide: function()
389     {
390         if(!this.getEl() || this.getEl().hasClass('hidden')){
391             return;
392         }
393         
394         this.getEl().addClass('hidden');
395         
396         if(!this.hideParent){
397             return;
398         }
399         
400         this.parent().getEl().addClass('hidden');
401         
402     }
403 });
404
405  /*
406  * - LGPL
407  *
408  * Body
409  *
410  */
411
412 /**
413  * @class Roo.bootstrap.Body
414  * @extends Roo.bootstrap.Component
415  * Bootstrap Body class
416  *
417  * @constructor
418  * Create a new body
419  * @param {Object} config The config object
420  */
421
422 Roo.bootstrap.Body = function(config){
423
424     config = config || {};
425
426     Roo.bootstrap.Body.superclass.constructor.call(this, config);
427     this.el = Roo.get(config.el ? config.el : document.body );
428     if (this.cls && this.cls.length) {
429         Roo.get(document.body).addClass(this.cls);
430     }
431 };
432
433 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
434
435     is_body : true,// just to make sure it's constructed?
436
437         autoCreate : {
438         cls: 'container'
439     },
440     onRender : function(ct, position)
441     {
442        /* Roo.log("Roo.bootstrap.Body - onRender");
443         if (this.cls && this.cls.length) {
444             Roo.get(document.body).addClass(this.cls);
445         }
446         // style??? xttr???
447         */
448     }
449
450
451
452
453 });
454 /*
455  * - LGPL
456  *
457  * button group
458  * 
459  */
460
461
462 /**
463  * @class Roo.bootstrap.ButtonGroup
464  * @extends Roo.bootstrap.Component
465  * Bootstrap ButtonGroup class
466  * @cfg {String} size lg | sm | xs (default empty normal)
467  * @cfg {String} align vertical | justified  (default none)
468  * @cfg {String} direction up | down (default down)
469  * @cfg {Boolean} toolbar false | true
470  * @cfg {Boolean} btn true | false
471  * 
472  * 
473  * @constructor
474  * Create a new Input
475  * @param {Object} config The config object
476  */
477
478 Roo.bootstrap.ButtonGroup = function(config){
479     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
480 };
481
482 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
483     
484     size: '',
485     align: '',
486     direction: '',
487     toolbar: false,
488     btn: true,
489
490     getAutoCreate : function(){
491         var cfg = {
492             cls: 'btn-group',
493             html : null
494         };
495         
496         cfg.html = this.html || cfg.html;
497         
498         if (this.toolbar) {
499             cfg = {
500                 cls: 'btn-toolbar',
501                 html: null
502             };
503             
504             return cfg;
505         }
506         
507         if (['vertical','justified'].indexOf(this.align)!==-1) {
508             cfg.cls = 'btn-group-' + this.align;
509             
510             if (this.align == 'justified') {
511                 console.log(this.items);
512             }
513         }
514         
515         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
516             cfg.cls += ' btn-group-' + this.size;
517         }
518         
519         if (this.direction == 'up') {
520             cfg.cls += ' dropup' ;
521         }
522         
523         return cfg;
524     }
525    
526 });
527
528  /*
529  * - LGPL
530  *
531  * button
532  * 
533  */
534
535 /**
536  * @class Roo.bootstrap.Button
537  * @extends Roo.bootstrap.Component
538  * Bootstrap Button class
539  * @cfg {String} html The button content
540  * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default 
541  * @cfg {String} size ( lg | sm | xs)
542  * @cfg {String} tag ( a | input | submit)
543  * @cfg {String} href empty or href
544  * @cfg {Boolean} disabled default false;
545  * @cfg {Boolean} isClose default false;
546  * @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)
547  * @cfg {String} badge text for badge
548  * @cfg {String} theme default 
549  * @cfg {Boolean} inverse 
550  * @cfg {Boolean} toggle 
551  * @cfg {String} ontext text for on toggle state
552  * @cfg {String} offtext text for off toggle state
553  * @cfg {Boolean} defaulton 
554  * @cfg {Boolean} preventDefault  default true
555  * @cfg {Boolean} removeClass remove the standard class..
556  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
557  * 
558  * @constructor
559  * Create a new button
560  * @param {Object} config The config object
561  */
562
563
564 Roo.bootstrap.Button = function(config){
565     Roo.bootstrap.Button.superclass.constructor.call(this, config);
566     this.weightClass = ["btn-default", 
567                        "btn-primary", 
568                        "btn-success", 
569                        "btn-info", 
570                        "btn-warning",
571                        "btn-danger",
572                        "btn-link"
573                       ],  
574     this.addEvents({
575         // raw events
576         /**
577          * @event click
578          * When a butotn is pressed
579          * @param {Roo.bootstrap.Button} this
580          * @param {Roo.EventObject} e
581          */
582         "click" : true,
583          /**
584          * @event toggle
585          * After the button has been toggles
586          * @param {Roo.EventObject} e
587          * @param {boolean} pressed (also available as button.pressed)
588          */
589         "toggle" : true
590     });
591 };
592
593 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
594     html: false,
595     active: false,
596     weight: '',
597     size: '',
598     tag: 'button',
599     href: '',
600     disabled: false,
601     isClose: false,
602     glyphicon: '',
603     badge: '',
604     theme: 'default',
605     inverse: false,
606     
607     toggle: false,
608     ontext: 'ON',
609     offtext: 'OFF',
610     defaulton: true,
611     preventDefault: true,
612     removeClass: false,
613     name: false,
614     target: false,
615     
616     
617     pressed : null,
618      
619     
620     getAutoCreate : function(){
621         
622         var cfg = {
623             tag : 'button',
624             cls : 'roo-button',
625             html: ''
626         };
627         
628         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
629             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
630             this.tag = 'button';
631         } else {
632             cfg.tag = this.tag;
633         }
634         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
635         
636         if (this.toggle == true) {
637             cfg={
638                 tag: 'div',
639                 cls: 'slider-frame roo-button',
640                 cn: [
641                     {
642                         tag: 'span',
643                         'data-on-text':'ON',
644                         'data-off-text':'OFF',
645                         cls: 'slider-button',
646                         html: this.offtext
647                     }
648                 ]
649             };
650             
651             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
652                 cfg.cls += ' '+this.weight;
653             }
654             
655             return cfg;
656         }
657         
658         if (this.isClose) {
659             cfg.cls += ' close';
660             
661             cfg["aria-hidden"] = true;
662             
663             cfg.html = "&times;";
664             
665             return cfg;
666         }
667         
668          
669         if (this.theme==='default') {
670             cfg.cls = 'btn roo-button';
671             
672             //if (this.parentType != 'Navbar') {
673             this.weight = this.weight.length ?  this.weight : 'default';
674             //}
675             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
676                 
677                 cfg.cls += ' btn-' + this.weight;
678             }
679         } else if (this.theme==='glow') {
680             
681             cfg.tag = 'a';
682             cfg.cls = 'btn-glow roo-button';
683             
684             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
685                 
686                 cfg.cls += ' ' + this.weight;
687             }
688         }
689    
690         
691         if (this.inverse) {
692             this.cls += ' inverse';
693         }
694         
695         
696         if (this.active) {
697             cfg.cls += ' active';
698         }
699         
700         if (this.disabled) {
701             cfg.disabled = 'disabled';
702         }
703         
704         if (this.items) {
705             Roo.log('changing to ul' );
706             cfg.tag = 'ul';
707             this.glyphicon = 'caret';
708         }
709         
710         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
711          
712         //gsRoo.log(this.parentType);
713         if (this.parentType === 'Navbar' && !this.parent().bar) {
714             Roo.log('changing to li?');
715             
716             cfg.tag = 'li';
717             
718             cfg.cls = '';
719             cfg.cn =  [{
720                 tag : 'a',
721                 cls : 'roo-button',
722                 html : this.html,
723                 href : this.href || '#'
724             }];
725             if (this.menu) {
726                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
727                 cfg.cls += ' dropdown';
728             }   
729             
730             delete cfg.html;
731             
732         }
733         
734        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
735         
736         if (this.glyphicon) {
737             cfg.html = ' ' + cfg.html;
738             
739             cfg.cn = [
740                 {
741                     tag: 'span',
742                     cls: 'glyphicon glyphicon-' + this.glyphicon
743                 }
744             ];
745         }
746         
747         if (this.badge) {
748             cfg.html += ' ';
749             
750             cfg.tag = 'a';
751             
752 //            cfg.cls='btn roo-button';
753             
754             cfg.href=this.href;
755             
756             var value = cfg.html;
757             
758             if(this.glyphicon){
759                 value = {
760                             tag: 'span',
761                             cls: 'glyphicon glyphicon-' + this.glyphicon,
762                             html: this.html
763                         };
764                 
765             }
766             
767             cfg.cn = [
768                 value,
769                 {
770                     tag: 'span',
771                     cls: 'badge',
772                     html: this.badge
773                 }
774             ];
775             
776             cfg.html='';
777         }
778         
779         if (this.menu) {
780             cfg.cls += ' dropdown';
781             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
782         }
783         
784         if (cfg.tag !== 'a' && this.href !== '') {
785             throw "Tag must be a to set href.";
786         } else if (this.href.length > 0) {
787             cfg.href = this.href;
788         }
789         
790         if(this.removeClass){
791             cfg.cls = '';
792         }
793         
794         if(this.target){
795             cfg.target = this.target;
796         }
797         
798         return cfg;
799     },
800     initEvents: function() {
801        // Roo.log('init events?');
802 //        Roo.log(this.el.dom);
803         // add the menu...
804         
805         if (typeof (this.menu) != 'undefined') {
806             this.menu.parentType = this.xtype;
807             this.menu.triggerEl = this.el;
808             this.addxtype(Roo.apply({}, this.menu));
809         }
810
811
812        if (this.el.hasClass('roo-button')) {
813             this.el.on('click', this.onClick, this);
814        } else {
815             this.el.select('.roo-button').on('click', this.onClick, this);
816        }
817        
818        if(this.removeClass){
819            this.el.on('click', this.onClick, this);
820        }
821        
822        this.el.enableDisplayMode();
823         
824     },
825     onClick : function(e)
826     {
827         if (this.disabled) {
828             return;
829         }
830         
831         
832         Roo.log('button on click ');
833         if(this.preventDefault){
834             e.preventDefault();
835         }
836         if (this.pressed === true || this.pressed === false) {
837             this.pressed = !this.pressed;
838             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
839             this.fireEvent('toggle', this, e, this.pressed);
840         }
841         
842         
843         this.fireEvent('click', this, e);
844     },
845     
846     /**
847      * Enables this button
848      */
849     enable : function()
850     {
851         this.disabled = false;
852         this.el.removeClass('disabled');
853     },
854     
855     /**
856      * Disable this button
857      */
858     disable : function()
859     {
860         this.disabled = true;
861         this.el.addClass('disabled');
862     },
863      /**
864      * sets the active state on/off, 
865      * @param {Boolean} state (optional) Force a particular state
866      */
867     setActive : function(v) {
868         
869         this.el[v ? 'addClass' : 'removeClass']('active');
870     },
871      /**
872      * toggles the current active state 
873      */
874     toggleActive : function()
875     {
876        var active = this.el.hasClass('active');
877        this.setActive(!active);
878        
879         
880     },
881     setText : function(str)
882     {
883         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
884     },
885     getText : function()
886     {
887         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
888     },
889     hide: function() {
890        
891      
892         this.el.hide();   
893     },
894     show: function() {
895        
896         this.el.show();   
897     },
898     setWeight : function(str)
899     {
900           this.el.removeClass(this.weightClass);
901         this.el.addClass('btn-' + str);        
902     }
903     
904     
905 });
906
907  /*
908  * - LGPL
909  *
910  * column
911  * 
912  */
913
914 /**
915  * @class Roo.bootstrap.Column
916  * @extends Roo.bootstrap.Component
917  * Bootstrap Column class
918  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
919  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
920  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
921  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
922  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
923  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
924  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
925  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
926  *
927  * 
928  * @cfg {Boolean} hidden (true|false) hide the element
929  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
930  * @cfg {String} fa (ban|check|...) font awesome icon
931  * @cfg {Number} fasize (1|2|....) font awsome size
932
933  * @cfg {String} icon (info-sign|check|...) glyphicon name
934
935  * @cfg {String} html content of column.
936  * 
937  * @constructor
938  * Create a new Column
939  * @param {Object} config The config object
940  */
941
942 Roo.bootstrap.Column = function(config){
943     Roo.bootstrap.Column.superclass.constructor.call(this, config);
944 };
945
946 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
947     
948     xs: false,
949     sm: false,
950     md: false,
951     lg: false,
952     xsoff: false,
953     smoff: false,
954     mdoff: false,
955     lgoff: false,
956     html: '',
957     offset: 0,
958     alert: false,
959     fa: false,
960     icon : false,
961     hidden : false,
962     fasize : 1,
963     
964     getAutoCreate : function(){
965         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
966         
967         cfg = {
968             tag: 'div',
969             cls: 'column'
970         };
971         
972         var settings=this;
973         ['xs','sm','md','lg'].map(function(size){
974             //Roo.log( size + ':' + settings[size]);
975             
976             if (settings[size+'off'] !== false) {
977                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
978             }
979             
980             if (settings[size] === false) {
981                 return;
982             }
983             
984             if (!settings[size]) { // 0 = hidden
985                 cfg.cls += ' hidden-' + size;
986                 return;
987             }
988             cfg.cls += ' col-' + size + '-' + settings[size];
989             
990         });
991         
992         if (this.hidden) {
993             cfg.cls += ' hidden';
994         }
995         
996         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
997             cfg.cls +=' alert alert-' + this.alert;
998         }
999         
1000         
1001         if (this.html.length) {
1002             cfg.html = this.html;
1003         }
1004         if (this.fa) {
1005             var fasize = '';
1006             if (this.fasize > 1) {
1007                 fasize = ' fa-' + this.fasize + 'x';
1008             }
1009             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1010             
1011             
1012         }
1013         if (this.icon) {
1014             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1015         }
1016         
1017         return cfg;
1018     }
1019    
1020 });
1021
1022  
1023
1024  /*
1025  * - LGPL
1026  *
1027  * page container.
1028  * 
1029  */
1030
1031
1032 /**
1033  * @class Roo.bootstrap.Container
1034  * @extends Roo.bootstrap.Component
1035  * Bootstrap Container class
1036  * @cfg {Boolean} jumbotron is it a jumbotron element
1037  * @cfg {String} html content of element
1038  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1039  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1040  * @cfg {String} header content of header (for panel)
1041  * @cfg {String} footer content of footer (for panel)
1042  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1043  * @cfg {String} tag (header|aside|section) type of HTML tag.
1044  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1045  * @cfg {String} fa font awesome icon
1046  * @cfg {String} icon (info-sign|check|...) glyphicon name
1047  * @cfg {Boolean} hidden (true|false) hide the element
1048  * @cfg {Boolean} expandable (true|false) default false
1049  * @cfg {Boolean} expanded (true|false) default true
1050  * @cfg {String} rheader contet on the right of header
1051  * @cfg {Boolean} clickable (true|false) default false
1052
1053  *     
1054  * @constructor
1055  * Create a new Container
1056  * @param {Object} config The config object
1057  */
1058
1059 Roo.bootstrap.Container = function(config){
1060     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1061     
1062     this.addEvents({
1063         // raw events
1064          /**
1065          * @event expand
1066          * After the panel has been expand
1067          * 
1068          * @param {Roo.bootstrap.Container} this
1069          */
1070         "expand" : true,
1071         /**
1072          * @event collapse
1073          * After the panel has been collapsed
1074          * 
1075          * @param {Roo.bootstrap.Container} this
1076          */
1077         "collapse" : true,
1078         /**
1079          * @event click
1080          * When a element is chick
1081          * @param {Roo.bootstrap.Container} this
1082          * @param {Roo.EventObject} e
1083          */
1084         "click" : true
1085     });
1086 };
1087
1088 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1089     
1090     jumbotron : false,
1091     well: '',
1092     panel : '',
1093     header: '',
1094     footer : '',
1095     sticky: '',
1096     tag : false,
1097     alert : false,
1098     fa: false,
1099     icon : false,
1100     expandable : false,
1101     rheader : '',
1102     expanded : true,
1103     clickable: false,
1104   
1105      
1106     getChildContainer : function() {
1107         
1108         if(!this.el){
1109             return false;
1110         }
1111         
1112         if (this.panel.length) {
1113             return this.el.select('.panel-body',true).first();
1114         }
1115         
1116         return this.el;
1117     },
1118     
1119     
1120     getAutoCreate : function(){
1121         
1122         var cfg = {
1123             tag : this.tag || 'div',
1124             html : '',
1125             cls : ''
1126         };
1127         if (this.jumbotron) {
1128             cfg.cls = 'jumbotron';
1129         }
1130         
1131         
1132         
1133         // - this is applied by the parent..
1134         //if (this.cls) {
1135         //    cfg.cls = this.cls + '';
1136         //}
1137         
1138         if (this.sticky.length) {
1139             
1140             var bd = Roo.get(document.body);
1141             if (!bd.hasClass('bootstrap-sticky')) {
1142                 bd.addClass('bootstrap-sticky');
1143                 Roo.select('html',true).setStyle('height', '100%');
1144             }
1145              
1146             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1147         }
1148         
1149         
1150         if (this.well.length) {
1151             switch (this.well) {
1152                 case 'lg':
1153                 case 'sm':
1154                     cfg.cls +=' well well-' +this.well;
1155                     break;
1156                 default:
1157                     cfg.cls +=' well';
1158                     break;
1159             }
1160         }
1161         
1162         if (this.hidden) {
1163             cfg.cls += ' hidden';
1164         }
1165         
1166         
1167         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1168             cfg.cls +=' alert alert-' + this.alert;
1169         }
1170         
1171         var body = cfg;
1172         
1173         if (this.panel.length) {
1174             cfg.cls += ' panel panel-' + this.panel;
1175             cfg.cn = [];
1176             if (this.header.length) {
1177                 
1178                 var h = [];
1179                 
1180                 if(this.expandable){
1181                     
1182                     cfg.cls = cfg.cls + ' expandable';
1183                     
1184                     h.push({
1185                         tag: 'i',
1186                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1187                     });
1188                     
1189                 }
1190                 
1191                 h.push(
1192                     {
1193                         tag: 'span',
1194                         cls : 'panel-title',
1195                         html : (this.expandable ? '&nbsp;' : '') + this.header
1196                     },
1197                     {
1198                         tag: 'span',
1199                         cls: 'panel-header-right',
1200                         html: this.rheader
1201                     }
1202                 );
1203                 
1204                 cfg.cn.push({
1205                     cls : 'panel-heading',
1206                     style : this.expandable ? 'cursor: pointer' : '',
1207                     cn : h
1208                 });
1209                 
1210             }
1211             
1212             body = false;
1213             cfg.cn.push({
1214                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1215                 html : this.html
1216             });
1217             
1218             
1219             if (this.footer.length) {
1220                 cfg.cn.push({
1221                     cls : 'panel-footer',
1222                     html : this.footer
1223                     
1224                 });
1225             }
1226             
1227         }
1228         
1229         if (body) {
1230             body.html = this.html || cfg.html;
1231             // prefix with the icons..
1232             if (this.fa) {
1233                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1234             }
1235             if (this.icon) {
1236                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1237             }
1238             
1239             
1240         }
1241         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1242             cfg.cls =  'container';
1243         }
1244         
1245         return cfg;
1246     },
1247     
1248     initEvents: function() 
1249     {
1250         if(this.expandable){
1251             var headerEl = this.headerEl();
1252         
1253             if(headerEl){
1254                 headerEl.on('click', this.onToggleClick, this);
1255             }
1256         }
1257         
1258         if(this.clickable){
1259             this.el.on('click', this.onClick, this);
1260         }
1261         
1262     },
1263     
1264     onToggleClick : function()
1265     {
1266         var headerEl = this.headerEl();
1267         
1268         if(!headerEl){
1269             return;
1270         }
1271         
1272         if(this.expanded){
1273             this.collapse();
1274             return;
1275         }
1276         
1277         this.expand();
1278     },
1279     
1280     expand : function()
1281     {
1282         if(this.fireEvent('expand', this)) {
1283             
1284             this.expanded = true;
1285             
1286             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1287             
1288             this.el.select('.panel-body',true).first().removeClass('hide');
1289             
1290             var toggleEl = this.toggleEl();
1291
1292             if(!toggleEl){
1293                 return;
1294             }
1295
1296             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1297         }
1298         
1299     },
1300     
1301     collapse : function()
1302     {
1303         if(this.fireEvent('collapse', this)) {
1304             
1305             this.expanded = false;
1306             
1307             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1308             this.el.select('.panel-body',true).first().addClass('hide');
1309         
1310             var toggleEl = this.toggleEl();
1311
1312             if(!toggleEl){
1313                 return;
1314             }
1315
1316             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1317         }
1318     },
1319     
1320     toggleEl : function()
1321     {
1322         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1323             return;
1324         }
1325         
1326         return this.el.select('.panel-heading .fa',true).first();
1327     },
1328     
1329     headerEl : function()
1330     {
1331         if(!this.el || !this.panel.length || !this.header.length){
1332             return;
1333         }
1334         
1335         return this.el.select('.panel-heading',true).first()
1336     },
1337     
1338     bodyEl : function()
1339     {
1340         if(!this.el || !this.panel.length){
1341             return;
1342         }
1343         
1344         return this.el.select('.panel-body',true).first()
1345     },
1346     
1347     titleEl : function()
1348     {
1349         if(!this.el || !this.panel.length || !this.header.length){
1350             return;
1351         }
1352         
1353         return this.el.select('.panel-title',true).first();
1354     },
1355     
1356     setTitle : function(v)
1357     {
1358         var titleEl = this.titleEl();
1359         
1360         if(!titleEl){
1361             return;
1362         }
1363         
1364         titleEl.dom.innerHTML = v;
1365     },
1366     
1367     getTitle : function()
1368     {
1369         
1370         var titleEl = this.titleEl();
1371         
1372         if(!titleEl){
1373             return '';
1374         }
1375         
1376         return titleEl.dom.innerHTML;
1377     },
1378     
1379     setRightTitle : function(v)
1380     {
1381         var t = this.el.select('.panel-header-right',true).first();
1382         
1383         if(!t){
1384             return;
1385         }
1386         
1387         t.dom.innerHTML = v;
1388     },
1389     
1390     onClick : function(e)
1391     {
1392         e.preventDefault();
1393         
1394         this.fireEvent('click', this, e);
1395     },
1396     
1397     allChildren : function()
1398     {
1399         var r=new Roo.util.MixedCollection(false, function(o){
1400             return o.id || (o.id = Roo.id());
1401         });
1402         var iter = function(el) {
1403             if (el.inputEl) {
1404                 r.add(el);
1405             }
1406             if (!el.items) {
1407                 return;
1408             }
1409             Roo.each(el.items,function(e) {
1410                 iter(e);
1411             });
1412         };
1413
1414         iter(this);
1415         return r;
1416     },
1417     
1418     checkEmpty : function()
1419     {
1420         var items = this.allChildren();
1421         var isEmpty = true;
1422         
1423         items.each(function(f){
1424             if(f.el.isVisible()) {
1425                 isEmpty = false;
1426             }
1427         });
1428         
1429         return isEmpty;
1430     }
1431    
1432 });
1433
1434  /*
1435  * - LGPL
1436  *
1437  * image
1438  * 
1439  */
1440
1441
1442 /**
1443  * @class Roo.bootstrap.Img
1444  * @extends Roo.bootstrap.Component
1445  * Bootstrap Img class
1446  * @cfg {Boolean} imgResponsive false | true
1447  * @cfg {String} border rounded | circle | thumbnail
1448  * @cfg {String} src image source
1449  * @cfg {String} alt image alternative text
1450  * @cfg {String} href a tag href
1451  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1452  * @cfg {String} xsUrl xs image source
1453  * @cfg {String} smUrl sm image source
1454  * @cfg {String} mdUrl md image source
1455  * @cfg {String} lgUrl lg image source
1456  * 
1457  * @constructor
1458  * Create a new Input
1459  * @param {Object} config The config object
1460  */
1461
1462 Roo.bootstrap.Img = function(config){
1463     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1464     
1465     this.addEvents({
1466         // img events
1467         /**
1468          * @event click
1469          * The img click event for the img.
1470          * @param {Roo.EventObject} e
1471          */
1472         "click" : true
1473     });
1474 };
1475
1476 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1477     
1478     imgResponsive: true,
1479     border: '',
1480     src: 'about:blank',
1481     href: false,
1482     target: false,
1483     xsUrl: '',
1484     smUrl: '',
1485     mdUrl: '',
1486     lgUrl: '',
1487
1488     getAutoCreate : function()
1489     {   
1490         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1491             return this.createSingleImg();
1492         }
1493         
1494         var cfg = {
1495             tag: 'div',
1496             cls: 'roo-image-responsive-group',
1497             cn: []
1498         };
1499         var _this = this;
1500         
1501         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1502             
1503             if(!_this[size + 'Url']){
1504                 return;
1505             }
1506             
1507             var img = {
1508                 tag: 'img',
1509                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1510                 html: _this.html || cfg.html,
1511                 src: _this[size + 'Url']
1512             };
1513             
1514             img.cls += ' roo-image-responsive-' + size;
1515             
1516             var s = ['xs', 'sm', 'md', 'lg'];
1517             
1518             s.splice(s.indexOf(size), 1);
1519             
1520             Roo.each(s, function(ss){
1521                 img.cls += ' hidden-' + ss;
1522             });
1523             
1524             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1525                 cfg.cls += ' img-' + _this.border;
1526             }
1527             
1528             if(_this.alt){
1529                 cfg.alt = _this.alt;
1530             }
1531             
1532             if(_this.href){
1533                 var a = {
1534                     tag: 'a',
1535                     href: _this.href,
1536                     cn: [
1537                         img
1538                     ]
1539                 };
1540
1541                 if(this.target){
1542                     a.target = _this.target;
1543                 }
1544             }
1545             
1546             cfg.cn.push((_this.href) ? a : img);
1547             
1548         });
1549         
1550         return cfg;
1551     },
1552     
1553     createSingleImg : function()
1554     {
1555         var cfg = {
1556             tag: 'img',
1557             cls: (this.imgResponsive) ? 'img-responsive' : '',
1558             html : null,
1559             src : 'about:blank'  // just incase src get's set to undefined?!?
1560         };
1561         
1562         cfg.html = this.html || cfg.html;
1563         
1564         cfg.src = this.src || cfg.src;
1565         
1566         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1567             cfg.cls += ' img-' + this.border;
1568         }
1569         
1570         if(this.alt){
1571             cfg.alt = this.alt;
1572         }
1573         
1574         if(this.href){
1575             var a = {
1576                 tag: 'a',
1577                 href: this.href,
1578                 cn: [
1579                     cfg
1580                 ]
1581             };
1582             
1583             if(this.target){
1584                 a.target = this.target;
1585             }
1586             
1587         }
1588         
1589         return (this.href) ? a : cfg;
1590     },
1591     
1592     initEvents: function() 
1593     {
1594         if(!this.href){
1595             this.el.on('click', this.onClick, this);
1596         }
1597         
1598     },
1599     
1600     onClick : function(e)
1601     {
1602         Roo.log('img onclick');
1603         this.fireEvent('click', this, e);
1604     },
1605     /**
1606      * Sets the url of the image - used to update it
1607      * @param {String} url the url of the image
1608      */
1609     
1610     setSrc : function(url)
1611     {
1612         this.src =  url;
1613         
1614         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1615             this.el.dom.src =  url;
1616             return;
1617         }
1618         
1619         this.el.select('img', true).first().dom.src =  url;
1620     }
1621     
1622     
1623    
1624 });
1625
1626  /*
1627  * - LGPL
1628  *
1629  * image
1630  * 
1631  */
1632
1633
1634 /**
1635  * @class Roo.bootstrap.Link
1636  * @extends Roo.bootstrap.Component
1637  * Bootstrap Link Class
1638  * @cfg {String} alt image alternative text
1639  * @cfg {String} href a tag href
1640  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1641  * @cfg {String} html the content of the link.
1642  * @cfg {String} anchor name for the anchor link
1643  * @cfg {String} fa - favicon
1644
1645  * @cfg {Boolean} preventDefault (true | false) default false
1646
1647  * 
1648  * @constructor
1649  * Create a new Input
1650  * @param {Object} config The config object
1651  */
1652
1653 Roo.bootstrap.Link = function(config){
1654     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1655     
1656     this.addEvents({
1657         // img events
1658         /**
1659          * @event click
1660          * The img click event for the img.
1661          * @param {Roo.EventObject} e
1662          */
1663         "click" : true
1664     });
1665 };
1666
1667 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1668     
1669     href: false,
1670     target: false,
1671     preventDefault: false,
1672     anchor : false,
1673     alt : false,
1674     fa: false,
1675
1676
1677     getAutoCreate : function()
1678     {
1679         var html = this.html || '';
1680         
1681         if (this.fa !== false) {
1682             html = '<i class="fa fa-' + this.fa + '"></i>';
1683         }
1684         var cfg = {
1685             tag: 'a'
1686         };
1687         // anchor's do not require html/href...
1688         if (this.anchor === false) {
1689             cfg.html = html;
1690             cfg.href = this.href || '#';
1691         } else {
1692             cfg.name = this.anchor;
1693             if (this.html !== false || this.fa !== false) {
1694                 cfg.html = html;
1695             }
1696             if (this.href !== false) {
1697                 cfg.href = this.href;
1698             }
1699         }
1700         
1701         if(this.alt !== false){
1702             cfg.alt = this.alt;
1703         }
1704         
1705         
1706         if(this.target !== false) {
1707             cfg.target = this.target;
1708         }
1709         
1710         return cfg;
1711     },
1712     
1713     initEvents: function() {
1714         
1715         if(!this.href || this.preventDefault){
1716             this.el.on('click', this.onClick, this);
1717         }
1718     },
1719     
1720     onClick : function(e)
1721     {
1722         if(this.preventDefault){
1723             e.preventDefault();
1724         }
1725         //Roo.log('img onclick');
1726         this.fireEvent('click', this, e);
1727     }
1728    
1729 });
1730
1731  /*
1732  * - LGPL
1733  *
1734  * header
1735  * 
1736  */
1737
1738 /**
1739  * @class Roo.bootstrap.Header
1740  * @extends Roo.bootstrap.Component
1741  * Bootstrap Header class
1742  * @cfg {String} html content of header
1743  * @cfg {Number} level (1|2|3|4|5|6) default 1
1744  * 
1745  * @constructor
1746  * Create a new Header
1747  * @param {Object} config The config object
1748  */
1749
1750
1751 Roo.bootstrap.Header  = function(config){
1752     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1753 };
1754
1755 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1756     
1757     //href : false,
1758     html : false,
1759     level : 1,
1760     
1761     
1762     
1763     getAutoCreate : function(){
1764         
1765         
1766         
1767         var cfg = {
1768             tag: 'h' + (1 *this.level),
1769             html: this.html || ''
1770         } ;
1771         
1772         return cfg;
1773     }
1774    
1775 });
1776
1777  
1778
1779  /*
1780  * Based on:
1781  * Ext JS Library 1.1.1
1782  * Copyright(c) 2006-2007, Ext JS, LLC.
1783  *
1784  * Originally Released Under LGPL - original licence link has changed is not relivant.
1785  *
1786  * Fork - LGPL
1787  * <script type="text/javascript">
1788  */
1789  
1790 /**
1791  * @class Roo.bootstrap.MenuMgr
1792  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1793  * @singleton
1794  */
1795 Roo.bootstrap.MenuMgr = function(){
1796    var menus, active, groups = {}, attached = false, lastShow = new Date();
1797
1798    // private - called when first menu is created
1799    function init(){
1800        menus = {};
1801        active = new Roo.util.MixedCollection();
1802        Roo.get(document).addKeyListener(27, function(){
1803            if(active.length > 0){
1804                hideAll();
1805            }
1806        });
1807    }
1808
1809    // private
1810    function hideAll(){
1811        if(active && active.length > 0){
1812            var c = active.clone();
1813            c.each(function(m){
1814                m.hide();
1815            });
1816        }
1817    }
1818
1819    // private
1820    function onHide(m){
1821        active.remove(m);
1822        if(active.length < 1){
1823            Roo.get(document).un("mouseup", onMouseDown);
1824             
1825            attached = false;
1826        }
1827    }
1828
1829    // private
1830    function onShow(m){
1831        var last = active.last();
1832        lastShow = new Date();
1833        active.add(m);
1834        if(!attached){
1835           Roo.get(document).on("mouseup", onMouseDown);
1836            
1837            attached = true;
1838        }
1839        if(m.parentMenu){
1840           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1841           m.parentMenu.activeChild = m;
1842        }else if(last && last.isVisible()){
1843           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1844        }
1845    }
1846
1847    // private
1848    function onBeforeHide(m){
1849        if(m.activeChild){
1850            m.activeChild.hide();
1851        }
1852        if(m.autoHideTimer){
1853            clearTimeout(m.autoHideTimer);
1854            delete m.autoHideTimer;
1855        }
1856    }
1857
1858    // private
1859    function onBeforeShow(m){
1860        var pm = m.parentMenu;
1861        if(!pm && !m.allowOtherMenus){
1862            hideAll();
1863        }else if(pm && pm.activeChild && active != m){
1864            pm.activeChild.hide();
1865        }
1866    }
1867
1868    // private this should really trigger on mouseup..
1869    function onMouseDown(e){
1870         Roo.log("on Mouse Up");
1871         
1872         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1873             Roo.log("MenuManager hideAll");
1874             hideAll();
1875             e.stopEvent();
1876         }
1877         
1878         
1879    }
1880
1881    // private
1882    function onBeforeCheck(mi, state){
1883        if(state){
1884            var g = groups[mi.group];
1885            for(var i = 0, l = g.length; i < l; i++){
1886                if(g[i] != mi){
1887                    g[i].setChecked(false);
1888                }
1889            }
1890        }
1891    }
1892
1893    return {
1894
1895        /**
1896         * Hides all menus that are currently visible
1897         */
1898        hideAll : function(){
1899             hideAll();  
1900        },
1901
1902        // private
1903        register : function(menu){
1904            if(!menus){
1905                init();
1906            }
1907            menus[menu.id] = menu;
1908            menu.on("beforehide", onBeforeHide);
1909            menu.on("hide", onHide);
1910            menu.on("beforeshow", onBeforeShow);
1911            menu.on("show", onShow);
1912            var g = menu.group;
1913            if(g && menu.events["checkchange"]){
1914                if(!groups[g]){
1915                    groups[g] = [];
1916                }
1917                groups[g].push(menu);
1918                menu.on("checkchange", onCheck);
1919            }
1920        },
1921
1922         /**
1923          * Returns a {@link Roo.menu.Menu} object
1924          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1925          * be used to generate and return a new Menu instance.
1926          */
1927        get : function(menu){
1928            if(typeof menu == "string"){ // menu id
1929                return menus[menu];
1930            }else if(menu.events){  // menu instance
1931                return menu;
1932            }
1933            /*else if(typeof menu.length == 'number'){ // array of menu items?
1934                return new Roo.bootstrap.Menu({items:menu});
1935            }else{ // otherwise, must be a config
1936                return new Roo.bootstrap.Menu(menu);
1937            }
1938            */
1939            return false;
1940        },
1941
1942        // private
1943        unregister : function(menu){
1944            delete menus[menu.id];
1945            menu.un("beforehide", onBeforeHide);
1946            menu.un("hide", onHide);
1947            menu.un("beforeshow", onBeforeShow);
1948            menu.un("show", onShow);
1949            var g = menu.group;
1950            if(g && menu.events["checkchange"]){
1951                groups[g].remove(menu);
1952                menu.un("checkchange", onCheck);
1953            }
1954        },
1955
1956        // private
1957        registerCheckable : function(menuItem){
1958            var g = menuItem.group;
1959            if(g){
1960                if(!groups[g]){
1961                    groups[g] = [];
1962                }
1963                groups[g].push(menuItem);
1964                menuItem.on("beforecheckchange", onBeforeCheck);
1965            }
1966        },
1967
1968        // private
1969        unregisterCheckable : function(menuItem){
1970            var g = menuItem.group;
1971            if(g){
1972                groups[g].remove(menuItem);
1973                menuItem.un("beforecheckchange", onBeforeCheck);
1974            }
1975        }
1976    };
1977 }();/*
1978  * - LGPL
1979  *
1980  * menu
1981  * 
1982  */
1983
1984 /**
1985  * @class Roo.bootstrap.Menu
1986  * @extends Roo.bootstrap.Component
1987  * Bootstrap Menu class - container for MenuItems
1988  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1989  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1990  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1991  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1992  * 
1993  * @constructor
1994  * Create a new Menu
1995  * @param {Object} config The config object
1996  */
1997
1998
1999 Roo.bootstrap.Menu = function(config){
2000     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2001     if (this.registerMenu && this.type != 'treeview')  {
2002         Roo.bootstrap.MenuMgr.register(this);
2003     }
2004     this.addEvents({
2005         /**
2006          * @event beforeshow
2007          * Fires before this menu is displayed
2008          * @param {Roo.menu.Menu} this
2009          */
2010         beforeshow : true,
2011         /**
2012          * @event beforehide
2013          * Fires before this menu is hidden
2014          * @param {Roo.menu.Menu} this
2015          */
2016         beforehide : true,
2017         /**
2018          * @event show
2019          * Fires after this menu is displayed
2020          * @param {Roo.menu.Menu} this
2021          */
2022         show : true,
2023         /**
2024          * @event hide
2025          * Fires after this menu is hidden
2026          * @param {Roo.menu.Menu} this
2027          */
2028         hide : true,
2029         /**
2030          * @event click
2031          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2032          * @param {Roo.menu.Menu} this
2033          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2034          * @param {Roo.EventObject} e
2035          */
2036         click : true,
2037         /**
2038          * @event mouseover
2039          * Fires when the mouse is hovering over this menu
2040          * @param {Roo.menu.Menu} this
2041          * @param {Roo.EventObject} e
2042          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2043          */
2044         mouseover : true,
2045         /**
2046          * @event mouseout
2047          * Fires when the mouse exits this menu
2048          * @param {Roo.menu.Menu} this
2049          * @param {Roo.EventObject} e
2050          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2051          */
2052         mouseout : true,
2053         /**
2054          * @event itemclick
2055          * Fires when a menu item contained in this menu is clicked
2056          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2057          * @param {Roo.EventObject} e
2058          */
2059         itemclick: true
2060     });
2061     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2062 };
2063
2064 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2065     
2066    /// html : false,
2067     //align : '',
2068     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2069     type: false,
2070     /**
2071      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2072      */
2073     registerMenu : true,
2074     
2075     menuItems :false, // stores the menu items..
2076     
2077     hidden:true,
2078         
2079     parentMenu : false,
2080     
2081     stopEvent : true,
2082     
2083     isLink : false,
2084     
2085     getChildContainer : function() {
2086         return this.el;  
2087     },
2088     
2089     getAutoCreate : function(){
2090          
2091         //if (['right'].indexOf(this.align)!==-1) {
2092         //    cfg.cn[1].cls += ' pull-right'
2093         //}
2094         
2095         
2096         var cfg = {
2097             tag : 'ul',
2098             cls : 'dropdown-menu' ,
2099             style : 'z-index:1000'
2100             
2101         };
2102         
2103         if (this.type === 'submenu') {
2104             cfg.cls = 'submenu active';
2105         }
2106         if (this.type === 'treeview') {
2107             cfg.cls = 'treeview-menu';
2108         }
2109         
2110         return cfg;
2111     },
2112     initEvents : function() {
2113         
2114        // Roo.log("ADD event");
2115        // Roo.log(this.triggerEl.dom);
2116         
2117         this.triggerEl.on('click', this.onTriggerClick, this);
2118         
2119         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2120         
2121         this.triggerEl.addClass('dropdown-toggle');
2122         
2123         if (Roo.isTouch) {
2124             this.el.on('touchstart'  , this.onTouch, this);
2125         }
2126         this.el.on('click' , this.onClick, this);
2127
2128         this.el.on("mouseover", this.onMouseOver, this);
2129         this.el.on("mouseout", this.onMouseOut, this);
2130         
2131     },
2132     
2133     findTargetItem : function(e)
2134     {
2135         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2136         if(!t){
2137             return false;
2138         }
2139         //Roo.log(t);         Roo.log(t.id);
2140         if(t && t.id){
2141             //Roo.log(this.menuitems);
2142             return this.menuitems.get(t.id);
2143             
2144             //return this.items.get(t.menuItemId);
2145         }
2146         
2147         return false;
2148     },
2149     
2150     onTouch : function(e) 
2151     {
2152         Roo.log("menu.onTouch");
2153         //e.stopEvent(); this make the user popdown broken
2154         this.onClick(e);
2155     },
2156     
2157     onClick : function(e)
2158     {
2159         Roo.log("menu.onClick");
2160         
2161         var t = this.findTargetItem(e);
2162         if(!t || t.isContainer){
2163             return;
2164         }
2165         Roo.log(e);
2166         /*
2167         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2168             if(t == this.activeItem && t.shouldDeactivate(e)){
2169                 this.activeItem.deactivate();
2170                 delete this.activeItem;
2171                 return;
2172             }
2173             if(t.canActivate){
2174                 this.setActiveItem(t, true);
2175             }
2176             return;
2177             
2178             
2179         }
2180         */
2181        
2182         Roo.log('pass click event');
2183         
2184         t.onClick(e);
2185         
2186         this.fireEvent("click", this, t, e);
2187         
2188         var _this = this;
2189         
2190         if(!t.href.length || t.href == '#'){
2191             (function() { _this.hide(); }).defer(100);
2192         }
2193         
2194     },
2195     
2196     onMouseOver : function(e){
2197         var t  = this.findTargetItem(e);
2198         //Roo.log(t);
2199         //if(t){
2200         //    if(t.canActivate && !t.disabled){
2201         //        this.setActiveItem(t, true);
2202         //    }
2203         //}
2204         
2205         this.fireEvent("mouseover", this, e, t);
2206     },
2207     isVisible : function(){
2208         return !this.hidden;
2209     },
2210      onMouseOut : function(e){
2211         var t  = this.findTargetItem(e);
2212         
2213         //if(t ){
2214         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2215         //        this.activeItem.deactivate();
2216         //        delete this.activeItem;
2217         //    }
2218         //}
2219         this.fireEvent("mouseout", this, e, t);
2220     },
2221     
2222     
2223     /**
2224      * Displays this menu relative to another element
2225      * @param {String/HTMLElement/Roo.Element} element The element to align to
2226      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2227      * the element (defaults to this.defaultAlign)
2228      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2229      */
2230     show : function(el, pos, parentMenu){
2231         this.parentMenu = parentMenu;
2232         if(!this.el){
2233             this.render();
2234         }
2235         this.fireEvent("beforeshow", this);
2236         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2237     },
2238      /**
2239      * Displays this menu at a specific xy position
2240      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2241      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2242      */
2243     showAt : function(xy, parentMenu, /* private: */_e){
2244         this.parentMenu = parentMenu;
2245         if(!this.el){
2246             this.render();
2247         }
2248         if(_e !== false){
2249             this.fireEvent("beforeshow", this);
2250             //xy = this.el.adjustForConstraints(xy);
2251         }
2252         
2253         //this.el.show();
2254         this.hideMenuItems();
2255         this.hidden = false;
2256         this.triggerEl.addClass('open');
2257         
2258         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2259             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2260         }
2261         
2262         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2263             this.el.setXY(xy);
2264         }
2265         
2266         this.focus();
2267         this.fireEvent("show", this);
2268     },
2269     
2270     focus : function(){
2271         return;
2272         if(!this.hidden){
2273             this.doFocus.defer(50, this);
2274         }
2275     },
2276
2277     doFocus : function(){
2278         if(!this.hidden){
2279             this.focusEl.focus();
2280         }
2281     },
2282
2283     /**
2284      * Hides this menu and optionally all parent menus
2285      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2286      */
2287     hide : function(deep)
2288     {
2289         
2290         this.hideMenuItems();
2291         if(this.el && this.isVisible()){
2292             this.fireEvent("beforehide", this);
2293             if(this.activeItem){
2294                 this.activeItem.deactivate();
2295                 this.activeItem = null;
2296             }
2297             this.triggerEl.removeClass('open');;
2298             this.hidden = true;
2299             this.fireEvent("hide", this);
2300         }
2301         if(deep === true && this.parentMenu){
2302             this.parentMenu.hide(true);
2303         }
2304     },
2305     
2306     onTriggerClick : function(e)
2307     {
2308         Roo.log('trigger click');
2309         
2310         var target = e.getTarget();
2311         
2312         Roo.log(target.nodeName.toLowerCase());
2313         
2314         if(target.nodeName.toLowerCase() === 'i'){
2315             e.preventDefault();
2316         }
2317         
2318     },
2319     
2320     onTriggerPress  : function(e)
2321     {
2322         Roo.log('trigger press');
2323         //Roo.log(e.getTarget());
2324        // Roo.log(this.triggerEl.dom);
2325        
2326         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2327         var pel = Roo.get(e.getTarget());
2328         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2329             Roo.log('is treeview or dropdown?');
2330             return;
2331         }
2332         
2333         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2334             return;
2335         }
2336         
2337         if (this.isVisible()) {
2338             Roo.log('hide');
2339             this.hide();
2340         } else {
2341             Roo.log('show');
2342             this.show(this.triggerEl, false, false);
2343         }
2344         
2345         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2346             e.stopEvent();
2347         }
2348         
2349     },
2350        
2351     
2352     hideMenuItems : function()
2353     {
2354         Roo.log("hide Menu Items");
2355         if (!this.el) { 
2356             return;
2357         }
2358         //$(backdrop).remove()
2359         this.el.select('.open',true).each(function(aa) {
2360             
2361             aa.removeClass('open');
2362           //var parent = getParent($(this))
2363           //var relatedTarget = { relatedTarget: this }
2364           
2365            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2366           //if (e.isDefaultPrevented()) return
2367            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2368         });
2369     },
2370     addxtypeChild : function (tree, cntr) {
2371         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2372           
2373         this.menuitems.add(comp);
2374         return comp;
2375
2376     },
2377     getEl : function()
2378     {
2379         Roo.log(this.el);
2380         return this.el;
2381     },
2382     
2383     clear : function()
2384     {
2385         this.getEl().dom.innerHTML = '';
2386         this.menuitems.clear();
2387     }
2388 });
2389
2390  
2391  /*
2392  * - LGPL
2393  *
2394  * menu item
2395  * 
2396  */
2397
2398
2399 /**
2400  * @class Roo.bootstrap.MenuItem
2401  * @extends Roo.bootstrap.Component
2402  * Bootstrap MenuItem class
2403  * @cfg {String} html the menu label
2404  * @cfg {String} href the link
2405  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2406  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2407  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2408  * @cfg {String} fa favicon to show on left of menu item.
2409  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2410  * 
2411  * 
2412  * @constructor
2413  * Create a new MenuItem
2414  * @param {Object} config The config object
2415  */
2416
2417
2418 Roo.bootstrap.MenuItem = function(config){
2419     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2420     this.addEvents({
2421         // raw events
2422         /**
2423          * @event click
2424          * The raw click event for the entire grid.
2425          * @param {Roo.bootstrap.MenuItem} this
2426          * @param {Roo.EventObject} e
2427          */
2428         "click" : true
2429     });
2430 };
2431
2432 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2433     
2434     href : false,
2435     html : false,
2436     preventDefault: false,
2437     isContainer : false,
2438     active : false,
2439     fa: false,
2440     
2441     getAutoCreate : function(){
2442         
2443         if(this.isContainer){
2444             return {
2445                 tag: 'li',
2446                 cls: 'dropdown-menu-item'
2447             };
2448         }
2449         var ctag = {
2450             tag: 'span',
2451             html: 'Link'
2452         };
2453         
2454         var anc = {
2455             tag : 'a',
2456             href : '#',
2457             cn : [  ]
2458         };
2459         
2460         if (this.fa !== false) {
2461             anc.cn.push({
2462                 tag : 'i',
2463                 cls : 'fa fa-' + this.fa
2464             });
2465         }
2466         
2467         anc.cn.push(ctag);
2468         
2469         
2470         var cfg= {
2471             tag: 'li',
2472             cls: 'dropdown-menu-item',
2473             cn: [ anc ]
2474         };
2475         if (this.parent().type == 'treeview') {
2476             cfg.cls = 'treeview-menu';
2477         }
2478         if (this.active) {
2479             cfg.cls += ' active';
2480         }
2481         
2482         
2483         
2484         anc.href = this.href || cfg.cn[0].href ;
2485         ctag.html = this.html || cfg.cn[0].html ;
2486         return cfg;
2487     },
2488     
2489     initEvents: function()
2490     {
2491         if (this.parent().type == 'treeview') {
2492             this.el.select('a').on('click', this.onClick, this);
2493         }
2494         
2495         if (this.menu) {
2496             this.menu.parentType = this.xtype;
2497             this.menu.triggerEl = this.el;
2498             this.menu = this.addxtype(Roo.apply({}, this.menu));
2499         }
2500         
2501     },
2502     onClick : function(e)
2503     {
2504         Roo.log('item on click ');
2505         
2506         if(this.preventDefault){
2507             e.preventDefault();
2508         }
2509         //this.parent().hideMenuItems();
2510         
2511         this.fireEvent('click', this, e);
2512     },
2513     getEl : function()
2514     {
2515         return this.el;
2516     } 
2517 });
2518
2519  
2520
2521  /*
2522  * - LGPL
2523  *
2524  * menu separator
2525  * 
2526  */
2527
2528
2529 /**
2530  * @class Roo.bootstrap.MenuSeparator
2531  * @extends Roo.bootstrap.Component
2532  * Bootstrap MenuSeparator class
2533  * 
2534  * @constructor
2535  * Create a new MenuItem
2536  * @param {Object} config The config object
2537  */
2538
2539
2540 Roo.bootstrap.MenuSeparator = function(config){
2541     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2542 };
2543
2544 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2545     
2546     getAutoCreate : function(){
2547         var cfg = {
2548             cls: 'divider',
2549             tag : 'li'
2550         };
2551         
2552         return cfg;
2553     }
2554    
2555 });
2556
2557  
2558
2559  
2560 /*
2561 * Licence: LGPL
2562 */
2563
2564 /**
2565  * @class Roo.bootstrap.Modal
2566  * @extends Roo.bootstrap.Component
2567  * Bootstrap Modal class
2568  * @cfg {String} title Title of dialog
2569  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2570  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2571  * @cfg {Boolean} specificTitle default false
2572  * @cfg {Array} buttons Array of buttons or standard button set..
2573  * @cfg {String} buttonPosition (left|right|center) default right
2574  * @cfg {Boolean} animate default true
2575  * @cfg {Boolean} allow_close default true
2576  * @cfg {Boolean} fitwindow default false
2577  * @cfg {String} size (sm|lg) default empty
2578  *
2579  *
2580  * @constructor
2581  * Create a new Modal Dialog
2582  * @param {Object} config The config object
2583  */
2584
2585 Roo.bootstrap.Modal = function(config){
2586     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2587     this.addEvents({
2588         // raw events
2589         /**
2590          * @event btnclick
2591          * The raw btnclick event for the button
2592          * @param {Roo.EventObject} e
2593          */
2594         "btnclick" : true,
2595         /**
2596          * @event resize
2597          * Fire when dialog resize
2598          * @param {Roo.bootstrap.Modal} this
2599          * @param {Roo.EventObject} e
2600          */
2601         "resize" : true
2602     });
2603     this.buttons = this.buttons || [];
2604
2605     if (this.tmpl) {
2606         this.tmpl = Roo.factory(this.tmpl);
2607     }
2608
2609 };
2610
2611 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2612
2613     title : 'test dialog',
2614
2615     buttons : false,
2616
2617     // set on load...
2618
2619     html: false,
2620
2621     tmp: false,
2622
2623     specificTitle: false,
2624
2625     buttonPosition: 'right',
2626
2627     allow_close : true,
2628
2629     animate : true,
2630
2631     fitwindow: false,
2632
2633
2634      // private
2635     dialogEl: false,
2636     bodyEl:  false,
2637     footerEl:  false,
2638     titleEl:  false,
2639     closeEl:  false,
2640
2641     size: '',
2642
2643
2644     onRender : function(ct, position)
2645     {
2646         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2647
2648         if(!this.el){
2649             var cfg = Roo.apply({},  this.getAutoCreate());
2650             cfg.id = Roo.id();
2651             //if(!cfg.name){
2652             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2653             //}
2654             //if (!cfg.name.length) {
2655             //    delete cfg.name;
2656            // }
2657             if (this.cls) {
2658                 cfg.cls += ' ' + this.cls;
2659             }
2660             if (this.style) {
2661                 cfg.style = this.style;
2662             }
2663             this.el = Roo.get(document.body).createChild(cfg, position);
2664         }
2665         //var type = this.el.dom.type;
2666
2667
2668         if(this.tabIndex !== undefined){
2669             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2670         }
2671
2672         this.dialogEl = this.el.select('.modal-dialog',true).first();
2673         this.bodyEl = this.el.select('.modal-body',true).first();
2674         this.closeEl = this.el.select('.modal-header .close', true).first();
2675         this.headerEl = this.el.select('.modal-header',true).first();
2676         this.titleEl = this.el.select('.modal-title',true).first();
2677         this.footerEl = this.el.select('.modal-footer',true).first();
2678
2679         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2680         this.maskEl.enableDisplayMode("block");
2681         this.maskEl.hide();
2682         //this.el.addClass("x-dlg-modal");
2683
2684         if (this.buttons.length) {
2685             Roo.each(this.buttons, function(bb) {
2686                 var b = Roo.apply({}, bb);
2687                 b.xns = b.xns || Roo.bootstrap;
2688                 b.xtype = b.xtype || 'Button';
2689                 if (typeof(b.listeners) == 'undefined') {
2690                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2691                 }
2692
2693                 var btn = Roo.factory(b);
2694
2695                 btn.render(this.el.select('.modal-footer div').first());
2696
2697             },this);
2698         }
2699         // render the children.
2700         var nitems = [];
2701
2702         if(typeof(this.items) != 'undefined'){
2703             var items = this.items;
2704             delete this.items;
2705
2706             for(var i =0;i < items.length;i++) {
2707                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2708             }
2709         }
2710
2711         this.items = nitems;
2712
2713         // where are these used - they used to be body/close/footer
2714
2715
2716         this.initEvents();
2717         //this.el.addClass([this.fieldClass, this.cls]);
2718
2719     },
2720
2721     getAutoCreate : function(){
2722
2723
2724         var bdy = {
2725                 cls : 'modal-body',
2726                 html : this.html || ''
2727         };
2728
2729         var title = {
2730             tag: 'h4',
2731             cls : 'modal-title',
2732             html : this.title
2733         };
2734
2735         if(this.specificTitle){
2736             title = this.title;
2737
2738         };
2739
2740         var header = [];
2741         if (this.allow_close) {
2742             header.push({
2743                 tag: 'button',
2744                 cls : 'close',
2745                 html : '&times'
2746             });
2747         }
2748
2749         header.push(title);
2750
2751         var size = '';
2752
2753         if(this.size.length){
2754             size = 'modal-' + this.size;
2755         }
2756
2757         var modal = {
2758             cls: "modal",
2759             style : 'display: none',
2760             cn : [
2761                 {
2762                     cls: "modal-dialog " + size,
2763                     cn : [
2764                         {
2765                             cls : "modal-content",
2766                             cn : [
2767                                 {
2768                                     cls : 'modal-header',
2769                                     cn : header
2770                                 },
2771                                 bdy,
2772                                 {
2773                                     cls : 'modal-footer',
2774                                     cn : [
2775                                         {
2776                                             tag: 'div',
2777                                             cls: 'btn-' + this.buttonPosition
2778                                         }
2779                                     ]
2780
2781                                 }
2782
2783
2784                             ]
2785
2786                         }
2787                     ]
2788
2789                 }
2790             ]
2791         };
2792
2793         if(this.animate){
2794             modal.cls += ' fade';
2795         }
2796
2797         return modal;
2798
2799     },
2800     getChildContainer : function() {
2801
2802          return this.bodyEl;
2803
2804     },
2805     getButtonContainer : function() {
2806          return this.el.select('.modal-footer div',true).first();
2807
2808     },
2809     initEvents : function()
2810     {
2811         if (this.allow_close) {
2812             this.closeEl.on('click', this.hide, this);
2813         }
2814         Roo.EventManager.onWindowResize(this.resize, this, true);
2815
2816
2817     },
2818
2819     resize : function()
2820     {
2821         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),  Roo.lib.Dom.getViewHeight(true));
2822         if (this.fitwindow) {
2823             var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2824             var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2825             this.setSize(w,h);
2826         }
2827     },
2828
2829     setSize : function(w,h)
2830     {
2831         if (!w && !h) {
2832             return;
2833         }
2834         this.resizeTo(w,h);
2835     },
2836
2837     show : function() {
2838
2839         if (!this.rendered) {
2840             this.render();
2841         }
2842
2843         this.el.setStyle('display', 'block');
2844
2845         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2846             var _this = this;
2847             (function(){
2848                 this.el.addClass('in');
2849             }).defer(50, this);
2850         }else{
2851             this.el.addClass('in');
2852
2853         }
2854
2855         // not sure how we can show data in here..
2856         //if (this.tmpl) {
2857         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2858         //}
2859
2860         Roo.get(document.body).addClass("x-body-masked");
2861         
2862         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2863         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2864         this.maskEl.show();
2865         
2866         this.resize();
2867         
2868         this.fireEvent('show', this);
2869
2870         // set zindex here - otherwise it appears to be ignored...
2871         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2872
2873         (function () {
2874             this.items.forEach( function(e) {
2875                 e.layout ? e.layout() : false;
2876
2877             });
2878         }).defer(100,this);
2879
2880     },
2881     hide : function()
2882     {
2883         if(this.fireEvent("beforehide", this) !== false){
2884             this.maskEl.hide();
2885             Roo.get(document.body).removeClass("x-body-masked");
2886             this.el.removeClass('in');
2887             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2888
2889             if(this.animate){ // why
2890                 var _this = this;
2891                 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2892             }else{
2893                 this.el.setStyle('display', 'none');
2894             }
2895             this.fireEvent('hide', this);
2896         }
2897     },
2898
2899     addButton : function(str, cb)
2900     {
2901
2902
2903         var b = Roo.apply({}, { html : str } );
2904         b.xns = b.xns || Roo.bootstrap;
2905         b.xtype = b.xtype || 'Button';
2906         if (typeof(b.listeners) == 'undefined') {
2907             b.listeners = { click : cb.createDelegate(this)  };
2908         }
2909
2910         var btn = Roo.factory(b);
2911
2912         btn.render(this.el.select('.modal-footer div').first());
2913
2914         return btn;
2915
2916     },
2917
2918     setDefaultButton : function(btn)
2919     {
2920         //this.el.select('.modal-footer').()
2921     },
2922     diff : false,
2923
2924     resizeTo: function(w,h)
2925     {
2926         // skip.. ?? why??
2927
2928         this.dialogEl.setWidth(w);
2929         if (this.diff === false) {
2930             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2931         }
2932
2933         this.bodyEl.setHeight(h-this.diff);
2934
2935         this.fireEvent('resize', this);
2936
2937     },
2938     setContentSize  : function(w, h)
2939     {
2940
2941     },
2942     onButtonClick: function(btn,e)
2943     {
2944         //Roo.log([a,b,c]);
2945         this.fireEvent('btnclick', btn.name, e);
2946     },
2947      /**
2948      * Set the title of the Dialog
2949      * @param {String} str new Title
2950      */
2951     setTitle: function(str) {
2952         this.titleEl.dom.innerHTML = str;
2953     },
2954     /**
2955      * Set the body of the Dialog
2956      * @param {String} str new Title
2957      */
2958     setBody: function(str) {
2959         this.bodyEl.dom.innerHTML = str;
2960     },
2961     /**
2962      * Set the body of the Dialog using the template
2963      * @param {Obj} data - apply this data to the template and replace the body contents.
2964      */
2965     applyBody: function(obj)
2966     {
2967         if (!this.tmpl) {
2968             Roo.log("Error - using apply Body without a template");
2969             //code
2970         }
2971         this.tmpl.overwrite(this.bodyEl, obj);
2972     }
2973
2974 });
2975
2976
2977 Roo.apply(Roo.bootstrap.Modal,  {
2978     /**
2979          * Button config that displays a single OK button
2980          * @type Object
2981          */
2982         OK :  [{
2983             name : 'ok',
2984             weight : 'primary',
2985             html : 'OK'
2986         }],
2987         /**
2988          * Button config that displays Yes and No buttons
2989          * @type Object
2990          */
2991         YESNO : [
2992             {
2993                 name  : 'no',
2994                 html : 'No'
2995             },
2996             {
2997                 name  :'yes',
2998                 weight : 'primary',
2999                 html : 'Yes'
3000             }
3001         ],
3002
3003         /**
3004          * Button config that displays OK and Cancel buttons
3005          * @type Object
3006          */
3007         OKCANCEL : [
3008             {
3009                name : 'cancel',
3010                 html : 'Cancel'
3011             },
3012             {
3013                 name : 'ok',
3014                 weight : 'primary',
3015                 html : 'OK'
3016             }
3017         ],
3018         /**
3019          * Button config that displays Yes, No and Cancel buttons
3020          * @type Object
3021          */
3022         YESNOCANCEL : [
3023             {
3024                 name : 'yes',
3025                 weight : 'primary',
3026                 html : 'Yes'
3027             },
3028             {
3029                 name : 'no',
3030                 html : 'No'
3031             },
3032             {
3033                 name : 'cancel',
3034                 html : 'Cancel'
3035             }
3036         ],
3037         
3038         zIndex : 10001
3039 });
3040 /*
3041  * - LGPL
3042  *
3043  * messagebox - can be used as a replace
3044  * 
3045  */
3046 /**
3047  * @class Roo.MessageBox
3048  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3049  * Example usage:
3050  *<pre><code>
3051 // Basic alert:
3052 Roo.Msg.alert('Status', 'Changes saved successfully.');
3053
3054 // Prompt for user data:
3055 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3056     if (btn == 'ok'){
3057         // process text value...
3058     }
3059 });
3060
3061 // Show a dialog using config options:
3062 Roo.Msg.show({
3063    title:'Save Changes?',
3064    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3065    buttons: Roo.Msg.YESNOCANCEL,
3066    fn: processResult,
3067    animEl: 'elId'
3068 });
3069 </code></pre>
3070  * @singleton
3071  */
3072 Roo.bootstrap.MessageBox = function(){
3073     var dlg, opt, mask, waitTimer;
3074     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3075     var buttons, activeTextEl, bwidth;
3076
3077     
3078     // private
3079     var handleButton = function(button){
3080         dlg.hide();
3081         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3082     };
3083
3084     // private
3085     var handleHide = function(){
3086         if(opt && opt.cls){
3087             dlg.el.removeClass(opt.cls);
3088         }
3089         //if(waitTimer){
3090         //    Roo.TaskMgr.stop(waitTimer);
3091         //    waitTimer = null;
3092         //}
3093     };
3094
3095     // private
3096     var updateButtons = function(b){
3097         var width = 0;
3098         if(!b){
3099             buttons["ok"].hide();
3100             buttons["cancel"].hide();
3101             buttons["yes"].hide();
3102             buttons["no"].hide();
3103             //dlg.footer.dom.style.display = 'none';
3104             return width;
3105         }
3106         dlg.footerEl.dom.style.display = '';
3107         for(var k in buttons){
3108             if(typeof buttons[k] != "function"){
3109                 if(b[k]){
3110                     buttons[k].show();
3111                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3112                     width += buttons[k].el.getWidth()+15;
3113                 }else{
3114                     buttons[k].hide();
3115                 }
3116             }
3117         }
3118         return width;
3119     };
3120
3121     // private
3122     var handleEsc = function(d, k, e){
3123         if(opt && opt.closable !== false){
3124             dlg.hide();
3125         }
3126         if(e){
3127             e.stopEvent();
3128         }
3129     };
3130
3131     return {
3132         /**
3133          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3134          * @return {Roo.BasicDialog} The BasicDialog element
3135          */
3136         getDialog : function(){
3137            if(!dlg){
3138                 dlg = new Roo.bootstrap.Modal( {
3139                     //draggable: true,
3140                     //resizable:false,
3141                     //constraintoviewport:false,
3142                     //fixedcenter:true,
3143                     //collapsible : false,
3144                     //shim:true,
3145                     //modal: true,
3146                 //    width: 'auto',
3147                   //  height:100,
3148                     //buttonAlign:"center",
3149                     closeClick : function(){
3150                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3151                             handleButton("no");
3152                         }else{
3153                             handleButton("cancel");
3154                         }
3155                     }
3156                 });
3157                 dlg.render();
3158                 dlg.on("hide", handleHide);
3159                 mask = dlg.mask;
3160                 //dlg.addKeyListener(27, handleEsc);
3161                 buttons = {};
3162                 this.buttons = buttons;
3163                 var bt = this.buttonText;
3164                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3165                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3166                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3167                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3168                 //Roo.log(buttons);
3169                 bodyEl = dlg.bodyEl.createChild({
3170
3171                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3172                         '<textarea class="roo-mb-textarea"></textarea>' +
3173                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3174                 });
3175                 msgEl = bodyEl.dom.firstChild;
3176                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3177                 textboxEl.enableDisplayMode();
3178                 textboxEl.addKeyListener([10,13], function(){
3179                     if(dlg.isVisible() && opt && opt.buttons){
3180                         if(opt.buttons.ok){
3181                             handleButton("ok");
3182                         }else if(opt.buttons.yes){
3183                             handleButton("yes");
3184                         }
3185                     }
3186                 });
3187                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3188                 textareaEl.enableDisplayMode();
3189                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3190                 progressEl.enableDisplayMode();
3191                 
3192                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3193                 var pf = progressEl.dom.firstChild;
3194                 if (pf) {
3195                     pp = Roo.get(pf.firstChild);
3196                     pp.setHeight(pf.offsetHeight);
3197                 }
3198                 
3199             }
3200             return dlg;
3201         },
3202
3203         /**
3204          * Updates the message box body text
3205          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3206          * the XHTML-compliant non-breaking space character '&amp;#160;')
3207          * @return {Roo.MessageBox} This message box
3208          */
3209         updateText : function(text)
3210         {
3211             if(!dlg.isVisible() && !opt.width){
3212                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3213                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3214             }
3215             msgEl.innerHTML = text || '&#160;';
3216       
3217             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3218             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3219             var w = Math.max(
3220                     Math.min(opt.width || cw , this.maxWidth), 
3221                     Math.max(opt.minWidth || this.minWidth, bwidth)
3222             );
3223             if(opt.prompt){
3224                 activeTextEl.setWidth(w);
3225             }
3226             if(dlg.isVisible()){
3227                 dlg.fixedcenter = false;
3228             }
3229             // to big, make it scroll. = But as usual stupid IE does not support
3230             // !important..
3231             
3232             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3233                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3234                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3235             } else {
3236                 bodyEl.dom.style.height = '';
3237                 bodyEl.dom.style.overflowY = '';
3238             }
3239             if (cw > w) {
3240                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3241             } else {
3242                 bodyEl.dom.style.overflowX = '';
3243             }
3244             
3245             dlg.setContentSize(w, bodyEl.getHeight());
3246             if(dlg.isVisible()){
3247                 dlg.fixedcenter = true;
3248             }
3249             return this;
3250         },
3251
3252         /**
3253          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3254          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3255          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3256          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3257          * @return {Roo.MessageBox} This message box
3258          */
3259         updateProgress : function(value, text){
3260             if(text){
3261                 this.updateText(text);
3262             }
3263             
3264             if (pp) { // weird bug on my firefox - for some reason this is not defined
3265                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3266                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3267             }
3268             return this;
3269         },        
3270
3271         /**
3272          * Returns true if the message box is currently displayed
3273          * @return {Boolean} True if the message box is visible, else false
3274          */
3275         isVisible : function(){
3276             return dlg && dlg.isVisible();  
3277         },
3278
3279         /**
3280          * Hides the message box if it is displayed
3281          */
3282         hide : function(){
3283             if(this.isVisible()){
3284                 dlg.hide();
3285             }  
3286         },
3287
3288         /**
3289          * Displays a new message box, or reinitializes an existing message box, based on the config options
3290          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3291          * The following config object properties are supported:
3292          * <pre>
3293 Property    Type             Description
3294 ----------  ---------------  ------------------------------------------------------------------------------------
3295 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3296                                    closes (defaults to undefined)
3297 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3298                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3299 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3300                                    progress and wait dialogs will ignore this property and always hide the
3301                                    close button as they can only be closed programmatically.
3302 cls               String           A custom CSS class to apply to the message box element
3303 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3304                                    displayed (defaults to 75)
3305 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3306                                    function will be btn (the name of the button that was clicked, if applicable,
3307                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3308                                    Progress and wait dialogs will ignore this option since they do not respond to
3309                                    user actions and can only be closed programmatically, so any required function
3310                                    should be called by the same code after it closes the dialog.
3311 icon              String           A CSS class that provides a background image to be used as an icon for
3312                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3313 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3314 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3315 modal             Boolean          False to allow user interaction with the page while the message box is
3316                                    displayed (defaults to true)
3317 msg               String           A string that will replace the existing message box body text (defaults
3318                                    to the XHTML-compliant non-breaking space character '&#160;')
3319 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3320 progress          Boolean          True to display a progress bar (defaults to false)
3321 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3322 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3323 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3324 title             String           The title text
3325 value             String           The string value to set into the active textbox element if displayed
3326 wait              Boolean          True to display a progress bar (defaults to false)
3327 width             Number           The width of the dialog in pixels
3328 </pre>
3329          *
3330          * Example usage:
3331          * <pre><code>
3332 Roo.Msg.show({
3333    title: 'Address',
3334    msg: 'Please enter your address:',
3335    width: 300,
3336    buttons: Roo.MessageBox.OKCANCEL,
3337    multiline: true,
3338    fn: saveAddress,
3339    animEl: 'addAddressBtn'
3340 });
3341 </code></pre>
3342          * @param {Object} config Configuration options
3343          * @return {Roo.MessageBox} This message box
3344          */
3345         show : function(options)
3346         {
3347             
3348             // this causes nightmares if you show one dialog after another
3349             // especially on callbacks..
3350              
3351             if(this.isVisible()){
3352                 
3353                 this.hide();
3354                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3355                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3356                 Roo.log("New Dialog Message:" +  options.msg )
3357                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3358                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3359                 
3360             }
3361             var d = this.getDialog();
3362             opt = options;
3363             d.setTitle(opt.title || "&#160;");
3364             d.closeEl.setDisplayed(opt.closable !== false);
3365             activeTextEl = textboxEl;
3366             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3367             if(opt.prompt){
3368                 if(opt.multiline){
3369                     textboxEl.hide();
3370                     textareaEl.show();
3371                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3372                         opt.multiline : this.defaultTextHeight);
3373                     activeTextEl = textareaEl;
3374                 }else{
3375                     textboxEl.show();
3376                     textareaEl.hide();
3377                 }
3378             }else{
3379                 textboxEl.hide();
3380                 textareaEl.hide();
3381             }
3382             progressEl.setDisplayed(opt.progress === true);
3383             this.updateProgress(0);
3384             activeTextEl.dom.value = opt.value || "";
3385             if(opt.prompt){
3386                 dlg.setDefaultButton(activeTextEl);
3387             }else{
3388                 var bs = opt.buttons;
3389                 var db = null;
3390                 if(bs && bs.ok){
3391                     db = buttons["ok"];
3392                 }else if(bs && bs.yes){
3393                     db = buttons["yes"];
3394                 }
3395                 dlg.setDefaultButton(db);
3396             }
3397             bwidth = updateButtons(opt.buttons);
3398             this.updateText(opt.msg);
3399             if(opt.cls){
3400                 d.el.addClass(opt.cls);
3401             }
3402             d.proxyDrag = opt.proxyDrag === true;
3403             d.modal = opt.modal !== false;
3404             d.mask = opt.modal !== false ? mask : false;
3405             if(!d.isVisible()){
3406                 // force it to the end of the z-index stack so it gets a cursor in FF
3407                 document.body.appendChild(dlg.el.dom);
3408                 d.animateTarget = null;
3409                 d.show(options.animEl);
3410             }
3411             return this;
3412         },
3413
3414         /**
3415          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3416          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3417          * and closing the message box when the process is complete.
3418          * @param {String} title The title bar text
3419          * @param {String} msg The message box body text
3420          * @return {Roo.MessageBox} This message box
3421          */
3422         progress : function(title, msg){
3423             this.show({
3424                 title : title,
3425                 msg : msg,
3426                 buttons: false,
3427                 progress:true,
3428                 closable:false,
3429                 minWidth: this.minProgressWidth,
3430                 modal : true
3431             });
3432             return this;
3433         },
3434
3435         /**
3436          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3437          * If a callback function is passed it will be called after the user clicks the button, and the
3438          * id of the button that was clicked will be passed as the only parameter to the callback
3439          * (could also be the top-right close button).
3440          * @param {String} title The title bar text
3441          * @param {String} msg The message box body text
3442          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3443          * @param {Object} scope (optional) The scope of the callback function
3444          * @return {Roo.MessageBox} This message box
3445          */
3446         alert : function(title, msg, fn, scope)
3447         {
3448             this.show({
3449                 title : title,
3450                 msg : msg,
3451                 buttons: this.OK,
3452                 fn: fn,
3453                 closable : false,
3454                 scope : scope,
3455                 modal : true
3456             });
3457             return this;
3458         },
3459
3460         /**
3461          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3462          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3463          * You are responsible for closing the message box when the process is complete.
3464          * @param {String} msg The message box body text
3465          * @param {String} title (optional) The title bar text
3466          * @return {Roo.MessageBox} This message box
3467          */
3468         wait : function(msg, title){
3469             this.show({
3470                 title : title,
3471                 msg : msg,
3472                 buttons: false,
3473                 closable:false,
3474                 progress:true,
3475                 modal:true,
3476                 width:300,
3477                 wait:true
3478             });
3479             waitTimer = Roo.TaskMgr.start({
3480                 run: function(i){
3481                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3482                 },
3483                 interval: 1000
3484             });
3485             return this;
3486         },
3487
3488         /**
3489          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3490          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3491          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3492          * @param {String} title The title bar text
3493          * @param {String} msg The message box body text
3494          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3495          * @param {Object} scope (optional) The scope of the callback function
3496          * @return {Roo.MessageBox} This message box
3497          */
3498         confirm : function(title, msg, fn, scope){
3499             this.show({
3500                 title : title,
3501                 msg : msg,
3502                 buttons: this.YESNO,
3503                 fn: fn,
3504                 scope : scope,
3505                 modal : true
3506             });
3507             return this;
3508         },
3509
3510         /**
3511          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3512          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3513          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3514          * (could also be the top-right close button) and the text that was entered will be passed as the two
3515          * parameters to the callback.
3516          * @param {String} title The title bar text
3517          * @param {String} msg The message box body text
3518          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3519          * @param {Object} scope (optional) The scope of the callback function
3520          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3521          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3522          * @return {Roo.MessageBox} This message box
3523          */
3524         prompt : function(title, msg, fn, scope, multiline){
3525             this.show({
3526                 title : title,
3527                 msg : msg,
3528                 buttons: this.OKCANCEL,
3529                 fn: fn,
3530                 minWidth:250,
3531                 scope : scope,
3532                 prompt:true,
3533                 multiline: multiline,
3534                 modal : true
3535             });
3536             return this;
3537         },
3538
3539         /**
3540          * Button config that displays a single OK button
3541          * @type Object
3542          */
3543         OK : {ok:true},
3544         /**
3545          * Button config that displays Yes and No buttons
3546          * @type Object
3547          */
3548         YESNO : {yes:true, no:true},
3549         /**
3550          * Button config that displays OK and Cancel buttons
3551          * @type Object
3552          */
3553         OKCANCEL : {ok:true, cancel:true},
3554         /**
3555          * Button config that displays Yes, No and Cancel buttons
3556          * @type Object
3557          */
3558         YESNOCANCEL : {yes:true, no:true, cancel:true},
3559
3560         /**
3561          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3562          * @type Number
3563          */
3564         defaultTextHeight : 75,
3565         /**
3566          * The maximum width in pixels of the message box (defaults to 600)
3567          * @type Number
3568          */
3569         maxWidth : 600,
3570         /**
3571          * The minimum width in pixels of the message box (defaults to 100)
3572          * @type Number
3573          */
3574         minWidth : 100,
3575         /**
3576          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3577          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3578          * @type Number
3579          */
3580         minProgressWidth : 250,
3581         /**
3582          * An object containing the default button text strings that can be overriden for localized language support.
3583          * Supported properties are: ok, cancel, yes and no.
3584          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3585          * @type Object
3586          */
3587         buttonText : {
3588             ok : "OK",
3589             cancel : "Cancel",
3590             yes : "Yes",
3591             no : "No"
3592         }
3593     };
3594 }();
3595
3596 /**
3597  * Shorthand for {@link Roo.MessageBox}
3598  */
3599 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3600 Roo.Msg = Roo.Msg || Roo.MessageBox;
3601 /*
3602  * - LGPL
3603  *
3604  * navbar
3605  * 
3606  */
3607
3608 /**
3609  * @class Roo.bootstrap.Navbar
3610  * @extends Roo.bootstrap.Component
3611  * Bootstrap Navbar class
3612
3613  * @constructor
3614  * Create a new Navbar
3615  * @param {Object} config The config object
3616  */
3617
3618
3619 Roo.bootstrap.Navbar = function(config){
3620     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3621     this.addEvents({
3622         // raw events
3623         /**
3624          * @event beforetoggle
3625          * Fire before toggle the menu
3626          * @param {Roo.EventObject} e
3627          */
3628         "beforetoggle" : true
3629     });
3630 };
3631
3632 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3633     
3634     
3635    
3636     // private
3637     navItems : false,
3638     loadMask : false,
3639     
3640     
3641     getAutoCreate : function(){
3642         
3643         
3644         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3645         
3646     },
3647     
3648     initEvents :function ()
3649     {
3650         //Roo.log(this.el.select('.navbar-toggle',true));
3651         this.el.select('.navbar-toggle',true).on('click', function() {
3652             if(this.fireEvent('beforetoggle', this) !== false){
3653                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3654             }
3655             
3656         }, this);
3657         
3658         var mark = {
3659             tag: "div",
3660             cls:"x-dlg-mask"
3661         };
3662         
3663         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3664         
3665         var size = this.el.getSize();
3666         this.maskEl.setSize(size.width, size.height);
3667         this.maskEl.enableDisplayMode("block");
3668         this.maskEl.hide();
3669         
3670         if(this.loadMask){
3671             this.maskEl.show();
3672         }
3673     },
3674     
3675     
3676     getChildContainer : function()
3677     {
3678         if (this.el.select('.collapse').getCount()) {
3679             return this.el.select('.collapse',true).first();
3680         }
3681         
3682         return this.el;
3683     },
3684     
3685     mask : function()
3686     {
3687         this.maskEl.show();
3688     },
3689     
3690     unmask : function()
3691     {
3692         this.maskEl.hide();
3693     } 
3694     
3695     
3696     
3697     
3698 });
3699
3700
3701
3702  
3703
3704  /*
3705  * - LGPL
3706  *
3707  * navbar
3708  * 
3709  */
3710
3711 /**
3712  * @class Roo.bootstrap.NavSimplebar
3713  * @extends Roo.bootstrap.Navbar
3714  * Bootstrap Sidebar class
3715  *
3716  * @cfg {Boolean} inverse is inverted color
3717  * 
3718  * @cfg {String} type (nav | pills | tabs)
3719  * @cfg {Boolean} arrangement stacked | justified
3720  * @cfg {String} align (left | right) alignment
3721  * 
3722  * @cfg {Boolean} main (true|false) main nav bar? default false
3723  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3724  * 
3725  * @cfg {String} tag (header|footer|nav|div) default is nav 
3726
3727  * 
3728  * 
3729  * 
3730  * @constructor
3731  * Create a new Sidebar
3732  * @param {Object} config The config object
3733  */
3734
3735
3736 Roo.bootstrap.NavSimplebar = function(config){
3737     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3738 };
3739
3740 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3741     
3742     inverse: false,
3743     
3744     type: false,
3745     arrangement: '',
3746     align : false,
3747     
3748     
3749     
3750     main : false,
3751     
3752     
3753     tag : false,
3754     
3755     
3756     getAutoCreate : function(){
3757         
3758         
3759         var cfg = {
3760             tag : this.tag || 'div',
3761             cls : 'navbar'
3762         };
3763           
3764         
3765         cfg.cn = [
3766             {
3767                 cls: 'nav',
3768                 tag : 'ul'
3769             }
3770         ];
3771         
3772          
3773         this.type = this.type || 'nav';
3774         if (['tabs','pills'].indexOf(this.type)!==-1) {
3775             cfg.cn[0].cls += ' nav-' + this.type
3776         
3777         
3778         } else {
3779             if (this.type!=='nav') {
3780                 Roo.log('nav type must be nav/tabs/pills')
3781             }
3782             cfg.cn[0].cls += ' navbar-nav'
3783         }
3784         
3785         
3786         
3787         
3788         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3789             cfg.cn[0].cls += ' nav-' + this.arrangement;
3790         }
3791         
3792         
3793         if (this.align === 'right') {
3794             cfg.cn[0].cls += ' navbar-right';
3795         }
3796         
3797         if (this.inverse) {
3798             cfg.cls += ' navbar-inverse';
3799             
3800         }
3801         
3802         
3803         return cfg;
3804     
3805         
3806     }
3807     
3808     
3809     
3810 });
3811
3812
3813
3814  
3815
3816  
3817        /*
3818  * - LGPL
3819  *
3820  * navbar
3821  * 
3822  */
3823
3824 /**
3825  * @class Roo.bootstrap.NavHeaderbar
3826  * @extends Roo.bootstrap.NavSimplebar
3827  * Bootstrap Sidebar class
3828  *
3829  * @cfg {String} brand what is brand
3830  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3831  * @cfg {String} brand_href href of the brand
3832  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3833  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3834  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3835  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3836  * 
3837  * @constructor
3838  * Create a new Sidebar
3839  * @param {Object} config The config object
3840  */
3841
3842
3843 Roo.bootstrap.NavHeaderbar = function(config){
3844     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3845       
3846 };
3847
3848 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3849     
3850     position: '',
3851     brand: '',
3852     brand_href: false,
3853     srButton : true,
3854     autohide : false,
3855     desktopCenter : false,
3856    
3857     
3858     getAutoCreate : function(){
3859         
3860         var   cfg = {
3861             tag: this.nav || 'nav',
3862             cls: 'navbar',
3863             role: 'navigation',
3864             cn: []
3865         };
3866         
3867         var cn = cfg.cn;
3868         if (this.desktopCenter) {
3869             cn.push({cls : 'container', cn : []});
3870             cn = cn[0].cn;
3871         }
3872         
3873         if(this.srButton){
3874             cn.push({
3875                 tag: 'div',
3876                 cls: 'navbar-header',
3877                 cn: [
3878                     {
3879                         tag: 'button',
3880                         type: 'button',
3881                         cls: 'navbar-toggle',
3882                         'data-toggle': 'collapse',
3883                         cn: [
3884                             {
3885                                 tag: 'span',
3886                                 cls: 'sr-only',
3887                                 html: 'Toggle navigation'
3888                             },
3889                             {
3890                                 tag: 'span',
3891                                 cls: 'icon-bar'
3892                             },
3893                             {
3894                                 tag: 'span',
3895                                 cls: 'icon-bar'
3896                             },
3897                             {
3898                                 tag: 'span',
3899                                 cls: 'icon-bar'
3900                             }
3901                         ]
3902                     }
3903                 ]
3904             });
3905         }
3906         
3907         cn.push({
3908             tag: 'div',
3909             cls: 'collapse navbar-collapse',
3910             cn : []
3911         });
3912         
3913         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3914         
3915         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3916             cfg.cls += ' navbar-' + this.position;
3917             
3918             // tag can override this..
3919             
3920             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3921         }
3922         
3923         if (this.brand !== '') {
3924             cn[0].cn.push({
3925                 tag: 'a',
3926                 href: this.brand_href ? this.brand_href : '#',
3927                 cls: 'navbar-brand',
3928                 cn: [
3929                 this.brand
3930                 ]
3931             });
3932         }
3933         
3934         if(this.main){
3935             cfg.cls += ' main-nav';
3936         }
3937         
3938         
3939         return cfg;
3940
3941         
3942     },
3943     getHeaderChildContainer : function()
3944     {
3945         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3946             return this.el.select('.navbar-header',true).first();
3947         }
3948         
3949         return this.getChildContainer();
3950     },
3951     
3952     
3953     initEvents : function()
3954     {
3955         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3956         
3957         if (this.autohide) {
3958             
3959             var prevScroll = 0;
3960             var ft = this.el;
3961             
3962             Roo.get(document).on('scroll',function(e) {
3963                 var ns = Roo.get(document).getScroll().top;
3964                 var os = prevScroll;
3965                 prevScroll = ns;
3966                 
3967                 if(ns > os){
3968                     ft.removeClass('slideDown');
3969                     ft.addClass('slideUp');
3970                     return;
3971                 }
3972                 ft.removeClass('slideUp');
3973                 ft.addClass('slideDown');
3974                  
3975               
3976           },this);
3977         }
3978     }    
3979     
3980 });
3981
3982
3983
3984  
3985
3986  /*
3987  * - LGPL
3988  *
3989  * navbar
3990  * 
3991  */
3992
3993 /**
3994  * @class Roo.bootstrap.NavSidebar
3995  * @extends Roo.bootstrap.Navbar
3996  * Bootstrap Sidebar class
3997  * 
3998  * @constructor
3999  * Create a new Sidebar
4000  * @param {Object} config The config object
4001  */
4002
4003
4004 Roo.bootstrap.NavSidebar = function(config){
4005     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4006 };
4007
4008 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4009     
4010     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4011     
4012     getAutoCreate : function(){
4013         
4014         
4015         return  {
4016             tag: 'div',
4017             cls: 'sidebar sidebar-nav'
4018         };
4019     
4020         
4021     }
4022     
4023     
4024     
4025 });
4026
4027
4028
4029  
4030
4031  /*
4032  * - LGPL
4033  *
4034  * nav group
4035  * 
4036  */
4037
4038 /**
4039  * @class Roo.bootstrap.NavGroup
4040  * @extends Roo.bootstrap.Component
4041  * Bootstrap NavGroup class
4042  * @cfg {String} align (left|right)
4043  * @cfg {Boolean} inverse
4044  * @cfg {String} type (nav|pills|tab) default nav
4045  * @cfg {String} navId - reference Id for navbar.
4046
4047  * 
4048  * @constructor
4049  * Create a new nav group
4050  * @param {Object} config The config object
4051  */
4052
4053 Roo.bootstrap.NavGroup = function(config){
4054     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4055     this.navItems = [];
4056    
4057     Roo.bootstrap.NavGroup.register(this);
4058      this.addEvents({
4059         /**
4060              * @event changed
4061              * Fires when the active item changes
4062              * @param {Roo.bootstrap.NavGroup} this
4063              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4064              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4065          */
4066         'changed': true
4067      });
4068     
4069 };
4070
4071 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4072     
4073     align: '',
4074     inverse: false,
4075     form: false,
4076     type: 'nav',
4077     navId : '',
4078     // private
4079     
4080     navItems : false, 
4081     
4082     getAutoCreate : function()
4083     {
4084         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4085         
4086         cfg = {
4087             tag : 'ul',
4088             cls: 'nav' 
4089         };
4090         
4091         if (['tabs','pills'].indexOf(this.type)!==-1) {
4092             cfg.cls += ' nav-' + this.type
4093         } else {
4094             if (this.type!=='nav') {
4095                 Roo.log('nav type must be nav/tabs/pills')
4096             }
4097             cfg.cls += ' navbar-nav'
4098         }
4099         
4100         if (this.parent() && this.parent().sidebar) {
4101             cfg = {
4102                 tag: 'ul',
4103                 cls: 'dashboard-menu sidebar-menu'
4104             };
4105             
4106             return cfg;
4107         }
4108         
4109         if (this.form === true) {
4110             cfg = {
4111                 tag: 'form',
4112                 cls: 'navbar-form'
4113             };
4114             
4115             if (this.align === 'right') {
4116                 cfg.cls += ' navbar-right';
4117             } else {
4118                 cfg.cls += ' navbar-left';
4119             }
4120         }
4121         
4122         if (this.align === 'right') {
4123             cfg.cls += ' navbar-right';
4124         }
4125         
4126         if (this.inverse) {
4127             cfg.cls += ' navbar-inverse';
4128             
4129         }
4130         
4131         
4132         return cfg;
4133     },
4134     /**
4135     * sets the active Navigation item
4136     * @param {Roo.bootstrap.NavItem} the new current navitem
4137     */
4138     setActiveItem : function(item)
4139     {
4140         var prev = false;
4141         Roo.each(this.navItems, function(v){
4142             if (v == item) {
4143                 return ;
4144             }
4145             if (v.isActive()) {
4146                 v.setActive(false, true);
4147                 prev = v;
4148                 
4149             }
4150             
4151         });
4152
4153         item.setActive(true, true);
4154         this.fireEvent('changed', this, item, prev);
4155         
4156         
4157     },
4158     /**
4159     * gets the active Navigation item
4160     * @return {Roo.bootstrap.NavItem} the current navitem
4161     */
4162     getActive : function()
4163     {
4164         
4165         var prev = false;
4166         Roo.each(this.navItems, function(v){
4167             
4168             if (v.isActive()) {
4169                 prev = v;
4170                 
4171             }
4172             
4173         });
4174         return prev;
4175     },
4176     
4177     indexOfNav : function()
4178     {
4179         
4180         var prev = false;
4181         Roo.each(this.navItems, function(v,i){
4182             
4183             if (v.isActive()) {
4184                 prev = i;
4185                 
4186             }
4187             
4188         });
4189         return prev;
4190     },
4191     /**
4192     * adds a Navigation item
4193     * @param {Roo.bootstrap.NavItem} the navitem to add
4194     */
4195     addItem : function(cfg)
4196     {
4197         var cn = new Roo.bootstrap.NavItem(cfg);
4198         this.register(cn);
4199         cn.parentId = this.id;
4200         cn.onRender(this.el, null);
4201         return cn;
4202     },
4203     /**
4204     * register a Navigation item
4205     * @param {Roo.bootstrap.NavItem} the navitem to add
4206     */
4207     register : function(item)
4208     {
4209         this.navItems.push( item);
4210         item.navId = this.navId;
4211     
4212     },
4213     
4214     /**
4215     * clear all the Navigation item
4216     */
4217    
4218     clearAll : function()
4219     {
4220         this.navItems = [];
4221         this.el.dom.innerHTML = '';
4222     },
4223     
4224     getNavItem: function(tabId)
4225     {
4226         var ret = false;
4227         Roo.each(this.navItems, function(e) {
4228             if (e.tabId == tabId) {
4229                ret =  e;
4230                return false;
4231             }
4232             return true;
4233             
4234         });
4235         return ret;
4236     },
4237     
4238     setActiveNext : function()
4239     {
4240         var i = this.indexOfNav(this.getActive());
4241         if (i > this.navItems.length) {
4242             return;
4243         }
4244         this.setActiveItem(this.navItems[i+1]);
4245     },
4246     setActivePrev : function()
4247     {
4248         var i = this.indexOfNav(this.getActive());
4249         if (i  < 1) {
4250             return;
4251         }
4252         this.setActiveItem(this.navItems[i-1]);
4253     },
4254     clearWasActive : function(except) {
4255         Roo.each(this.navItems, function(e) {
4256             if (e.tabId != except.tabId && e.was_active) {
4257                e.was_active = false;
4258                return false;
4259             }
4260             return true;
4261             
4262         });
4263     },
4264     getWasActive : function ()
4265     {
4266         var r = false;
4267         Roo.each(this.navItems, function(e) {
4268             if (e.was_active) {
4269                r = e;
4270                return false;
4271             }
4272             return true;
4273             
4274         });
4275         return r;
4276     }
4277     
4278     
4279 });
4280
4281  
4282 Roo.apply(Roo.bootstrap.NavGroup, {
4283     
4284     groups: {},
4285      /**
4286     * register a Navigation Group
4287     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4288     */
4289     register : function(navgrp)
4290     {
4291         this.groups[navgrp.navId] = navgrp;
4292         
4293     },
4294     /**
4295     * fetch a Navigation Group based on the navigation ID
4296     * @param {string} the navgroup to add
4297     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4298     */
4299     get: function(navId) {
4300         if (typeof(this.groups[navId]) == 'undefined') {
4301             return false;
4302             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4303         }
4304         return this.groups[navId] ;
4305     }
4306     
4307     
4308     
4309 });
4310
4311  /*
4312  * - LGPL
4313  *
4314  * row
4315  * 
4316  */
4317
4318 /**
4319  * @class Roo.bootstrap.NavItem
4320  * @extends Roo.bootstrap.Component
4321  * Bootstrap Navbar.NavItem class
4322  * @cfg {String} href  link to
4323  * @cfg {String} html content of button
4324  * @cfg {String} badge text inside badge
4325  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4326  * @cfg {String} glyphicon name of glyphicon
4327  * @cfg {String} icon name of font awesome icon
4328  * @cfg {Boolean} active Is item active
4329  * @cfg {Boolean} disabled Is item disabled
4330  
4331  * @cfg {Boolean} preventDefault (true | false) default false
4332  * @cfg {String} tabId the tab that this item activates.
4333  * @cfg {String} tagtype (a|span) render as a href or span?
4334  * @cfg {Boolean} animateRef (true|false) link to element default false  
4335   
4336  * @constructor
4337  * Create a new Navbar Item
4338  * @param {Object} config The config object
4339  */
4340 Roo.bootstrap.NavItem = function(config){
4341     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4342     this.addEvents({
4343         // raw events
4344         /**
4345          * @event click
4346          * The raw click event for the entire grid.
4347          * @param {Roo.EventObject} e
4348          */
4349         "click" : true,
4350          /**
4351             * @event changed
4352             * Fires when the active item active state changes
4353             * @param {Roo.bootstrap.NavItem} this
4354             * @param {boolean} state the new state
4355              
4356          */
4357         'changed': true,
4358         /**
4359             * @event scrollto
4360             * Fires when scroll to element
4361             * @param {Roo.bootstrap.NavItem} this
4362             * @param {Object} options
4363             * @param {Roo.EventObject} e
4364              
4365          */
4366         'scrollto': true
4367     });
4368    
4369 };
4370
4371 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4372     
4373     href: false,
4374     html: '',
4375     badge: '',
4376     icon: false,
4377     glyphicon: false,
4378     active: false,
4379     preventDefault : false,
4380     tabId : false,
4381     tagtype : 'a',
4382     disabled : false,
4383     animateRef : false,
4384     was_active : false,
4385     
4386     getAutoCreate : function(){
4387          
4388         var cfg = {
4389             tag: 'li',
4390             cls: 'nav-item'
4391             
4392         };
4393         
4394         if (this.active) {
4395             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4396         }
4397         if (this.disabled) {
4398             cfg.cls += ' disabled';
4399         }
4400         
4401         if (this.href || this.html || this.glyphicon || this.icon) {
4402             cfg.cn = [
4403                 {
4404                     tag: this.tagtype,
4405                     href : this.href || "#",
4406                     html: this.html || ''
4407                 }
4408             ];
4409             
4410             if (this.icon) {
4411                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4412             }
4413
4414             if(this.glyphicon) {
4415                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4416             }
4417             
4418             if (this.menu) {
4419                 
4420                 cfg.cn[0].html += " <span class='caret'></span>";
4421              
4422             }
4423             
4424             if (this.badge !== '') {
4425                  
4426                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4427             }
4428         }
4429         
4430         
4431         
4432         return cfg;
4433     },
4434     initEvents: function() 
4435     {
4436         if (typeof (this.menu) != 'undefined') {
4437             this.menu.parentType = this.xtype;
4438             this.menu.triggerEl = this.el;
4439             this.menu = this.addxtype(Roo.apply({}, this.menu));
4440         }
4441         
4442         this.el.select('a',true).on('click', this.onClick, this);
4443         
4444         if(this.tagtype == 'span'){
4445             this.el.select('span',true).on('click', this.onClick, this);
4446         }
4447        
4448         // at this point parent should be available..
4449         this.parent().register(this);
4450     },
4451     
4452     onClick : function(e)
4453     {
4454         if (e.getTarget('.dropdown-menu-item')) {
4455             // did you click on a menu itemm.... - then don't trigger onclick..
4456             return;
4457         }
4458         
4459         if(
4460                 this.preventDefault || 
4461                 this.href == '#' 
4462         ){
4463             Roo.log("NavItem - prevent Default?");
4464             e.preventDefault();
4465         }
4466         
4467         if (this.disabled) {
4468             return;
4469         }
4470         
4471         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4472         if (tg && tg.transition) {
4473             Roo.log("waiting for the transitionend");
4474             return;
4475         }
4476         
4477         
4478         
4479         //Roo.log("fire event clicked");
4480         if(this.fireEvent('click', this, e) === false){
4481             return;
4482         };
4483         
4484         if(this.tagtype == 'span'){
4485             return;
4486         }
4487         
4488         //Roo.log(this.href);
4489         var ael = this.el.select('a',true).first();
4490         //Roo.log(ael);
4491         
4492         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4493             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4494             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4495                 return; // ignore... - it's a 'hash' to another page.
4496             }
4497             Roo.log("NavItem - prevent Default?");
4498             e.preventDefault();
4499             this.scrollToElement(e);
4500         }
4501         
4502         
4503         var p =  this.parent();
4504    
4505         if (['tabs','pills'].indexOf(p.type)!==-1) {
4506             if (typeof(p.setActiveItem) !== 'undefined') {
4507                 p.setActiveItem(this);
4508             }
4509         }
4510         
4511         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4512         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4513             // remove the collapsed menu expand...
4514             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4515         }
4516     },
4517     
4518     isActive: function () {
4519         return this.active
4520     },
4521     setActive : function(state, fire, is_was_active)
4522     {
4523         if (this.active && !state && this.navId) {
4524             this.was_active = true;
4525             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4526             if (nv) {
4527                 nv.clearWasActive(this);
4528             }
4529             
4530         }
4531         this.active = state;
4532         
4533         if (!state ) {
4534             this.el.removeClass('active');
4535         } else if (!this.el.hasClass('active')) {
4536             this.el.addClass('active');
4537         }
4538         if (fire) {
4539             this.fireEvent('changed', this, state);
4540         }
4541         
4542         // show a panel if it's registered and related..
4543         
4544         if (!this.navId || !this.tabId || !state || is_was_active) {
4545             return;
4546         }
4547         
4548         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4549         if (!tg) {
4550             return;
4551         }
4552         var pan = tg.getPanelByName(this.tabId);
4553         if (!pan) {
4554             return;
4555         }
4556         // if we can not flip to new panel - go back to old nav highlight..
4557         if (false == tg.showPanel(pan)) {
4558             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4559             if (nv) {
4560                 var onav = nv.getWasActive();
4561                 if (onav) {
4562                     onav.setActive(true, false, true);
4563                 }
4564             }
4565             
4566         }
4567         
4568         
4569         
4570     },
4571      // this should not be here...
4572     setDisabled : function(state)
4573     {
4574         this.disabled = state;
4575         if (!state ) {
4576             this.el.removeClass('disabled');
4577         } else if (!this.el.hasClass('disabled')) {
4578             this.el.addClass('disabled');
4579         }
4580         
4581     },
4582     
4583     /**
4584      * Fetch the element to display the tooltip on.
4585      * @return {Roo.Element} defaults to this.el
4586      */
4587     tooltipEl : function()
4588     {
4589         return this.el.select('' + this.tagtype + '', true).first();
4590     },
4591     
4592     scrollToElement : function(e)
4593     {
4594         var c = document.body;
4595         
4596         /*
4597          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4598          */
4599         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4600             c = document.documentElement;
4601         }
4602         
4603         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4604         
4605         if(!target){
4606             return;
4607         }
4608
4609         var o = target.calcOffsetsTo(c);
4610         
4611         var options = {
4612             target : target,
4613             value : o[1]
4614         };
4615         
4616         this.fireEvent('scrollto', this, options, e);
4617         
4618         Roo.get(c).scrollTo('top', options.value, true);
4619         
4620         return;
4621     }
4622 });
4623  
4624
4625  /*
4626  * - LGPL
4627  *
4628  * sidebar item
4629  *
4630  *  li
4631  *    <span> icon </span>
4632  *    <span> text </span>
4633  *    <span>badge </span>
4634  */
4635
4636 /**
4637  * @class Roo.bootstrap.NavSidebarItem
4638  * @extends Roo.bootstrap.NavItem
4639  * Bootstrap Navbar.NavSidebarItem class
4640  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4641  * {Boolean} open is the menu open
4642  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4643  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4644  * {String} buttonSize (sm|md|lg)the extra classes for the button
4645  * {Boolean} showArrow show arrow next to the text (default true)
4646  * @constructor
4647  * Create a new Navbar Button
4648  * @param {Object} config The config object
4649  */
4650 Roo.bootstrap.NavSidebarItem = function(config){
4651     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4652     this.addEvents({
4653         // raw events
4654         /**
4655          * @event click
4656          * The raw click event for the entire grid.
4657          * @param {Roo.EventObject} e
4658          */
4659         "click" : true,
4660          /**
4661             * @event changed
4662             * Fires when the active item active state changes
4663             * @param {Roo.bootstrap.NavSidebarItem} this
4664             * @param {boolean} state the new state
4665              
4666          */
4667         'changed': true
4668     });
4669    
4670 };
4671
4672 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4673     
4674     badgeWeight : 'default',
4675     
4676     open: false,
4677     
4678     buttonView : false,
4679     
4680     buttonWeight : 'default',
4681     
4682     buttonSize : 'md',
4683     
4684     showArrow : true,
4685     
4686     getAutoCreate : function(){
4687         
4688         
4689         var a = {
4690                 tag: 'a',
4691                 href : this.href || '#',
4692                 cls: '',
4693                 html : '',
4694                 cn : []
4695         };
4696         
4697         if(this.buttonView){
4698             a = {
4699                 tag: 'button',
4700                 href : this.href || '#',
4701                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4702                 html : this.html,
4703                 cn : []
4704             };
4705         }
4706         
4707         var cfg = {
4708             tag: 'li',
4709             cls: '',
4710             cn: [ a ]
4711         };
4712         
4713         if (this.active) {
4714             cfg.cls += ' active';
4715         }
4716         
4717         if (this.disabled) {
4718             cfg.cls += ' disabled';
4719         }
4720         if (this.open) {
4721             cfg.cls += ' open x-open';
4722         }
4723         // left icon..
4724         if (this.glyphicon || this.icon) {
4725             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4726             a.cn.push({ tag : 'i', cls : c }) ;
4727         }
4728         
4729         if(!this.buttonView){
4730             var span = {
4731                 tag: 'span',
4732                 html : this.html || ''
4733             };
4734
4735             a.cn.push(span);
4736             
4737         }
4738         
4739         if (this.badge !== '') {
4740             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4741         }
4742         
4743         if (this.menu) {
4744             
4745             if(this.showArrow){
4746                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4747             }
4748             
4749             a.cls += ' dropdown-toggle treeview' ;
4750         }
4751         
4752         return cfg;
4753     },
4754     
4755     initEvents : function()
4756     { 
4757         if (typeof (this.menu) != 'undefined') {
4758             this.menu.parentType = this.xtype;
4759             this.menu.triggerEl = this.el;
4760             this.menu = this.addxtype(Roo.apply({}, this.menu));
4761         }
4762         
4763         this.el.on('click', this.onClick, this);
4764         
4765         if(this.badge !== ''){
4766             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4767         }
4768         
4769     },
4770     
4771     onClick : function(e)
4772     {
4773         if(this.disabled){
4774             e.preventDefault();
4775             return;
4776         }
4777         
4778         if(this.preventDefault){
4779             e.preventDefault();
4780         }
4781         
4782         this.fireEvent('click', this);
4783     },
4784     
4785     disable : function()
4786     {
4787         this.setDisabled(true);
4788     },
4789     
4790     enable : function()
4791     {
4792         this.setDisabled(false);
4793     },
4794     
4795     setDisabled : function(state)
4796     {
4797         if(this.disabled == state){
4798             return;
4799         }
4800         
4801         this.disabled = state;
4802         
4803         if (state) {
4804             this.el.addClass('disabled');
4805             return;
4806         }
4807         
4808         this.el.removeClass('disabled');
4809         
4810         return;
4811     },
4812     
4813     setActive : function(state)
4814     {
4815         if(this.active == state){
4816             return;
4817         }
4818         
4819         this.active = state;
4820         
4821         if (state) {
4822             this.el.addClass('active');
4823             return;
4824         }
4825         
4826         this.el.removeClass('active');
4827         
4828         return;
4829     },
4830     
4831     isActive: function () 
4832     {
4833         return this.active;
4834     },
4835     
4836     setBadge : function(str)
4837     {
4838         if(!this.badgeEl){
4839             return;
4840         }
4841         
4842         this.badgeEl.dom.innerHTML = str;
4843     }
4844     
4845    
4846      
4847  
4848 });
4849  
4850
4851  /*
4852  * - LGPL
4853  *
4854  * row
4855  * 
4856  */
4857
4858 /**
4859  * @class Roo.bootstrap.Row
4860  * @extends Roo.bootstrap.Component
4861  * Bootstrap Row class (contains columns...)
4862  * 
4863  * @constructor
4864  * Create a new Row
4865  * @param {Object} config The config object
4866  */
4867
4868 Roo.bootstrap.Row = function(config){
4869     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4870 };
4871
4872 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4873     
4874     getAutoCreate : function(){
4875        return {
4876             cls: 'row clearfix'
4877        };
4878     }
4879     
4880     
4881 });
4882
4883  
4884
4885  /*
4886  * - LGPL
4887  *
4888  * element
4889  * 
4890  */
4891
4892 /**
4893  * @class Roo.bootstrap.Element
4894  * @extends Roo.bootstrap.Component
4895  * Bootstrap Element class
4896  * @cfg {String} html contents of the element
4897  * @cfg {String} tag tag of the element
4898  * @cfg {String} cls class of the element
4899  * @cfg {Boolean} preventDefault (true|false) default false
4900  * @cfg {Boolean} clickable (true|false) default false
4901  * 
4902  * @constructor
4903  * Create a new Element
4904  * @param {Object} config The config object
4905  */
4906
4907 Roo.bootstrap.Element = function(config){
4908     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4909     
4910     this.addEvents({
4911         // raw events
4912         /**
4913          * @event click
4914          * When a element is chick
4915          * @param {Roo.bootstrap.Element} this
4916          * @param {Roo.EventObject} e
4917          */
4918         "click" : true
4919     });
4920 };
4921
4922 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4923     
4924     tag: 'div',
4925     cls: '',
4926     html: '',
4927     preventDefault: false, 
4928     clickable: false,
4929     
4930     getAutoCreate : function(){
4931         
4932         var cfg = {
4933             tag: this.tag,
4934             // cls: this.cls,
4935             html: this.html
4936         };
4937         
4938         return cfg;
4939     },
4940     
4941     initEvents: function() 
4942     {
4943         Roo.bootstrap.Element.superclass.initEvents.call(this);
4944         
4945         if(this.clickable){
4946             this.el.on('click', this.onClick, this);
4947         }
4948         
4949     },
4950     
4951     onClick : function(e)
4952     {
4953         if(this.preventDefault){
4954             e.preventDefault();
4955         }
4956         
4957         this.fireEvent('click', this, e);
4958     },
4959     
4960     getValue : function()
4961     {
4962         return this.el.dom.innerHTML;
4963     },
4964     
4965     setValue : function(value)
4966     {
4967         this.el.dom.innerHTML = value;
4968     }
4969    
4970 });
4971
4972  
4973
4974  /*
4975  * - LGPL
4976  *
4977  * pagination
4978  * 
4979  */
4980
4981 /**
4982  * @class Roo.bootstrap.Pagination
4983  * @extends Roo.bootstrap.Component
4984  * Bootstrap Pagination class
4985  * @cfg {String} size xs | sm | md | lg
4986  * @cfg {Boolean} inverse false | true
4987  * 
4988  * @constructor
4989  * Create a new Pagination
4990  * @param {Object} config The config object
4991  */
4992
4993 Roo.bootstrap.Pagination = function(config){
4994     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4995 };
4996
4997 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4998     
4999     cls: false,
5000     size: false,
5001     inverse: false,
5002     
5003     getAutoCreate : function(){
5004         var cfg = {
5005             tag: 'ul',
5006                 cls: 'pagination'
5007         };
5008         if (this.inverse) {
5009             cfg.cls += ' inverse';
5010         }
5011         if (this.html) {
5012             cfg.html=this.html;
5013         }
5014         if (this.cls) {
5015             cfg.cls += " " + this.cls;
5016         }
5017         return cfg;
5018     }
5019    
5020 });
5021
5022  
5023
5024  /*
5025  * - LGPL
5026  *
5027  * Pagination item
5028  * 
5029  */
5030
5031
5032 /**
5033  * @class Roo.bootstrap.PaginationItem
5034  * @extends Roo.bootstrap.Component
5035  * Bootstrap PaginationItem class
5036  * @cfg {String} html text
5037  * @cfg {String} href the link
5038  * @cfg {Boolean} preventDefault (true | false) default true
5039  * @cfg {Boolean} active (true | false) default false
5040  * @cfg {Boolean} disabled default false
5041  * 
5042  * 
5043  * @constructor
5044  * Create a new PaginationItem
5045  * @param {Object} config The config object
5046  */
5047
5048
5049 Roo.bootstrap.PaginationItem = function(config){
5050     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5051     this.addEvents({
5052         // raw events
5053         /**
5054          * @event click
5055          * The raw click event for the entire grid.
5056          * @param {Roo.EventObject} e
5057          */
5058         "click" : true
5059     });
5060 };
5061
5062 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5063     
5064     href : false,
5065     html : false,
5066     preventDefault: true,
5067     active : false,
5068     cls : false,
5069     disabled: false,
5070     
5071     getAutoCreate : function(){
5072         var cfg= {
5073             tag: 'li',
5074             cn: [
5075                 {
5076                     tag : 'a',
5077                     href : this.href ? this.href : '#',
5078                     html : this.html ? this.html : ''
5079                 }
5080             ]
5081         };
5082         
5083         if(this.cls){
5084             cfg.cls = this.cls;
5085         }
5086         
5087         if(this.disabled){
5088             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5089         }
5090         
5091         if(this.active){
5092             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5093         }
5094         
5095         return cfg;
5096     },
5097     
5098     initEvents: function() {
5099         
5100         this.el.on('click', this.onClick, this);
5101         
5102     },
5103     onClick : function(e)
5104     {
5105         Roo.log('PaginationItem on click ');
5106         if(this.preventDefault){
5107             e.preventDefault();
5108         }
5109         
5110         if(this.disabled){
5111             return;
5112         }
5113         
5114         this.fireEvent('click', this, e);
5115     }
5116    
5117 });
5118
5119  
5120
5121  /*
5122  * - LGPL
5123  *
5124  * slider
5125  * 
5126  */
5127
5128
5129 /**
5130  * @class Roo.bootstrap.Slider
5131  * @extends Roo.bootstrap.Component
5132  * Bootstrap Slider class
5133  *    
5134  * @constructor
5135  * Create a new Slider
5136  * @param {Object} config The config object
5137  */
5138
5139 Roo.bootstrap.Slider = function(config){
5140     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5141 };
5142
5143 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5144     
5145     getAutoCreate : function(){
5146         
5147         var cfg = {
5148             tag: 'div',
5149             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5150             cn: [
5151                 {
5152                     tag: 'a',
5153                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5154                 }
5155             ]
5156         };
5157         
5158         return cfg;
5159     }
5160    
5161 });
5162
5163  /*
5164  * Based on:
5165  * Ext JS Library 1.1.1
5166  * Copyright(c) 2006-2007, Ext JS, LLC.
5167  *
5168  * Originally Released Under LGPL - original licence link has changed is not relivant.
5169  *
5170  * Fork - LGPL
5171  * <script type="text/javascript">
5172  */
5173  
5174
5175 /**
5176  * @class Roo.grid.ColumnModel
5177  * @extends Roo.util.Observable
5178  * This is the default implementation of a ColumnModel used by the Grid. It defines
5179  * the columns in the grid.
5180  * <br>Usage:<br>
5181  <pre><code>
5182  var colModel = new Roo.grid.ColumnModel([
5183         {header: "Ticker", width: 60, sortable: true, locked: true},
5184         {header: "Company Name", width: 150, sortable: true},
5185         {header: "Market Cap.", width: 100, sortable: true},
5186         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5187         {header: "Employees", width: 100, sortable: true, resizable: false}
5188  ]);
5189  </code></pre>
5190  * <p>
5191  
5192  * The config options listed for this class are options which may appear in each
5193  * individual column definition.
5194  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5195  * @constructor
5196  * @param {Object} config An Array of column config objects. See this class's
5197  * config objects for details.
5198 */
5199 Roo.grid.ColumnModel = function(config){
5200         /**
5201      * The config passed into the constructor
5202      */
5203     this.config = config;
5204     this.lookup = {};
5205
5206     // if no id, create one
5207     // if the column does not have a dataIndex mapping,
5208     // map it to the order it is in the config
5209     for(var i = 0, len = config.length; i < len; i++){
5210         var c = config[i];
5211         if(typeof c.dataIndex == "undefined"){
5212             c.dataIndex = i;
5213         }
5214         if(typeof c.renderer == "string"){
5215             c.renderer = Roo.util.Format[c.renderer];
5216         }
5217         if(typeof c.id == "undefined"){
5218             c.id = Roo.id();
5219         }
5220         if(c.editor && c.editor.xtype){
5221             c.editor  = Roo.factory(c.editor, Roo.grid);
5222         }
5223         if(c.editor && c.editor.isFormField){
5224             c.editor = new Roo.grid.GridEditor(c.editor);
5225         }
5226         this.lookup[c.id] = c;
5227     }
5228
5229     /**
5230      * The width of columns which have no width specified (defaults to 100)
5231      * @type Number
5232      */
5233     this.defaultWidth = 100;
5234
5235     /**
5236      * Default sortable of columns which have no sortable specified (defaults to false)
5237      * @type Boolean
5238      */
5239     this.defaultSortable = false;
5240
5241     this.addEvents({
5242         /**
5243              * @event widthchange
5244              * Fires when the width of a column changes.
5245              * @param {ColumnModel} this
5246              * @param {Number} columnIndex The column index
5247              * @param {Number} newWidth The new width
5248              */
5249             "widthchange": true,
5250         /**
5251              * @event headerchange
5252              * Fires when the text of a header changes.
5253              * @param {ColumnModel} this
5254              * @param {Number} columnIndex The column index
5255              * @param {Number} newText The new header text
5256              */
5257             "headerchange": true,
5258         /**
5259              * @event hiddenchange
5260              * Fires when a column is hidden or "unhidden".
5261              * @param {ColumnModel} this
5262              * @param {Number} columnIndex The column index
5263              * @param {Boolean} hidden true if hidden, false otherwise
5264              */
5265             "hiddenchange": true,
5266             /**
5267          * @event columnmoved
5268          * Fires when a column is moved.
5269          * @param {ColumnModel} this
5270          * @param {Number} oldIndex
5271          * @param {Number} newIndex
5272          */
5273         "columnmoved" : true,
5274         /**
5275          * @event columlockchange
5276          * Fires when a column's locked state is changed
5277          * @param {ColumnModel} this
5278          * @param {Number} colIndex
5279          * @param {Boolean} locked true if locked
5280          */
5281         "columnlockchange" : true
5282     });
5283     Roo.grid.ColumnModel.superclass.constructor.call(this);
5284 };
5285 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5286     /**
5287      * @cfg {String} header The header text to display in the Grid view.
5288      */
5289     /**
5290      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5291      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5292      * specified, the column's index is used as an index into the Record's data Array.
5293      */
5294     /**
5295      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5296      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5297      */
5298     /**
5299      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5300      * Defaults to the value of the {@link #defaultSortable} property.
5301      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5302      */
5303     /**
5304      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5305      */
5306     /**
5307      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5308      */
5309     /**
5310      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5311      */
5312     /**
5313      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5314      */
5315     /**
5316      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5317      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5318      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5319      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5320      */
5321        /**
5322      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5323      */
5324     /**
5325      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5326      */
5327     /**
5328      * @cfg {String} cursor (Optional)
5329      */
5330     /**
5331      * @cfg {String} tooltip (Optional)
5332      */
5333     /**
5334      * @cfg {Number} xs (Optional)
5335      */
5336     /**
5337      * @cfg {Number} sm (Optional)
5338      */
5339     /**
5340      * @cfg {Number} md (Optional)
5341      */
5342     /**
5343      * @cfg {Number} lg (Optional)
5344      */
5345     /**
5346      * Returns the id of the column at the specified index.
5347      * @param {Number} index The column index
5348      * @return {String} the id
5349      */
5350     getColumnId : function(index){
5351         return this.config[index].id;
5352     },
5353
5354     /**
5355      * Returns the column for a specified id.
5356      * @param {String} id The column id
5357      * @return {Object} the column
5358      */
5359     getColumnById : function(id){
5360         return this.lookup[id];
5361     },
5362
5363     
5364     /**
5365      * Returns the column for a specified dataIndex.
5366      * @param {String} dataIndex The column dataIndex
5367      * @return {Object|Boolean} the column or false if not found
5368      */
5369     getColumnByDataIndex: function(dataIndex){
5370         var index = this.findColumnIndex(dataIndex);
5371         return index > -1 ? this.config[index] : false;
5372     },
5373     
5374     /**
5375      * Returns the index for a specified column id.
5376      * @param {String} id The column id
5377      * @return {Number} the index, or -1 if not found
5378      */
5379     getIndexById : function(id){
5380         for(var i = 0, len = this.config.length; i < len; i++){
5381             if(this.config[i].id == id){
5382                 return i;
5383             }
5384         }
5385         return -1;
5386     },
5387     
5388     /**
5389      * Returns the index for a specified column dataIndex.
5390      * @param {String} dataIndex The column dataIndex
5391      * @return {Number} the index, or -1 if not found
5392      */
5393     
5394     findColumnIndex : function(dataIndex){
5395         for(var i = 0, len = this.config.length; i < len; i++){
5396             if(this.config[i].dataIndex == dataIndex){
5397                 return i;
5398             }
5399         }
5400         return -1;
5401     },
5402     
5403     
5404     moveColumn : function(oldIndex, newIndex){
5405         var c = this.config[oldIndex];
5406         this.config.splice(oldIndex, 1);
5407         this.config.splice(newIndex, 0, c);
5408         this.dataMap = null;
5409         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5410     },
5411
5412     isLocked : function(colIndex){
5413         return this.config[colIndex].locked === true;
5414     },
5415
5416     setLocked : function(colIndex, value, suppressEvent){
5417         if(this.isLocked(colIndex) == value){
5418             return;
5419         }
5420         this.config[colIndex].locked = value;
5421         if(!suppressEvent){
5422             this.fireEvent("columnlockchange", this, colIndex, value);
5423         }
5424     },
5425
5426     getTotalLockedWidth : function(){
5427         var totalWidth = 0;
5428         for(var i = 0; i < this.config.length; i++){
5429             if(this.isLocked(i) && !this.isHidden(i)){
5430                 this.totalWidth += this.getColumnWidth(i);
5431             }
5432         }
5433         return totalWidth;
5434     },
5435
5436     getLockedCount : function(){
5437         for(var i = 0, len = this.config.length; i < len; i++){
5438             if(!this.isLocked(i)){
5439                 return i;
5440             }
5441         }
5442         
5443         return this.config.length;
5444     },
5445
5446     /**
5447      * Returns the number of columns.
5448      * @return {Number}
5449      */
5450     getColumnCount : function(visibleOnly){
5451         if(visibleOnly === true){
5452             var c = 0;
5453             for(var i = 0, len = this.config.length; i < len; i++){
5454                 if(!this.isHidden(i)){
5455                     c++;
5456                 }
5457             }
5458             return c;
5459         }
5460         return this.config.length;
5461     },
5462
5463     /**
5464      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5465      * @param {Function} fn
5466      * @param {Object} scope (optional)
5467      * @return {Array} result
5468      */
5469     getColumnsBy : function(fn, scope){
5470         var r = [];
5471         for(var i = 0, len = this.config.length; i < len; i++){
5472             var c = this.config[i];
5473             if(fn.call(scope||this, c, i) === true){
5474                 r[r.length] = c;
5475             }
5476         }
5477         return r;
5478     },
5479
5480     /**
5481      * Returns true if the specified column is sortable.
5482      * @param {Number} col The column index
5483      * @return {Boolean}
5484      */
5485     isSortable : function(col){
5486         if(typeof this.config[col].sortable == "undefined"){
5487             return this.defaultSortable;
5488         }
5489         return this.config[col].sortable;
5490     },
5491
5492     /**
5493      * Returns the rendering (formatting) function defined for the column.
5494      * @param {Number} col The column index.
5495      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5496      */
5497     getRenderer : function(col){
5498         if(!this.config[col].renderer){
5499             return Roo.grid.ColumnModel.defaultRenderer;
5500         }
5501         return this.config[col].renderer;
5502     },
5503
5504     /**
5505      * Sets the rendering (formatting) function for a column.
5506      * @param {Number} col The column index
5507      * @param {Function} fn The function to use to process the cell's raw data
5508      * to return HTML markup for the grid view. The render function is called with
5509      * the following parameters:<ul>
5510      * <li>Data value.</li>
5511      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5512      * <li>css A CSS style string to apply to the table cell.</li>
5513      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5514      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5515      * <li>Row index</li>
5516      * <li>Column index</li>
5517      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5518      */
5519     setRenderer : function(col, fn){
5520         this.config[col].renderer = fn;
5521     },
5522
5523     /**
5524      * Returns the width for the specified column.
5525      * @param {Number} col The column index
5526      * @return {Number}
5527      */
5528     getColumnWidth : function(col){
5529         return this.config[col].width * 1 || this.defaultWidth;
5530     },
5531
5532     /**
5533      * Sets the width for a column.
5534      * @param {Number} col The column index
5535      * @param {Number} width The new width
5536      */
5537     setColumnWidth : function(col, width, suppressEvent){
5538         this.config[col].width = width;
5539         this.totalWidth = null;
5540         if(!suppressEvent){
5541              this.fireEvent("widthchange", this, col, width);
5542         }
5543     },
5544
5545     /**
5546      * Returns the total width of all columns.
5547      * @param {Boolean} includeHidden True to include hidden column widths
5548      * @return {Number}
5549      */
5550     getTotalWidth : function(includeHidden){
5551         if(!this.totalWidth){
5552             this.totalWidth = 0;
5553             for(var i = 0, len = this.config.length; i < len; i++){
5554                 if(includeHidden || !this.isHidden(i)){
5555                     this.totalWidth += this.getColumnWidth(i);
5556                 }
5557             }
5558         }
5559         return this.totalWidth;
5560     },
5561
5562     /**
5563      * Returns the header for the specified column.
5564      * @param {Number} col The column index
5565      * @return {String}
5566      */
5567     getColumnHeader : function(col){
5568         return this.config[col].header;
5569     },
5570
5571     /**
5572      * Sets the header for a column.
5573      * @param {Number} col The column index
5574      * @param {String} header The new header
5575      */
5576     setColumnHeader : function(col, header){
5577         this.config[col].header = header;
5578         this.fireEvent("headerchange", this, col, header);
5579     },
5580
5581     /**
5582      * Returns the tooltip for the specified column.
5583      * @param {Number} col The column index
5584      * @return {String}
5585      */
5586     getColumnTooltip : function(col){
5587             return this.config[col].tooltip;
5588     },
5589     /**
5590      * Sets the tooltip for a column.
5591      * @param {Number} col The column index
5592      * @param {String} tooltip The new tooltip
5593      */
5594     setColumnTooltip : function(col, tooltip){
5595             this.config[col].tooltip = tooltip;
5596     },
5597
5598     /**
5599      * Returns the dataIndex for the specified column.
5600      * @param {Number} col The column index
5601      * @return {Number}
5602      */
5603     getDataIndex : function(col){
5604         return this.config[col].dataIndex;
5605     },
5606
5607     /**
5608      * Sets the dataIndex for a column.
5609      * @param {Number} col The column index
5610      * @param {Number} dataIndex The new dataIndex
5611      */
5612     setDataIndex : function(col, dataIndex){
5613         this.config[col].dataIndex = dataIndex;
5614     },
5615
5616     
5617     
5618     /**
5619      * Returns true if the cell is editable.
5620      * @param {Number} colIndex The column index
5621      * @param {Number} rowIndex The row index - this is nto actually used..?
5622      * @return {Boolean}
5623      */
5624     isCellEditable : function(colIndex, rowIndex){
5625         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5626     },
5627
5628     /**
5629      * Returns the editor defined for the cell/column.
5630      * return false or null to disable editing.
5631      * @param {Number} colIndex The column index
5632      * @param {Number} rowIndex The row index
5633      * @return {Object}
5634      */
5635     getCellEditor : function(colIndex, rowIndex){
5636         return this.config[colIndex].editor;
5637     },
5638
5639     /**
5640      * Sets if a column is editable.
5641      * @param {Number} col The column index
5642      * @param {Boolean} editable True if the column is editable
5643      */
5644     setEditable : function(col, editable){
5645         this.config[col].editable = editable;
5646     },
5647
5648
5649     /**
5650      * Returns true if the column is hidden.
5651      * @param {Number} colIndex The column index
5652      * @return {Boolean}
5653      */
5654     isHidden : function(colIndex){
5655         return this.config[colIndex].hidden;
5656     },
5657
5658
5659     /**
5660      * Returns true if the column width cannot be changed
5661      */
5662     isFixed : function(colIndex){
5663         return this.config[colIndex].fixed;
5664     },
5665
5666     /**
5667      * Returns true if the column can be resized
5668      * @return {Boolean}
5669      */
5670     isResizable : function(colIndex){
5671         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5672     },
5673     /**
5674      * Sets if a column is hidden.
5675      * @param {Number} colIndex The column index
5676      * @param {Boolean} hidden True if the column is hidden
5677      */
5678     setHidden : function(colIndex, hidden){
5679         this.config[colIndex].hidden = hidden;
5680         this.totalWidth = null;
5681         this.fireEvent("hiddenchange", this, colIndex, hidden);
5682     },
5683
5684     /**
5685      * Sets the editor for a column.
5686      * @param {Number} col The column index
5687      * @param {Object} editor The editor object
5688      */
5689     setEditor : function(col, editor){
5690         this.config[col].editor = editor;
5691     }
5692 });
5693
5694 Roo.grid.ColumnModel.defaultRenderer = function(value)
5695 {
5696     if(typeof value == "object") {
5697         return value;
5698     }
5699         if(typeof value == "string" && value.length < 1){
5700             return "&#160;";
5701         }
5702     
5703         return String.format("{0}", value);
5704 };
5705
5706 // Alias for backwards compatibility
5707 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5708 /*
5709  * Based on:
5710  * Ext JS Library 1.1.1
5711  * Copyright(c) 2006-2007, Ext JS, LLC.
5712  *
5713  * Originally Released Under LGPL - original licence link has changed is not relivant.
5714  *
5715  * Fork - LGPL
5716  * <script type="text/javascript">
5717  */
5718  
5719 /**
5720  * @class Roo.LoadMask
5721  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5722  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5723  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5724  * element's UpdateManager load indicator and will be destroyed after the initial load.
5725  * @constructor
5726  * Create a new LoadMask
5727  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5728  * @param {Object} config The config object
5729  */
5730 Roo.LoadMask = function(el, config){
5731     this.el = Roo.get(el);
5732     Roo.apply(this, config);
5733     if(this.store){
5734         this.store.on('beforeload', this.onBeforeLoad, this);
5735         this.store.on('load', this.onLoad, this);
5736         this.store.on('loadexception', this.onLoadException, this);
5737         this.removeMask = false;
5738     }else{
5739         var um = this.el.getUpdateManager();
5740         um.showLoadIndicator = false; // disable the default indicator
5741         um.on('beforeupdate', this.onBeforeLoad, this);
5742         um.on('update', this.onLoad, this);
5743         um.on('failure', this.onLoad, this);
5744         this.removeMask = true;
5745     }
5746 };
5747
5748 Roo.LoadMask.prototype = {
5749     /**
5750      * @cfg {Boolean} removeMask
5751      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5752      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5753      */
5754     /**
5755      * @cfg {String} msg
5756      * The text to display in a centered loading message box (defaults to 'Loading...')
5757      */
5758     msg : 'Loading...',
5759     /**
5760      * @cfg {String} msgCls
5761      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5762      */
5763     msgCls : 'x-mask-loading',
5764
5765     /**
5766      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5767      * @type Boolean
5768      */
5769     disabled: false,
5770
5771     /**
5772      * Disables the mask to prevent it from being displayed
5773      */
5774     disable : function(){
5775        this.disabled = true;
5776     },
5777
5778     /**
5779      * Enables the mask so that it can be displayed
5780      */
5781     enable : function(){
5782         this.disabled = false;
5783     },
5784     
5785     onLoadException : function()
5786     {
5787         Roo.log(arguments);
5788         
5789         if (typeof(arguments[3]) != 'undefined') {
5790             Roo.MessageBox.alert("Error loading",arguments[3]);
5791         } 
5792         /*
5793         try {
5794             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5795                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5796             }   
5797         } catch(e) {
5798             
5799         }
5800         */
5801     
5802         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5803     },
5804     // private
5805     onLoad : function()
5806     {
5807         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5808     },
5809
5810     // private
5811     onBeforeLoad : function(){
5812         if(!this.disabled){
5813             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5814         }
5815     },
5816
5817     // private
5818     destroy : function(){
5819         if(this.store){
5820             this.store.un('beforeload', this.onBeforeLoad, this);
5821             this.store.un('load', this.onLoad, this);
5822             this.store.un('loadexception', this.onLoadException, this);
5823         }else{
5824             var um = this.el.getUpdateManager();
5825             um.un('beforeupdate', this.onBeforeLoad, this);
5826             um.un('update', this.onLoad, this);
5827             um.un('failure', this.onLoad, this);
5828         }
5829     }
5830 };/*
5831  * - LGPL
5832  *
5833  * table
5834  * 
5835  */
5836
5837 /**
5838  * @class Roo.bootstrap.Table
5839  * @extends Roo.bootstrap.Component
5840  * Bootstrap Table class
5841  * @cfg {String} cls table class
5842  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5843  * @cfg {String} bgcolor Specifies the background color for a table
5844  * @cfg {Number} border Specifies whether the table cells should have borders or not
5845  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5846  * @cfg {Number} cellspacing Specifies the space between cells
5847  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5848  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5849  * @cfg {String} sortable Specifies that the table should be sortable
5850  * @cfg {String} summary Specifies a summary of the content of a table
5851  * @cfg {Number} width Specifies the width of a table
5852  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5853  * 
5854  * @cfg {boolean} striped Should the rows be alternative striped
5855  * @cfg {boolean} bordered Add borders to the table
5856  * @cfg {boolean} hover Add hover highlighting
5857  * @cfg {boolean} condensed Format condensed
5858  * @cfg {boolean} responsive Format condensed
5859  * @cfg {Boolean} loadMask (true|false) default false
5860  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5861  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5862  * @cfg {Boolean} rowSelection (true|false) default false
5863  * @cfg {Boolean} cellSelection (true|false) default false
5864  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5865  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5866  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
5867  
5868  * 
5869  * @constructor
5870  * Create a new Table
5871  * @param {Object} config The config object
5872  */
5873
5874 Roo.bootstrap.Table = function(config){
5875     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5876     
5877   
5878     
5879     // BC...
5880     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5881     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5882     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5883     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5884     
5885     this.sm = this.sm || {xtype: 'RowSelectionModel'};
5886     if (this.sm) {
5887         this.sm.grid = this;
5888         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5889         this.sm = this.selModel;
5890         this.sm.xmodule = this.xmodule || false;
5891     }
5892     
5893     if (this.cm && typeof(this.cm.config) == 'undefined') {
5894         this.colModel = new Roo.grid.ColumnModel(this.cm);
5895         this.cm = this.colModel;
5896         this.cm.xmodule = this.xmodule || false;
5897     }
5898     if (this.store) {
5899         this.store= Roo.factory(this.store, Roo.data);
5900         this.ds = this.store;
5901         this.ds.xmodule = this.xmodule || false;
5902          
5903     }
5904     if (this.footer && this.store) {
5905         this.footer.dataSource = this.ds;
5906         this.footer = Roo.factory(this.footer);
5907     }
5908     
5909     /** @private */
5910     this.addEvents({
5911         /**
5912          * @event cellclick
5913          * Fires when a cell is clicked
5914          * @param {Roo.bootstrap.Table} this
5915          * @param {Roo.Element} el
5916          * @param {Number} rowIndex
5917          * @param {Number} columnIndex
5918          * @param {Roo.EventObject} e
5919          */
5920         "cellclick" : true,
5921         /**
5922          * @event celldblclick
5923          * Fires when a cell is double clicked
5924          * @param {Roo.bootstrap.Table} this
5925          * @param {Roo.Element} el
5926          * @param {Number} rowIndex
5927          * @param {Number} columnIndex
5928          * @param {Roo.EventObject} e
5929          */
5930         "celldblclick" : true,
5931         /**
5932          * @event rowclick
5933          * Fires when a row is clicked
5934          * @param {Roo.bootstrap.Table} this
5935          * @param {Roo.Element} el
5936          * @param {Number} rowIndex
5937          * @param {Roo.EventObject} e
5938          */
5939         "rowclick" : true,
5940         /**
5941          * @event rowdblclick
5942          * Fires when a row is double clicked
5943          * @param {Roo.bootstrap.Table} this
5944          * @param {Roo.Element} el
5945          * @param {Number} rowIndex
5946          * @param {Roo.EventObject} e
5947          */
5948         "rowdblclick" : true,
5949         /**
5950          * @event mouseover
5951          * Fires when a mouseover occur
5952          * @param {Roo.bootstrap.Table} this
5953          * @param {Roo.Element} el
5954          * @param {Number} rowIndex
5955          * @param {Number} columnIndex
5956          * @param {Roo.EventObject} e
5957          */
5958         "mouseover" : true,
5959         /**
5960          * @event mouseout
5961          * Fires when a mouseout occur
5962          * @param {Roo.bootstrap.Table} this
5963          * @param {Roo.Element} el
5964          * @param {Number} rowIndex
5965          * @param {Number} columnIndex
5966          * @param {Roo.EventObject} e
5967          */
5968         "mouseout" : true,
5969         /**
5970          * @event rowclass
5971          * Fires when a row is rendered, so you can change add a style to it.
5972          * @param {Roo.bootstrap.Table} this
5973          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5974          */
5975         'rowclass' : true,
5976           /**
5977          * @event rowsrendered
5978          * Fires when all the  rows have been rendered
5979          * @param {Roo.bootstrap.Table} this
5980          */
5981         'rowsrendered' : true,
5982         /**
5983          * @event contextmenu
5984          * The raw contextmenu event for the entire grid.
5985          * @param {Roo.EventObject} e
5986          */
5987         "contextmenu" : true,
5988         /**
5989          * @event rowcontextmenu
5990          * Fires when a row is right clicked
5991          * @param {Roo.bootstrap.Table} this
5992          * @param {Number} rowIndex
5993          * @param {Roo.EventObject} e
5994          */
5995         "rowcontextmenu" : true,
5996         /**
5997          * @event cellcontextmenu
5998          * Fires when a cell is right clicked
5999          * @param {Roo.bootstrap.Table} this
6000          * @param {Number} rowIndex
6001          * @param {Number} cellIndex
6002          * @param {Roo.EventObject} e
6003          */
6004          "cellcontextmenu" : true,
6005          /**
6006          * @event headercontextmenu
6007          * Fires when a header is right clicked
6008          * @param {Roo.bootstrap.Table} this
6009          * @param {Number} columnIndex
6010          * @param {Roo.EventObject} e
6011          */
6012         "headercontextmenu" : true
6013     });
6014 };
6015
6016 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6017     
6018     cls: false,
6019     align: false,
6020     bgcolor: false,
6021     border: false,
6022     cellpadding: false,
6023     cellspacing: false,
6024     frame: false,
6025     rules: false,
6026     sortable: false,
6027     summary: false,
6028     width: false,
6029     striped : false,
6030     scrollBody : false,
6031     bordered: false,
6032     hover:  false,
6033     condensed : false,
6034     responsive : false,
6035     sm : false,
6036     cm : false,
6037     store : false,
6038     loadMask : false,
6039     footerShow : true,
6040     headerShow : true,
6041   
6042     rowSelection : false,
6043     cellSelection : false,
6044     layout : false,
6045     
6046     // Roo.Element - the tbody
6047     mainBody: false,
6048     // Roo.Element - thead element
6049     mainHead: false,
6050     
6051     container: false, // used by gridpanel...
6052     
6053     lazyLoad : false,
6054     
6055     getAutoCreate : function()
6056     {
6057         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6058         
6059         cfg = {
6060             tag: 'table',
6061             cls : 'table',
6062             cn : []
6063         };
6064         if (this.scrollBody) {
6065             cfg.cls += ' table-body-fixed';
6066         }    
6067         if (this.striped) {
6068             cfg.cls += ' table-striped';
6069         }
6070         
6071         if (this.hover) {
6072             cfg.cls += ' table-hover';
6073         }
6074         if (this.bordered) {
6075             cfg.cls += ' table-bordered';
6076         }
6077         if (this.condensed) {
6078             cfg.cls += ' table-condensed';
6079         }
6080         if (this.responsive) {
6081             cfg.cls += ' table-responsive';
6082         }
6083         
6084         if (this.cls) {
6085             cfg.cls+=  ' ' +this.cls;
6086         }
6087         
6088         // this lot should be simplifed...
6089         
6090         if (this.align) {
6091             cfg.align=this.align;
6092         }
6093         if (this.bgcolor) {
6094             cfg.bgcolor=this.bgcolor;
6095         }
6096         if (this.border) {
6097             cfg.border=this.border;
6098         }
6099         if (this.cellpadding) {
6100             cfg.cellpadding=this.cellpadding;
6101         }
6102         if (this.cellspacing) {
6103             cfg.cellspacing=this.cellspacing;
6104         }
6105         if (this.frame) {
6106             cfg.frame=this.frame;
6107         }
6108         if (this.rules) {
6109             cfg.rules=this.rules;
6110         }
6111         if (this.sortable) {
6112             cfg.sortable=this.sortable;
6113         }
6114         if (this.summary) {
6115             cfg.summary=this.summary;
6116         }
6117         if (this.width) {
6118             cfg.width=this.width;
6119         }
6120         if (this.layout) {
6121             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6122         }
6123         
6124         if(this.store || this.cm){
6125             if(this.headerShow){
6126                 cfg.cn.push(this.renderHeader());
6127             }
6128             
6129             cfg.cn.push(this.renderBody());
6130             
6131             if(this.footerShow){
6132                 cfg.cn.push(this.renderFooter());
6133             }
6134             // where does this come from?
6135             //cfg.cls+=  ' TableGrid';
6136         }
6137         
6138         return { cn : [ cfg ] };
6139     },
6140     
6141     initEvents : function()
6142     {   
6143         if(!this.store || !this.cm){
6144             return;
6145         }
6146         if (this.selModel) {
6147             this.selModel.initEvents();
6148         }
6149         
6150         
6151         //Roo.log('initEvents with ds!!!!');
6152         
6153         this.mainBody = this.el.select('tbody', true).first();
6154         this.mainHead = this.el.select('thead', true).first();
6155         
6156         
6157         
6158         
6159         var _this = this;
6160         
6161         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6162             e.on('click', _this.sort, _this);
6163         });
6164         
6165         this.mainBody.on("click", this.onClick, this);
6166         this.mainBody.on("dblclick", this.onDblClick, this);
6167         
6168         // why is this done????? = it breaks dialogs??
6169         //this.parent().el.setStyle('position', 'relative');
6170         
6171         
6172         if (this.footer) {
6173             this.footer.parentId = this.id;
6174             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6175             
6176             if(this.lazyLoad){
6177                 this.el.select('tfoot tr td').first().addClass('hide');
6178             }
6179         } 
6180         
6181         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6182         
6183         this.store.on('load', this.onLoad, this);
6184         this.store.on('beforeload', this.onBeforeLoad, this);
6185         this.store.on('update', this.onUpdate, this);
6186         this.store.on('add', this.onAdd, this);
6187         this.store.on("clear", this.clear, this);
6188         
6189         this.el.on("contextmenu", this.onContextMenu, this);
6190         
6191         this.mainBody.on('scroll', this.onBodyScroll, this);
6192         
6193         this.cm.on("headerchange", this.onHeaderChange, this);
6194         
6195     },
6196     
6197     onContextMenu : function(e, t)
6198     {
6199         this.processEvent("contextmenu", e);
6200     },
6201     
6202     processEvent : function(name, e)
6203     {
6204         if (name != 'touchstart' ) {
6205             this.fireEvent(name, e);    
6206         }
6207         
6208         var t = e.getTarget();
6209         
6210         var cell = Roo.get(t);
6211         
6212         if(!cell){
6213             return;
6214         }
6215         
6216         if(cell.findParent('tfoot', false, true)){
6217             return;
6218         }
6219         
6220         if(cell.findParent('thead', false, true)){
6221             
6222             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6223                 cell = Roo.get(t).findParent('th', false, true);
6224                 if (!cell) {
6225                     Roo.log("failed to find th in thead?");
6226                     Roo.log(e.getTarget());
6227                     return;
6228                 }
6229             }
6230             
6231             var cellIndex = cell.dom.cellIndex;
6232             
6233             var ename = name == 'touchstart' ? 'click' : name;
6234             this.fireEvent("header" + ename, this, cellIndex, e);
6235             
6236             return;
6237         }
6238         
6239         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6240             cell = Roo.get(t).findParent('td', false, true);
6241             if (!cell) {
6242                 Roo.log("failed to find th in tbody?");
6243                 Roo.log(e.getTarget());
6244                 return;
6245             }
6246         }
6247         
6248         var row = cell.findParent('tr', false, true);
6249         var cellIndex = cell.dom.cellIndex;
6250         var rowIndex = row.dom.rowIndex - 1;
6251         
6252         if(row !== false){
6253             
6254             this.fireEvent("row" + name, this, rowIndex, e);
6255             
6256             if(cell !== false){
6257             
6258                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6259             }
6260         }
6261         
6262     },
6263     
6264     onMouseover : function(e, el)
6265     {
6266         var cell = Roo.get(el);
6267         
6268         if(!cell){
6269             return;
6270         }
6271         
6272         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6273             cell = cell.findParent('td', false, true);
6274         }
6275         
6276         var row = cell.findParent('tr', false, true);
6277         var cellIndex = cell.dom.cellIndex;
6278         var rowIndex = row.dom.rowIndex - 1; // start from 0
6279         
6280         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6281         
6282     },
6283     
6284     onMouseout : function(e, el)
6285     {
6286         var cell = Roo.get(el);
6287         
6288         if(!cell){
6289             return;
6290         }
6291         
6292         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6293             cell = cell.findParent('td', false, true);
6294         }
6295         
6296         var row = cell.findParent('tr', false, true);
6297         var cellIndex = cell.dom.cellIndex;
6298         var rowIndex = row.dom.rowIndex - 1; // start from 0
6299         
6300         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6301         
6302     },
6303     
6304     onClick : function(e, el)
6305     {
6306         var cell = Roo.get(el);
6307         
6308         if(!cell || (!this.cellSelection && !this.rowSelection)){
6309             return;
6310         }
6311         
6312         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6313             cell = cell.findParent('td', false, true);
6314         }
6315         
6316         if(!cell || typeof(cell) == 'undefined'){
6317             return;
6318         }
6319         
6320         var row = cell.findParent('tr', false, true);
6321         
6322         if(!row || typeof(row) == 'undefined'){
6323             return;
6324         }
6325         
6326         var cellIndex = cell.dom.cellIndex;
6327         var rowIndex = this.getRowIndex(row);
6328         
6329         // why??? - should these not be based on SelectionModel?
6330         if(this.cellSelection){
6331             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6332         }
6333         
6334         if(this.rowSelection){
6335             this.fireEvent('rowclick', this, row, rowIndex, e);
6336         }
6337         
6338         
6339     },
6340         
6341     onDblClick : function(e,el)
6342     {
6343         var cell = Roo.get(el);
6344         
6345         if(!cell || (!this.cellSelection && !this.rowSelection)){
6346             return;
6347         }
6348         
6349         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6350             cell = cell.findParent('td', false, true);
6351         }
6352         
6353         if(!cell || typeof(cell) == 'undefined'){
6354             return;
6355         }
6356         
6357         var row = cell.findParent('tr', false, true);
6358         
6359         if(!row || typeof(row) == 'undefined'){
6360             return;
6361         }
6362         
6363         var cellIndex = cell.dom.cellIndex;
6364         var rowIndex = this.getRowIndex(row);
6365         
6366         if(this.cellSelection){
6367             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6368         }
6369         
6370         if(this.rowSelection){
6371             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6372         }
6373     },
6374     
6375     sort : function(e,el)
6376     {
6377         var col = Roo.get(el);
6378         
6379         if(!col.hasClass('sortable')){
6380             return;
6381         }
6382         
6383         var sort = col.attr('sort');
6384         var dir = 'ASC';
6385         
6386         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6387             dir = 'DESC';
6388         }
6389         
6390         this.store.sortInfo = {field : sort, direction : dir};
6391         
6392         if (this.footer) {
6393             Roo.log("calling footer first");
6394             this.footer.onClick('first');
6395         } else {
6396         
6397             this.store.load({ params : { start : 0 } });
6398         }
6399     },
6400     
6401     renderHeader : function()
6402     {
6403         var header = {
6404             tag: 'thead',
6405             cn : []
6406         };
6407         
6408         var cm = this.cm;
6409         this.totalWidth = 0;
6410         
6411         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6412             
6413             var config = cm.config[i];
6414             
6415             var c = {
6416                 tag: 'th',
6417                 style : '',
6418                 html: cm.getColumnHeader(i)
6419             };
6420             
6421             var hh = '';
6422             
6423             if(typeof(config.sortable) != 'undefined' && config.sortable){
6424                 c.cls = 'sortable';
6425                 c.html = '<i class="glyphicon"></i>' + c.html;
6426             }
6427             
6428             if(typeof(config.lgHeader) != 'undefined'){
6429                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6430             }
6431             
6432             if(typeof(config.mdHeader) != 'undefined'){
6433                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6434             }
6435             
6436             if(typeof(config.smHeader) != 'undefined'){
6437                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6438             }
6439             
6440             if(typeof(config.xsHeader) != 'undefined'){
6441                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6442             }
6443             
6444             if(hh.length){
6445                 c.html = hh;
6446             }
6447             
6448             if(typeof(config.tooltip) != 'undefined'){
6449                 c.tooltip = config.tooltip;
6450             }
6451             
6452             if(typeof(config.colspan) != 'undefined'){
6453                 c.colspan = config.colspan;
6454             }
6455             
6456             if(typeof(config.hidden) != 'undefined' && config.hidden){
6457                 c.style += ' display:none;';
6458             }
6459             
6460             if(typeof(config.dataIndex) != 'undefined'){
6461                 c.sort = config.dataIndex;
6462             }
6463             
6464            
6465             
6466             if(typeof(config.align) != 'undefined' && config.align.length){
6467                 c.style += ' text-align:' + config.align + ';';
6468             }
6469             
6470             if(typeof(config.width) != 'undefined'){
6471                 c.style += ' width:' + config.width + 'px;';
6472                 this.totalWidth += config.width;
6473             } else {
6474                 this.totalWidth += 100; // assume minimum of 100 per column?
6475             }
6476             
6477             if(typeof(config.cls) != 'undefined'){
6478                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6479             }
6480             
6481             ['xs','sm','md','lg'].map(function(size){
6482                 
6483                 if(typeof(config[size]) == 'undefined'){
6484                     return;
6485                 }
6486                 
6487                 if (!config[size]) { // 0 = hidden
6488                     c.cls += ' hidden-' + size;
6489                     return;
6490                 }
6491                 
6492                 c.cls += ' col-' + size + '-' + config[size];
6493
6494             });
6495             
6496             header.cn.push(c)
6497         }
6498         
6499         return header;
6500     },
6501     
6502     renderBody : function()
6503     {
6504         var body = {
6505             tag: 'tbody',
6506             cn : [
6507                 {
6508                     tag: 'tr',
6509                     cn : [
6510                         {
6511                             tag : 'td',
6512                             colspan :  this.cm.getColumnCount()
6513                         }
6514                     ]
6515                 }
6516             ]
6517         };
6518         
6519         return body;
6520     },
6521     
6522     renderFooter : function()
6523     {
6524         var footer = {
6525             tag: 'tfoot',
6526             cn : [
6527                 {
6528                     tag: 'tr',
6529                     cn : [
6530                         {
6531                             tag : 'td',
6532                             colspan :  this.cm.getColumnCount()
6533                         }
6534                     ]
6535                 }
6536             ]
6537         };
6538         
6539         return footer;
6540     },
6541     
6542     
6543     
6544     onLoad : function()
6545     {
6546 //        Roo.log('ds onload');
6547         this.clear();
6548         
6549         var _this = this;
6550         var cm = this.cm;
6551         var ds = this.store;
6552         
6553         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6554             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6555             if (_this.store.sortInfo) {
6556                     
6557                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6558                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6559                 }
6560                 
6561                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6562                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6563                 }
6564             }
6565         });
6566         
6567         var tbody =  this.mainBody;
6568               
6569         if(ds.getCount() > 0){
6570             ds.data.each(function(d,rowIndex){
6571                 var row =  this.renderRow(cm, ds, rowIndex);
6572                 
6573                 tbody.createChild(row);
6574                 
6575                 var _this = this;
6576                 
6577                 if(row.cellObjects.length){
6578                     Roo.each(row.cellObjects, function(r){
6579                         _this.renderCellObject(r);
6580                     })
6581                 }
6582                 
6583             }, this);
6584         }
6585         
6586         Roo.each(this.el.select('tbody td', true).elements, function(e){
6587             e.on('mouseover', _this.onMouseover, _this);
6588         });
6589         
6590         Roo.each(this.el.select('tbody td', true).elements, function(e){
6591             e.on('mouseout', _this.onMouseout, _this);
6592         });
6593         this.fireEvent('rowsrendered', this);
6594         //if(this.loadMask){
6595         //    this.maskEl.hide();
6596         //}
6597         
6598         this.autoSize();
6599     },
6600     
6601     
6602     onUpdate : function(ds,record)
6603     {
6604         this.refreshRow(record);
6605         this.autoSize();
6606     },
6607     
6608     onRemove : function(ds, record, index, isUpdate){
6609         if(isUpdate !== true){
6610             this.fireEvent("beforerowremoved", this, index, record);
6611         }
6612         var bt = this.mainBody.dom;
6613         
6614         var rows = this.el.select('tbody > tr', true).elements;
6615         
6616         if(typeof(rows[index]) != 'undefined'){
6617             bt.removeChild(rows[index].dom);
6618         }
6619         
6620 //        if(bt.rows[index]){
6621 //            bt.removeChild(bt.rows[index]);
6622 //        }
6623         
6624         if(isUpdate !== true){
6625             //this.stripeRows(index);
6626             //this.syncRowHeights(index, index);
6627             //this.layout();
6628             this.fireEvent("rowremoved", this, index, record);
6629         }
6630     },
6631     
6632     onAdd : function(ds, records, rowIndex)
6633     {
6634         //Roo.log('on Add called');
6635         // - note this does not handle multiple adding very well..
6636         var bt = this.mainBody.dom;
6637         for (var i =0 ; i < records.length;i++) {
6638             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6639             //Roo.log(records[i]);
6640             //Roo.log(this.store.getAt(rowIndex+i));
6641             this.insertRow(this.store, rowIndex + i, false);
6642             return;
6643         }
6644         
6645     },
6646     
6647     
6648     refreshRow : function(record){
6649         var ds = this.store, index;
6650         if(typeof record == 'number'){
6651             index = record;
6652             record = ds.getAt(index);
6653         }else{
6654             index = ds.indexOf(record);
6655         }
6656         this.insertRow(ds, index, true);
6657         this.autoSize();
6658         this.onRemove(ds, record, index+1, true);
6659         this.autoSize();
6660         //this.syncRowHeights(index, index);
6661         //this.layout();
6662         this.fireEvent("rowupdated", this, index, record);
6663     },
6664     
6665     insertRow : function(dm, rowIndex, isUpdate){
6666         
6667         if(!isUpdate){
6668             this.fireEvent("beforerowsinserted", this, rowIndex);
6669         }
6670             //var s = this.getScrollState();
6671         var row = this.renderRow(this.cm, this.store, rowIndex);
6672         // insert before rowIndex..
6673         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6674         
6675         var _this = this;
6676                 
6677         if(row.cellObjects.length){
6678             Roo.each(row.cellObjects, function(r){
6679                 _this.renderCellObject(r);
6680             })
6681         }
6682             
6683         if(!isUpdate){
6684             this.fireEvent("rowsinserted", this, rowIndex);
6685             //this.syncRowHeights(firstRow, lastRow);
6686             //this.stripeRows(firstRow);
6687             //this.layout();
6688         }
6689         
6690     },
6691     
6692     
6693     getRowDom : function(rowIndex)
6694     {
6695         var rows = this.el.select('tbody > tr', true).elements;
6696         
6697         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6698         
6699     },
6700     // returns the object tree for a tr..
6701   
6702     
6703     renderRow : function(cm, ds, rowIndex) 
6704     {
6705         
6706         var d = ds.getAt(rowIndex);
6707         
6708         var row = {
6709             tag : 'tr',
6710             cn : []
6711         };
6712             
6713         var cellObjects = [];
6714         
6715         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6716             var config = cm.config[i];
6717             
6718             var renderer = cm.getRenderer(i);
6719             var value = '';
6720             var id = false;
6721             
6722             if(typeof(renderer) !== 'undefined'){
6723                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6724             }
6725             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6726             // and are rendered into the cells after the row is rendered - using the id for the element.
6727             
6728             if(typeof(value) === 'object'){
6729                 id = Roo.id();
6730                 cellObjects.push({
6731                     container : id,
6732                     cfg : value 
6733                 })
6734             }
6735             
6736             var rowcfg = {
6737                 record: d,
6738                 rowIndex : rowIndex,
6739                 colIndex : i,
6740                 rowClass : ''
6741             };
6742
6743             this.fireEvent('rowclass', this, rowcfg);
6744             
6745             var td = {
6746                 tag: 'td',
6747                 cls : rowcfg.rowClass,
6748                 style: '',
6749                 html: (typeof(value) === 'object') ? '' : value
6750             };
6751             
6752             if (id) {
6753                 td.id = id;
6754             }
6755             
6756             if(typeof(config.colspan) != 'undefined'){
6757                 td.colspan = config.colspan;
6758             }
6759             
6760             if(typeof(config.hidden) != 'undefined' && config.hidden){
6761                 td.style += ' display:none;';
6762             }
6763             
6764             if(typeof(config.align) != 'undefined' && config.align.length){
6765                 td.style += ' text-align:' + config.align + ';';
6766             }
6767             
6768             if(typeof(config.width) != 'undefined'){
6769                 td.style += ' width:' +  config.width + 'px;';
6770             }
6771             
6772             if(typeof(config.cursor) != 'undefined'){
6773                 td.style += ' cursor:' +  config.cursor + ';';
6774             }
6775             
6776             if(typeof(config.cls) != 'undefined'){
6777                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6778             }
6779             
6780             ['xs','sm','md','lg'].map(function(size){
6781                 
6782                 if(typeof(config[size]) == 'undefined'){
6783                     return;
6784                 }
6785                 
6786                 if (!config[size]) { // 0 = hidden
6787                     td.cls += ' hidden-' + size;
6788                     return;
6789                 }
6790                 
6791                 td.cls += ' col-' + size + '-' + config[size];
6792
6793             });
6794              
6795             row.cn.push(td);
6796            
6797         }
6798         
6799         row.cellObjects = cellObjects;
6800         
6801         return row;
6802           
6803     },
6804     
6805     
6806     
6807     onBeforeLoad : function()
6808     {
6809         //Roo.log('ds onBeforeLoad');
6810         
6811         //this.clear();
6812         
6813         //if(this.loadMask){
6814         //    this.maskEl.show();
6815         //}
6816     },
6817      /**
6818      * Remove all rows
6819      */
6820     clear : function()
6821     {
6822         this.el.select('tbody', true).first().dom.innerHTML = '';
6823     },
6824     /**
6825      * Show or hide a row.
6826      * @param {Number} rowIndex to show or hide
6827      * @param {Boolean} state hide
6828      */
6829     setRowVisibility : function(rowIndex, state)
6830     {
6831         var bt = this.mainBody.dom;
6832         
6833         var rows = this.el.select('tbody > tr', true).elements;
6834         
6835         if(typeof(rows[rowIndex]) == 'undefined'){
6836             return;
6837         }
6838         rows[rowIndex].dom.style.display = state ? '' : 'none';
6839     },
6840     
6841     
6842     getSelectionModel : function(){
6843         if(!this.selModel){
6844             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6845         }
6846         return this.selModel;
6847     },
6848     /*
6849      * Render the Roo.bootstrap object from renderder
6850      */
6851     renderCellObject : function(r)
6852     {
6853         var _this = this;
6854         
6855         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6856         
6857         var t = r.cfg.render(r.container);
6858         
6859         if(r.cfg.cn){
6860             Roo.each(r.cfg.cn, function(c){
6861                 var child = {
6862                     container: t.getChildContainer(),
6863                     cfg: c
6864                 };
6865                 _this.renderCellObject(child);
6866             })
6867         }
6868     },
6869     
6870     getRowIndex : function(row)
6871     {
6872         var rowIndex = -1;
6873         
6874         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6875             if(el != row){
6876                 return;
6877             }
6878             
6879             rowIndex = index;
6880         });
6881         
6882         return rowIndex;
6883     },
6884      /**
6885      * Returns the grid's underlying element = used by panel.Grid
6886      * @return {Element} The element
6887      */
6888     getGridEl : function(){
6889         return this.el;
6890     },
6891      /**
6892      * Forces a resize - used by panel.Grid
6893      * @return {Element} The element
6894      */
6895     autoSize : function()
6896     {
6897         //var ctr = Roo.get(this.container.dom.parentElement);
6898         var ctr = Roo.get(this.el.dom);
6899         
6900         var thd = this.getGridEl().select('thead',true).first();
6901         var tbd = this.getGridEl().select('tbody', true).first();
6902         var tfd = this.getGridEl().select('tfoot', true).first();
6903         
6904         var cw = ctr.getWidth();
6905         
6906         if (tbd) {
6907             
6908             tbd.setSize(ctr.getWidth(),
6909                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
6910             );
6911             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6912             cw -= barsize;
6913         }
6914         cw = Math.max(cw, this.totalWidth);
6915         this.getGridEl().select('tr',true).setWidth(cw);
6916         // resize 'expandable coloumn?
6917         
6918         return; // we doe not have a view in this design..
6919         
6920     },
6921     onBodyScroll: function()
6922     {
6923         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6924         if(this.mainHead){
6925             this.mainHead.setStyle({
6926                 'position' : 'relative',
6927                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6928             });
6929         }
6930         
6931         if(this.lazyLoad){
6932             
6933             var scrollHeight = this.mainBody.dom.scrollHeight;
6934             
6935             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
6936             
6937             var height = this.mainBody.getHeight();
6938             
6939             if(scrollHeight - height == scrollTop) {
6940                 
6941                 var total = this.ds.getTotalCount();
6942                 
6943                 if(this.footer.cursor + this.footer.pageSize < total){
6944                     
6945                     this.footer.ds.load({
6946                         params : {
6947                             start : this.footer.cursor + this.footer.pageSize,
6948                             limit : this.footer.pageSize
6949                         },
6950                         add : true
6951                     });
6952                 }
6953             }
6954             
6955         }
6956     },
6957     
6958     onHeaderChange : function()
6959     {
6960         
6961         var header = this.renderHeader();
6962         var table = this.el.select('table', true).first();
6963         
6964         this.mainHead.remove();
6965         this.mainHead = table.createChild(header, this.mainBody, false);
6966     }
6967     
6968 });
6969
6970  
6971
6972  /*
6973  * - LGPL
6974  *
6975  * table cell
6976  * 
6977  */
6978
6979 /**
6980  * @class Roo.bootstrap.TableCell
6981  * @extends Roo.bootstrap.Component
6982  * Bootstrap TableCell class
6983  * @cfg {String} html cell contain text
6984  * @cfg {String} cls cell class
6985  * @cfg {String} tag cell tag (td|th) default td
6986  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6987  * @cfg {String} align Aligns the content in a cell
6988  * @cfg {String} axis Categorizes cells
6989  * @cfg {String} bgcolor Specifies the background color of a cell
6990  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6991  * @cfg {Number} colspan Specifies the number of columns a cell should span
6992  * @cfg {String} headers Specifies one or more header cells a cell is related to
6993  * @cfg {Number} height Sets the height of a cell
6994  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6995  * @cfg {Number} rowspan Sets the number of rows a cell should span
6996  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6997  * @cfg {String} valign Vertical aligns the content in a cell
6998  * @cfg {Number} width Specifies the width of a cell
6999  * 
7000  * @constructor
7001  * Create a new TableCell
7002  * @param {Object} config The config object
7003  */
7004
7005 Roo.bootstrap.TableCell = function(config){
7006     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7007 };
7008
7009 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7010     
7011     html: false,
7012     cls: false,
7013     tag: false,
7014     abbr: false,
7015     align: false,
7016     axis: false,
7017     bgcolor: false,
7018     charoff: false,
7019     colspan: false,
7020     headers: false,
7021     height: false,
7022     nowrap: false,
7023     rowspan: false,
7024     scope: false,
7025     valign: false,
7026     width: false,
7027     
7028     
7029     getAutoCreate : function(){
7030         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7031         
7032         cfg = {
7033             tag: 'td'
7034         };
7035         
7036         if(this.tag){
7037             cfg.tag = this.tag;
7038         }
7039         
7040         if (this.html) {
7041             cfg.html=this.html
7042         }
7043         if (this.cls) {
7044             cfg.cls=this.cls
7045         }
7046         if (this.abbr) {
7047             cfg.abbr=this.abbr
7048         }
7049         if (this.align) {
7050             cfg.align=this.align
7051         }
7052         if (this.axis) {
7053             cfg.axis=this.axis
7054         }
7055         if (this.bgcolor) {
7056             cfg.bgcolor=this.bgcolor
7057         }
7058         if (this.charoff) {
7059             cfg.charoff=this.charoff
7060         }
7061         if (this.colspan) {
7062             cfg.colspan=this.colspan
7063         }
7064         if (this.headers) {
7065             cfg.headers=this.headers
7066         }
7067         if (this.height) {
7068             cfg.height=this.height
7069         }
7070         if (this.nowrap) {
7071             cfg.nowrap=this.nowrap
7072         }
7073         if (this.rowspan) {
7074             cfg.rowspan=this.rowspan
7075         }
7076         if (this.scope) {
7077             cfg.scope=this.scope
7078         }
7079         if (this.valign) {
7080             cfg.valign=this.valign
7081         }
7082         if (this.width) {
7083             cfg.width=this.width
7084         }
7085         
7086         
7087         return cfg;
7088     }
7089    
7090 });
7091
7092  
7093
7094  /*
7095  * - LGPL
7096  *
7097  * table row
7098  * 
7099  */
7100
7101 /**
7102  * @class Roo.bootstrap.TableRow
7103  * @extends Roo.bootstrap.Component
7104  * Bootstrap TableRow class
7105  * @cfg {String} cls row class
7106  * @cfg {String} align Aligns the content in a table row
7107  * @cfg {String} bgcolor Specifies a background color for a table row
7108  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7109  * @cfg {String} valign Vertical aligns the content in a table row
7110  * 
7111  * @constructor
7112  * Create a new TableRow
7113  * @param {Object} config The config object
7114  */
7115
7116 Roo.bootstrap.TableRow = function(config){
7117     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7118 };
7119
7120 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7121     
7122     cls: false,
7123     align: false,
7124     bgcolor: false,
7125     charoff: false,
7126     valign: false,
7127     
7128     getAutoCreate : function(){
7129         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7130         
7131         cfg = {
7132             tag: 'tr'
7133         };
7134             
7135         if(this.cls){
7136             cfg.cls = this.cls;
7137         }
7138         if(this.align){
7139             cfg.align = this.align;
7140         }
7141         if(this.bgcolor){
7142             cfg.bgcolor = this.bgcolor;
7143         }
7144         if(this.charoff){
7145             cfg.charoff = this.charoff;
7146         }
7147         if(this.valign){
7148             cfg.valign = this.valign;
7149         }
7150         
7151         return cfg;
7152     }
7153    
7154 });
7155
7156  
7157
7158  /*
7159  * - LGPL
7160  *
7161  * table body
7162  * 
7163  */
7164
7165 /**
7166  * @class Roo.bootstrap.TableBody
7167  * @extends Roo.bootstrap.Component
7168  * Bootstrap TableBody class
7169  * @cfg {String} cls element class
7170  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7171  * @cfg {String} align Aligns the content inside the element
7172  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7173  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7174  * 
7175  * @constructor
7176  * Create a new TableBody
7177  * @param {Object} config The config object
7178  */
7179
7180 Roo.bootstrap.TableBody = function(config){
7181     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7182 };
7183
7184 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7185     
7186     cls: false,
7187     tag: false,
7188     align: false,
7189     charoff: false,
7190     valign: false,
7191     
7192     getAutoCreate : function(){
7193         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7194         
7195         cfg = {
7196             tag: 'tbody'
7197         };
7198             
7199         if (this.cls) {
7200             cfg.cls=this.cls
7201         }
7202         if(this.tag){
7203             cfg.tag = this.tag;
7204         }
7205         
7206         if(this.align){
7207             cfg.align = this.align;
7208         }
7209         if(this.charoff){
7210             cfg.charoff = this.charoff;
7211         }
7212         if(this.valign){
7213             cfg.valign = this.valign;
7214         }
7215         
7216         return cfg;
7217     }
7218     
7219     
7220 //    initEvents : function()
7221 //    {
7222 //        
7223 //        if(!this.store){
7224 //            return;
7225 //        }
7226 //        
7227 //        this.store = Roo.factory(this.store, Roo.data);
7228 //        this.store.on('load', this.onLoad, this);
7229 //        
7230 //        this.store.load();
7231 //        
7232 //    },
7233 //    
7234 //    onLoad: function () 
7235 //    {   
7236 //        this.fireEvent('load', this);
7237 //    }
7238 //    
7239 //   
7240 });
7241
7242  
7243
7244  /*
7245  * Based on:
7246  * Ext JS Library 1.1.1
7247  * Copyright(c) 2006-2007, Ext JS, LLC.
7248  *
7249  * Originally Released Under LGPL - original licence link has changed is not relivant.
7250  *
7251  * Fork - LGPL
7252  * <script type="text/javascript">
7253  */
7254
7255 // as we use this in bootstrap.
7256 Roo.namespace('Roo.form');
7257  /**
7258  * @class Roo.form.Action
7259  * Internal Class used to handle form actions
7260  * @constructor
7261  * @param {Roo.form.BasicForm} el The form element or its id
7262  * @param {Object} config Configuration options
7263  */
7264
7265  
7266  
7267 // define the action interface
7268 Roo.form.Action = function(form, options){
7269     this.form = form;
7270     this.options = options || {};
7271 };
7272 /**
7273  * Client Validation Failed
7274  * @const 
7275  */
7276 Roo.form.Action.CLIENT_INVALID = 'client';
7277 /**
7278  * Server Validation Failed
7279  * @const 
7280  */
7281 Roo.form.Action.SERVER_INVALID = 'server';
7282  /**
7283  * Connect to Server Failed
7284  * @const 
7285  */
7286 Roo.form.Action.CONNECT_FAILURE = 'connect';
7287 /**
7288  * Reading Data from Server Failed
7289  * @const 
7290  */
7291 Roo.form.Action.LOAD_FAILURE = 'load';
7292
7293 Roo.form.Action.prototype = {
7294     type : 'default',
7295     failureType : undefined,
7296     response : undefined,
7297     result : undefined,
7298
7299     // interface method
7300     run : function(options){
7301
7302     },
7303
7304     // interface method
7305     success : function(response){
7306
7307     },
7308
7309     // interface method
7310     handleResponse : function(response){
7311
7312     },
7313
7314     // default connection failure
7315     failure : function(response){
7316         
7317         this.response = response;
7318         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7319         this.form.afterAction(this, false);
7320     },
7321
7322     processResponse : function(response){
7323         this.response = response;
7324         if(!response.responseText){
7325             return true;
7326         }
7327         this.result = this.handleResponse(response);
7328         return this.result;
7329     },
7330
7331     // utility functions used internally
7332     getUrl : function(appendParams){
7333         var url = this.options.url || this.form.url || this.form.el.dom.action;
7334         if(appendParams){
7335             var p = this.getParams();
7336             if(p){
7337                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7338             }
7339         }
7340         return url;
7341     },
7342
7343     getMethod : function(){
7344         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7345     },
7346
7347     getParams : function(){
7348         var bp = this.form.baseParams;
7349         var p = this.options.params;
7350         if(p){
7351             if(typeof p == "object"){
7352                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7353             }else if(typeof p == 'string' && bp){
7354                 p += '&' + Roo.urlEncode(bp);
7355             }
7356         }else if(bp){
7357             p = Roo.urlEncode(bp);
7358         }
7359         return p;
7360     },
7361
7362     createCallback : function(){
7363         return {
7364             success: this.success,
7365             failure: this.failure,
7366             scope: this,
7367             timeout: (this.form.timeout*1000),
7368             upload: this.form.fileUpload ? this.success : undefined
7369         };
7370     }
7371 };
7372
7373 Roo.form.Action.Submit = function(form, options){
7374     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7375 };
7376
7377 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7378     type : 'submit',
7379
7380     haveProgress : false,
7381     uploadComplete : false,
7382     
7383     // uploadProgress indicator.
7384     uploadProgress : function()
7385     {
7386         if (!this.form.progressUrl) {
7387             return;
7388         }
7389         
7390         if (!this.haveProgress) {
7391             Roo.MessageBox.progress("Uploading", "Uploading");
7392         }
7393         if (this.uploadComplete) {
7394            Roo.MessageBox.hide();
7395            return;
7396         }
7397         
7398         this.haveProgress = true;
7399    
7400         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7401         
7402         var c = new Roo.data.Connection();
7403         c.request({
7404             url : this.form.progressUrl,
7405             params: {
7406                 id : uid
7407             },
7408             method: 'GET',
7409             success : function(req){
7410                //console.log(data);
7411                 var rdata = false;
7412                 var edata;
7413                 try  {
7414                    rdata = Roo.decode(req.responseText)
7415                 } catch (e) {
7416                     Roo.log("Invalid data from server..");
7417                     Roo.log(edata);
7418                     return;
7419                 }
7420                 if (!rdata || !rdata.success) {
7421                     Roo.log(rdata);
7422                     Roo.MessageBox.alert(Roo.encode(rdata));
7423                     return;
7424                 }
7425                 var data = rdata.data;
7426                 
7427                 if (this.uploadComplete) {
7428                    Roo.MessageBox.hide();
7429                    return;
7430                 }
7431                    
7432                 if (data){
7433                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7434                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7435                     );
7436                 }
7437                 this.uploadProgress.defer(2000,this);
7438             },
7439        
7440             failure: function(data) {
7441                 Roo.log('progress url failed ');
7442                 Roo.log(data);
7443             },
7444             scope : this
7445         });
7446            
7447     },
7448     
7449     
7450     run : function()
7451     {
7452         // run get Values on the form, so it syncs any secondary forms.
7453         this.form.getValues();
7454         
7455         var o = this.options;
7456         var method = this.getMethod();
7457         var isPost = method == 'POST';
7458         if(o.clientValidation === false || this.form.isValid()){
7459             
7460             if (this.form.progressUrl) {
7461                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7462                     (new Date() * 1) + '' + Math.random());
7463                     
7464             } 
7465             
7466             
7467             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7468                 form:this.form.el.dom,
7469                 url:this.getUrl(!isPost),
7470                 method: method,
7471                 params:isPost ? this.getParams() : null,
7472                 isUpload: this.form.fileUpload
7473             }));
7474             
7475             this.uploadProgress();
7476
7477         }else if (o.clientValidation !== false){ // client validation failed
7478             this.failureType = Roo.form.Action.CLIENT_INVALID;
7479             this.form.afterAction(this, false);
7480         }
7481     },
7482
7483     success : function(response)
7484     {
7485         this.uploadComplete= true;
7486         if (this.haveProgress) {
7487             Roo.MessageBox.hide();
7488         }
7489         
7490         
7491         var result = this.processResponse(response);
7492         if(result === true || result.success){
7493             this.form.afterAction(this, true);
7494             return;
7495         }
7496         if(result.errors){
7497             this.form.markInvalid(result.errors);
7498             this.failureType = Roo.form.Action.SERVER_INVALID;
7499         }
7500         this.form.afterAction(this, false);
7501     },
7502     failure : function(response)
7503     {
7504         this.uploadComplete= true;
7505         if (this.haveProgress) {
7506             Roo.MessageBox.hide();
7507         }
7508         
7509         this.response = response;
7510         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7511         this.form.afterAction(this, false);
7512     },
7513     
7514     handleResponse : function(response){
7515         if(this.form.errorReader){
7516             var rs = this.form.errorReader.read(response);
7517             var errors = [];
7518             if(rs.records){
7519                 for(var i = 0, len = rs.records.length; i < len; i++) {
7520                     var r = rs.records[i];
7521                     errors[i] = r.data;
7522                 }
7523             }
7524             if(errors.length < 1){
7525                 errors = null;
7526             }
7527             return {
7528                 success : rs.success,
7529                 errors : errors
7530             };
7531         }
7532         var ret = false;
7533         try {
7534             ret = Roo.decode(response.responseText);
7535         } catch (e) {
7536             ret = {
7537                 success: false,
7538                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7539                 errors : []
7540             };
7541         }
7542         return ret;
7543         
7544     }
7545 });
7546
7547
7548 Roo.form.Action.Load = function(form, options){
7549     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7550     this.reader = this.form.reader;
7551 };
7552
7553 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7554     type : 'load',
7555
7556     run : function(){
7557         
7558         Roo.Ajax.request(Roo.apply(
7559                 this.createCallback(), {
7560                     method:this.getMethod(),
7561                     url:this.getUrl(false),
7562                     params:this.getParams()
7563         }));
7564     },
7565
7566     success : function(response){
7567         
7568         var result = this.processResponse(response);
7569         if(result === true || !result.success || !result.data){
7570             this.failureType = Roo.form.Action.LOAD_FAILURE;
7571             this.form.afterAction(this, false);
7572             return;
7573         }
7574         this.form.clearInvalid();
7575         this.form.setValues(result.data);
7576         this.form.afterAction(this, true);
7577     },
7578
7579     handleResponse : function(response){
7580         if(this.form.reader){
7581             var rs = this.form.reader.read(response);
7582             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7583             return {
7584                 success : rs.success,
7585                 data : data
7586             };
7587         }
7588         return Roo.decode(response.responseText);
7589     }
7590 });
7591
7592 Roo.form.Action.ACTION_TYPES = {
7593     'load' : Roo.form.Action.Load,
7594     'submit' : Roo.form.Action.Submit
7595 };/*
7596  * - LGPL
7597  *
7598  * form
7599  *
7600  */
7601
7602 /**
7603  * @class Roo.bootstrap.Form
7604  * @extends Roo.bootstrap.Component
7605  * Bootstrap Form class
7606  * @cfg {String} method  GET | POST (default POST)
7607  * @cfg {String} labelAlign top | left (default top)
7608  * @cfg {String} align left  | right - for navbars
7609  * @cfg {Boolean} loadMask load mask when submit (default true)
7610
7611  *
7612  * @constructor
7613  * Create a new Form
7614  * @param {Object} config The config object
7615  */
7616
7617
7618 Roo.bootstrap.Form = function(config){
7619     
7620     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7621     
7622     Roo.bootstrap.Form.popover.apply();
7623     
7624     this.addEvents({
7625         /**
7626          * @event clientvalidation
7627          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7628          * @param {Form} this
7629          * @param {Boolean} valid true if the form has passed client-side validation
7630          */
7631         clientvalidation: true,
7632         /**
7633          * @event beforeaction
7634          * Fires before any action is performed. Return false to cancel the action.
7635          * @param {Form} this
7636          * @param {Action} action The action to be performed
7637          */
7638         beforeaction: true,
7639         /**
7640          * @event actionfailed
7641          * Fires when an action fails.
7642          * @param {Form} this
7643          * @param {Action} action The action that failed
7644          */
7645         actionfailed : true,
7646         /**
7647          * @event actioncomplete
7648          * Fires when an action is completed.
7649          * @param {Form} this
7650          * @param {Action} action The action that completed
7651          */
7652         actioncomplete : true
7653     });
7654 };
7655
7656 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7657
7658      /**
7659      * @cfg {String} method
7660      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7661      */
7662     method : 'POST',
7663     /**
7664      * @cfg {String} url
7665      * The URL to use for form actions if one isn't supplied in the action options.
7666      */
7667     /**
7668      * @cfg {Boolean} fileUpload
7669      * Set to true if this form is a file upload.
7670      */
7671
7672     /**
7673      * @cfg {Object} baseParams
7674      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7675      */
7676
7677     /**
7678      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7679      */
7680     timeout: 30,
7681     /**
7682      * @cfg {Sting} align (left|right) for navbar forms
7683      */
7684     align : 'left',
7685
7686     // private
7687     activeAction : null,
7688
7689     /**
7690      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7691      * element by passing it or its id or mask the form itself by passing in true.
7692      * @type Mixed
7693      */
7694     waitMsgTarget : false,
7695
7696     loadMask : true,
7697     
7698     /**
7699      * @cfg {Boolean} errorMask (true|false) default false
7700      */
7701     errorMask : false,
7702     
7703     /**
7704      * @cfg {Number} maskOffset Default 100
7705      */
7706     maskOffset : 100,
7707     
7708     /**
7709      * @cfg {Boolean} maskBody
7710      */
7711     maskBody : false,
7712
7713     getAutoCreate : function(){
7714
7715         var cfg = {
7716             tag: 'form',
7717             method : this.method || 'POST',
7718             id : this.id || Roo.id(),
7719             cls : ''
7720         };
7721         if (this.parent().xtype.match(/^Nav/)) {
7722             cfg.cls = 'navbar-form navbar-' + this.align;
7723
7724         }
7725
7726         if (this.labelAlign == 'left' ) {
7727             cfg.cls += ' form-horizontal';
7728         }
7729
7730
7731         return cfg;
7732     },
7733     initEvents : function()
7734     {
7735         this.el.on('submit', this.onSubmit, this);
7736         // this was added as random key presses on the form where triggering form submit.
7737         this.el.on('keypress', function(e) {
7738             if (e.getCharCode() != 13) {
7739                 return true;
7740             }
7741             // we might need to allow it for textareas.. and some other items.
7742             // check e.getTarget().
7743
7744             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7745                 return true;
7746             }
7747
7748             Roo.log("keypress blocked");
7749
7750             e.preventDefault();
7751             return false;
7752         });
7753         
7754     },
7755     // private
7756     onSubmit : function(e){
7757         e.stopEvent();
7758     },
7759
7760      /**
7761      * Returns true if client-side validation on the form is successful.
7762      * @return Boolean
7763      */
7764     isValid : function(){
7765         var items = this.getItems();
7766         var valid = true;
7767         var target = false;
7768         
7769         items.each(function(f){
7770             if(f.validate()){
7771                 return;
7772             }
7773             valid = false;
7774
7775             if(!target && f.el.isVisible(true)){
7776                 target = f;
7777             }
7778            
7779         });
7780         
7781         if(this.errorMask && !valid){
7782             Roo.bootstrap.Form.popover.mask(this, target);
7783         }
7784         
7785         return valid;
7786     },
7787     
7788     /**
7789      * Returns true if any fields in this form have changed since their original load.
7790      * @return Boolean
7791      */
7792     isDirty : function(){
7793         var dirty = false;
7794         var items = this.getItems();
7795         items.each(function(f){
7796            if(f.isDirty()){
7797                dirty = true;
7798                return false;
7799            }
7800            return true;
7801         });
7802         return dirty;
7803     },
7804      /**
7805      * Performs a predefined action (submit or load) or custom actions you define on this form.
7806      * @param {String} actionName The name of the action type
7807      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7808      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7809      * accept other config options):
7810      * <pre>
7811 Property          Type             Description
7812 ----------------  ---------------  ----------------------------------------------------------------------------------
7813 url               String           The url for the action (defaults to the form's url)
7814 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7815 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7816 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7817                                    validate the form on the client (defaults to false)
7818      * </pre>
7819      * @return {BasicForm} this
7820      */
7821     doAction : function(action, options){
7822         if(typeof action == 'string'){
7823             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7824         }
7825         if(this.fireEvent('beforeaction', this, action) !== false){
7826             this.beforeAction(action);
7827             action.run.defer(100, action);
7828         }
7829         return this;
7830     },
7831
7832     // private
7833     beforeAction : function(action){
7834         var o = action.options;
7835         
7836         if(this.loadMask){
7837             
7838             if(this.maskBody){
7839                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7840             } else {
7841                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7842             }
7843         }
7844         // not really supported yet.. ??
7845
7846         //if(this.waitMsgTarget === true){
7847         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7848         //}else if(this.waitMsgTarget){
7849         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7850         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7851         //}else {
7852         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7853        // }
7854
7855     },
7856
7857     // private
7858     afterAction : function(action, success){
7859         this.activeAction = null;
7860         var o = action.options;
7861
7862         if(this.loadMask){
7863             
7864             if(this.maskBody){
7865                 Roo.get(document.body).unmask();
7866             } else {
7867                 this.el.unmask();
7868             }
7869         }
7870         
7871         //if(this.waitMsgTarget === true){
7872 //            this.el.unmask();
7873         //}else if(this.waitMsgTarget){
7874         //    this.waitMsgTarget.unmask();
7875         //}else{
7876         //    Roo.MessageBox.updateProgress(1);
7877         //    Roo.MessageBox.hide();
7878        // }
7879         //
7880         if(success){
7881             if(o.reset){
7882                 this.reset();
7883             }
7884             Roo.callback(o.success, o.scope, [this, action]);
7885             this.fireEvent('actioncomplete', this, action);
7886
7887         }else{
7888
7889             // failure condition..
7890             // we have a scenario where updates need confirming.
7891             // eg. if a locking scenario exists..
7892             // we look for { errors : { needs_confirm : true }} in the response.
7893             if (
7894                 (typeof(action.result) != 'undefined')  &&
7895                 (typeof(action.result.errors) != 'undefined')  &&
7896                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7897            ){
7898                 var _t = this;
7899                 Roo.log("not supported yet");
7900                  /*
7901
7902                 Roo.MessageBox.confirm(
7903                     "Change requires confirmation",
7904                     action.result.errorMsg,
7905                     function(r) {
7906                         if (r != 'yes') {
7907                             return;
7908                         }
7909                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7910                     }
7911
7912                 );
7913                 */
7914
7915
7916                 return;
7917             }
7918
7919             Roo.callback(o.failure, o.scope, [this, action]);
7920             // show an error message if no failed handler is set..
7921             if (!this.hasListener('actionfailed')) {
7922                 Roo.log("need to add dialog support");
7923                 /*
7924                 Roo.MessageBox.alert("Error",
7925                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7926                         action.result.errorMsg :
7927                         "Saving Failed, please check your entries or try again"
7928                 );
7929                 */
7930             }
7931
7932             this.fireEvent('actionfailed', this, action);
7933         }
7934
7935     },
7936     /**
7937      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7938      * @param {String} id The value to search for
7939      * @return Field
7940      */
7941     findField : function(id){
7942         var items = this.getItems();
7943         var field = items.get(id);
7944         if(!field){
7945              items.each(function(f){
7946                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7947                     field = f;
7948                     return false;
7949                 }
7950                 return true;
7951             });
7952         }
7953         return field || null;
7954     },
7955      /**
7956      * Mark fields in this form invalid in bulk.
7957      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7958      * @return {BasicForm} this
7959      */
7960     markInvalid : function(errors){
7961         if(errors instanceof Array){
7962             for(var i = 0, len = errors.length; i < len; i++){
7963                 var fieldError = errors[i];
7964                 var f = this.findField(fieldError.id);
7965                 if(f){
7966                     f.markInvalid(fieldError.msg);
7967                 }
7968             }
7969         }else{
7970             var field, id;
7971             for(id in errors){
7972                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7973                     field.markInvalid(errors[id]);
7974                 }
7975             }
7976         }
7977         //Roo.each(this.childForms || [], function (f) {
7978         //    f.markInvalid(errors);
7979         //});
7980
7981         return this;
7982     },
7983
7984     /**
7985      * Set values for fields in this form in bulk.
7986      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7987      * @return {BasicForm} this
7988      */
7989     setValues : function(values){
7990         if(values instanceof Array){ // array of objects
7991             for(var i = 0, len = values.length; i < len; i++){
7992                 var v = values[i];
7993                 var f = this.findField(v.id);
7994                 if(f){
7995                     f.setValue(v.value);
7996                     if(this.trackResetOnLoad){
7997                         f.originalValue = f.getValue();
7998                     }
7999                 }
8000             }
8001         }else{ // object hash
8002             var field, id;
8003             for(id in values){
8004                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8005
8006                     if (field.setFromData &&
8007                         field.valueField &&
8008                         field.displayField &&
8009                         // combos' with local stores can
8010                         // be queried via setValue()
8011                         // to set their value..
8012                         (field.store && !field.store.isLocal)
8013                         ) {
8014                         // it's a combo
8015                         var sd = { };
8016                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8017                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8018                         field.setFromData(sd);
8019
8020                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8021                         
8022                         field.setFromData(values);
8023                         
8024                     } else {
8025                         field.setValue(values[id]);
8026                     }
8027
8028
8029                     if(this.trackResetOnLoad){
8030                         field.originalValue = field.getValue();
8031                     }
8032                 }
8033             }
8034         }
8035
8036         //Roo.each(this.childForms || [], function (f) {
8037         //    f.setValues(values);
8038         //});
8039
8040         return this;
8041     },
8042
8043     /**
8044      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8045      * they are returned as an array.
8046      * @param {Boolean} asString
8047      * @return {Object}
8048      */
8049     getValues : function(asString){
8050         //if (this.childForms) {
8051             // copy values from the child forms
8052         //    Roo.each(this.childForms, function (f) {
8053         //        this.setValues(f.getValues());
8054         //    }, this);
8055         //}
8056
8057
8058
8059         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8060         if(asString === true){
8061             return fs;
8062         }
8063         return Roo.urlDecode(fs);
8064     },
8065
8066     /**
8067      * Returns the fields in this form as an object with key/value pairs.
8068      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8069      * @return {Object}
8070      */
8071     getFieldValues : function(with_hidden)
8072     {
8073         var items = this.getItems();
8074         var ret = {};
8075         items.each(function(f){
8076             
8077             if (!f.getName()) {
8078                 return;
8079             }
8080             
8081             var v = f.getValue();
8082             
8083             if (f.inputType =='radio') {
8084                 if (typeof(ret[f.getName()]) == 'undefined') {
8085                     ret[f.getName()] = ''; // empty..
8086                 }
8087
8088                 if (!f.el.dom.checked) {
8089                     return;
8090
8091                 }
8092                 v = f.el.dom.value;
8093
8094             }
8095             
8096             if(f.xtype == 'MoneyField'){
8097                 ret[f.currencyName] = f.getCurrency();
8098             }
8099
8100             // not sure if this supported any more..
8101             if ((typeof(v) == 'object') && f.getRawValue) {
8102                 v = f.getRawValue() ; // dates..
8103             }
8104             // combo boxes where name != hiddenName...
8105             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8106                 ret[f.name] = f.getRawValue();
8107             }
8108             ret[f.getName()] = v;
8109         });
8110
8111         return ret;
8112     },
8113
8114     /**
8115      * Clears all invalid messages in this form.
8116      * @return {BasicForm} this
8117      */
8118     clearInvalid : function(){
8119         var items = this.getItems();
8120
8121         items.each(function(f){
8122            f.clearInvalid();
8123         });
8124
8125         return this;
8126     },
8127
8128     /**
8129      * Resets this form.
8130      * @return {BasicForm} this
8131      */
8132     reset : function(){
8133         var items = this.getItems();
8134         items.each(function(f){
8135             f.reset();
8136         });
8137
8138         Roo.each(this.childForms || [], function (f) {
8139             f.reset();
8140         });
8141
8142
8143         return this;
8144     },
8145     
8146     getItems : function()
8147     {
8148         var r=new Roo.util.MixedCollection(false, function(o){
8149             return o.id || (o.id = Roo.id());
8150         });
8151         var iter = function(el) {
8152             if (el.inputEl) {
8153                 r.add(el);
8154             }
8155             if (!el.items) {
8156                 return;
8157             }
8158             Roo.each(el.items,function(e) {
8159                 iter(e);
8160             });
8161         };
8162
8163         iter(this);
8164         return r;
8165     },
8166     
8167     hideFields : function(items)
8168     {
8169         Roo.each(items, function(i){
8170             
8171             var f = this.findField(i);
8172             
8173             if(!f){
8174                 return;
8175             }
8176             
8177             if(f.xtype == 'DateField'){
8178                 f.setVisible(false);
8179                 return;
8180             }
8181             
8182             f.hide();
8183             
8184         }, this);
8185     },
8186     
8187     showFields : function(items)
8188     {
8189         Roo.each(items, function(i){
8190             
8191             var f = this.findField(i);
8192             
8193             if(!f){
8194                 return;
8195             }
8196             
8197             if(f.xtype == 'DateField'){
8198                 f.setVisible(true);
8199                 return;
8200             }
8201             
8202             f.show();
8203             
8204         }, this);
8205     }
8206
8207 });
8208
8209 Roo.apply(Roo.bootstrap.Form, {
8210     
8211     popover : {
8212         
8213         padding : 5,
8214         
8215         isApplied : false,
8216         
8217         isMasked : false,
8218         
8219         form : false,
8220         
8221         target : false,
8222         
8223         toolTip : false,
8224         
8225         intervalID : false,
8226         
8227         maskEl : false,
8228         
8229         apply : function()
8230         {
8231             if(this.isApplied){
8232                 return;
8233             }
8234             
8235             this.maskEl = {
8236                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8237                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8238                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8239                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8240             };
8241             
8242             this.maskEl.top.enableDisplayMode("block");
8243             this.maskEl.left.enableDisplayMode("block");
8244             this.maskEl.bottom.enableDisplayMode("block");
8245             this.maskEl.right.enableDisplayMode("block");
8246             
8247             this.toolTip = new Roo.bootstrap.Tooltip({
8248                 cls : 'roo-form-error-popover',
8249                 alignment : {
8250                     'left' : ['r-l', [-2,0], 'right'],
8251                     'right' : ['l-r', [2,0], 'left'],
8252                     'bottom' : ['tl-bl', [0,2], 'top'],
8253                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8254                 }
8255             });
8256             
8257             this.toolTip.render(Roo.get(document.body));
8258
8259             this.toolTip.el.enableDisplayMode("block");
8260             
8261             Roo.get(document.body).on('click', function(){
8262                 this.unmask();
8263             }, this);
8264             
8265             Roo.get(document.body).on('touchstart', function(){
8266                 this.unmask();
8267             }, this);
8268             
8269             this.isApplied = true
8270         },
8271         
8272         mask : function(form, target)
8273         {
8274             this.form = form;
8275             
8276             this.target = target;
8277             
8278             if(!this.form.errorMask || !target.el){
8279                 return;
8280             }
8281             
8282             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8283             
8284             Roo.log(scrollable);
8285             
8286             var ot = this.target.el.calcOffsetsTo(scrollable);
8287             
8288             var scrollTo = ot[1] - this.form.maskOffset;
8289             
8290             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8291             
8292             scrollable.scrollTo('top', scrollTo);
8293             
8294             var box = this.target.el.getBox();
8295             Roo.log(box);
8296             var zIndex = Roo.bootstrap.Modal.zIndex++;
8297
8298             
8299             this.maskEl.top.setStyle('position', 'absolute');
8300             this.maskEl.top.setStyle('z-index', zIndex);
8301             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8302             this.maskEl.top.setLeft(0);
8303             this.maskEl.top.setTop(0);
8304             this.maskEl.top.show();
8305             
8306             this.maskEl.left.setStyle('position', 'absolute');
8307             this.maskEl.left.setStyle('z-index', zIndex);
8308             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8309             this.maskEl.left.setLeft(0);
8310             this.maskEl.left.setTop(box.y - this.padding);
8311             this.maskEl.left.show();
8312
8313             this.maskEl.bottom.setStyle('position', 'absolute');
8314             this.maskEl.bottom.setStyle('z-index', zIndex);
8315             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8316             this.maskEl.bottom.setLeft(0);
8317             this.maskEl.bottom.setTop(box.bottom + this.padding);
8318             this.maskEl.bottom.show();
8319
8320             this.maskEl.right.setStyle('position', 'absolute');
8321             this.maskEl.right.setStyle('z-index', zIndex);
8322             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8323             this.maskEl.right.setLeft(box.right + this.padding);
8324             this.maskEl.right.setTop(box.y - this.padding);
8325             this.maskEl.right.show();
8326
8327             this.toolTip.bindEl = this.target.el;
8328
8329             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8330
8331             var tip = this.target.blankText;
8332
8333             if(this.target.getValue() !== '' ) {
8334                 
8335                 if (this.target.invalidText.length) {
8336                     tip = this.target.invalidText;
8337                 } else if (this.target.regexText.length){
8338                     tip = this.target.regexText;
8339                 }
8340             }
8341
8342             this.toolTip.show(tip);
8343
8344             this.intervalID = window.setInterval(function() {
8345                 Roo.bootstrap.Form.popover.unmask();
8346             }, 10000);
8347
8348             window.onwheel = function(){ return false;};
8349             
8350             (function(){ this.isMasked = true; }).defer(500, this);
8351             
8352         },
8353         
8354         unmask : function()
8355         {
8356             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8357                 return;
8358             }
8359             
8360             this.maskEl.top.setStyle('position', 'absolute');
8361             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8362             this.maskEl.top.hide();
8363
8364             this.maskEl.left.setStyle('position', 'absolute');
8365             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8366             this.maskEl.left.hide();
8367
8368             this.maskEl.bottom.setStyle('position', 'absolute');
8369             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8370             this.maskEl.bottom.hide();
8371
8372             this.maskEl.right.setStyle('position', 'absolute');
8373             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8374             this.maskEl.right.hide();
8375             
8376             this.toolTip.hide();
8377             
8378             this.toolTip.el.hide();
8379             
8380             window.onwheel = function(){ return true;};
8381             
8382             if(this.intervalID){
8383                 window.clearInterval(this.intervalID);
8384                 this.intervalID = false;
8385             }
8386             
8387             this.isMasked = false;
8388             
8389         }
8390         
8391     }
8392     
8393 });
8394
8395 /*
8396  * Based on:
8397  * Ext JS Library 1.1.1
8398  * Copyright(c) 2006-2007, Ext JS, LLC.
8399  *
8400  * Originally Released Under LGPL - original licence link has changed is not relivant.
8401  *
8402  * Fork - LGPL
8403  * <script type="text/javascript">
8404  */
8405 /**
8406  * @class Roo.form.VTypes
8407  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8408  * @singleton
8409  */
8410 Roo.form.VTypes = function(){
8411     // closure these in so they are only created once.
8412     var alpha = /^[a-zA-Z_]+$/;
8413     var alphanum = /^[a-zA-Z0-9_]+$/;
8414     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8415     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8416
8417     // All these messages and functions are configurable
8418     return {
8419         /**
8420          * The function used to validate email addresses
8421          * @param {String} value The email address
8422          */
8423         'email' : function(v){
8424             return email.test(v);
8425         },
8426         /**
8427          * The error text to display when the email validation function returns false
8428          * @type String
8429          */
8430         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8431         /**
8432          * The keystroke filter mask to be applied on email input
8433          * @type RegExp
8434          */
8435         'emailMask' : /[a-z0-9_\.\-@]/i,
8436
8437         /**
8438          * The function used to validate URLs
8439          * @param {String} value The URL
8440          */
8441         'url' : function(v){
8442             return url.test(v);
8443         },
8444         /**
8445          * The error text to display when the url validation function returns false
8446          * @type String
8447          */
8448         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8449         
8450         /**
8451          * The function used to validate alpha values
8452          * @param {String} value The value
8453          */
8454         'alpha' : function(v){
8455             return alpha.test(v);
8456         },
8457         /**
8458          * The error text to display when the alpha validation function returns false
8459          * @type String
8460          */
8461         'alphaText' : 'This field should only contain letters and _',
8462         /**
8463          * The keystroke filter mask to be applied on alpha input
8464          * @type RegExp
8465          */
8466         'alphaMask' : /[a-z_]/i,
8467
8468         /**
8469          * The function used to validate alphanumeric values
8470          * @param {String} value The value
8471          */
8472         'alphanum' : function(v){
8473             return alphanum.test(v);
8474         },
8475         /**
8476          * The error text to display when the alphanumeric validation function returns false
8477          * @type String
8478          */
8479         'alphanumText' : 'This field should only contain letters, numbers and _',
8480         /**
8481          * The keystroke filter mask to be applied on alphanumeric input
8482          * @type RegExp
8483          */
8484         'alphanumMask' : /[a-z0-9_]/i
8485     };
8486 }();/*
8487  * - LGPL
8488  *
8489  * Input
8490  * 
8491  */
8492
8493 /**
8494  * @class Roo.bootstrap.Input
8495  * @extends Roo.bootstrap.Component
8496  * Bootstrap Input class
8497  * @cfg {Boolean} disabled is it disabled
8498  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8499  * @cfg {String} name name of the input
8500  * @cfg {string} fieldLabel - the label associated
8501  * @cfg {string} placeholder - placeholder to put in text.
8502  * @cfg {string}  before - input group add on before
8503  * @cfg {string} after - input group add on after
8504  * @cfg {string} size - (lg|sm) or leave empty..
8505  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8506  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8507  * @cfg {Number} md colspan out of 12 for computer-sized screens
8508  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8509  * @cfg {string} value default value of the input
8510  * @cfg {Number} labelWidth set the width of label 
8511  * @cfg {Number} labellg set the width of label (1-12)
8512  * @cfg {Number} labelmd set the width of label (1-12)
8513  * @cfg {Number} labelsm set the width of label (1-12)
8514  * @cfg {Number} labelxs set the width of label (1-12)
8515  * @cfg {String} labelAlign (top|left)
8516  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8517  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8518  * @cfg {String} indicatorpos (left|right) default left
8519
8520  * @cfg {String} align (left|center|right) Default left
8521  * @cfg {Boolean} forceFeedback (true|false) Default false
8522  * @cfg {Boolean} hideParent (true|false) Default false also hide the parent
8523  * 
8524  * 
8525  * 
8526  * @constructor
8527  * Create a new Input
8528  * @param {Object} config The config object
8529  */
8530
8531 Roo.bootstrap.Input = function(config){
8532     
8533     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8534     
8535     this.addEvents({
8536         /**
8537          * @event focus
8538          * Fires when this field receives input focus.
8539          * @param {Roo.form.Field} this
8540          */
8541         focus : true,
8542         /**
8543          * @event blur
8544          * Fires when this field loses input focus.
8545          * @param {Roo.form.Field} this
8546          */
8547         blur : true,
8548         /**
8549          * @event specialkey
8550          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8551          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8552          * @param {Roo.form.Field} this
8553          * @param {Roo.EventObject} e The event object
8554          */
8555         specialkey : true,
8556         /**
8557          * @event change
8558          * Fires just before the field blurs if the field value has changed.
8559          * @param {Roo.form.Field} this
8560          * @param {Mixed} newValue The new value
8561          * @param {Mixed} oldValue The original value
8562          */
8563         change : true,
8564         /**
8565          * @event invalid
8566          * Fires after the field has been marked as invalid.
8567          * @param {Roo.form.Field} this
8568          * @param {String} msg The validation message
8569          */
8570         invalid : true,
8571         /**
8572          * @event valid
8573          * Fires after the field has been validated with no errors.
8574          * @param {Roo.form.Field} this
8575          */
8576         valid : true,
8577          /**
8578          * @event keyup
8579          * Fires after the key up
8580          * @param {Roo.form.Field} this
8581          * @param {Roo.EventObject}  e The event Object
8582          */
8583         keyup : true
8584     });
8585 };
8586
8587 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8588      /**
8589      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8590       automatic validation (defaults to "keyup").
8591      */
8592     validationEvent : "keyup",
8593      /**
8594      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8595      */
8596     validateOnBlur : true,
8597     /**
8598      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8599      */
8600     validationDelay : 250,
8601      /**
8602      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8603      */
8604     focusClass : "x-form-focus",  // not needed???
8605     
8606        
8607     /**
8608      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8609      */
8610     invalidClass : "has-warning",
8611     
8612     /**
8613      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8614      */
8615     validClass : "has-success",
8616     
8617     /**
8618      * @cfg {Boolean} hasFeedback (true|false) default true
8619      */
8620     hasFeedback : true,
8621     
8622     /**
8623      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8624      */
8625     invalidFeedbackClass : "glyphicon-warning-sign",
8626     
8627     /**
8628      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8629      */
8630     validFeedbackClass : "glyphicon-ok",
8631     
8632     /**
8633      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8634      */
8635     selectOnFocus : false,
8636     
8637      /**
8638      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8639      */
8640     maskRe : null,
8641        /**
8642      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8643      */
8644     vtype : null,
8645     
8646       /**
8647      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8648      */
8649     disableKeyFilter : false,
8650     
8651        /**
8652      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8653      */
8654     disabled : false,
8655      /**
8656      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8657      */
8658     allowBlank : true,
8659     /**
8660      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8661      */
8662     blankText : "Please complete this mandatory field",
8663     
8664      /**
8665      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8666      */
8667     minLength : 0,
8668     /**
8669      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8670      */
8671     maxLength : Number.MAX_VALUE,
8672     /**
8673      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8674      */
8675     minLengthText : "The minimum length for this field is {0}",
8676     /**
8677      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8678      */
8679     maxLengthText : "The maximum length for this field is {0}",
8680   
8681     
8682     /**
8683      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8684      * If available, this function will be called only after the basic validators all return true, and will be passed the
8685      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8686      */
8687     validator : null,
8688     /**
8689      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8690      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8691      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8692      */
8693     regex : null,
8694     /**
8695      * @cfg {String} regexText -- Depricated - use Invalid Text
8696      */
8697     regexText : "",
8698     
8699     /**
8700      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8701      */
8702     invalidText : "",
8703     
8704     
8705     
8706     autocomplete: false,
8707     
8708     
8709     fieldLabel : '',
8710     inputType : 'text',
8711     
8712     name : false,
8713     placeholder: false,
8714     before : false,
8715     after : false,
8716     size : false,
8717     hasFocus : false,
8718     preventMark: false,
8719     isFormField : true,
8720     value : '',
8721     labelWidth : 2,
8722     labelAlign : false,
8723     readOnly : false,
8724     align : false,
8725     formatedValue : false,
8726     forceFeedback : false,
8727     
8728     indicatorpos : 'left',
8729     
8730     labellg : 0,
8731     labelmd : 0,
8732     labelsm : 0,
8733     labelxs : 0,
8734     
8735     hideParent : false,
8736     
8737     parentLabelAlign : function()
8738     {
8739         var parent = this;
8740         while (parent.parent()) {
8741             parent = parent.parent();
8742             if (typeof(parent.labelAlign) !='undefined') {
8743                 return parent.labelAlign;
8744             }
8745         }
8746         return 'left';
8747         
8748     },
8749     
8750     getAutoCreate : function()
8751     {
8752         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8753         
8754         var id = Roo.id();
8755         
8756         var cfg = {};
8757         
8758         if(this.inputType != 'hidden'){
8759             cfg.cls = 'form-group' //input-group
8760         }
8761         
8762         var input =  {
8763             tag: 'input',
8764             id : id,
8765             type : this.inputType,
8766             value : this.value,
8767             cls : 'form-control',
8768             placeholder : this.placeholder || '',
8769             autocomplete : this.autocomplete || 'new-password'
8770         };
8771         
8772         if(this.align){
8773             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8774         }
8775         
8776         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8777             input.maxLength = this.maxLength;
8778         }
8779         
8780         if (this.disabled) {
8781             input.disabled=true;
8782         }
8783         
8784         if (this.readOnly) {
8785             input.readonly=true;
8786         }
8787         
8788         if (this.name) {
8789             input.name = this.name;
8790         }
8791         
8792         if (this.size) {
8793             input.cls += ' input-' + this.size;
8794         }
8795         
8796         var settings=this;
8797         ['xs','sm','md','lg'].map(function(size){
8798             if (settings[size]) {
8799                 cfg.cls += ' col-' + size + '-' + settings[size];
8800             }
8801         });
8802         
8803         var inputblock = input;
8804         
8805         var feedback = {
8806             tag: 'span',
8807             cls: 'glyphicon form-control-feedback'
8808         };
8809             
8810         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8811             
8812             inputblock = {
8813                 cls : 'has-feedback',
8814                 cn :  [
8815                     input,
8816                     feedback
8817                 ] 
8818             };  
8819         }
8820         
8821         if (this.before || this.after) {
8822             
8823             inputblock = {
8824                 cls : 'input-group',
8825                 cn :  [] 
8826             };
8827             
8828             if (this.before && typeof(this.before) == 'string') {
8829                 
8830                 inputblock.cn.push({
8831                     tag :'span',
8832                     cls : 'roo-input-before input-group-addon',
8833                     html : this.before
8834                 });
8835             }
8836             if (this.before && typeof(this.before) == 'object') {
8837                 this.before = Roo.factory(this.before);
8838                 
8839                 inputblock.cn.push({
8840                     tag :'span',
8841                     cls : 'roo-input-before input-group-' +
8842                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8843                 });
8844             }
8845             
8846             inputblock.cn.push(input);
8847             
8848             if (this.after && typeof(this.after) == 'string') {
8849                 inputblock.cn.push({
8850                     tag :'span',
8851                     cls : 'roo-input-after input-group-addon',
8852                     html : this.after
8853                 });
8854             }
8855             if (this.after && typeof(this.after) == 'object') {
8856                 this.after = Roo.factory(this.after);
8857                 
8858                 inputblock.cn.push({
8859                     tag :'span',
8860                     cls : 'roo-input-after input-group-' +
8861                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8862                 });
8863             }
8864             
8865             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8866                 inputblock.cls += ' has-feedback';
8867                 inputblock.cn.push(feedback);
8868             }
8869         };
8870         
8871         if (align ==='left' && this.fieldLabel.length) {
8872             
8873             cfg.cls += ' roo-form-group-label-left';
8874             
8875             cfg.cn = [
8876                 {
8877                     tag : 'i',
8878                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8879                     tooltip : 'This field is required'
8880                 },
8881                 {
8882                     tag: 'label',
8883                     'for' :  id,
8884                     cls : 'control-label',
8885                     html : this.fieldLabel
8886
8887                 },
8888                 {
8889                     cls : "", 
8890                     cn: [
8891                         inputblock
8892                     ]
8893                 }
8894             ];
8895             
8896             var labelCfg = cfg.cn[1];
8897             var contentCfg = cfg.cn[2];
8898             
8899             if(this.indicatorpos == 'right'){
8900                 cfg.cn = [
8901                     {
8902                         tag: 'label',
8903                         'for' :  id,
8904                         cls : 'control-label',
8905                         cn : [
8906                             {
8907                                 tag : 'span',
8908                                 html : this.fieldLabel
8909                             },
8910                             {
8911                                 tag : 'i',
8912                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8913                                 tooltip : 'This field is required'
8914                             }
8915                         ]
8916                     },
8917                     {
8918                         cls : "",
8919                         cn: [
8920                             inputblock
8921                         ]
8922                     }
8923
8924                 ];
8925                 
8926                 labelCfg = cfg.cn[0];
8927                 contentCfg = cfg.cn[1];
8928             
8929             }
8930             
8931             if(this.labelWidth > 12){
8932                 labelCfg.style = "width: " + this.labelWidth + 'px';
8933             }
8934             
8935             if(this.labelWidth < 13 && this.labelmd == 0){
8936                 this.labelmd = this.labelWidth;
8937             }
8938             
8939             if(this.labellg > 0){
8940                 labelCfg.cls += ' col-lg-' + this.labellg;
8941                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
8942             }
8943             
8944             if(this.labelmd > 0){
8945                 labelCfg.cls += ' col-md-' + this.labelmd;
8946                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
8947             }
8948             
8949             if(this.labelsm > 0){
8950                 labelCfg.cls += ' col-sm-' + this.labelsm;
8951                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
8952             }
8953             
8954             if(this.labelxs > 0){
8955                 labelCfg.cls += ' col-xs-' + this.labelxs;
8956                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
8957             }
8958             
8959             
8960         } else if ( this.fieldLabel.length) {
8961                 
8962             cfg.cn = [
8963                 {
8964                     tag : 'i',
8965                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8966                     tooltip : 'This field is required'
8967                 },
8968                 {
8969                     tag: 'label',
8970                    //cls : 'input-group-addon',
8971                     html : this.fieldLabel
8972
8973                 },
8974
8975                inputblock
8976
8977            ];
8978            
8979            if(this.indicatorpos == 'right'){
8980                 
8981                 cfg.cn = [
8982                     {
8983                         tag: 'label',
8984                        //cls : 'input-group-addon',
8985                         html : this.fieldLabel
8986
8987                     },
8988                     {
8989                         tag : 'i',
8990                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8991                         tooltip : 'This field is required'
8992                     },
8993
8994                    inputblock
8995
8996                ];
8997
8998             }
8999
9000         } else {
9001             
9002             cfg.cn = [
9003
9004                     inputblock
9005
9006             ];
9007                 
9008                 
9009         };
9010         
9011         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9012            cfg.cls += ' navbar-form';
9013         }
9014         
9015         if (this.parentType === 'NavGroup') {
9016            cfg.cls += ' navbar-form';
9017            cfg.tag = 'li';
9018         }
9019         
9020         return cfg;
9021         
9022     },
9023     /**
9024      * return the real input element.
9025      */
9026     inputEl: function ()
9027     {
9028         return this.el.select('input.form-control',true).first();
9029     },
9030     
9031     tooltipEl : function()
9032     {
9033         return this.inputEl();
9034     },
9035     
9036     indicatorEl : function()
9037     {
9038         var indicator = this.el.select('i.roo-required-indicator',true).first();
9039         
9040         if(!indicator){
9041             return false;
9042         }
9043         
9044         return indicator;
9045         
9046     },
9047     
9048     setDisabled : function(v)
9049     {
9050         var i  = this.inputEl().dom;
9051         if (!v) {
9052             i.removeAttribute('disabled');
9053             return;
9054             
9055         }
9056         i.setAttribute('disabled','true');
9057     },
9058     initEvents : function()
9059     {
9060           
9061         this.inputEl().on("keydown" , this.fireKey,  this);
9062         this.inputEl().on("focus", this.onFocus,  this);
9063         this.inputEl().on("blur", this.onBlur,  this);
9064         
9065         this.inputEl().relayEvent('keyup', this);
9066         
9067         this.indicator = this.indicatorEl();
9068         
9069         if(this.indicator){
9070             this.indicator.addClass('invisible');
9071             
9072         }
9073  
9074         // reference to original value for reset
9075         this.originalValue = this.getValue();
9076         //Roo.form.TextField.superclass.initEvents.call(this);
9077         if(this.validationEvent == 'keyup'){
9078             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9079             this.inputEl().on('keyup', this.filterValidation, this);
9080         }
9081         else if(this.validationEvent !== false){
9082             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9083         }
9084         
9085         if(this.selectOnFocus){
9086             this.on("focus", this.preFocus, this);
9087             
9088         }
9089         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9090             this.inputEl().on("keypress", this.filterKeys, this);
9091         } else {
9092             this.inputEl().relayEvent('keypress', this);
9093         }
9094        /* if(this.grow){
9095             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9096             this.el.on("click", this.autoSize,  this);
9097         }
9098         */
9099         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9100             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9101         }
9102         
9103         if (typeof(this.before) == 'object') {
9104             this.before.render(this.el.select('.roo-input-before',true).first());
9105         }
9106         if (typeof(this.after) == 'object') {
9107             this.after.render(this.el.select('.roo-input-after',true).first());
9108         }
9109         
9110         
9111     },
9112     filterValidation : function(e){
9113         if(!e.isNavKeyPress()){
9114             this.validationTask.delay(this.validationDelay);
9115         }
9116     },
9117      /**
9118      * Validates the field value
9119      * @return {Boolean} True if the value is valid, else false
9120      */
9121     validate : function(){
9122         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9123         if(this.disabled || this.validateValue(this.getRawValue())){
9124             this.markValid();
9125             return true;
9126         }
9127         
9128         this.markInvalid();
9129         return false;
9130     },
9131     
9132     
9133     /**
9134      * Validates a value according to the field's validation rules and marks the field as invalid
9135      * if the validation fails
9136      * @param {Mixed} value The value to validate
9137      * @return {Boolean} True if the value is valid, else false
9138      */
9139     validateValue : function(value)
9140     {
9141         if(this.getEl().hasClass('hidden') || !this.inputEl().isVisible(true)){
9142             return true;
9143         }
9144         
9145         if(value.length < 1)  { // if it's blank
9146             if(this.allowBlank){
9147                 return true;
9148             }
9149             return false;
9150         }
9151         
9152         if(value.length < this.minLength){
9153             return false;
9154         }
9155         if(value.length > this.maxLength){
9156             return false;
9157         }
9158         if(this.vtype){
9159             var vt = Roo.form.VTypes;
9160             if(!vt[this.vtype](value, this)){
9161                 return false;
9162             }
9163         }
9164         if(typeof this.validator == "function"){
9165             var msg = this.validator(value);
9166             if(msg !== true){
9167                 return false;
9168             }
9169             if (typeof(msg) == 'string') {
9170                 this.invalidText = msg;
9171             }
9172         }
9173         
9174         if(this.regex && !this.regex.test(value)){
9175             return false;
9176         }
9177         
9178         return true;
9179     },
9180     
9181      // private
9182     fireKey : function(e){
9183         //Roo.log('field ' + e.getKey());
9184         if(e.isNavKeyPress()){
9185             this.fireEvent("specialkey", this, e);
9186         }
9187     },
9188     focus : function (selectText){
9189         if(this.rendered){
9190             this.inputEl().focus();
9191             if(selectText === true){
9192                 this.inputEl().dom.select();
9193             }
9194         }
9195         return this;
9196     } ,
9197     
9198     onFocus : function(){
9199         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9200            // this.el.addClass(this.focusClass);
9201         }
9202         if(!this.hasFocus){
9203             this.hasFocus = true;
9204             this.startValue = this.getValue();
9205             this.fireEvent("focus", this);
9206         }
9207     },
9208     
9209     beforeBlur : Roo.emptyFn,
9210
9211     
9212     // private
9213     onBlur : function(){
9214         this.beforeBlur();
9215         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9216             //this.el.removeClass(this.focusClass);
9217         }
9218         this.hasFocus = false;
9219         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9220             this.validate();
9221         }
9222         var v = this.getValue();
9223         if(String(v) !== String(this.startValue)){
9224             this.fireEvent('change', this, v, this.startValue);
9225         }
9226         this.fireEvent("blur", this);
9227     },
9228     
9229     /**
9230      * Resets the current field value to the originally loaded value and clears any validation messages
9231      */
9232     reset : function(){
9233         this.setValue(this.originalValue);
9234         this.validate();
9235     },
9236      /**
9237      * Returns the name of the field
9238      * @return {Mixed} name The name field
9239      */
9240     getName: function(){
9241         return this.name;
9242     },
9243      /**
9244      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9245      * @return {Mixed} value The field value
9246      */
9247     getValue : function(){
9248         
9249         var v = this.inputEl().getValue();
9250         
9251         return v;
9252     },
9253     /**
9254      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9255      * @return {Mixed} value The field value
9256      */
9257     getRawValue : function(){
9258         var v = this.inputEl().getValue();
9259         
9260         return v;
9261     },
9262     
9263     /**
9264      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9265      * @param {Mixed} value The value to set
9266      */
9267     setRawValue : function(v){
9268         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9269     },
9270     
9271     selectText : function(start, end){
9272         var v = this.getRawValue();
9273         if(v.length > 0){
9274             start = start === undefined ? 0 : start;
9275             end = end === undefined ? v.length : end;
9276             var d = this.inputEl().dom;
9277             if(d.setSelectionRange){
9278                 d.setSelectionRange(start, end);
9279             }else if(d.createTextRange){
9280                 var range = d.createTextRange();
9281                 range.moveStart("character", start);
9282                 range.moveEnd("character", v.length-end);
9283                 range.select();
9284             }
9285         }
9286     },
9287     
9288     /**
9289      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9290      * @param {Mixed} value The value to set
9291      */
9292     setValue : function(v){
9293         this.value = v;
9294         if(this.rendered){
9295             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9296             this.validate();
9297         }
9298     },
9299     
9300     /*
9301     processValue : function(value){
9302         if(this.stripCharsRe){
9303             var newValue = value.replace(this.stripCharsRe, '');
9304             if(newValue !== value){
9305                 this.setRawValue(newValue);
9306                 return newValue;
9307             }
9308         }
9309         return value;
9310     },
9311   */
9312     preFocus : function(){
9313         
9314         if(this.selectOnFocus){
9315             this.inputEl().dom.select();
9316         }
9317     },
9318     filterKeys : function(e){
9319         var k = e.getKey();
9320         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9321             return;
9322         }
9323         var c = e.getCharCode(), cc = String.fromCharCode(c);
9324         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9325             return;
9326         }
9327         if(!this.maskRe.test(cc)){
9328             e.stopEvent();
9329         }
9330     },
9331      /**
9332      * Clear any invalid styles/messages for this field
9333      */
9334     clearInvalid : function(){
9335         
9336         if(!this.el || this.preventMark){ // not rendered
9337             return;
9338         }
9339         
9340      
9341         this.el.removeClass(this.invalidClass);
9342         
9343         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9344             
9345             var feedback = this.el.select('.form-control-feedback', true).first();
9346             
9347             if(feedback){
9348                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9349             }
9350             
9351         }
9352         
9353         this.fireEvent('valid', this);
9354     },
9355     
9356      /**
9357      * Mark this field as valid
9358      */
9359     markValid : function()
9360     {
9361         if(!this.el  || this.preventMark){ // not rendered...
9362             return;
9363         }
9364         
9365         this.el.removeClass([this.invalidClass, this.validClass]);
9366         
9367         var feedback = this.el.select('.form-control-feedback', true).first();
9368             
9369         if(feedback){
9370             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9371         }
9372         
9373         if(this.indicator){
9374             this.indicator.removeClass('visible');
9375             this.indicator.addClass('invisible');
9376         }
9377         
9378         if(this.disabled){
9379             return;
9380         }
9381         
9382         if(this.allowBlank && !this.getRawValue().length){
9383             return;
9384         }
9385         
9386         this.el.addClass(this.validClass);
9387         
9388         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9389             
9390             var feedback = this.el.select('.form-control-feedback', true).first();
9391             
9392             if(feedback){
9393                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9394                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9395             }
9396             
9397         }
9398         
9399         this.fireEvent('valid', this);
9400     },
9401     
9402      /**
9403      * Mark this field as invalid
9404      * @param {String} msg The validation message
9405      */
9406     markInvalid : function(msg)
9407     {
9408         if(!this.el  || this.preventMark){ // not rendered
9409             return;
9410         }
9411         
9412         this.el.removeClass([this.invalidClass, this.validClass]);
9413         
9414         var feedback = this.el.select('.form-control-feedback', true).first();
9415             
9416         if(feedback){
9417             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9418         }
9419
9420         if(this.disabled){
9421             return;
9422         }
9423         
9424         if(this.allowBlank && !this.getRawValue().length){
9425             return;
9426         }
9427         
9428         if(this.indicator){
9429             this.indicator.removeClass('invisible');
9430             this.indicator.addClass('visible');
9431         }
9432         
9433         this.el.addClass(this.invalidClass);
9434         
9435         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9436             
9437             var feedback = this.el.select('.form-control-feedback', true).first();
9438             
9439             if(feedback){
9440                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9441                 
9442                 if(this.getValue().length || this.forceFeedback){
9443                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9444                 }
9445                 
9446             }
9447             
9448         }
9449         
9450         this.fireEvent('invalid', this, msg);
9451     },
9452     // private
9453     SafariOnKeyDown : function(event)
9454     {
9455         // this is a workaround for a password hang bug on chrome/ webkit.
9456         if (this.inputEl().dom.type != 'password') {
9457             return;
9458         }
9459         
9460         var isSelectAll = false;
9461         
9462         if(this.inputEl().dom.selectionEnd > 0){
9463             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9464         }
9465         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9466             event.preventDefault();
9467             this.setValue('');
9468             return;
9469         }
9470         
9471         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9472             
9473             event.preventDefault();
9474             // this is very hacky as keydown always get's upper case.
9475             //
9476             var cc = String.fromCharCode(event.getCharCode());
9477             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9478             
9479         }
9480     },
9481     adjustWidth : function(tag, w){
9482         tag = tag.toLowerCase();
9483         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9484             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9485                 if(tag == 'input'){
9486                     return w + 2;
9487                 }
9488                 if(tag == 'textarea'){
9489                     return w-2;
9490                 }
9491             }else if(Roo.isOpera){
9492                 if(tag == 'input'){
9493                     return w + 2;
9494                 }
9495                 if(tag == 'textarea'){
9496                     return w-2;
9497                 }
9498             }
9499         }
9500         return w;
9501     },
9502     
9503     setFieldLabel : function(v)
9504     {
9505         if(!this.rendered){
9506             return;
9507         }
9508         
9509         if(this.indicator){
9510             var ar = this.el.select('label > span',true);
9511             
9512             if (ar.elements.length) {
9513                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9514                 this.fieldLabel = v;
9515                 return;
9516             }
9517             
9518             var br = this.el.select('label',true);
9519             
9520             if(br.elements.length) {
9521                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9522                 this.fieldLabel = v;
9523                 return;
9524             }
9525             
9526             Roo.log('Cannot Found any of label > span || label in input');
9527             return;
9528         }
9529         
9530         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9531         this.fieldLabel = v;
9532         
9533         
9534     }
9535 });
9536
9537  
9538 /*
9539  * - LGPL
9540  *
9541  * Input
9542  * 
9543  */
9544
9545 /**
9546  * @class Roo.bootstrap.TextArea
9547  * @extends Roo.bootstrap.Input
9548  * Bootstrap TextArea class
9549  * @cfg {Number} cols Specifies the visible width of a text area
9550  * @cfg {Number} rows Specifies the visible number of lines in a text area
9551  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9552  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9553  * @cfg {string} html text
9554  * 
9555  * @constructor
9556  * Create a new TextArea
9557  * @param {Object} config The config object
9558  */
9559
9560 Roo.bootstrap.TextArea = function(config){
9561     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9562    
9563 };
9564
9565 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9566      
9567     cols : false,
9568     rows : 5,
9569     readOnly : false,
9570     warp : 'soft',
9571     resize : false,
9572     value: false,
9573     html: false,
9574     
9575     getAutoCreate : function(){
9576         
9577         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9578         
9579         var id = Roo.id();
9580         
9581         var cfg = {};
9582         
9583         if(this.inputType != 'hidden'){
9584             cfg.cls = 'form-group' //input-group
9585         }
9586         
9587         var input =  {
9588             tag: 'textarea',
9589             id : id,
9590             warp : this.warp,
9591             rows : this.rows,
9592             value : this.value || '',
9593             html: this.html || '',
9594             cls : 'form-control',
9595             placeholder : this.placeholder || '' 
9596             
9597         };
9598         
9599         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9600             input.maxLength = this.maxLength;
9601         }
9602         
9603         if(this.resize){
9604             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9605         }
9606         
9607         if(this.cols){
9608             input.cols = this.cols;
9609         }
9610         
9611         if (this.readOnly) {
9612             input.readonly = true;
9613         }
9614         
9615         if (this.name) {
9616             input.name = this.name;
9617         }
9618         
9619         if (this.size) {
9620             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9621         }
9622         
9623         var settings=this;
9624         ['xs','sm','md','lg'].map(function(size){
9625             if (settings[size]) {
9626                 cfg.cls += ' col-' + size + '-' + settings[size];
9627             }
9628         });
9629         
9630         var inputblock = input;
9631         
9632         if(this.hasFeedback && !this.allowBlank){
9633             
9634             var feedback = {
9635                 tag: 'span',
9636                 cls: 'glyphicon form-control-feedback'
9637             };
9638
9639             inputblock = {
9640                 cls : 'has-feedback',
9641                 cn :  [
9642                     input,
9643                     feedback
9644                 ] 
9645             };  
9646         }
9647         
9648         
9649         if (this.before || this.after) {
9650             
9651             inputblock = {
9652                 cls : 'input-group',
9653                 cn :  [] 
9654             };
9655             if (this.before) {
9656                 inputblock.cn.push({
9657                     tag :'span',
9658                     cls : 'input-group-addon',
9659                     html : this.before
9660                 });
9661             }
9662             
9663             inputblock.cn.push(input);
9664             
9665             if(this.hasFeedback && !this.allowBlank){
9666                 inputblock.cls += ' has-feedback';
9667                 inputblock.cn.push(feedback);
9668             }
9669             
9670             if (this.after) {
9671                 inputblock.cn.push({
9672                     tag :'span',
9673                     cls : 'input-group-addon',
9674                     html : this.after
9675                 });
9676             }
9677             
9678         }
9679         
9680         if (align ==='left' && this.fieldLabel.length) {
9681             cfg.cn = [
9682                 {
9683                     tag: 'label',
9684                     'for' :  id,
9685                     cls : 'control-label',
9686                     html : this.fieldLabel
9687                 },
9688                 {
9689                     cls : "",
9690                     cn: [
9691                         inputblock
9692                     ]
9693                 }
9694
9695             ];
9696             
9697             if(this.labelWidth > 12){
9698                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9699             }
9700
9701             if(this.labelWidth < 13 && this.labelmd == 0){
9702                 this.labelmd = this.labelWidth;
9703             }
9704
9705             if(this.labellg > 0){
9706                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9707                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9708             }
9709
9710             if(this.labelmd > 0){
9711                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9712                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9713             }
9714
9715             if(this.labelsm > 0){
9716                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9717                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9718             }
9719
9720             if(this.labelxs > 0){
9721                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9722                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9723             }
9724             
9725         } else if ( this.fieldLabel.length) {
9726             cfg.cn = [
9727
9728                {
9729                    tag: 'label',
9730                    //cls : 'input-group-addon',
9731                    html : this.fieldLabel
9732
9733                },
9734
9735                inputblock
9736
9737            ];
9738
9739         } else {
9740
9741             cfg.cn = [
9742
9743                 inputblock
9744
9745             ];
9746                 
9747         }
9748         
9749         if (this.disabled) {
9750             input.disabled=true;
9751         }
9752         
9753         return cfg;
9754         
9755     },
9756     /**
9757      * return the real textarea element.
9758      */
9759     inputEl: function ()
9760     {
9761         return this.el.select('textarea.form-control',true).first();
9762     },
9763     
9764     /**
9765      * Clear any invalid styles/messages for this field
9766      */
9767     clearInvalid : function()
9768     {
9769         
9770         if(!this.el || this.preventMark){ // not rendered
9771             return;
9772         }
9773         
9774         var label = this.el.select('label', true).first();
9775         var icon = this.el.select('i.fa-star', true).first();
9776         
9777         if(label && icon){
9778             icon.remove();
9779         }
9780         
9781         this.el.removeClass(this.invalidClass);
9782         
9783         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9784             
9785             var feedback = this.el.select('.form-control-feedback', true).first();
9786             
9787             if(feedback){
9788                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9789             }
9790             
9791         }
9792         
9793         this.fireEvent('valid', this);
9794     },
9795     
9796      /**
9797      * Mark this field as valid
9798      */
9799     markValid : function()
9800     {
9801         if(!this.el  || this.preventMark){ // not rendered
9802             return;
9803         }
9804         
9805         this.el.removeClass([this.invalidClass, this.validClass]);
9806         
9807         var feedback = this.el.select('.form-control-feedback', true).first();
9808             
9809         if(feedback){
9810             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9811         }
9812
9813         if(this.disabled || this.allowBlank){
9814             return;
9815         }
9816         
9817         var label = this.el.select('label', true).first();
9818         var icon = this.el.select('i.fa-star', true).first();
9819         
9820         if(label && icon){
9821             icon.remove();
9822         }
9823         
9824         this.el.addClass(this.validClass);
9825         
9826         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9827             
9828             var feedback = this.el.select('.form-control-feedback', true).first();
9829             
9830             if(feedback){
9831                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9832                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9833             }
9834             
9835         }
9836         
9837         this.fireEvent('valid', this);
9838     },
9839     
9840      /**
9841      * Mark this field as invalid
9842      * @param {String} msg The validation message
9843      */
9844     markInvalid : function(msg)
9845     {
9846         if(!this.el  || this.preventMark){ // not rendered
9847             return;
9848         }
9849         
9850         this.el.removeClass([this.invalidClass, this.validClass]);
9851         
9852         var feedback = this.el.select('.form-control-feedback', true).first();
9853             
9854         if(feedback){
9855             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9856         }
9857
9858         if(this.disabled || this.allowBlank){
9859             return;
9860         }
9861         
9862         var label = this.el.select('label', true).first();
9863         var icon = this.el.select('i.fa-star', true).first();
9864         
9865         if(!this.getValue().length && label && !icon){
9866             this.el.createChild({
9867                 tag : 'i',
9868                 cls : 'text-danger fa fa-lg fa-star',
9869                 tooltip : 'This field is required',
9870                 style : 'margin-right:5px;'
9871             }, label, true);
9872         }
9873
9874         this.el.addClass(this.invalidClass);
9875         
9876         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9877             
9878             var feedback = this.el.select('.form-control-feedback', true).first();
9879             
9880             if(feedback){
9881                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9882                 
9883                 if(this.getValue().length || this.forceFeedback){
9884                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9885                 }
9886                 
9887             }
9888             
9889         }
9890         
9891         this.fireEvent('invalid', this, msg);
9892     }
9893 });
9894
9895  
9896 /*
9897  * - LGPL
9898  *
9899  * trigger field - base class for combo..
9900  * 
9901  */
9902  
9903 /**
9904  * @class Roo.bootstrap.TriggerField
9905  * @extends Roo.bootstrap.Input
9906  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9907  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9908  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9909  * for which you can provide a custom implementation.  For example:
9910  * <pre><code>
9911 var trigger = new Roo.bootstrap.TriggerField();
9912 trigger.onTriggerClick = myTriggerFn;
9913 trigger.applyTo('my-field');
9914 </code></pre>
9915  *
9916  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9917  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9918  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9919  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9920  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9921
9922  * @constructor
9923  * Create a new TriggerField.
9924  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9925  * to the base TextField)
9926  */
9927 Roo.bootstrap.TriggerField = function(config){
9928     this.mimicing = false;
9929     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9930 };
9931
9932 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9933     /**
9934      * @cfg {String} triggerClass A CSS class to apply to the trigger
9935      */
9936      /**
9937      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9938      */
9939     hideTrigger:false,
9940
9941     /**
9942      * @cfg {Boolean} removable (true|false) special filter default false
9943      */
9944     removable : false,
9945     
9946     /** @cfg {Boolean} grow @hide */
9947     /** @cfg {Number} growMin @hide */
9948     /** @cfg {Number} growMax @hide */
9949
9950     /**
9951      * @hide 
9952      * @method
9953      */
9954     autoSize: Roo.emptyFn,
9955     // private
9956     monitorTab : true,
9957     // private
9958     deferHeight : true,
9959
9960     
9961     actionMode : 'wrap',
9962     
9963     caret : false,
9964     
9965     
9966     getAutoCreate : function(){
9967        
9968         var align = this.labelAlign || this.parentLabelAlign();
9969         
9970         var id = Roo.id();
9971         
9972         var cfg = {
9973             cls: 'form-group' //input-group
9974         };
9975         
9976         
9977         var input =  {
9978             tag: 'input',
9979             id : id,
9980             type : this.inputType,
9981             cls : 'form-control',
9982             autocomplete: 'new-password',
9983             placeholder : this.placeholder || '' 
9984             
9985         };
9986         if (this.name) {
9987             input.name = this.name;
9988         }
9989         if (this.size) {
9990             input.cls += ' input-' + this.size;
9991         }
9992         
9993         if (this.disabled) {
9994             input.disabled=true;
9995         }
9996         
9997         var inputblock = input;
9998         
9999         if(this.hasFeedback && !this.allowBlank){
10000             
10001             var feedback = {
10002                 tag: 'span',
10003                 cls: 'glyphicon form-control-feedback'
10004             };
10005             
10006             if(this.removable && !this.editable && !this.tickable){
10007                 inputblock = {
10008                     cls : 'has-feedback',
10009                     cn :  [
10010                         inputblock,
10011                         {
10012                             tag: 'button',
10013                             html : 'x',
10014                             cls : 'roo-combo-removable-btn close'
10015                         },
10016                         feedback
10017                     ] 
10018                 };
10019             } else {
10020                 inputblock = {
10021                     cls : 'has-feedback',
10022                     cn :  [
10023                         inputblock,
10024                         feedback
10025                     ] 
10026                 };
10027             }
10028
10029         } else {
10030             if(this.removable && !this.editable && !this.tickable){
10031                 inputblock = {
10032                     cls : 'roo-removable',
10033                     cn :  [
10034                         inputblock,
10035                         {
10036                             tag: 'button',
10037                             html : 'x',
10038                             cls : 'roo-combo-removable-btn close'
10039                         }
10040                     ] 
10041                 };
10042             }
10043         }
10044         
10045         if (this.before || this.after) {
10046             
10047             inputblock = {
10048                 cls : 'input-group',
10049                 cn :  [] 
10050             };
10051             if (this.before) {
10052                 inputblock.cn.push({
10053                     tag :'span',
10054                     cls : 'input-group-addon',
10055                     html : this.before
10056                 });
10057             }
10058             
10059             inputblock.cn.push(input);
10060             
10061             if(this.hasFeedback && !this.allowBlank){
10062                 inputblock.cls += ' has-feedback';
10063                 inputblock.cn.push(feedback);
10064             }
10065             
10066             if (this.after) {
10067                 inputblock.cn.push({
10068                     tag :'span',
10069                     cls : 'input-group-addon',
10070                     html : this.after
10071                 });
10072             }
10073             
10074         };
10075         
10076         var box = {
10077             tag: 'div',
10078             cn: [
10079                 {
10080                     tag: 'input',
10081                     type : 'hidden',
10082                     cls: 'form-hidden-field'
10083                 },
10084                 inputblock
10085             ]
10086             
10087         };
10088         
10089         if(this.multiple){
10090             box = {
10091                 tag: 'div',
10092                 cn: [
10093                     {
10094                         tag: 'input',
10095                         type : 'hidden',
10096                         cls: 'form-hidden-field'
10097                     },
10098                     {
10099                         tag: 'ul',
10100                         cls: 'roo-select2-choices',
10101                         cn:[
10102                             {
10103                                 tag: 'li',
10104                                 cls: 'roo-select2-search-field',
10105                                 cn: [
10106
10107                                     inputblock
10108                                 ]
10109                             }
10110                         ]
10111                     }
10112                 ]
10113             }
10114         };
10115         
10116         var combobox = {
10117             cls: 'roo-select2-container input-group',
10118             cn: [
10119                 box
10120 //                {
10121 //                    tag: 'ul',
10122 //                    cls: 'typeahead typeahead-long dropdown-menu',
10123 //                    style: 'display:none'
10124 //                }
10125             ]
10126         };
10127         
10128         if(!this.multiple && this.showToggleBtn){
10129             
10130             var caret = {
10131                         tag: 'span',
10132                         cls: 'caret'
10133              };
10134             if (this.caret != false) {
10135                 caret = {
10136                      tag: 'i',
10137                      cls: 'fa fa-' + this.caret
10138                 };
10139                 
10140             }
10141             
10142             combobox.cn.push({
10143                 tag :'span',
10144                 cls : 'input-group-addon btn dropdown-toggle',
10145                 cn : [
10146                     caret,
10147                     {
10148                         tag: 'span',
10149                         cls: 'combobox-clear',
10150                         cn  : [
10151                             {
10152                                 tag : 'i',
10153                                 cls: 'icon-remove'
10154                             }
10155                         ]
10156                     }
10157                 ]
10158
10159             })
10160         }
10161         
10162         if(this.multiple){
10163             combobox.cls += ' roo-select2-container-multi';
10164         }
10165         
10166         if (align ==='left' && this.fieldLabel.length) {
10167             
10168             cfg.cls += ' roo-form-group-label-left';
10169
10170             cfg.cn = [
10171                 {
10172                     tag : 'i',
10173                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10174                     tooltip : 'This field is required'
10175                 },
10176                 {
10177                     tag: 'label',
10178                     'for' :  id,
10179                     cls : 'control-label',
10180                     html : this.fieldLabel
10181
10182                 },
10183                 {
10184                     cls : "", 
10185                     cn: [
10186                         combobox
10187                     ]
10188                 }
10189
10190             ];
10191             
10192             var labelCfg = cfg.cn[1];
10193             var contentCfg = cfg.cn[2];
10194             
10195             if(this.indicatorpos == 'right'){
10196                 cfg.cn = [
10197                     {
10198                         tag: 'label',
10199                         'for' :  id,
10200                         cls : 'control-label',
10201                         cn : [
10202                             {
10203                                 tag : 'span',
10204                                 html : this.fieldLabel
10205                             },
10206                             {
10207                                 tag : 'i',
10208                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10209                                 tooltip : 'This field is required'
10210                             }
10211                         ]
10212                     },
10213                     {
10214                         cls : "", 
10215                         cn: [
10216                             combobox
10217                         ]
10218                     }
10219
10220                 ];
10221                 
10222                 labelCfg = cfg.cn[0];
10223                 contentCfg = cfg.cn[1];
10224             }
10225             
10226             if(this.labelWidth > 12){
10227                 labelCfg.style = "width: " + this.labelWidth + 'px';
10228             }
10229             
10230             if(this.labelWidth < 13 && this.labelmd == 0){
10231                 this.labelmd = this.labelWidth;
10232             }
10233             
10234             if(this.labellg > 0){
10235                 labelCfg.cls += ' col-lg-' + this.labellg;
10236                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10237             }
10238             
10239             if(this.labelmd > 0){
10240                 labelCfg.cls += ' col-md-' + this.labelmd;
10241                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10242             }
10243             
10244             if(this.labelsm > 0){
10245                 labelCfg.cls += ' col-sm-' + this.labelsm;
10246                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10247             }
10248             
10249             if(this.labelxs > 0){
10250                 labelCfg.cls += ' col-xs-' + this.labelxs;
10251                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10252             }
10253             
10254         } else if ( this.fieldLabel.length) {
10255 //                Roo.log(" label");
10256             cfg.cn = [
10257                 {
10258                    tag : 'i',
10259                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10260                    tooltip : 'This field is required'
10261                },
10262                {
10263                    tag: 'label',
10264                    //cls : 'input-group-addon',
10265                    html : this.fieldLabel
10266
10267                },
10268
10269                combobox
10270
10271             ];
10272             
10273             if(this.indicatorpos == 'right'){
10274                 
10275                 cfg.cn = [
10276                     {
10277                        tag: 'label',
10278                        cn : [
10279                            {
10280                                tag : 'span',
10281                                html : this.fieldLabel
10282                            },
10283                            {
10284                               tag : 'i',
10285                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10286                               tooltip : 'This field is required'
10287                            }
10288                        ]
10289
10290                     },
10291                     combobox
10292
10293                 ];
10294
10295             }
10296
10297         } else {
10298             
10299 //                Roo.log(" no label && no align");
10300                 cfg = combobox
10301                      
10302                 
10303         }
10304         
10305         var settings=this;
10306         ['xs','sm','md','lg'].map(function(size){
10307             if (settings[size]) {
10308                 cfg.cls += ' col-' + size + '-' + settings[size];
10309             }
10310         });
10311         
10312         return cfg;
10313         
10314     },
10315     
10316     
10317     
10318     // private
10319     onResize : function(w, h){
10320 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10321 //        if(typeof w == 'number'){
10322 //            var x = w - this.trigger.getWidth();
10323 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10324 //            this.trigger.setStyle('left', x+'px');
10325 //        }
10326     },
10327
10328     // private
10329     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10330
10331     // private
10332     getResizeEl : function(){
10333         return this.inputEl();
10334     },
10335
10336     // private
10337     getPositionEl : function(){
10338         return this.inputEl();
10339     },
10340
10341     // private
10342     alignErrorIcon : function(){
10343         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10344     },
10345
10346     // private
10347     initEvents : function(){
10348         
10349         this.createList();
10350         
10351         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10352         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10353         if(!this.multiple && this.showToggleBtn){
10354             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10355             if(this.hideTrigger){
10356                 this.trigger.setDisplayed(false);
10357             }
10358             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10359         }
10360         
10361         if(this.multiple){
10362             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10363         }
10364         
10365         if(this.removable && !this.editable && !this.tickable){
10366             var close = this.closeTriggerEl();
10367             
10368             if(close){
10369                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10370                 close.on('click', this.removeBtnClick, this, close);
10371             }
10372         }
10373         
10374         //this.trigger.addClassOnOver('x-form-trigger-over');
10375         //this.trigger.addClassOnClick('x-form-trigger-click');
10376         
10377         //if(!this.width){
10378         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10379         //}
10380     },
10381     
10382     closeTriggerEl : function()
10383     {
10384         var close = this.el.select('.roo-combo-removable-btn', true).first();
10385         return close ? close : false;
10386     },
10387     
10388     removeBtnClick : function(e, h, el)
10389     {
10390         e.preventDefault();
10391         
10392         if(this.fireEvent("remove", this) !== false){
10393             this.reset();
10394             this.fireEvent("afterremove", this)
10395         }
10396     },
10397     
10398     createList : function()
10399     {
10400         this.list = Roo.get(document.body).createChild({
10401             tag: 'ul',
10402             cls: 'typeahead typeahead-long dropdown-menu',
10403             style: 'display:none'
10404         });
10405         
10406         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10407         
10408     },
10409
10410     // private
10411     initTrigger : function(){
10412        
10413     },
10414
10415     // private
10416     onDestroy : function(){
10417         if(this.trigger){
10418             this.trigger.removeAllListeners();
10419           //  this.trigger.remove();
10420         }
10421         //if(this.wrap){
10422         //    this.wrap.remove();
10423         //}
10424         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10425     },
10426
10427     // private
10428     onFocus : function(){
10429         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10430         /*
10431         if(!this.mimicing){
10432             this.wrap.addClass('x-trigger-wrap-focus');
10433             this.mimicing = true;
10434             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10435             if(this.monitorTab){
10436                 this.el.on("keydown", this.checkTab, this);
10437             }
10438         }
10439         */
10440     },
10441
10442     // private
10443     checkTab : function(e){
10444         if(e.getKey() == e.TAB){
10445             this.triggerBlur();
10446         }
10447     },
10448
10449     // private
10450     onBlur : function(){
10451         // do nothing
10452     },
10453
10454     // private
10455     mimicBlur : function(e, t){
10456         /*
10457         if(!this.wrap.contains(t) && this.validateBlur()){
10458             this.triggerBlur();
10459         }
10460         */
10461     },
10462
10463     // private
10464     triggerBlur : function(){
10465         this.mimicing = false;
10466         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10467         if(this.monitorTab){
10468             this.el.un("keydown", this.checkTab, this);
10469         }
10470         //this.wrap.removeClass('x-trigger-wrap-focus');
10471         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10472     },
10473
10474     // private
10475     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10476     validateBlur : function(e, t){
10477         return true;
10478     },
10479
10480     // private
10481     onDisable : function(){
10482         this.inputEl().dom.disabled = true;
10483         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10484         //if(this.wrap){
10485         //    this.wrap.addClass('x-item-disabled');
10486         //}
10487     },
10488
10489     // private
10490     onEnable : function(){
10491         this.inputEl().dom.disabled = false;
10492         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10493         //if(this.wrap){
10494         //    this.el.removeClass('x-item-disabled');
10495         //}
10496     },
10497
10498     // private
10499     onShow : function(){
10500         var ae = this.getActionEl();
10501         
10502         if(ae){
10503             ae.dom.style.display = '';
10504             ae.dom.style.visibility = 'visible';
10505         }
10506     },
10507
10508     // private
10509     
10510     onHide : function(){
10511         var ae = this.getActionEl();
10512         ae.dom.style.display = 'none';
10513     },
10514
10515     /**
10516      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10517      * by an implementing function.
10518      * @method
10519      * @param {EventObject} e
10520      */
10521     onTriggerClick : Roo.emptyFn
10522 });
10523  /*
10524  * Based on:
10525  * Ext JS Library 1.1.1
10526  * Copyright(c) 2006-2007, Ext JS, LLC.
10527  *
10528  * Originally Released Under LGPL - original licence link has changed is not relivant.
10529  *
10530  * Fork - LGPL
10531  * <script type="text/javascript">
10532  */
10533
10534
10535 /**
10536  * @class Roo.data.SortTypes
10537  * @singleton
10538  * Defines the default sorting (casting?) comparison functions used when sorting data.
10539  */
10540 Roo.data.SortTypes = {
10541     /**
10542      * Default sort that does nothing
10543      * @param {Mixed} s The value being converted
10544      * @return {Mixed} The comparison value
10545      */
10546     none : function(s){
10547         return s;
10548     },
10549     
10550     /**
10551      * The regular expression used to strip tags
10552      * @type {RegExp}
10553      * @property
10554      */
10555     stripTagsRE : /<\/?[^>]+>/gi,
10556     
10557     /**
10558      * Strips all HTML tags to sort on text only
10559      * @param {Mixed} s The value being converted
10560      * @return {String} The comparison value
10561      */
10562     asText : function(s){
10563         return String(s).replace(this.stripTagsRE, "");
10564     },
10565     
10566     /**
10567      * Strips all HTML tags to sort on text only - Case insensitive
10568      * @param {Mixed} s The value being converted
10569      * @return {String} The comparison value
10570      */
10571     asUCText : function(s){
10572         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10573     },
10574     
10575     /**
10576      * Case insensitive string
10577      * @param {Mixed} s The value being converted
10578      * @return {String} The comparison value
10579      */
10580     asUCString : function(s) {
10581         return String(s).toUpperCase();
10582     },
10583     
10584     /**
10585      * Date sorting
10586      * @param {Mixed} s The value being converted
10587      * @return {Number} The comparison value
10588      */
10589     asDate : function(s) {
10590         if(!s){
10591             return 0;
10592         }
10593         if(s instanceof Date){
10594             return s.getTime();
10595         }
10596         return Date.parse(String(s));
10597     },
10598     
10599     /**
10600      * Float sorting
10601      * @param {Mixed} s The value being converted
10602      * @return {Float} The comparison value
10603      */
10604     asFloat : function(s) {
10605         var val = parseFloat(String(s).replace(/,/g, ""));
10606         if(isNaN(val)) {
10607             val = 0;
10608         }
10609         return val;
10610     },
10611     
10612     /**
10613      * Integer sorting
10614      * @param {Mixed} s The value being converted
10615      * @return {Number} The comparison value
10616      */
10617     asInt : function(s) {
10618         var val = parseInt(String(s).replace(/,/g, ""));
10619         if(isNaN(val)) {
10620             val = 0;
10621         }
10622         return val;
10623     }
10624 };/*
10625  * Based on:
10626  * Ext JS Library 1.1.1
10627  * Copyright(c) 2006-2007, Ext JS, LLC.
10628  *
10629  * Originally Released Under LGPL - original licence link has changed is not relivant.
10630  *
10631  * Fork - LGPL
10632  * <script type="text/javascript">
10633  */
10634
10635 /**
10636 * @class Roo.data.Record
10637  * Instances of this class encapsulate both record <em>definition</em> information, and record
10638  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10639  * to access Records cached in an {@link Roo.data.Store} object.<br>
10640  * <p>
10641  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10642  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10643  * objects.<br>
10644  * <p>
10645  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10646  * @constructor
10647  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10648  * {@link #create}. The parameters are the same.
10649  * @param {Array} data An associative Array of data values keyed by the field name.
10650  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10651  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10652  * not specified an integer id is generated.
10653  */
10654 Roo.data.Record = function(data, id){
10655     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10656     this.data = data;
10657 };
10658
10659 /**
10660  * Generate a constructor for a specific record layout.
10661  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10662  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10663  * Each field definition object may contain the following properties: <ul>
10664  * <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,
10665  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10666  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10667  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10668  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10669  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10670  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10671  * this may be omitted.</p></li>
10672  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10673  * <ul><li>auto (Default, implies no conversion)</li>
10674  * <li>string</li>
10675  * <li>int</li>
10676  * <li>float</li>
10677  * <li>boolean</li>
10678  * <li>date</li></ul></p></li>
10679  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10680  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10681  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10682  * by the Reader into an object that will be stored in the Record. It is passed the
10683  * following parameters:<ul>
10684  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10685  * </ul></p></li>
10686  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10687  * </ul>
10688  * <br>usage:<br><pre><code>
10689 var TopicRecord = Roo.data.Record.create(
10690     {name: 'title', mapping: 'topic_title'},
10691     {name: 'author', mapping: 'username'},
10692     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10693     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10694     {name: 'lastPoster', mapping: 'user2'},
10695     {name: 'excerpt', mapping: 'post_text'}
10696 );
10697
10698 var myNewRecord = new TopicRecord({
10699     title: 'Do my job please',
10700     author: 'noobie',
10701     totalPosts: 1,
10702     lastPost: new Date(),
10703     lastPoster: 'Animal',
10704     excerpt: 'No way dude!'
10705 });
10706 myStore.add(myNewRecord);
10707 </code></pre>
10708  * @method create
10709  * @static
10710  */
10711 Roo.data.Record.create = function(o){
10712     var f = function(){
10713         f.superclass.constructor.apply(this, arguments);
10714     };
10715     Roo.extend(f, Roo.data.Record);
10716     var p = f.prototype;
10717     p.fields = new Roo.util.MixedCollection(false, function(field){
10718         return field.name;
10719     });
10720     for(var i = 0, len = o.length; i < len; i++){
10721         p.fields.add(new Roo.data.Field(o[i]));
10722     }
10723     f.getField = function(name){
10724         return p.fields.get(name);  
10725     };
10726     return f;
10727 };
10728
10729 Roo.data.Record.AUTO_ID = 1000;
10730 Roo.data.Record.EDIT = 'edit';
10731 Roo.data.Record.REJECT = 'reject';
10732 Roo.data.Record.COMMIT = 'commit';
10733
10734 Roo.data.Record.prototype = {
10735     /**
10736      * Readonly flag - true if this record has been modified.
10737      * @type Boolean
10738      */
10739     dirty : false,
10740     editing : false,
10741     error: null,
10742     modified: null,
10743
10744     // private
10745     join : function(store){
10746         this.store = store;
10747     },
10748
10749     /**
10750      * Set the named field to the specified value.
10751      * @param {String} name The name of the field to set.
10752      * @param {Object} value The value to set the field to.
10753      */
10754     set : function(name, value){
10755         if(this.data[name] == value){
10756             return;
10757         }
10758         this.dirty = true;
10759         if(!this.modified){
10760             this.modified = {};
10761         }
10762         if(typeof this.modified[name] == 'undefined'){
10763             this.modified[name] = this.data[name];
10764         }
10765         this.data[name] = value;
10766         if(!this.editing && this.store){
10767             this.store.afterEdit(this);
10768         }       
10769     },
10770
10771     /**
10772      * Get the value of the named field.
10773      * @param {String} name The name of the field to get the value of.
10774      * @return {Object} The value of the field.
10775      */
10776     get : function(name){
10777         return this.data[name]; 
10778     },
10779
10780     // private
10781     beginEdit : function(){
10782         this.editing = true;
10783         this.modified = {}; 
10784     },
10785
10786     // private
10787     cancelEdit : function(){
10788         this.editing = false;
10789         delete this.modified;
10790     },
10791
10792     // private
10793     endEdit : function(){
10794         this.editing = false;
10795         if(this.dirty && this.store){
10796             this.store.afterEdit(this);
10797         }
10798     },
10799
10800     /**
10801      * Usually called by the {@link Roo.data.Store} which owns the Record.
10802      * Rejects all changes made to the Record since either creation, or the last commit operation.
10803      * Modified fields are reverted to their original values.
10804      * <p>
10805      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10806      * of reject operations.
10807      */
10808     reject : function(){
10809         var m = this.modified;
10810         for(var n in m){
10811             if(typeof m[n] != "function"){
10812                 this.data[n] = m[n];
10813             }
10814         }
10815         this.dirty = false;
10816         delete this.modified;
10817         this.editing = false;
10818         if(this.store){
10819             this.store.afterReject(this);
10820         }
10821     },
10822
10823     /**
10824      * Usually called by the {@link Roo.data.Store} which owns the Record.
10825      * Commits all changes made to the Record since either creation, or the last commit operation.
10826      * <p>
10827      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10828      * of commit operations.
10829      */
10830     commit : function(){
10831         this.dirty = false;
10832         delete this.modified;
10833         this.editing = false;
10834         if(this.store){
10835             this.store.afterCommit(this);
10836         }
10837     },
10838
10839     // private
10840     hasError : function(){
10841         return this.error != null;
10842     },
10843
10844     // private
10845     clearError : function(){
10846         this.error = null;
10847     },
10848
10849     /**
10850      * Creates a copy of this record.
10851      * @param {String} id (optional) A new record id if you don't want to use this record's id
10852      * @return {Record}
10853      */
10854     copy : function(newId) {
10855         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10856     }
10857 };/*
10858  * Based on:
10859  * Ext JS Library 1.1.1
10860  * Copyright(c) 2006-2007, Ext JS, LLC.
10861  *
10862  * Originally Released Under LGPL - original licence link has changed is not relivant.
10863  *
10864  * Fork - LGPL
10865  * <script type="text/javascript">
10866  */
10867
10868
10869
10870 /**
10871  * @class Roo.data.Store
10872  * @extends Roo.util.Observable
10873  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10874  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10875  * <p>
10876  * 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
10877  * has no knowledge of the format of the data returned by the Proxy.<br>
10878  * <p>
10879  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10880  * instances from the data object. These records are cached and made available through accessor functions.
10881  * @constructor
10882  * Creates a new Store.
10883  * @param {Object} config A config object containing the objects needed for the Store to access data,
10884  * and read the data into Records.
10885  */
10886 Roo.data.Store = function(config){
10887     this.data = new Roo.util.MixedCollection(false);
10888     this.data.getKey = function(o){
10889         return o.id;
10890     };
10891     this.baseParams = {};
10892     // private
10893     this.paramNames = {
10894         "start" : "start",
10895         "limit" : "limit",
10896         "sort" : "sort",
10897         "dir" : "dir",
10898         "multisort" : "_multisort"
10899     };
10900
10901     if(config && config.data){
10902         this.inlineData = config.data;
10903         delete config.data;
10904     }
10905
10906     Roo.apply(this, config);
10907     
10908     if(this.reader){ // reader passed
10909         this.reader = Roo.factory(this.reader, Roo.data);
10910         this.reader.xmodule = this.xmodule || false;
10911         if(!this.recordType){
10912             this.recordType = this.reader.recordType;
10913         }
10914         if(this.reader.onMetaChange){
10915             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10916         }
10917     }
10918
10919     if(this.recordType){
10920         this.fields = this.recordType.prototype.fields;
10921     }
10922     this.modified = [];
10923
10924     this.addEvents({
10925         /**
10926          * @event datachanged
10927          * Fires when the data cache has changed, and a widget which is using this Store
10928          * as a Record cache should refresh its view.
10929          * @param {Store} this
10930          */
10931         datachanged : true,
10932         /**
10933          * @event metachange
10934          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10935          * @param {Store} this
10936          * @param {Object} meta The JSON metadata
10937          */
10938         metachange : true,
10939         /**
10940          * @event add
10941          * Fires when Records have been added to the Store
10942          * @param {Store} this
10943          * @param {Roo.data.Record[]} records The array of Records added
10944          * @param {Number} index The index at which the record(s) were added
10945          */
10946         add : true,
10947         /**
10948          * @event remove
10949          * Fires when a Record has been removed from the Store
10950          * @param {Store} this
10951          * @param {Roo.data.Record} record The Record that was removed
10952          * @param {Number} index The index at which the record was removed
10953          */
10954         remove : true,
10955         /**
10956          * @event update
10957          * Fires when a Record has been updated
10958          * @param {Store} this
10959          * @param {Roo.data.Record} record The Record that was updated
10960          * @param {String} operation The update operation being performed.  Value may be one of:
10961          * <pre><code>
10962  Roo.data.Record.EDIT
10963  Roo.data.Record.REJECT
10964  Roo.data.Record.COMMIT
10965          * </code></pre>
10966          */
10967         update : true,
10968         /**
10969          * @event clear
10970          * Fires when the data cache has been cleared.
10971          * @param {Store} this
10972          */
10973         clear : true,
10974         /**
10975          * @event beforeload
10976          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10977          * the load action will be canceled.
10978          * @param {Store} this
10979          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10980          */
10981         beforeload : true,
10982         /**
10983          * @event beforeloadadd
10984          * Fires after a new set of Records has been loaded.
10985          * @param {Store} this
10986          * @param {Roo.data.Record[]} records The Records that were loaded
10987          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10988          */
10989         beforeloadadd : true,
10990         /**
10991          * @event load
10992          * Fires after a new set of Records has been loaded, before they are added to the store.
10993          * @param {Store} this
10994          * @param {Roo.data.Record[]} records The Records that were loaded
10995          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10996          * @params {Object} return from reader
10997          */
10998         load : true,
10999         /**
11000          * @event loadexception
11001          * Fires if an exception occurs in the Proxy during loading.
11002          * Called with the signature of the Proxy's "loadexception" event.
11003          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11004          * 
11005          * @param {Proxy} 
11006          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11007          * @param {Object} load options 
11008          * @param {Object} jsonData from your request (normally this contains the Exception)
11009          */
11010         loadexception : true
11011     });
11012     
11013     if(this.proxy){
11014         this.proxy = Roo.factory(this.proxy, Roo.data);
11015         this.proxy.xmodule = this.xmodule || false;
11016         this.relayEvents(this.proxy,  ["loadexception"]);
11017     }
11018     this.sortToggle = {};
11019     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11020
11021     Roo.data.Store.superclass.constructor.call(this);
11022
11023     if(this.inlineData){
11024         this.loadData(this.inlineData);
11025         delete this.inlineData;
11026     }
11027 };
11028
11029 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11030      /**
11031     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11032     * without a remote query - used by combo/forms at present.
11033     */
11034     
11035     /**
11036     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11037     */
11038     /**
11039     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11040     */
11041     /**
11042     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11043     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11044     */
11045     /**
11046     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11047     * on any HTTP request
11048     */
11049     /**
11050     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11051     */
11052     /**
11053     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11054     */
11055     multiSort: false,
11056     /**
11057     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11058     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11059     */
11060     remoteSort : false,
11061
11062     /**
11063     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11064      * loaded or when a record is removed. (defaults to false).
11065     */
11066     pruneModifiedRecords : false,
11067
11068     // private
11069     lastOptions : null,
11070
11071     /**
11072      * Add Records to the Store and fires the add event.
11073      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11074      */
11075     add : function(records){
11076         records = [].concat(records);
11077         for(var i = 0, len = records.length; i < len; i++){
11078             records[i].join(this);
11079         }
11080         var index = this.data.length;
11081         this.data.addAll(records);
11082         this.fireEvent("add", this, records, index);
11083     },
11084
11085     /**
11086      * Remove a Record from the Store and fires the remove event.
11087      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11088      */
11089     remove : function(record){
11090         var index = this.data.indexOf(record);
11091         this.data.removeAt(index);
11092         if(this.pruneModifiedRecords){
11093             this.modified.remove(record);
11094         }
11095         this.fireEvent("remove", this, record, index);
11096     },
11097
11098     /**
11099      * Remove all Records from the Store and fires the clear event.
11100      */
11101     removeAll : function(){
11102         this.data.clear();
11103         if(this.pruneModifiedRecords){
11104             this.modified = [];
11105         }
11106         this.fireEvent("clear", this);
11107     },
11108
11109     /**
11110      * Inserts Records to the Store at the given index and fires the add event.
11111      * @param {Number} index The start index at which to insert the passed Records.
11112      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11113      */
11114     insert : function(index, records){
11115         records = [].concat(records);
11116         for(var i = 0, len = records.length; i < len; i++){
11117             this.data.insert(index, records[i]);
11118             records[i].join(this);
11119         }
11120         this.fireEvent("add", this, records, index);
11121     },
11122
11123     /**
11124      * Get the index within the cache of the passed Record.
11125      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11126      * @return {Number} The index of the passed Record. Returns -1 if not found.
11127      */
11128     indexOf : function(record){
11129         return this.data.indexOf(record);
11130     },
11131
11132     /**
11133      * Get the index within the cache of the Record with the passed id.
11134      * @param {String} id The id of the Record to find.
11135      * @return {Number} The index of the Record. Returns -1 if not found.
11136      */
11137     indexOfId : function(id){
11138         return this.data.indexOfKey(id);
11139     },
11140
11141     /**
11142      * Get the Record with the specified id.
11143      * @param {String} id The id of the Record to find.
11144      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11145      */
11146     getById : function(id){
11147         return this.data.key(id);
11148     },
11149
11150     /**
11151      * Get the Record at the specified index.
11152      * @param {Number} index The index of the Record to find.
11153      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11154      */
11155     getAt : function(index){
11156         return this.data.itemAt(index);
11157     },
11158
11159     /**
11160      * Returns a range of Records between specified indices.
11161      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11162      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11163      * @return {Roo.data.Record[]} An array of Records
11164      */
11165     getRange : function(start, end){
11166         return this.data.getRange(start, end);
11167     },
11168
11169     // private
11170     storeOptions : function(o){
11171         o = Roo.apply({}, o);
11172         delete o.callback;
11173         delete o.scope;
11174         this.lastOptions = o;
11175     },
11176
11177     /**
11178      * Loads the Record cache from the configured Proxy using the configured Reader.
11179      * <p>
11180      * If using remote paging, then the first load call must specify the <em>start</em>
11181      * and <em>limit</em> properties in the options.params property to establish the initial
11182      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11183      * <p>
11184      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11185      * and this call will return before the new data has been loaded. Perform any post-processing
11186      * in a callback function, or in a "load" event handler.</strong>
11187      * <p>
11188      * @param {Object} options An object containing properties which control loading options:<ul>
11189      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11190      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11191      * passed the following arguments:<ul>
11192      * <li>r : Roo.data.Record[]</li>
11193      * <li>options: Options object from the load call</li>
11194      * <li>success: Boolean success indicator</li></ul></li>
11195      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11196      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11197      * </ul>
11198      */
11199     load : function(options){
11200         options = options || {};
11201         if(this.fireEvent("beforeload", this, options) !== false){
11202             this.storeOptions(options);
11203             var p = Roo.apply(options.params || {}, this.baseParams);
11204             // if meta was not loaded from remote source.. try requesting it.
11205             if (!this.reader.metaFromRemote) {
11206                 p._requestMeta = 1;
11207             }
11208             if(this.sortInfo && this.remoteSort){
11209                 var pn = this.paramNames;
11210                 p[pn["sort"]] = this.sortInfo.field;
11211                 p[pn["dir"]] = this.sortInfo.direction;
11212             }
11213             if (this.multiSort) {
11214                 var pn = this.paramNames;
11215                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11216             }
11217             
11218             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11219         }
11220     },
11221
11222     /**
11223      * Reloads the Record cache from the configured Proxy using the configured Reader and
11224      * the options from the last load operation performed.
11225      * @param {Object} options (optional) An object containing properties which may override the options
11226      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11227      * the most recently used options are reused).
11228      */
11229     reload : function(options){
11230         this.load(Roo.applyIf(options||{}, this.lastOptions));
11231     },
11232
11233     // private
11234     // Called as a callback by the Reader during a load operation.
11235     loadRecords : function(o, options, success){
11236         if(!o || success === false){
11237             if(success !== false){
11238                 this.fireEvent("load", this, [], options, o);
11239             }
11240             if(options.callback){
11241                 options.callback.call(options.scope || this, [], options, false);
11242             }
11243             return;
11244         }
11245         // if data returned failure - throw an exception.
11246         if (o.success === false) {
11247             // show a message if no listener is registered.
11248             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11249                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11250             }
11251             // loadmask wil be hooked into this..
11252             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11253             return;
11254         }
11255         var r = o.records, t = o.totalRecords || r.length;
11256         
11257         this.fireEvent("beforeloadadd", this, r, options, o);
11258         
11259         if(!options || options.add !== true){
11260             if(this.pruneModifiedRecords){
11261                 this.modified = [];
11262             }
11263             for(var i = 0, len = r.length; i < len; i++){
11264                 r[i].join(this);
11265             }
11266             if(this.snapshot){
11267                 this.data = this.snapshot;
11268                 delete this.snapshot;
11269             }
11270             this.data.clear();
11271             this.data.addAll(r);
11272             this.totalLength = t;
11273             this.applySort();
11274             this.fireEvent("datachanged", this);
11275         }else{
11276             this.totalLength = Math.max(t, this.data.length+r.length);
11277             this.add(r);
11278         }
11279         
11280         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11281                 
11282             var e = new Roo.data.Record({});
11283
11284             e.set(this.parent.displayField, this.parent.emptyTitle);
11285             e.set(this.parent.valueField, '');
11286
11287             this.insert(0, e);
11288         }
11289             
11290         this.fireEvent("load", this, r, options, o);
11291         if(options.callback){
11292             options.callback.call(options.scope || this, r, options, true);
11293         }
11294     },
11295
11296
11297     /**
11298      * Loads data from a passed data block. A Reader which understands the format of the data
11299      * must have been configured in the constructor.
11300      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11301      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11302      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11303      */
11304     loadData : function(o, append){
11305         var r = this.reader.readRecords(o);
11306         this.loadRecords(r, {add: append}, true);
11307     },
11308
11309     /**
11310      * Gets the number of cached records.
11311      * <p>
11312      * <em>If using paging, this may not be the total size of the dataset. If the data object
11313      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11314      * the data set size</em>
11315      */
11316     getCount : function(){
11317         return this.data.length || 0;
11318     },
11319
11320     /**
11321      * Gets the total number of records in the dataset as returned by the server.
11322      * <p>
11323      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11324      * the dataset size</em>
11325      */
11326     getTotalCount : function(){
11327         return this.totalLength || 0;
11328     },
11329
11330     /**
11331      * Returns the sort state of the Store as an object with two properties:
11332      * <pre><code>
11333  field {String} The name of the field by which the Records are sorted
11334  direction {String} The sort order, "ASC" or "DESC"
11335      * </code></pre>
11336      */
11337     getSortState : function(){
11338         return this.sortInfo;
11339     },
11340
11341     // private
11342     applySort : function(){
11343         if(this.sortInfo && !this.remoteSort){
11344             var s = this.sortInfo, f = s.field;
11345             var st = this.fields.get(f).sortType;
11346             var fn = function(r1, r2){
11347                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11348                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11349             };
11350             this.data.sort(s.direction, fn);
11351             if(this.snapshot && this.snapshot != this.data){
11352                 this.snapshot.sort(s.direction, fn);
11353             }
11354         }
11355     },
11356
11357     /**
11358      * Sets the default sort column and order to be used by the next load operation.
11359      * @param {String} fieldName The name of the field to sort by.
11360      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11361      */
11362     setDefaultSort : function(field, dir){
11363         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11364     },
11365
11366     /**
11367      * Sort the Records.
11368      * If remote sorting is used, the sort is performed on the server, and the cache is
11369      * reloaded. If local sorting is used, the cache is sorted internally.
11370      * @param {String} fieldName The name of the field to sort by.
11371      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11372      */
11373     sort : function(fieldName, dir){
11374         var f = this.fields.get(fieldName);
11375         if(!dir){
11376             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11377             
11378             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11379                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11380             }else{
11381                 dir = f.sortDir;
11382             }
11383         }
11384         this.sortToggle[f.name] = dir;
11385         this.sortInfo = {field: f.name, direction: dir};
11386         if(!this.remoteSort){
11387             this.applySort();
11388             this.fireEvent("datachanged", this);
11389         }else{
11390             this.load(this.lastOptions);
11391         }
11392     },
11393
11394     /**
11395      * Calls the specified function for each of the Records in the cache.
11396      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11397      * Returning <em>false</em> aborts and exits the iteration.
11398      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11399      */
11400     each : function(fn, scope){
11401         this.data.each(fn, scope);
11402     },
11403
11404     /**
11405      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11406      * (e.g., during paging).
11407      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11408      */
11409     getModifiedRecords : function(){
11410         return this.modified;
11411     },
11412
11413     // private
11414     createFilterFn : function(property, value, anyMatch){
11415         if(!value.exec){ // not a regex
11416             value = String(value);
11417             if(value.length == 0){
11418                 return false;
11419             }
11420             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11421         }
11422         return function(r){
11423             return value.test(r.data[property]);
11424         };
11425     },
11426
11427     /**
11428      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11429      * @param {String} property A field on your records
11430      * @param {Number} start The record index to start at (defaults to 0)
11431      * @param {Number} end The last record index to include (defaults to length - 1)
11432      * @return {Number} The sum
11433      */
11434     sum : function(property, start, end){
11435         var rs = this.data.items, v = 0;
11436         start = start || 0;
11437         end = (end || end === 0) ? end : rs.length-1;
11438
11439         for(var i = start; i <= end; i++){
11440             v += (rs[i].data[property] || 0);
11441         }
11442         return v;
11443     },
11444
11445     /**
11446      * Filter the records by a specified property.
11447      * @param {String} field A field on your records
11448      * @param {String/RegExp} value Either a string that the field
11449      * should start with or a RegExp to test against the field
11450      * @param {Boolean} anyMatch True to match any part not just the beginning
11451      */
11452     filter : function(property, value, anyMatch){
11453         var fn = this.createFilterFn(property, value, anyMatch);
11454         return fn ? this.filterBy(fn) : this.clearFilter();
11455     },
11456
11457     /**
11458      * Filter by a function. The specified function will be called with each
11459      * record in this data source. If the function returns true the record is included,
11460      * otherwise it is filtered.
11461      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11462      * @param {Object} scope (optional) The scope of the function (defaults to this)
11463      */
11464     filterBy : function(fn, scope){
11465         this.snapshot = this.snapshot || this.data;
11466         this.data = this.queryBy(fn, scope||this);
11467         this.fireEvent("datachanged", this);
11468     },
11469
11470     /**
11471      * Query the records by a specified property.
11472      * @param {String} field A field on your records
11473      * @param {String/RegExp} value Either a string that the field
11474      * should start with or a RegExp to test against the field
11475      * @param {Boolean} anyMatch True to match any part not just the beginning
11476      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11477      */
11478     query : function(property, value, anyMatch){
11479         var fn = this.createFilterFn(property, value, anyMatch);
11480         return fn ? this.queryBy(fn) : this.data.clone();
11481     },
11482
11483     /**
11484      * Query by a function. The specified function will be called with each
11485      * record in this data source. If the function returns true the record is included
11486      * in the results.
11487      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11488      * @param {Object} scope (optional) The scope of the function (defaults to this)
11489       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11490      **/
11491     queryBy : function(fn, scope){
11492         var data = this.snapshot || this.data;
11493         return data.filterBy(fn, scope||this);
11494     },
11495
11496     /**
11497      * Collects unique values for a particular dataIndex from this store.
11498      * @param {String} dataIndex The property to collect
11499      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11500      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11501      * @return {Array} An array of the unique values
11502      **/
11503     collect : function(dataIndex, allowNull, bypassFilter){
11504         var d = (bypassFilter === true && this.snapshot) ?
11505                 this.snapshot.items : this.data.items;
11506         var v, sv, r = [], l = {};
11507         for(var i = 0, len = d.length; i < len; i++){
11508             v = d[i].data[dataIndex];
11509             sv = String(v);
11510             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11511                 l[sv] = true;
11512                 r[r.length] = v;
11513             }
11514         }
11515         return r;
11516     },
11517
11518     /**
11519      * Revert to a view of the Record cache with no filtering applied.
11520      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11521      */
11522     clearFilter : function(suppressEvent){
11523         if(this.snapshot && this.snapshot != this.data){
11524             this.data = this.snapshot;
11525             delete this.snapshot;
11526             if(suppressEvent !== true){
11527                 this.fireEvent("datachanged", this);
11528             }
11529         }
11530     },
11531
11532     // private
11533     afterEdit : function(record){
11534         if(this.modified.indexOf(record) == -1){
11535             this.modified.push(record);
11536         }
11537         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11538     },
11539     
11540     // private
11541     afterReject : function(record){
11542         this.modified.remove(record);
11543         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11544     },
11545
11546     // private
11547     afterCommit : function(record){
11548         this.modified.remove(record);
11549         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11550     },
11551
11552     /**
11553      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11554      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11555      */
11556     commitChanges : function(){
11557         var m = this.modified.slice(0);
11558         this.modified = [];
11559         for(var i = 0, len = m.length; i < len; i++){
11560             m[i].commit();
11561         }
11562     },
11563
11564     /**
11565      * Cancel outstanding changes on all changed records.
11566      */
11567     rejectChanges : function(){
11568         var m = this.modified.slice(0);
11569         this.modified = [];
11570         for(var i = 0, len = m.length; i < len; i++){
11571             m[i].reject();
11572         }
11573     },
11574
11575     onMetaChange : function(meta, rtype, o){
11576         this.recordType = rtype;
11577         this.fields = rtype.prototype.fields;
11578         delete this.snapshot;
11579         this.sortInfo = meta.sortInfo || this.sortInfo;
11580         this.modified = [];
11581         this.fireEvent('metachange', this, this.reader.meta);
11582     },
11583     
11584     moveIndex : function(data, type)
11585     {
11586         var index = this.indexOf(data);
11587         
11588         var newIndex = index + type;
11589         
11590         this.remove(data);
11591         
11592         this.insert(newIndex, data);
11593         
11594     }
11595 });/*
11596  * Based on:
11597  * Ext JS Library 1.1.1
11598  * Copyright(c) 2006-2007, Ext JS, LLC.
11599  *
11600  * Originally Released Under LGPL - original licence link has changed is not relivant.
11601  *
11602  * Fork - LGPL
11603  * <script type="text/javascript">
11604  */
11605
11606 /**
11607  * @class Roo.data.SimpleStore
11608  * @extends Roo.data.Store
11609  * Small helper class to make creating Stores from Array data easier.
11610  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11611  * @cfg {Array} fields An array of field definition objects, or field name strings.
11612  * @cfg {Array} data The multi-dimensional array of data
11613  * @constructor
11614  * @param {Object} config
11615  */
11616 Roo.data.SimpleStore = function(config){
11617     Roo.data.SimpleStore.superclass.constructor.call(this, {
11618         isLocal : true,
11619         reader: new Roo.data.ArrayReader({
11620                 id: config.id
11621             },
11622             Roo.data.Record.create(config.fields)
11623         ),
11624         proxy : new Roo.data.MemoryProxy(config.data)
11625     });
11626     this.load();
11627 };
11628 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11629  * Based on:
11630  * Ext JS Library 1.1.1
11631  * Copyright(c) 2006-2007, Ext JS, LLC.
11632  *
11633  * Originally Released Under LGPL - original licence link has changed is not relivant.
11634  *
11635  * Fork - LGPL
11636  * <script type="text/javascript">
11637  */
11638
11639 /**
11640 /**
11641  * @extends Roo.data.Store
11642  * @class Roo.data.JsonStore
11643  * Small helper class to make creating Stores for JSON data easier. <br/>
11644 <pre><code>
11645 var store = new Roo.data.JsonStore({
11646     url: 'get-images.php',
11647     root: 'images',
11648     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11649 });
11650 </code></pre>
11651  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11652  * JsonReader and HttpProxy (unless inline data is provided).</b>
11653  * @cfg {Array} fields An array of field definition objects, or field name strings.
11654  * @constructor
11655  * @param {Object} config
11656  */
11657 Roo.data.JsonStore = function(c){
11658     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11659         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11660         reader: new Roo.data.JsonReader(c, c.fields)
11661     }));
11662 };
11663 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11664  * Based on:
11665  * Ext JS Library 1.1.1
11666  * Copyright(c) 2006-2007, Ext JS, LLC.
11667  *
11668  * Originally Released Under LGPL - original licence link has changed is not relivant.
11669  *
11670  * Fork - LGPL
11671  * <script type="text/javascript">
11672  */
11673
11674  
11675 Roo.data.Field = function(config){
11676     if(typeof config == "string"){
11677         config = {name: config};
11678     }
11679     Roo.apply(this, config);
11680     
11681     if(!this.type){
11682         this.type = "auto";
11683     }
11684     
11685     var st = Roo.data.SortTypes;
11686     // named sortTypes are supported, here we look them up
11687     if(typeof this.sortType == "string"){
11688         this.sortType = st[this.sortType];
11689     }
11690     
11691     // set default sortType for strings and dates
11692     if(!this.sortType){
11693         switch(this.type){
11694             case "string":
11695                 this.sortType = st.asUCString;
11696                 break;
11697             case "date":
11698                 this.sortType = st.asDate;
11699                 break;
11700             default:
11701                 this.sortType = st.none;
11702         }
11703     }
11704
11705     // define once
11706     var stripRe = /[\$,%]/g;
11707
11708     // prebuilt conversion function for this field, instead of
11709     // switching every time we're reading a value
11710     if(!this.convert){
11711         var cv, dateFormat = this.dateFormat;
11712         switch(this.type){
11713             case "":
11714             case "auto":
11715             case undefined:
11716                 cv = function(v){ return v; };
11717                 break;
11718             case "string":
11719                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11720                 break;
11721             case "int":
11722                 cv = function(v){
11723                     return v !== undefined && v !== null && v !== '' ?
11724                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11725                     };
11726                 break;
11727             case "float":
11728                 cv = function(v){
11729                     return v !== undefined && v !== null && v !== '' ?
11730                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11731                     };
11732                 break;
11733             case "bool":
11734             case "boolean":
11735                 cv = function(v){ return v === true || v === "true" || v == 1; };
11736                 break;
11737             case "date":
11738                 cv = function(v){
11739                     if(!v){
11740                         return '';
11741                     }
11742                     if(v instanceof Date){
11743                         return v;
11744                     }
11745                     if(dateFormat){
11746                         if(dateFormat == "timestamp"){
11747                             return new Date(v*1000);
11748                         }
11749                         return Date.parseDate(v, dateFormat);
11750                     }
11751                     var parsed = Date.parse(v);
11752                     return parsed ? new Date(parsed) : null;
11753                 };
11754              break;
11755             
11756         }
11757         this.convert = cv;
11758     }
11759 };
11760
11761 Roo.data.Field.prototype = {
11762     dateFormat: null,
11763     defaultValue: "",
11764     mapping: null,
11765     sortType : null,
11766     sortDir : "ASC"
11767 };/*
11768  * Based on:
11769  * Ext JS Library 1.1.1
11770  * Copyright(c) 2006-2007, Ext JS, LLC.
11771  *
11772  * Originally Released Under LGPL - original licence link has changed is not relivant.
11773  *
11774  * Fork - LGPL
11775  * <script type="text/javascript">
11776  */
11777  
11778 // Base class for reading structured data from a data source.  This class is intended to be
11779 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11780
11781 /**
11782  * @class Roo.data.DataReader
11783  * Base class for reading structured data from a data source.  This class is intended to be
11784  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11785  */
11786
11787 Roo.data.DataReader = function(meta, recordType){
11788     
11789     this.meta = meta;
11790     
11791     this.recordType = recordType instanceof Array ? 
11792         Roo.data.Record.create(recordType) : recordType;
11793 };
11794
11795 Roo.data.DataReader.prototype = {
11796      /**
11797      * Create an empty record
11798      * @param {Object} data (optional) - overlay some values
11799      * @return {Roo.data.Record} record created.
11800      */
11801     newRow :  function(d) {
11802         var da =  {};
11803         this.recordType.prototype.fields.each(function(c) {
11804             switch( c.type) {
11805                 case 'int' : da[c.name] = 0; break;
11806                 case 'date' : da[c.name] = new Date(); break;
11807                 case 'float' : da[c.name] = 0.0; break;
11808                 case 'boolean' : da[c.name] = false; break;
11809                 default : da[c.name] = ""; break;
11810             }
11811             
11812         });
11813         return new this.recordType(Roo.apply(da, d));
11814     }
11815     
11816 };/*
11817  * Based on:
11818  * Ext JS Library 1.1.1
11819  * Copyright(c) 2006-2007, Ext JS, LLC.
11820  *
11821  * Originally Released Under LGPL - original licence link has changed is not relivant.
11822  *
11823  * Fork - LGPL
11824  * <script type="text/javascript">
11825  */
11826
11827 /**
11828  * @class Roo.data.DataProxy
11829  * @extends Roo.data.Observable
11830  * This class is an abstract base class for implementations which provide retrieval of
11831  * unformatted data objects.<br>
11832  * <p>
11833  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11834  * (of the appropriate type which knows how to parse the data object) to provide a block of
11835  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11836  * <p>
11837  * Custom implementations must implement the load method as described in
11838  * {@link Roo.data.HttpProxy#load}.
11839  */
11840 Roo.data.DataProxy = function(){
11841     this.addEvents({
11842         /**
11843          * @event beforeload
11844          * Fires before a network request is made to retrieve a data object.
11845          * @param {Object} This DataProxy object.
11846          * @param {Object} params The params parameter to the load function.
11847          */
11848         beforeload : true,
11849         /**
11850          * @event load
11851          * Fires before the load method's callback is called.
11852          * @param {Object} This DataProxy object.
11853          * @param {Object} o The data object.
11854          * @param {Object} arg The callback argument object passed to the load function.
11855          */
11856         load : true,
11857         /**
11858          * @event loadexception
11859          * Fires if an Exception occurs during data retrieval.
11860          * @param {Object} This DataProxy object.
11861          * @param {Object} o The data object.
11862          * @param {Object} arg The callback argument object passed to the load function.
11863          * @param {Object} e The Exception.
11864          */
11865         loadexception : true
11866     });
11867     Roo.data.DataProxy.superclass.constructor.call(this);
11868 };
11869
11870 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11871
11872     /**
11873      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11874      */
11875 /*
11876  * Based on:
11877  * Ext JS Library 1.1.1
11878  * Copyright(c) 2006-2007, Ext JS, LLC.
11879  *
11880  * Originally Released Under LGPL - original licence link has changed is not relivant.
11881  *
11882  * Fork - LGPL
11883  * <script type="text/javascript">
11884  */
11885 /**
11886  * @class Roo.data.MemoryProxy
11887  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11888  * to the Reader when its load method is called.
11889  * @constructor
11890  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11891  */
11892 Roo.data.MemoryProxy = function(data){
11893     if (data.data) {
11894         data = data.data;
11895     }
11896     Roo.data.MemoryProxy.superclass.constructor.call(this);
11897     this.data = data;
11898 };
11899
11900 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11901     
11902     /**
11903      * Load data from the requested source (in this case an in-memory
11904      * data object passed to the constructor), read the data object into
11905      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11906      * process that block using the passed callback.
11907      * @param {Object} params This parameter is not used by the MemoryProxy class.
11908      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11909      * object into a block of Roo.data.Records.
11910      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11911      * The function must be passed <ul>
11912      * <li>The Record block object</li>
11913      * <li>The "arg" argument from the load function</li>
11914      * <li>A boolean success indicator</li>
11915      * </ul>
11916      * @param {Object} scope The scope in which to call the callback
11917      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11918      */
11919     load : function(params, reader, callback, scope, arg){
11920         params = params || {};
11921         var result;
11922         try {
11923             result = reader.readRecords(this.data);
11924         }catch(e){
11925             this.fireEvent("loadexception", this, arg, null, e);
11926             callback.call(scope, null, arg, false);
11927             return;
11928         }
11929         callback.call(scope, result, arg, true);
11930     },
11931     
11932     // private
11933     update : function(params, records){
11934         
11935     }
11936 });/*
11937  * Based on:
11938  * Ext JS Library 1.1.1
11939  * Copyright(c) 2006-2007, Ext JS, LLC.
11940  *
11941  * Originally Released Under LGPL - original licence link has changed is not relivant.
11942  *
11943  * Fork - LGPL
11944  * <script type="text/javascript">
11945  */
11946 /**
11947  * @class Roo.data.HttpProxy
11948  * @extends Roo.data.DataProxy
11949  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11950  * configured to reference a certain URL.<br><br>
11951  * <p>
11952  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11953  * from which the running page was served.<br><br>
11954  * <p>
11955  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11956  * <p>
11957  * Be aware that to enable the browser to parse an XML document, the server must set
11958  * the Content-Type header in the HTTP response to "text/xml".
11959  * @constructor
11960  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11961  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11962  * will be used to make the request.
11963  */
11964 Roo.data.HttpProxy = function(conn){
11965     Roo.data.HttpProxy.superclass.constructor.call(this);
11966     // is conn a conn config or a real conn?
11967     this.conn = conn;
11968     this.useAjax = !conn || !conn.events;
11969   
11970 };
11971
11972 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11973     // thse are take from connection...
11974     
11975     /**
11976      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11977      */
11978     /**
11979      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11980      * extra parameters to each request made by this object. (defaults to undefined)
11981      */
11982     /**
11983      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11984      *  to each request made by this object. (defaults to undefined)
11985      */
11986     /**
11987      * @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)
11988      */
11989     /**
11990      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11991      */
11992      /**
11993      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11994      * @type Boolean
11995      */
11996   
11997
11998     /**
11999      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12000      * @type Boolean
12001      */
12002     /**
12003      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12004      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12005      * a finer-grained basis than the DataProxy events.
12006      */
12007     getConnection : function(){
12008         return this.useAjax ? Roo.Ajax : this.conn;
12009     },
12010
12011     /**
12012      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12013      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12014      * process that block using the passed callback.
12015      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12016      * for the request to the remote server.
12017      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12018      * object into a block of Roo.data.Records.
12019      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12020      * The function must be passed <ul>
12021      * <li>The Record block object</li>
12022      * <li>The "arg" argument from the load function</li>
12023      * <li>A boolean success indicator</li>
12024      * </ul>
12025      * @param {Object} scope The scope in which to call the callback
12026      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12027      */
12028     load : function(params, reader, callback, scope, arg){
12029         if(this.fireEvent("beforeload", this, params) !== false){
12030             var  o = {
12031                 params : params || {},
12032                 request: {
12033                     callback : callback,
12034                     scope : scope,
12035                     arg : arg
12036                 },
12037                 reader: reader,
12038                 callback : this.loadResponse,
12039                 scope: this
12040             };
12041             if(this.useAjax){
12042                 Roo.applyIf(o, this.conn);
12043                 if(this.activeRequest){
12044                     Roo.Ajax.abort(this.activeRequest);
12045                 }
12046                 this.activeRequest = Roo.Ajax.request(o);
12047             }else{
12048                 this.conn.request(o);
12049             }
12050         }else{
12051             callback.call(scope||this, null, arg, false);
12052         }
12053     },
12054
12055     // private
12056     loadResponse : function(o, success, response){
12057         delete this.activeRequest;
12058         if(!success){
12059             this.fireEvent("loadexception", this, o, response);
12060             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12061             return;
12062         }
12063         var result;
12064         try {
12065             result = o.reader.read(response);
12066         }catch(e){
12067             this.fireEvent("loadexception", this, o, response, e);
12068             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12069             return;
12070         }
12071         
12072         this.fireEvent("load", this, o, o.request.arg);
12073         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12074     },
12075
12076     // private
12077     update : function(dataSet){
12078
12079     },
12080
12081     // private
12082     updateResponse : function(dataSet){
12083
12084     }
12085 });/*
12086  * Based on:
12087  * Ext JS Library 1.1.1
12088  * Copyright(c) 2006-2007, Ext JS, LLC.
12089  *
12090  * Originally Released Under LGPL - original licence link has changed is not relivant.
12091  *
12092  * Fork - LGPL
12093  * <script type="text/javascript">
12094  */
12095
12096 /**
12097  * @class Roo.data.ScriptTagProxy
12098  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12099  * other than the originating domain of the running page.<br><br>
12100  * <p>
12101  * <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
12102  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12103  * <p>
12104  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12105  * source code that is used as the source inside a &lt;script> tag.<br><br>
12106  * <p>
12107  * In order for the browser to process the returned data, the server must wrap the data object
12108  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12109  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12110  * depending on whether the callback name was passed:
12111  * <p>
12112  * <pre><code>
12113 boolean scriptTag = false;
12114 String cb = request.getParameter("callback");
12115 if (cb != null) {
12116     scriptTag = true;
12117     response.setContentType("text/javascript");
12118 } else {
12119     response.setContentType("application/x-json");
12120 }
12121 Writer out = response.getWriter();
12122 if (scriptTag) {
12123     out.write(cb + "(");
12124 }
12125 out.print(dataBlock.toJsonString());
12126 if (scriptTag) {
12127     out.write(");");
12128 }
12129 </pre></code>
12130  *
12131  * @constructor
12132  * @param {Object} config A configuration object.
12133  */
12134 Roo.data.ScriptTagProxy = function(config){
12135     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12136     Roo.apply(this, config);
12137     this.head = document.getElementsByTagName("head")[0];
12138 };
12139
12140 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12141
12142 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12143     /**
12144      * @cfg {String} url The URL from which to request the data object.
12145      */
12146     /**
12147      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12148      */
12149     timeout : 30000,
12150     /**
12151      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12152      * the server the name of the callback function set up by the load call to process the returned data object.
12153      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12154      * javascript output which calls this named function passing the data object as its only parameter.
12155      */
12156     callbackParam : "callback",
12157     /**
12158      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12159      * name to the request.
12160      */
12161     nocache : true,
12162
12163     /**
12164      * Load data from the configured URL, read the data object into
12165      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12166      * process that block using the passed callback.
12167      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12168      * for the request to the remote server.
12169      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12170      * object into a block of Roo.data.Records.
12171      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12172      * The function must be passed <ul>
12173      * <li>The Record block object</li>
12174      * <li>The "arg" argument from the load function</li>
12175      * <li>A boolean success indicator</li>
12176      * </ul>
12177      * @param {Object} scope The scope in which to call the callback
12178      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12179      */
12180     load : function(params, reader, callback, scope, arg){
12181         if(this.fireEvent("beforeload", this, params) !== false){
12182
12183             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12184
12185             var url = this.url;
12186             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12187             if(this.nocache){
12188                 url += "&_dc=" + (new Date().getTime());
12189             }
12190             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12191             var trans = {
12192                 id : transId,
12193                 cb : "stcCallback"+transId,
12194                 scriptId : "stcScript"+transId,
12195                 params : params,
12196                 arg : arg,
12197                 url : url,
12198                 callback : callback,
12199                 scope : scope,
12200                 reader : reader
12201             };
12202             var conn = this;
12203
12204             window[trans.cb] = function(o){
12205                 conn.handleResponse(o, trans);
12206             };
12207
12208             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12209
12210             if(this.autoAbort !== false){
12211                 this.abort();
12212             }
12213
12214             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12215
12216             var script = document.createElement("script");
12217             script.setAttribute("src", url);
12218             script.setAttribute("type", "text/javascript");
12219             script.setAttribute("id", trans.scriptId);
12220             this.head.appendChild(script);
12221
12222             this.trans = trans;
12223         }else{
12224             callback.call(scope||this, null, arg, false);
12225         }
12226     },
12227
12228     // private
12229     isLoading : function(){
12230         return this.trans ? true : false;
12231     },
12232
12233     /**
12234      * Abort the current server request.
12235      */
12236     abort : function(){
12237         if(this.isLoading()){
12238             this.destroyTrans(this.trans);
12239         }
12240     },
12241
12242     // private
12243     destroyTrans : function(trans, isLoaded){
12244         this.head.removeChild(document.getElementById(trans.scriptId));
12245         clearTimeout(trans.timeoutId);
12246         if(isLoaded){
12247             window[trans.cb] = undefined;
12248             try{
12249                 delete window[trans.cb];
12250             }catch(e){}
12251         }else{
12252             // if hasn't been loaded, wait for load to remove it to prevent script error
12253             window[trans.cb] = function(){
12254                 window[trans.cb] = undefined;
12255                 try{
12256                     delete window[trans.cb];
12257                 }catch(e){}
12258             };
12259         }
12260     },
12261
12262     // private
12263     handleResponse : function(o, trans){
12264         this.trans = false;
12265         this.destroyTrans(trans, true);
12266         var result;
12267         try {
12268             result = trans.reader.readRecords(o);
12269         }catch(e){
12270             this.fireEvent("loadexception", this, o, trans.arg, e);
12271             trans.callback.call(trans.scope||window, null, trans.arg, false);
12272             return;
12273         }
12274         this.fireEvent("load", this, o, trans.arg);
12275         trans.callback.call(trans.scope||window, result, trans.arg, true);
12276     },
12277
12278     // private
12279     handleFailure : function(trans){
12280         this.trans = false;
12281         this.destroyTrans(trans, false);
12282         this.fireEvent("loadexception", this, null, trans.arg);
12283         trans.callback.call(trans.scope||window, null, trans.arg, false);
12284     }
12285 });/*
12286  * Based on:
12287  * Ext JS Library 1.1.1
12288  * Copyright(c) 2006-2007, Ext JS, LLC.
12289  *
12290  * Originally Released Under LGPL - original licence link has changed is not relivant.
12291  *
12292  * Fork - LGPL
12293  * <script type="text/javascript">
12294  */
12295
12296 /**
12297  * @class Roo.data.JsonReader
12298  * @extends Roo.data.DataReader
12299  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12300  * based on mappings in a provided Roo.data.Record constructor.
12301  * 
12302  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12303  * in the reply previously. 
12304  * 
12305  * <p>
12306  * Example code:
12307  * <pre><code>
12308 var RecordDef = Roo.data.Record.create([
12309     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12310     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12311 ]);
12312 var myReader = new Roo.data.JsonReader({
12313     totalProperty: "results",    // The property which contains the total dataset size (optional)
12314     root: "rows",                // The property which contains an Array of row objects
12315     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12316 }, RecordDef);
12317 </code></pre>
12318  * <p>
12319  * This would consume a JSON file like this:
12320  * <pre><code>
12321 { 'results': 2, 'rows': [
12322     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12323     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12324 }
12325 </code></pre>
12326  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12327  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12328  * paged from the remote server.
12329  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12330  * @cfg {String} root name of the property which contains the Array of row objects.
12331  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12332  * @cfg {Array} fields Array of field definition objects
12333  * @constructor
12334  * Create a new JsonReader
12335  * @param {Object} meta Metadata configuration options
12336  * @param {Object} recordType Either an Array of field definition objects,
12337  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12338  */
12339 Roo.data.JsonReader = function(meta, recordType){
12340     
12341     meta = meta || {};
12342     // set some defaults:
12343     Roo.applyIf(meta, {
12344         totalProperty: 'total',
12345         successProperty : 'success',
12346         root : 'data',
12347         id : 'id'
12348     });
12349     
12350     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12351 };
12352 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12353     
12354     /**
12355      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12356      * Used by Store query builder to append _requestMeta to params.
12357      * 
12358      */
12359     metaFromRemote : false,
12360     /**
12361      * This method is only used by a DataProxy which has retrieved data from a remote server.
12362      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12363      * @return {Object} data A data block which is used by an Roo.data.Store object as
12364      * a cache of Roo.data.Records.
12365      */
12366     read : function(response){
12367         var json = response.responseText;
12368        
12369         var o = /* eval:var:o */ eval("("+json+")");
12370         if(!o) {
12371             throw {message: "JsonReader.read: Json object not found"};
12372         }
12373         
12374         if(o.metaData){
12375             
12376             delete this.ef;
12377             this.metaFromRemote = true;
12378             this.meta = o.metaData;
12379             this.recordType = Roo.data.Record.create(o.metaData.fields);
12380             this.onMetaChange(this.meta, this.recordType, o);
12381         }
12382         return this.readRecords(o);
12383     },
12384
12385     // private function a store will implement
12386     onMetaChange : function(meta, recordType, o){
12387
12388     },
12389
12390     /**
12391          * @ignore
12392          */
12393     simpleAccess: function(obj, subsc) {
12394         return obj[subsc];
12395     },
12396
12397         /**
12398          * @ignore
12399          */
12400     getJsonAccessor: function(){
12401         var re = /[\[\.]/;
12402         return function(expr) {
12403             try {
12404                 return(re.test(expr))
12405                     ? new Function("obj", "return obj." + expr)
12406                     : function(obj){
12407                         return obj[expr];
12408                     };
12409             } catch(e){}
12410             return Roo.emptyFn;
12411         };
12412     }(),
12413
12414     /**
12415      * Create a data block containing Roo.data.Records from an XML document.
12416      * @param {Object} o An object which contains an Array of row objects in the property specified
12417      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12418      * which contains the total size of the dataset.
12419      * @return {Object} data A data block which is used by an Roo.data.Store object as
12420      * a cache of Roo.data.Records.
12421      */
12422     readRecords : function(o){
12423         /**
12424          * After any data loads, the raw JSON data is available for further custom processing.
12425          * @type Object
12426          */
12427         this.o = o;
12428         var s = this.meta, Record = this.recordType,
12429             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12430
12431 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12432         if (!this.ef) {
12433             if(s.totalProperty) {
12434                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12435                 }
12436                 if(s.successProperty) {
12437                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12438                 }
12439                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12440                 if (s.id) {
12441                         var g = this.getJsonAccessor(s.id);
12442                         this.getId = function(rec) {
12443                                 var r = g(rec);  
12444                                 return (r === undefined || r === "") ? null : r;
12445                         };
12446                 } else {
12447                         this.getId = function(){return null;};
12448                 }
12449             this.ef = [];
12450             for(var jj = 0; jj < fl; jj++){
12451                 f = fi[jj];
12452                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12453                 this.ef[jj] = this.getJsonAccessor(map);
12454             }
12455         }
12456
12457         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12458         if(s.totalProperty){
12459             var vt = parseInt(this.getTotal(o), 10);
12460             if(!isNaN(vt)){
12461                 totalRecords = vt;
12462             }
12463         }
12464         if(s.successProperty){
12465             var vs = this.getSuccess(o);
12466             if(vs === false || vs === 'false'){
12467                 success = false;
12468             }
12469         }
12470         var records = [];
12471         for(var i = 0; i < c; i++){
12472                 var n = root[i];
12473             var values = {};
12474             var id = this.getId(n);
12475             for(var j = 0; j < fl; j++){
12476                 f = fi[j];
12477             var v = this.ef[j](n);
12478             if (!f.convert) {
12479                 Roo.log('missing convert for ' + f.name);
12480                 Roo.log(f);
12481                 continue;
12482             }
12483             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12484             }
12485             var record = new Record(values, id);
12486             record.json = n;
12487             records[i] = record;
12488         }
12489         return {
12490             raw : o,
12491             success : success,
12492             records : records,
12493             totalRecords : totalRecords
12494         };
12495     }
12496 });/*
12497  * Based on:
12498  * Ext JS Library 1.1.1
12499  * Copyright(c) 2006-2007, Ext JS, LLC.
12500  *
12501  * Originally Released Under LGPL - original licence link has changed is not relivant.
12502  *
12503  * Fork - LGPL
12504  * <script type="text/javascript">
12505  */
12506
12507 /**
12508  * @class Roo.data.ArrayReader
12509  * @extends Roo.data.DataReader
12510  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12511  * Each element of that Array represents a row of data fields. The
12512  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12513  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12514  * <p>
12515  * Example code:.
12516  * <pre><code>
12517 var RecordDef = Roo.data.Record.create([
12518     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12519     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12520 ]);
12521 var myReader = new Roo.data.ArrayReader({
12522     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12523 }, RecordDef);
12524 </code></pre>
12525  * <p>
12526  * This would consume an Array like this:
12527  * <pre><code>
12528 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12529   </code></pre>
12530  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12531  * @constructor
12532  * Create a new JsonReader
12533  * @param {Object} meta Metadata configuration options.
12534  * @param {Object} recordType Either an Array of field definition objects
12535  * as specified to {@link Roo.data.Record#create},
12536  * or an {@link Roo.data.Record} object
12537  * created using {@link Roo.data.Record#create}.
12538  */
12539 Roo.data.ArrayReader = function(meta, recordType){
12540     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12541 };
12542
12543 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12544     /**
12545      * Create a data block containing Roo.data.Records from an XML document.
12546      * @param {Object} o An Array of row objects which represents the dataset.
12547      * @return {Object} data A data block which is used by an Roo.data.Store object as
12548      * a cache of Roo.data.Records.
12549      */
12550     readRecords : function(o){
12551         var sid = this.meta ? this.meta.id : null;
12552         var recordType = this.recordType, fields = recordType.prototype.fields;
12553         var records = [];
12554         var root = o;
12555             for(var i = 0; i < root.length; i++){
12556                     var n = root[i];
12557                 var values = {};
12558                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12559                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12560                 var f = fields.items[j];
12561                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12562                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12563                 v = f.convert(v);
12564                 values[f.name] = v;
12565             }
12566                 var record = new recordType(values, id);
12567                 record.json = n;
12568                 records[records.length] = record;
12569             }
12570             return {
12571                 records : records,
12572                 totalRecords : records.length
12573             };
12574     }
12575 });/*
12576  * - LGPL
12577  * * 
12578  */
12579
12580 /**
12581  * @class Roo.bootstrap.ComboBox
12582  * @extends Roo.bootstrap.TriggerField
12583  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12584  * @cfg {Boolean} append (true|false) default false
12585  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12586  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12587  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12588  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12589  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12590  * @cfg {Boolean} animate default true
12591  * @cfg {Boolean} emptyResultText only for touch device
12592  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12593  * @cfg {String} emptyTitle default ''
12594  * @constructor
12595  * Create a new ComboBox.
12596  * @param {Object} config Configuration options
12597  */
12598 Roo.bootstrap.ComboBox = function(config){
12599     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12600     this.addEvents({
12601         /**
12602          * @event expand
12603          * Fires when the dropdown list is expanded
12604         * @param {Roo.bootstrap.ComboBox} combo This combo box
12605         */
12606         'expand' : true,
12607         /**
12608          * @event collapse
12609          * Fires when the dropdown list is collapsed
12610         * @param {Roo.bootstrap.ComboBox} combo This combo box
12611         */
12612         'collapse' : true,
12613         /**
12614          * @event beforeselect
12615          * Fires before a list item is selected. Return false to cancel the selection.
12616         * @param {Roo.bootstrap.ComboBox} combo This combo box
12617         * @param {Roo.data.Record} record The data record returned from the underlying store
12618         * @param {Number} index The index of the selected item in the dropdown list
12619         */
12620         'beforeselect' : true,
12621         /**
12622          * @event select
12623          * Fires when a list item is selected
12624         * @param {Roo.bootstrap.ComboBox} combo This combo box
12625         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12626         * @param {Number} index The index of the selected item in the dropdown list
12627         */
12628         'select' : true,
12629         /**
12630          * @event beforequery
12631          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12632          * The event object passed has these properties:
12633         * @param {Roo.bootstrap.ComboBox} combo This combo box
12634         * @param {String} query The query
12635         * @param {Boolean} forceAll true to force "all" query
12636         * @param {Boolean} cancel true to cancel the query
12637         * @param {Object} e The query event object
12638         */
12639         'beforequery': true,
12640          /**
12641          * @event add
12642          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12643         * @param {Roo.bootstrap.ComboBox} combo This combo box
12644         */
12645         'add' : true,
12646         /**
12647          * @event edit
12648          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12649         * @param {Roo.bootstrap.ComboBox} combo This combo box
12650         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12651         */
12652         'edit' : true,
12653         /**
12654          * @event remove
12655          * Fires when the remove value from the combobox array
12656         * @param {Roo.bootstrap.ComboBox} combo This combo box
12657         */
12658         'remove' : true,
12659         /**
12660          * @event afterremove
12661          * Fires when the remove value from the combobox array
12662         * @param {Roo.bootstrap.ComboBox} combo This combo box
12663         */
12664         'afterremove' : true,
12665         /**
12666          * @event specialfilter
12667          * Fires when specialfilter
12668             * @param {Roo.bootstrap.ComboBox} combo This combo box
12669             */
12670         'specialfilter' : true,
12671         /**
12672          * @event tick
12673          * Fires when tick the element
12674             * @param {Roo.bootstrap.ComboBox} combo This combo box
12675             */
12676         'tick' : true,
12677         /**
12678          * @event touchviewdisplay
12679          * Fires when touch view require special display (default is using displayField)
12680             * @param {Roo.bootstrap.ComboBox} combo This combo box
12681             * @param {Object} cfg set html .
12682             */
12683         'touchviewdisplay' : true
12684         
12685     });
12686     
12687     this.item = [];
12688     this.tickItems = [];
12689     
12690     this.selectedIndex = -1;
12691     if(this.mode == 'local'){
12692         if(config.queryDelay === undefined){
12693             this.queryDelay = 10;
12694         }
12695         if(config.minChars === undefined){
12696             this.minChars = 0;
12697         }
12698     }
12699 };
12700
12701 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12702      
12703     /**
12704      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12705      * rendering into an Roo.Editor, defaults to false)
12706      */
12707     /**
12708      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12709      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12710      */
12711     /**
12712      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12713      */
12714     /**
12715      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12716      * the dropdown list (defaults to undefined, with no header element)
12717      */
12718
12719      /**
12720      * @cfg {String/Roo.Template} tpl The template to use to render the output
12721      */
12722      
12723      /**
12724      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12725      */
12726     listWidth: undefined,
12727     /**
12728      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12729      * mode = 'remote' or 'text' if mode = 'local')
12730      */
12731     displayField: undefined,
12732     
12733     /**
12734      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12735      * mode = 'remote' or 'value' if mode = 'local'). 
12736      * Note: use of a valueField requires the user make a selection
12737      * in order for a value to be mapped.
12738      */
12739     valueField: undefined,
12740     /**
12741      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12742      */
12743     modalTitle : '',
12744     
12745     /**
12746      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12747      * field's data value (defaults to the underlying DOM element's name)
12748      */
12749     hiddenName: undefined,
12750     /**
12751      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12752      */
12753     listClass: '',
12754     /**
12755      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12756      */
12757     selectedClass: 'active',
12758     
12759     /**
12760      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12761      */
12762     shadow:'sides',
12763     /**
12764      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12765      * anchor positions (defaults to 'tl-bl')
12766      */
12767     listAlign: 'tl-bl?',
12768     /**
12769      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12770      */
12771     maxHeight: 300,
12772     /**
12773      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12774      * query specified by the allQuery config option (defaults to 'query')
12775      */
12776     triggerAction: 'query',
12777     /**
12778      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12779      * (defaults to 4, does not apply if editable = false)
12780      */
12781     minChars : 4,
12782     /**
12783      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12784      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12785      */
12786     typeAhead: false,
12787     /**
12788      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12789      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12790      */
12791     queryDelay: 500,
12792     /**
12793      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12794      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12795      */
12796     pageSize: 0,
12797     /**
12798      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12799      * when editable = true (defaults to false)
12800      */
12801     selectOnFocus:false,
12802     /**
12803      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12804      */
12805     queryParam: 'query',
12806     /**
12807      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12808      * when mode = 'remote' (defaults to 'Loading...')
12809      */
12810     loadingText: 'Loading...',
12811     /**
12812      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12813      */
12814     resizable: false,
12815     /**
12816      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12817      */
12818     handleHeight : 8,
12819     /**
12820      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12821      * traditional select (defaults to true)
12822      */
12823     editable: true,
12824     /**
12825      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12826      */
12827     allQuery: '',
12828     /**
12829      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12830      */
12831     mode: 'remote',
12832     /**
12833      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12834      * listWidth has a higher value)
12835      */
12836     minListWidth : 70,
12837     /**
12838      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12839      * allow the user to set arbitrary text into the field (defaults to false)
12840      */
12841     forceSelection:false,
12842     /**
12843      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12844      * if typeAhead = true (defaults to 250)
12845      */
12846     typeAheadDelay : 250,
12847     /**
12848      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12849      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12850      */
12851     valueNotFoundText : undefined,
12852     /**
12853      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12854      */
12855     blockFocus : false,
12856     
12857     /**
12858      * @cfg {Boolean} disableClear Disable showing of clear button.
12859      */
12860     disableClear : false,
12861     /**
12862      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12863      */
12864     alwaysQuery : false,
12865     
12866     /**
12867      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12868      */
12869     multiple : false,
12870     
12871     /**
12872      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12873      */
12874     invalidClass : "has-warning",
12875     
12876     /**
12877      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12878      */
12879     validClass : "has-success",
12880     
12881     /**
12882      * @cfg {Boolean} specialFilter (true|false) special filter default false
12883      */
12884     specialFilter : false,
12885     
12886     /**
12887      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12888      */
12889     mobileTouchView : true,
12890     
12891     /**
12892      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12893      */
12894     useNativeIOS : false,
12895     
12896     ios_options : false,
12897     
12898     //private
12899     addicon : false,
12900     editicon: false,
12901     
12902     page: 0,
12903     hasQuery: false,
12904     append: false,
12905     loadNext: false,
12906     autoFocus : true,
12907     tickable : false,
12908     btnPosition : 'right',
12909     triggerList : true,
12910     showToggleBtn : true,
12911     animate : true,
12912     emptyResultText: 'Empty',
12913     triggerText : 'Select',
12914     emptyTitle : '',
12915     
12916     // element that contains real text value.. (when hidden is used..)
12917     
12918     getAutoCreate : function()
12919     {   
12920         var cfg = false;
12921         //render
12922         /*
12923          * Render classic select for iso
12924          */
12925         
12926         if(Roo.isIOS && this.useNativeIOS){
12927             cfg = this.getAutoCreateNativeIOS();
12928             return cfg;
12929         }
12930         
12931         /*
12932          * Touch Devices
12933          */
12934         
12935         if(Roo.isTouch && this.mobileTouchView){
12936             cfg = this.getAutoCreateTouchView();
12937             return cfg;;
12938         }
12939         
12940         /*
12941          *  Normal ComboBox
12942          */
12943         if(!this.tickable){
12944             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12945             return cfg;
12946         }
12947         
12948         /*
12949          *  ComboBox with tickable selections
12950          */
12951              
12952         var align = this.labelAlign || this.parentLabelAlign();
12953         
12954         cfg = {
12955             cls : 'form-group roo-combobox-tickable' //input-group
12956         };
12957         
12958         var btn_text_select = '';
12959         var btn_text_done = '';
12960         var btn_text_cancel = '';
12961         
12962         if (this.btn_text_show) {
12963             btn_text_select = 'Select';
12964             btn_text_done = 'Done';
12965             btn_text_cancel = 'Cancel'; 
12966         }
12967         
12968         var buttons = {
12969             tag : 'div',
12970             cls : 'tickable-buttons',
12971             cn : [
12972                 {
12973                     tag : 'button',
12974                     type : 'button',
12975                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12976                     //html : this.triggerText
12977                     html: btn_text_select
12978                 },
12979                 {
12980                     tag : 'button',
12981                     type : 'button',
12982                     name : 'ok',
12983                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12984                     //html : 'Done'
12985                     html: btn_text_done
12986                 },
12987                 {
12988                     tag : 'button',
12989                     type : 'button',
12990                     name : 'cancel',
12991                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12992                     //html : 'Cancel'
12993                     html: btn_text_cancel
12994                 }
12995             ]
12996         };
12997         
12998         if(this.editable){
12999             buttons.cn.unshift({
13000                 tag: 'input',
13001                 cls: 'roo-select2-search-field-input'
13002             });
13003         }
13004         
13005         var _this = this;
13006         
13007         Roo.each(buttons.cn, function(c){
13008             if (_this.size) {
13009                 c.cls += ' btn-' + _this.size;
13010             }
13011
13012             if (_this.disabled) {
13013                 c.disabled = true;
13014             }
13015         });
13016         
13017         var box = {
13018             tag: 'div',
13019             cn: [
13020                 {
13021                     tag: 'input',
13022                     type : 'hidden',
13023                     cls: 'form-hidden-field'
13024                 },
13025                 {
13026                     tag: 'ul',
13027                     cls: 'roo-select2-choices',
13028                     cn:[
13029                         {
13030                             tag: 'li',
13031                             cls: 'roo-select2-search-field',
13032                             cn: [
13033                                 buttons
13034                             ]
13035                         }
13036                     ]
13037                 }
13038             ]
13039         };
13040         
13041         var combobox = {
13042             cls: 'roo-select2-container input-group roo-select2-container-multi',
13043             cn: [
13044                 box
13045 //                {
13046 //                    tag: 'ul',
13047 //                    cls: 'typeahead typeahead-long dropdown-menu',
13048 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13049 //                }
13050             ]
13051         };
13052         
13053         if(this.hasFeedback && !this.allowBlank){
13054             
13055             var feedback = {
13056                 tag: 'span',
13057                 cls: 'glyphicon form-control-feedback'
13058             };
13059
13060             combobox.cn.push(feedback);
13061         }
13062         
13063         
13064         if (align ==='left' && this.fieldLabel.length) {
13065             
13066             cfg.cls += ' roo-form-group-label-left';
13067             
13068             cfg.cn = [
13069                 {
13070                     tag : 'i',
13071                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13072                     tooltip : 'This field is required'
13073                 },
13074                 {
13075                     tag: 'label',
13076                     'for' :  id,
13077                     cls : 'control-label',
13078                     html : this.fieldLabel
13079
13080                 },
13081                 {
13082                     cls : "", 
13083                     cn: [
13084                         combobox
13085                     ]
13086                 }
13087
13088             ];
13089             
13090             var labelCfg = cfg.cn[1];
13091             var contentCfg = cfg.cn[2];
13092             
13093
13094             if(this.indicatorpos == 'right'){
13095                 
13096                 cfg.cn = [
13097                     {
13098                         tag: 'label',
13099                         'for' :  id,
13100                         cls : 'control-label',
13101                         cn : [
13102                             {
13103                                 tag : 'span',
13104                                 html : this.fieldLabel
13105                             },
13106                             {
13107                                 tag : 'i',
13108                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13109                                 tooltip : 'This field is required'
13110                             }
13111                         ]
13112                     },
13113                     {
13114                         cls : "",
13115                         cn: [
13116                             combobox
13117                         ]
13118                     }
13119
13120                 ];
13121                 
13122                 
13123                 
13124                 labelCfg = cfg.cn[0];
13125                 contentCfg = cfg.cn[1];
13126             
13127             }
13128             
13129             if(this.labelWidth > 12){
13130                 labelCfg.style = "width: " + this.labelWidth + 'px';
13131             }
13132             
13133             if(this.labelWidth < 13 && this.labelmd == 0){
13134                 this.labelmd = this.labelWidth;
13135             }
13136             
13137             if(this.labellg > 0){
13138                 labelCfg.cls += ' col-lg-' + this.labellg;
13139                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13140             }
13141             
13142             if(this.labelmd > 0){
13143                 labelCfg.cls += ' col-md-' + this.labelmd;
13144                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13145             }
13146             
13147             if(this.labelsm > 0){
13148                 labelCfg.cls += ' col-sm-' + this.labelsm;
13149                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13150             }
13151             
13152             if(this.labelxs > 0){
13153                 labelCfg.cls += ' col-xs-' + this.labelxs;
13154                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13155             }
13156                 
13157                 
13158         } else if ( this.fieldLabel.length) {
13159 //                Roo.log(" label");
13160                  cfg.cn = [
13161                     {
13162                         tag : 'i',
13163                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13164                         tooltip : 'This field is required'
13165                     },
13166                     {
13167                         tag: 'label',
13168                         //cls : 'input-group-addon',
13169                         html : this.fieldLabel
13170                     },
13171                     combobox
13172                 ];
13173                 
13174                 if(this.indicatorpos == 'right'){
13175                     cfg.cn = [
13176                         {
13177                             tag: 'label',
13178                             //cls : 'input-group-addon',
13179                             html : this.fieldLabel
13180                         },
13181                         {
13182                             tag : 'i',
13183                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13184                             tooltip : 'This field is required'
13185                         },
13186                         combobox
13187                     ];
13188                     
13189                 }
13190
13191         } else {
13192             
13193 //                Roo.log(" no label && no align");
13194                 cfg = combobox
13195                      
13196                 
13197         }
13198          
13199         var settings=this;
13200         ['xs','sm','md','lg'].map(function(size){
13201             if (settings[size]) {
13202                 cfg.cls += ' col-' + size + '-' + settings[size];
13203             }
13204         });
13205         
13206         return cfg;
13207         
13208     },
13209     
13210     _initEventsCalled : false,
13211     
13212     // private
13213     initEvents: function()
13214     {   
13215         if (this._initEventsCalled) { // as we call render... prevent looping...
13216             return;
13217         }
13218         this._initEventsCalled = true;
13219         
13220         if (!this.store) {
13221             throw "can not find store for combo";
13222         }
13223         
13224         this.indicator = this.indicatorEl();
13225         
13226         this.store = Roo.factory(this.store, Roo.data);
13227         this.store.parent = this;
13228         
13229         // if we are building from html. then this element is so complex, that we can not really
13230         // use the rendered HTML.
13231         // so we have to trash and replace the previous code.
13232         if (Roo.XComponent.build_from_html) {
13233             // remove this element....
13234             var e = this.el.dom, k=0;
13235             while (e ) { e = e.previousSibling;  ++k;}
13236
13237             this.el.remove();
13238             
13239             this.el=false;
13240             this.rendered = false;
13241             
13242             this.render(this.parent().getChildContainer(true), k);
13243         }
13244         
13245         if(Roo.isIOS && this.useNativeIOS){
13246             this.initIOSView();
13247             return;
13248         }
13249         
13250         /*
13251          * Touch Devices
13252          */
13253         
13254         if(Roo.isTouch && this.mobileTouchView){
13255             this.initTouchView();
13256             return;
13257         }
13258         
13259         if(this.tickable){
13260             this.initTickableEvents();
13261             return;
13262         }
13263         
13264         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13265         
13266         if(this.hiddenName){
13267             
13268             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13269             
13270             this.hiddenField.dom.value =
13271                 this.hiddenValue !== undefined ? this.hiddenValue :
13272                 this.value !== undefined ? this.value : '';
13273
13274             // prevent input submission
13275             this.el.dom.removeAttribute('name');
13276             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13277              
13278              
13279         }
13280         //if(Roo.isGecko){
13281         //    this.el.dom.setAttribute('autocomplete', 'off');
13282         //}
13283         
13284         var cls = 'x-combo-list';
13285         
13286         //this.list = new Roo.Layer({
13287         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13288         //});
13289         
13290         var _this = this;
13291         
13292         (function(){
13293             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13294             _this.list.setWidth(lw);
13295         }).defer(100);
13296         
13297         this.list.on('mouseover', this.onViewOver, this);
13298         this.list.on('mousemove', this.onViewMove, this);
13299         this.list.on('scroll', this.onViewScroll, this);
13300         
13301         /*
13302         this.list.swallowEvent('mousewheel');
13303         this.assetHeight = 0;
13304
13305         if(this.title){
13306             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13307             this.assetHeight += this.header.getHeight();
13308         }
13309
13310         this.innerList = this.list.createChild({cls:cls+'-inner'});
13311         this.innerList.on('mouseover', this.onViewOver, this);
13312         this.innerList.on('mousemove', this.onViewMove, this);
13313         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13314         
13315         if(this.allowBlank && !this.pageSize && !this.disableClear){
13316             this.footer = this.list.createChild({cls:cls+'-ft'});
13317             this.pageTb = new Roo.Toolbar(this.footer);
13318            
13319         }
13320         if(this.pageSize){
13321             this.footer = this.list.createChild({cls:cls+'-ft'});
13322             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13323                     {pageSize: this.pageSize});
13324             
13325         }
13326         
13327         if (this.pageTb && this.allowBlank && !this.disableClear) {
13328             var _this = this;
13329             this.pageTb.add(new Roo.Toolbar.Fill(), {
13330                 cls: 'x-btn-icon x-btn-clear',
13331                 text: '&#160;',
13332                 handler: function()
13333                 {
13334                     _this.collapse();
13335                     _this.clearValue();
13336                     _this.onSelect(false, -1);
13337                 }
13338             });
13339         }
13340         if (this.footer) {
13341             this.assetHeight += this.footer.getHeight();
13342         }
13343         */
13344             
13345         if(!this.tpl){
13346             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13347         }
13348
13349         this.view = new Roo.View(this.list, this.tpl, {
13350             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13351         });
13352         //this.view.wrapEl.setDisplayed(false);
13353         this.view.on('click', this.onViewClick, this);
13354         
13355         
13356         this.store.on('beforeload', this.onBeforeLoad, this);
13357         this.store.on('load', this.onLoad, this);
13358         this.store.on('loadexception', this.onLoadException, this);
13359         /*
13360         if(this.resizable){
13361             this.resizer = new Roo.Resizable(this.list,  {
13362                pinned:true, handles:'se'
13363             });
13364             this.resizer.on('resize', function(r, w, h){
13365                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13366                 this.listWidth = w;
13367                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13368                 this.restrictHeight();
13369             }, this);
13370             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13371         }
13372         */
13373         if(!this.editable){
13374             this.editable = true;
13375             this.setEditable(false);
13376         }
13377         
13378         /*
13379         
13380         if (typeof(this.events.add.listeners) != 'undefined') {
13381             
13382             this.addicon = this.wrap.createChild(
13383                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13384        
13385             this.addicon.on('click', function(e) {
13386                 this.fireEvent('add', this);
13387             }, this);
13388         }
13389         if (typeof(this.events.edit.listeners) != 'undefined') {
13390             
13391             this.editicon = this.wrap.createChild(
13392                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13393             if (this.addicon) {
13394                 this.editicon.setStyle('margin-left', '40px');
13395             }
13396             this.editicon.on('click', function(e) {
13397                 
13398                 // we fire even  if inothing is selected..
13399                 this.fireEvent('edit', this, this.lastData );
13400                 
13401             }, this);
13402         }
13403         */
13404         
13405         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13406             "up" : function(e){
13407                 this.inKeyMode = true;
13408                 this.selectPrev();
13409             },
13410
13411             "down" : function(e){
13412                 if(!this.isExpanded()){
13413                     this.onTriggerClick();
13414                 }else{
13415                     this.inKeyMode = true;
13416                     this.selectNext();
13417                 }
13418             },
13419
13420             "enter" : function(e){
13421 //                this.onViewClick();
13422                 //return true;
13423                 this.collapse();
13424                 
13425                 if(this.fireEvent("specialkey", this, e)){
13426                     this.onViewClick(false);
13427                 }
13428                 
13429                 return true;
13430             },
13431
13432             "esc" : function(e){
13433                 this.collapse();
13434             },
13435
13436             "tab" : function(e){
13437                 this.collapse();
13438                 
13439                 if(this.fireEvent("specialkey", this, e)){
13440                     this.onViewClick(false);
13441                 }
13442                 
13443                 return true;
13444             },
13445
13446             scope : this,
13447
13448             doRelay : function(foo, bar, hname){
13449                 if(hname == 'down' || this.scope.isExpanded()){
13450                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13451                 }
13452                 return true;
13453             },
13454
13455             forceKeyDown: true
13456         });
13457         
13458         
13459         this.queryDelay = Math.max(this.queryDelay || 10,
13460                 this.mode == 'local' ? 10 : 250);
13461         
13462         
13463         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13464         
13465         if(this.typeAhead){
13466             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13467         }
13468         if(this.editable !== false){
13469             this.inputEl().on("keyup", this.onKeyUp, this);
13470         }
13471         if(this.forceSelection){
13472             this.inputEl().on('blur', this.doForce, this);
13473         }
13474         
13475         if(this.multiple){
13476             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13477             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13478         }
13479     },
13480     
13481     initTickableEvents: function()
13482     {   
13483         this.createList();
13484         
13485         if(this.hiddenName){
13486             
13487             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13488             
13489             this.hiddenField.dom.value =
13490                 this.hiddenValue !== undefined ? this.hiddenValue :
13491                 this.value !== undefined ? this.value : '';
13492
13493             // prevent input submission
13494             this.el.dom.removeAttribute('name');
13495             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13496              
13497              
13498         }
13499         
13500 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13501         
13502         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13503         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13504         if(this.triggerList){
13505             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13506         }
13507          
13508         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13509         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13510         
13511         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13512         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13513         
13514         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13515         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13516         
13517         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13518         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13519         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13520         
13521         this.okBtn.hide();
13522         this.cancelBtn.hide();
13523         
13524         var _this = this;
13525         
13526         (function(){
13527             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13528             _this.list.setWidth(lw);
13529         }).defer(100);
13530         
13531         this.list.on('mouseover', this.onViewOver, this);
13532         this.list.on('mousemove', this.onViewMove, this);
13533         
13534         this.list.on('scroll', this.onViewScroll, this);
13535         
13536         if(!this.tpl){
13537             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}" type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13538         }
13539
13540         this.view = new Roo.View(this.list, this.tpl, {
13541             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13542         });
13543         
13544         //this.view.wrapEl.setDisplayed(false);
13545         this.view.on('click', this.onViewClick, this);
13546         
13547         
13548         
13549         this.store.on('beforeload', this.onBeforeLoad, this);
13550         this.store.on('load', this.onLoad, this);
13551         this.store.on('loadexception', this.onLoadException, this);
13552         
13553         if(this.editable){
13554             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13555                 "up" : function(e){
13556                     this.inKeyMode = true;
13557                     this.selectPrev();
13558                 },
13559
13560                 "down" : function(e){
13561                     this.inKeyMode = true;
13562                     this.selectNext();
13563                 },
13564
13565                 "enter" : function(e){
13566                     if(this.fireEvent("specialkey", this, e)){
13567                         this.onViewClick(false);
13568                     }
13569                     
13570                     return true;
13571                 },
13572
13573                 "esc" : function(e){
13574                     this.onTickableFooterButtonClick(e, false, false);
13575                 },
13576
13577                 "tab" : function(e){
13578                     this.fireEvent("specialkey", this, e);
13579                     
13580                     this.onTickableFooterButtonClick(e, false, false);
13581                     
13582                     return true;
13583                 },
13584
13585                 scope : this,
13586
13587                 doRelay : function(e, fn, key){
13588                     if(this.scope.isExpanded()){
13589                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13590                     }
13591                     return true;
13592                 },
13593
13594                 forceKeyDown: true
13595             });
13596         }
13597         
13598         this.queryDelay = Math.max(this.queryDelay || 10,
13599                 this.mode == 'local' ? 10 : 250);
13600         
13601         
13602         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13603         
13604         if(this.typeAhead){
13605             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13606         }
13607         
13608         if(this.editable !== false){
13609             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13610         }
13611         
13612         this.indicator = this.indicatorEl();
13613         
13614         if(this.indicator){
13615             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13616             this.indicator.hide();
13617         }
13618         
13619     },
13620
13621     onDestroy : function(){
13622         if(this.view){
13623             this.view.setStore(null);
13624             this.view.el.removeAllListeners();
13625             this.view.el.remove();
13626             this.view.purgeListeners();
13627         }
13628         if(this.list){
13629             this.list.dom.innerHTML  = '';
13630         }
13631         
13632         if(this.store){
13633             this.store.un('beforeload', this.onBeforeLoad, this);
13634             this.store.un('load', this.onLoad, this);
13635             this.store.un('loadexception', this.onLoadException, this);
13636         }
13637         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13638     },
13639
13640     // private
13641     fireKey : function(e){
13642         if(e.isNavKeyPress() && !this.list.isVisible()){
13643             this.fireEvent("specialkey", this, e);
13644         }
13645     },
13646
13647     // private
13648     onResize: function(w, h){
13649 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13650 //        
13651 //        if(typeof w != 'number'){
13652 //            // we do not handle it!?!?
13653 //            return;
13654 //        }
13655 //        var tw = this.trigger.getWidth();
13656 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13657 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13658 //        var x = w - tw;
13659 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13660 //            
13661 //        //this.trigger.setStyle('left', x+'px');
13662 //        
13663 //        if(this.list && this.listWidth === undefined){
13664 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13665 //            this.list.setWidth(lw);
13666 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13667 //        }
13668         
13669     
13670         
13671     },
13672
13673     /**
13674      * Allow or prevent the user from directly editing the field text.  If false is passed,
13675      * the user will only be able to select from the items defined in the dropdown list.  This method
13676      * is the runtime equivalent of setting the 'editable' config option at config time.
13677      * @param {Boolean} value True to allow the user to directly edit the field text
13678      */
13679     setEditable : function(value){
13680         if(value == this.editable){
13681             return;
13682         }
13683         this.editable = value;
13684         if(!value){
13685             this.inputEl().dom.setAttribute('readOnly', true);
13686             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13687             this.inputEl().addClass('x-combo-noedit');
13688         }else{
13689             this.inputEl().dom.setAttribute('readOnly', false);
13690             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13691             this.inputEl().removeClass('x-combo-noedit');
13692         }
13693     },
13694
13695     // private
13696     
13697     onBeforeLoad : function(combo,opts){
13698         if(!this.hasFocus){
13699             return;
13700         }
13701          if (!opts.add) {
13702             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13703          }
13704         this.restrictHeight();
13705         this.selectedIndex = -1;
13706     },
13707
13708     // private
13709     onLoad : function(){
13710         
13711         this.hasQuery = false;
13712         
13713         if(!this.hasFocus){
13714             return;
13715         }
13716         
13717         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13718             this.loading.hide();
13719         }
13720         
13721         if(this.store.getCount() > 0){
13722             
13723             this.expand();
13724             this.restrictHeight();
13725             if(this.lastQuery == this.allQuery){
13726                 if(this.editable && !this.tickable){
13727                     this.inputEl().dom.select();
13728                 }
13729                 
13730                 if(
13731                     !this.selectByValue(this.value, true) &&
13732                     this.autoFocus && 
13733                     (
13734                         !this.store.lastOptions ||
13735                         typeof(this.store.lastOptions.add) == 'undefined' || 
13736                         this.store.lastOptions.add != true
13737                     )
13738                 ){
13739                     this.select(0, true);
13740                 }
13741             }else{
13742                 if(this.autoFocus){
13743                     this.selectNext();
13744                 }
13745                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13746                     this.taTask.delay(this.typeAheadDelay);
13747                 }
13748             }
13749         }else{
13750             this.onEmptyResults();
13751         }
13752         
13753         //this.el.focus();
13754     },
13755     // private
13756     onLoadException : function()
13757     {
13758         this.hasQuery = false;
13759         
13760         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13761             this.loading.hide();
13762         }
13763         
13764         if(this.tickable && this.editable){
13765             return;
13766         }
13767         
13768         this.collapse();
13769         // only causes errors at present
13770         //Roo.log(this.store.reader.jsonData);
13771         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13772             // fixme
13773             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13774         //}
13775         
13776         
13777     },
13778     // private
13779     onTypeAhead : function(){
13780         if(this.store.getCount() > 0){
13781             var r = this.store.getAt(0);
13782             var newValue = r.data[this.displayField];
13783             var len = newValue.length;
13784             var selStart = this.getRawValue().length;
13785             
13786             if(selStart != len){
13787                 this.setRawValue(newValue);
13788                 this.selectText(selStart, newValue.length);
13789             }
13790         }
13791     },
13792
13793     // private
13794     onSelect : function(record, index){
13795         
13796         if(this.fireEvent('beforeselect', this, record, index) !== false){
13797         
13798             this.setFromData(index > -1 ? record.data : false);
13799             
13800             this.collapse();
13801             this.fireEvent('select', this, record, index);
13802         }
13803     },
13804
13805     /**
13806      * Returns the currently selected field value or empty string if no value is set.
13807      * @return {String} value The selected value
13808      */
13809     getValue : function()
13810     {
13811         if(Roo.isIOS && this.useNativeIOS){
13812             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13813         }
13814         
13815         if(this.multiple){
13816             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13817         }
13818         
13819         if(this.valueField){
13820             return typeof this.value != 'undefined' ? this.value : '';
13821         }else{
13822             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13823         }
13824     },
13825     
13826     getRawValue : function()
13827     {
13828         if(Roo.isIOS && this.useNativeIOS){
13829             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13830         }
13831         
13832         var v = this.inputEl().getValue();
13833         
13834         return v;
13835     },
13836
13837     /**
13838      * Clears any text/value currently set in the field
13839      */
13840     clearValue : function(){
13841         
13842         if(this.hiddenField){
13843             this.hiddenField.dom.value = '';
13844         }
13845         this.value = '';
13846         this.setRawValue('');
13847         this.lastSelectionText = '';
13848         this.lastData = false;
13849         
13850         var close = this.closeTriggerEl();
13851         
13852         if(close){
13853             close.hide();
13854         }
13855         
13856         this.validate();
13857         
13858     },
13859
13860     /**
13861      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13862      * will be displayed in the field.  If the value does not match the data value of an existing item,
13863      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13864      * Otherwise the field will be blank (although the value will still be set).
13865      * @param {String} value The value to match
13866      */
13867     setValue : function(v)
13868     {
13869         if(Roo.isIOS && this.useNativeIOS){
13870             this.setIOSValue(v);
13871             return;
13872         }
13873         
13874         if(this.multiple){
13875             this.syncValue();
13876             return;
13877         }
13878         
13879         var text = v;
13880         if(this.valueField){
13881             var r = this.findRecord(this.valueField, v);
13882             if(r){
13883                 text = r.data[this.displayField];
13884             }else if(this.valueNotFoundText !== undefined){
13885                 text = this.valueNotFoundText;
13886             }
13887         }
13888         this.lastSelectionText = text;
13889         if(this.hiddenField){
13890             this.hiddenField.dom.value = v;
13891         }
13892         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13893         this.value = v;
13894         
13895         var close = this.closeTriggerEl();
13896         
13897         if(close){
13898             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13899         }
13900         
13901         this.validate();
13902     },
13903     /**
13904      * @property {Object} the last set data for the element
13905      */
13906     
13907     lastData : false,
13908     /**
13909      * Sets the value of the field based on a object which is related to the record format for the store.
13910      * @param {Object} value the value to set as. or false on reset?
13911      */
13912     setFromData : function(o){
13913         
13914         if(this.multiple){
13915             this.addItem(o);
13916             return;
13917         }
13918             
13919         var dv = ''; // display value
13920         var vv = ''; // value value..
13921         this.lastData = o;
13922         if (this.displayField) {
13923             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13924         } else {
13925             // this is an error condition!!!
13926             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13927         }
13928         
13929         if(this.valueField){
13930             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13931         }
13932         
13933         var close = this.closeTriggerEl();
13934         
13935         if(close){
13936             if(dv.length || vv * 1 > 0){
13937                 close.show() ;
13938                 this.blockFocus=true;
13939             } else {
13940                 close.hide();
13941             }             
13942         }
13943         
13944         if(this.hiddenField){
13945             this.hiddenField.dom.value = vv;
13946             
13947             this.lastSelectionText = dv;
13948             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13949             this.value = vv;
13950             return;
13951         }
13952         // no hidden field.. - we store the value in 'value', but still display
13953         // display field!!!!
13954         this.lastSelectionText = dv;
13955         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13956         this.value = vv;
13957         
13958         
13959         
13960     },
13961     // private
13962     reset : function(){
13963         // overridden so that last data is reset..
13964         
13965         if(this.multiple){
13966             this.clearItem();
13967             return;
13968         }
13969         
13970         this.setValue(this.originalValue);
13971         //this.clearInvalid();
13972         this.lastData = false;
13973         if (this.view) {
13974             this.view.clearSelections();
13975         }
13976         
13977         this.validate();
13978     },
13979     // private
13980     findRecord : function(prop, value){
13981         var record;
13982         if(this.store.getCount() > 0){
13983             this.store.each(function(r){
13984                 if(r.data[prop] == value){
13985                     record = r;
13986                     return false;
13987                 }
13988                 return true;
13989             });
13990         }
13991         return record;
13992     },
13993     
13994     getName: function()
13995     {
13996         // returns hidden if it's set..
13997         if (!this.rendered) {return ''};
13998         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13999         
14000     },
14001     // private
14002     onViewMove : function(e, t){
14003         this.inKeyMode = false;
14004     },
14005
14006     // private
14007     onViewOver : function(e, t){
14008         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14009             return;
14010         }
14011         var item = this.view.findItemFromChild(t);
14012         
14013         if(item){
14014             var index = this.view.indexOf(item);
14015             this.select(index, false);
14016         }
14017     },
14018
14019     // private
14020     onViewClick : function(view, doFocus, el, e)
14021     {
14022         var index = this.view.getSelectedIndexes()[0];
14023         
14024         var r = this.store.getAt(index);
14025         
14026         if(this.tickable){
14027             
14028             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14029                 return;
14030             }
14031             
14032             var rm = false;
14033             var _this = this;
14034             
14035             Roo.each(this.tickItems, function(v,k){
14036                 
14037                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14038                     Roo.log(v);
14039                     _this.tickItems.splice(k, 1);
14040                     
14041                     if(typeof(e) == 'undefined' && view == false){
14042                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14043                     }
14044                     
14045                     rm = true;
14046                     return;
14047                 }
14048             });
14049             
14050             if(rm){
14051                 return;
14052             }
14053             
14054             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14055                 this.tickItems.push(r.data);
14056             }
14057             
14058             if(typeof(e) == 'undefined' && view == false){
14059                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14060             }
14061                     
14062             return;
14063         }
14064         
14065         if(r){
14066             this.onSelect(r, index);
14067         }
14068         if(doFocus !== false && !this.blockFocus){
14069             this.inputEl().focus();
14070         }
14071     },
14072
14073     // private
14074     restrictHeight : function(){
14075         //this.innerList.dom.style.height = '';
14076         //var inner = this.innerList.dom;
14077         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14078         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14079         //this.list.beginUpdate();
14080         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14081         this.list.alignTo(this.inputEl(), this.listAlign);
14082         this.list.alignTo(this.inputEl(), this.listAlign);
14083         //this.list.endUpdate();
14084     },
14085
14086     // private
14087     onEmptyResults : function(){
14088         
14089         if(this.tickable && this.editable){
14090             this.hasFocus = false;
14091             this.restrictHeight();
14092             return;
14093         }
14094         
14095         this.collapse();
14096     },
14097
14098     /**
14099      * Returns true if the dropdown list is expanded, else false.
14100      */
14101     isExpanded : function(){
14102         return this.list.isVisible();
14103     },
14104
14105     /**
14106      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14107      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14108      * @param {String} value The data value of the item to select
14109      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14110      * selected item if it is not currently in view (defaults to true)
14111      * @return {Boolean} True if the value matched an item in the list, else false
14112      */
14113     selectByValue : function(v, scrollIntoView){
14114         if(v !== undefined && v !== null){
14115             var r = this.findRecord(this.valueField || this.displayField, v);
14116             if(r){
14117                 this.select(this.store.indexOf(r), scrollIntoView);
14118                 return true;
14119             }
14120         }
14121         return false;
14122     },
14123
14124     /**
14125      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14126      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14127      * @param {Number} index The zero-based index of the list item to select
14128      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14129      * selected item if it is not currently in view (defaults to true)
14130      */
14131     select : function(index, scrollIntoView){
14132         this.selectedIndex = index;
14133         this.view.select(index);
14134         if(scrollIntoView !== false){
14135             var el = this.view.getNode(index);
14136             /*
14137              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14138              */
14139             if(el){
14140                 this.list.scrollChildIntoView(el, false);
14141             }
14142         }
14143     },
14144
14145     // private
14146     selectNext : function(){
14147         var ct = this.store.getCount();
14148         if(ct > 0){
14149             if(this.selectedIndex == -1){
14150                 this.select(0);
14151             }else if(this.selectedIndex < ct-1){
14152                 this.select(this.selectedIndex+1);
14153             }
14154         }
14155     },
14156
14157     // private
14158     selectPrev : function(){
14159         var ct = this.store.getCount();
14160         if(ct > 0){
14161             if(this.selectedIndex == -1){
14162                 this.select(0);
14163             }else if(this.selectedIndex != 0){
14164                 this.select(this.selectedIndex-1);
14165             }
14166         }
14167     },
14168
14169     // private
14170     onKeyUp : function(e){
14171         if(this.editable !== false && !e.isSpecialKey()){
14172             this.lastKey = e.getKey();
14173             this.dqTask.delay(this.queryDelay);
14174         }
14175     },
14176
14177     // private
14178     validateBlur : function(){
14179         return !this.list || !this.list.isVisible();   
14180     },
14181
14182     // private
14183     initQuery : function(){
14184         
14185         var v = this.getRawValue();
14186         
14187         if(this.tickable && this.editable){
14188             v = this.tickableInputEl().getValue();
14189         }
14190         
14191         this.doQuery(v);
14192     },
14193
14194     // private
14195     doForce : function(){
14196         if(this.inputEl().dom.value.length > 0){
14197             this.inputEl().dom.value =
14198                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14199              
14200         }
14201     },
14202
14203     /**
14204      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14205      * query allowing the query action to be canceled if needed.
14206      * @param {String} query The SQL query to execute
14207      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14208      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14209      * saved in the current store (defaults to false)
14210      */
14211     doQuery : function(q, forceAll){
14212         
14213         if(q === undefined || q === null){
14214             q = '';
14215         }
14216         var qe = {
14217             query: q,
14218             forceAll: forceAll,
14219             combo: this,
14220             cancel:false
14221         };
14222         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14223             return false;
14224         }
14225         q = qe.query;
14226         
14227         forceAll = qe.forceAll;
14228         if(forceAll === true || (q.length >= this.minChars)){
14229             
14230             this.hasQuery = true;
14231             
14232             if(this.lastQuery != q || this.alwaysQuery){
14233                 this.lastQuery = q;
14234                 if(this.mode == 'local'){
14235                     this.selectedIndex = -1;
14236                     if(forceAll){
14237                         this.store.clearFilter();
14238                     }else{
14239                         
14240                         if(this.specialFilter){
14241                             this.fireEvent('specialfilter', this);
14242                             this.onLoad();
14243                             return;
14244                         }
14245                         
14246                         this.store.filter(this.displayField, q);
14247                     }
14248                     
14249                     this.store.fireEvent("datachanged", this.store);
14250                     
14251                     this.onLoad();
14252                     
14253                     
14254                 }else{
14255                     
14256                     this.store.baseParams[this.queryParam] = q;
14257                     
14258                     var options = {params : this.getParams(q)};
14259                     
14260                     if(this.loadNext){
14261                         options.add = true;
14262                         options.params.start = this.page * this.pageSize;
14263                     }
14264                     
14265                     this.store.load(options);
14266                     
14267                     /*
14268                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14269                      *  we should expand the list on onLoad
14270                      *  so command out it
14271                      */
14272 //                    this.expand();
14273                 }
14274             }else{
14275                 this.selectedIndex = -1;
14276                 this.onLoad();   
14277             }
14278         }
14279         
14280         this.loadNext = false;
14281     },
14282     
14283     // private
14284     getParams : function(q){
14285         var p = {};
14286         //p[this.queryParam] = q;
14287         
14288         if(this.pageSize){
14289             p.start = 0;
14290             p.limit = this.pageSize;
14291         }
14292         return p;
14293     },
14294
14295     /**
14296      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14297      */
14298     collapse : function(){
14299         if(!this.isExpanded()){
14300             return;
14301         }
14302         
14303         this.list.hide();
14304         
14305         this.hasFocus = false;
14306         
14307         if(this.tickable){
14308             this.okBtn.hide();
14309             this.cancelBtn.hide();
14310             this.trigger.show();
14311             
14312             if(this.editable){
14313                 this.tickableInputEl().dom.value = '';
14314                 this.tickableInputEl().blur();
14315             }
14316             
14317         }
14318         
14319         Roo.get(document).un('mousedown', this.collapseIf, this);
14320         Roo.get(document).un('mousewheel', this.collapseIf, this);
14321         if (!this.editable) {
14322             Roo.get(document).un('keydown', this.listKeyPress, this);
14323         }
14324         this.fireEvent('collapse', this);
14325         
14326         this.validate();
14327     },
14328
14329     // private
14330     collapseIf : function(e){
14331         var in_combo  = e.within(this.el);
14332         var in_list =  e.within(this.list);
14333         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14334         
14335         if (in_combo || in_list || is_list) {
14336             //e.stopPropagation();
14337             return;
14338         }
14339         
14340         if(this.tickable){
14341             this.onTickableFooterButtonClick(e, false, false);
14342         }
14343
14344         this.collapse();
14345         
14346     },
14347
14348     /**
14349      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14350      */
14351     expand : function(){
14352        
14353         if(this.isExpanded() || !this.hasFocus){
14354             return;
14355         }
14356         
14357         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14358         this.list.setWidth(lw);
14359         
14360         Roo.log('expand');
14361         
14362         this.list.show();
14363         
14364         this.restrictHeight();
14365         
14366         if(this.tickable){
14367             
14368             this.tickItems = Roo.apply([], this.item);
14369             
14370             this.okBtn.show();
14371             this.cancelBtn.show();
14372             this.trigger.hide();
14373             
14374             if(this.editable){
14375                 this.tickableInputEl().focus();
14376             }
14377             
14378         }
14379         
14380         Roo.get(document).on('mousedown', this.collapseIf, this);
14381         Roo.get(document).on('mousewheel', this.collapseIf, this);
14382         if (!this.editable) {
14383             Roo.get(document).on('keydown', this.listKeyPress, this);
14384         }
14385         
14386         this.fireEvent('expand', this);
14387     },
14388
14389     // private
14390     // Implements the default empty TriggerField.onTriggerClick function
14391     onTriggerClick : function(e)
14392     {
14393         Roo.log('trigger click');
14394         
14395         if(this.disabled || !this.triggerList){
14396             return;
14397         }
14398         
14399         this.page = 0;
14400         this.loadNext = false;
14401         
14402         if(this.isExpanded()){
14403             this.collapse();
14404             if (!this.blockFocus) {
14405                 this.inputEl().focus();
14406             }
14407             
14408         }else {
14409             this.hasFocus = true;
14410             if(this.triggerAction == 'all') {
14411                 this.doQuery(this.allQuery, true);
14412             } else {
14413                 this.doQuery(this.getRawValue());
14414             }
14415             if (!this.blockFocus) {
14416                 this.inputEl().focus();
14417             }
14418         }
14419     },
14420     
14421     onTickableTriggerClick : function(e)
14422     {
14423         if(this.disabled){
14424             return;
14425         }
14426         
14427         this.page = 0;
14428         this.loadNext = false;
14429         this.hasFocus = true;
14430         
14431         if(this.triggerAction == 'all') {
14432             this.doQuery(this.allQuery, true);
14433         } else {
14434             this.doQuery(this.getRawValue());
14435         }
14436     },
14437     
14438     onSearchFieldClick : function(e)
14439     {
14440         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14441             this.onTickableFooterButtonClick(e, false, false);
14442             return;
14443         }
14444         
14445         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14446             return;
14447         }
14448         
14449         this.page = 0;
14450         this.loadNext = false;
14451         this.hasFocus = true;
14452         
14453         if(this.triggerAction == 'all') {
14454             this.doQuery(this.allQuery, true);
14455         } else {
14456             this.doQuery(this.getRawValue());
14457         }
14458     },
14459     
14460     listKeyPress : function(e)
14461     {
14462         //Roo.log('listkeypress');
14463         // scroll to first matching element based on key pres..
14464         if (e.isSpecialKey()) {
14465             return false;
14466         }
14467         var k = String.fromCharCode(e.getKey()).toUpperCase();
14468         //Roo.log(k);
14469         var match  = false;
14470         var csel = this.view.getSelectedNodes();
14471         var cselitem = false;
14472         if (csel.length) {
14473             var ix = this.view.indexOf(csel[0]);
14474             cselitem  = this.store.getAt(ix);
14475             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14476                 cselitem = false;
14477             }
14478             
14479         }
14480         
14481         this.store.each(function(v) { 
14482             if (cselitem) {
14483                 // start at existing selection.
14484                 if (cselitem.id == v.id) {
14485                     cselitem = false;
14486                 }
14487                 return true;
14488             }
14489                 
14490             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14491                 match = this.store.indexOf(v);
14492                 return false;
14493             }
14494             return true;
14495         }, this);
14496         
14497         if (match === false) {
14498             return true; // no more action?
14499         }
14500         // scroll to?
14501         this.view.select(match);
14502         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14503         sn.scrollIntoView(sn.dom.parentNode, false);
14504     },
14505     
14506     onViewScroll : function(e, t){
14507         
14508         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){
14509             return;
14510         }
14511         
14512         this.hasQuery = true;
14513         
14514         this.loading = this.list.select('.loading', true).first();
14515         
14516         if(this.loading === null){
14517             this.list.createChild({
14518                 tag: 'div',
14519                 cls: 'loading roo-select2-more-results roo-select2-active',
14520                 html: 'Loading more results...'
14521             });
14522             
14523             this.loading = this.list.select('.loading', true).first();
14524             
14525             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14526             
14527             this.loading.hide();
14528         }
14529         
14530         this.loading.show();
14531         
14532         var _combo = this;
14533         
14534         this.page++;
14535         this.loadNext = true;
14536         
14537         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14538         
14539         return;
14540     },
14541     
14542     addItem : function(o)
14543     {   
14544         var dv = ''; // display value
14545         
14546         if (this.displayField) {
14547             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14548         } else {
14549             // this is an error condition!!!
14550             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14551         }
14552         
14553         if(!dv.length){
14554             return;
14555         }
14556         
14557         var choice = this.choices.createChild({
14558             tag: 'li',
14559             cls: 'roo-select2-search-choice',
14560             cn: [
14561                 {
14562                     tag: 'div',
14563                     html: dv
14564                 },
14565                 {
14566                     tag: 'a',
14567                     href: '#',
14568                     cls: 'roo-select2-search-choice-close fa fa-times',
14569                     tabindex: '-1'
14570                 }
14571             ]
14572             
14573         }, this.searchField);
14574         
14575         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14576         
14577         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14578         
14579         this.item.push(o);
14580         
14581         this.lastData = o;
14582         
14583         this.syncValue();
14584         
14585         this.inputEl().dom.value = '';
14586         
14587         this.validate();
14588     },
14589     
14590     onRemoveItem : function(e, _self, o)
14591     {
14592         e.preventDefault();
14593         
14594         this.lastItem = Roo.apply([], this.item);
14595         
14596         var index = this.item.indexOf(o.data) * 1;
14597         
14598         if( index < 0){
14599             Roo.log('not this item?!');
14600             return;
14601         }
14602         
14603         this.item.splice(index, 1);
14604         o.item.remove();
14605         
14606         this.syncValue();
14607         
14608         this.fireEvent('remove', this, e);
14609         
14610         this.validate();
14611         
14612     },
14613     
14614     syncValue : function()
14615     {
14616         if(!this.item.length){
14617             this.clearValue();
14618             return;
14619         }
14620             
14621         var value = [];
14622         var _this = this;
14623         Roo.each(this.item, function(i){
14624             if(_this.valueField){
14625                 value.push(i[_this.valueField]);
14626                 return;
14627             }
14628
14629             value.push(i);
14630         });
14631
14632         this.value = value.join(',');
14633
14634         if(this.hiddenField){
14635             this.hiddenField.dom.value = this.value;
14636         }
14637         
14638         this.store.fireEvent("datachanged", this.store);
14639         
14640         this.validate();
14641     },
14642     
14643     clearItem : function()
14644     {
14645         if(!this.multiple){
14646             return;
14647         }
14648         
14649         this.item = [];
14650         
14651         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14652            c.remove();
14653         });
14654         
14655         this.syncValue();
14656         
14657         this.validate();
14658         
14659         if(this.tickable && !Roo.isTouch){
14660             this.view.refresh();
14661         }
14662     },
14663     
14664     inputEl: function ()
14665     {
14666         if(Roo.isIOS && this.useNativeIOS){
14667             return this.el.select('select.roo-ios-select', true).first();
14668         }
14669         
14670         if(Roo.isTouch && this.mobileTouchView){
14671             return this.el.select('input.form-control',true).first();
14672         }
14673         
14674         if(this.tickable){
14675             return this.searchField;
14676         }
14677         
14678         return this.el.select('input.form-control',true).first();
14679     },
14680     
14681     onTickableFooterButtonClick : function(e, btn, el)
14682     {
14683         e.preventDefault();
14684         
14685         this.lastItem = Roo.apply([], this.item);
14686         
14687         if(btn && btn.name == 'cancel'){
14688             this.tickItems = Roo.apply([], this.item);
14689             this.collapse();
14690             return;
14691         }
14692         
14693         this.clearItem();
14694         
14695         var _this = this;
14696         
14697         Roo.each(this.tickItems, function(o){
14698             _this.addItem(o);
14699         });
14700         
14701         this.collapse();
14702         
14703     },
14704     
14705     validate : function()
14706     {
14707         if(this.getEl().hasClass('hidden')){
14708             return true;
14709         }
14710         
14711         var v = this.getRawValue();
14712         
14713         if(this.multiple){
14714             v = this.getValue();
14715         }
14716         
14717         if(this.disabled || this.allowBlank || v.length){
14718             this.markValid();
14719             return true;
14720         }
14721         
14722         this.markInvalid();
14723         return false;
14724     },
14725     
14726     tickableInputEl : function()
14727     {
14728         if(!this.tickable || !this.editable){
14729             return this.inputEl();
14730         }
14731         
14732         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14733     },
14734     
14735     
14736     getAutoCreateTouchView : function()
14737     {
14738         var id = Roo.id();
14739         
14740         var cfg = {
14741             cls: 'form-group' //input-group
14742         };
14743         
14744         var input =  {
14745             tag: 'input',
14746             id : id,
14747             type : this.inputType,
14748             cls : 'form-control x-combo-noedit',
14749             autocomplete: 'new-password',
14750             placeholder : this.placeholder || '',
14751             readonly : true
14752         };
14753         
14754         if (this.name) {
14755             input.name = this.name;
14756         }
14757         
14758         if (this.size) {
14759             input.cls += ' input-' + this.size;
14760         }
14761         
14762         if (this.disabled) {
14763             input.disabled = true;
14764         }
14765         
14766         var inputblock = {
14767             cls : '',
14768             cn : [
14769                 input
14770             ]
14771         };
14772         
14773         if(this.before){
14774             inputblock.cls += ' input-group';
14775             
14776             inputblock.cn.unshift({
14777                 tag :'span',
14778                 cls : 'input-group-addon',
14779                 html : this.before
14780             });
14781         }
14782         
14783         if(this.removable && !this.multiple){
14784             inputblock.cls += ' roo-removable';
14785             
14786             inputblock.cn.push({
14787                 tag: 'button',
14788                 html : 'x',
14789                 cls : 'roo-combo-removable-btn close'
14790             });
14791         }
14792
14793         if(this.hasFeedback && !this.allowBlank){
14794             
14795             inputblock.cls += ' has-feedback';
14796             
14797             inputblock.cn.push({
14798                 tag: 'span',
14799                 cls: 'glyphicon form-control-feedback'
14800             });
14801             
14802         }
14803         
14804         if (this.after) {
14805             
14806             inputblock.cls += (this.before) ? '' : ' input-group';
14807             
14808             inputblock.cn.push({
14809                 tag :'span',
14810                 cls : 'input-group-addon',
14811                 html : this.after
14812             });
14813         }
14814
14815         var box = {
14816             tag: 'div',
14817             cn: [
14818                 {
14819                     tag: 'input',
14820                     type : 'hidden',
14821                     cls: 'form-hidden-field'
14822                 },
14823                 inputblock
14824             ]
14825             
14826         };
14827         
14828         if(this.multiple){
14829             box = {
14830                 tag: 'div',
14831                 cn: [
14832                     {
14833                         tag: 'input',
14834                         type : 'hidden',
14835                         cls: 'form-hidden-field'
14836                     },
14837                     {
14838                         tag: 'ul',
14839                         cls: 'roo-select2-choices',
14840                         cn:[
14841                             {
14842                                 tag: 'li',
14843                                 cls: 'roo-select2-search-field',
14844                                 cn: [
14845
14846                                     inputblock
14847                                 ]
14848                             }
14849                         ]
14850                     }
14851                 ]
14852             }
14853         };
14854         
14855         var combobox = {
14856             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14857             cn: [
14858                 box
14859             ]
14860         };
14861         
14862         if(!this.multiple && this.showToggleBtn){
14863             
14864             var caret = {
14865                         tag: 'span',
14866                         cls: 'caret'
14867             };
14868             
14869             if (this.caret != false) {
14870                 caret = {
14871                      tag: 'i',
14872                      cls: 'fa fa-' + this.caret
14873                 };
14874                 
14875             }
14876             
14877             combobox.cn.push({
14878                 tag :'span',
14879                 cls : 'input-group-addon btn dropdown-toggle',
14880                 cn : [
14881                     caret,
14882                     {
14883                         tag: 'span',
14884                         cls: 'combobox-clear',
14885                         cn  : [
14886                             {
14887                                 tag : 'i',
14888                                 cls: 'icon-remove'
14889                             }
14890                         ]
14891                     }
14892                 ]
14893
14894             })
14895         }
14896         
14897         if(this.multiple){
14898             combobox.cls += ' roo-select2-container-multi';
14899         }
14900         
14901         var align = this.labelAlign || this.parentLabelAlign();
14902         
14903         if (align ==='left' && this.fieldLabel.length) {
14904
14905             cfg.cn = [
14906                 {
14907                    tag : 'i',
14908                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14909                    tooltip : 'This field is required'
14910                 },
14911                 {
14912                     tag: 'label',
14913                     cls : 'control-label',
14914                     html : this.fieldLabel
14915
14916                 },
14917                 {
14918                     cls : '', 
14919                     cn: [
14920                         combobox
14921                     ]
14922                 }
14923             ];
14924             
14925             var labelCfg = cfg.cn[1];
14926             var contentCfg = cfg.cn[2];
14927             
14928
14929             if(this.indicatorpos == 'right'){
14930                 cfg.cn = [
14931                     {
14932                         tag: 'label',
14933                         'for' :  id,
14934                         cls : 'control-label',
14935                         cn : [
14936                             {
14937                                 tag : 'span',
14938                                 html : this.fieldLabel
14939                             },
14940                             {
14941                                 tag : 'i',
14942                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14943                                 tooltip : 'This field is required'
14944                             }
14945                         ]
14946                     },
14947                     {
14948                         cls : "",
14949                         cn: [
14950                             combobox
14951                         ]
14952                     }
14953
14954                 ];
14955                 
14956                 labelCfg = cfg.cn[0];
14957                 contentCfg = cfg.cn[1];
14958             }
14959             
14960            
14961             
14962             if(this.labelWidth > 12){
14963                 labelCfg.style = "width: " + this.labelWidth + 'px';
14964             }
14965             
14966             if(this.labelWidth < 13 && this.labelmd == 0){
14967                 this.labelmd = this.labelWidth;
14968             }
14969             
14970             if(this.labellg > 0){
14971                 labelCfg.cls += ' col-lg-' + this.labellg;
14972                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14973             }
14974             
14975             if(this.labelmd > 0){
14976                 labelCfg.cls += ' col-md-' + this.labelmd;
14977                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14978             }
14979             
14980             if(this.labelsm > 0){
14981                 labelCfg.cls += ' col-sm-' + this.labelsm;
14982                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14983             }
14984             
14985             if(this.labelxs > 0){
14986                 labelCfg.cls += ' col-xs-' + this.labelxs;
14987                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14988             }
14989                 
14990                 
14991         } else if ( this.fieldLabel.length) {
14992             cfg.cn = [
14993                 {
14994                    tag : 'i',
14995                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14996                    tooltip : 'This field is required'
14997                 },
14998                 {
14999                     tag: 'label',
15000                     cls : 'control-label',
15001                     html : this.fieldLabel
15002
15003                 },
15004                 {
15005                     cls : '', 
15006                     cn: [
15007                         combobox
15008                     ]
15009                 }
15010             ];
15011             
15012             if(this.indicatorpos == 'right'){
15013                 cfg.cn = [
15014                     {
15015                         tag: 'label',
15016                         cls : 'control-label',
15017                         html : this.fieldLabel,
15018                         cn : [
15019                             {
15020                                tag : 'i',
15021                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15022                                tooltip : 'This field is required'
15023                             }
15024                         ]
15025                     },
15026                     {
15027                         cls : '', 
15028                         cn: [
15029                             combobox
15030                         ]
15031                     }
15032                 ];
15033             }
15034         } else {
15035             cfg.cn = combobox;    
15036         }
15037         
15038         
15039         var settings = this;
15040         
15041         ['xs','sm','md','lg'].map(function(size){
15042             if (settings[size]) {
15043                 cfg.cls += ' col-' + size + '-' + settings[size];
15044             }
15045         });
15046         
15047         return cfg;
15048     },
15049     
15050     initTouchView : function()
15051     {
15052         this.renderTouchView();
15053         
15054         this.touchViewEl.on('scroll', function(){
15055             this.el.dom.scrollTop = 0;
15056         }, this);
15057         
15058         this.originalValue = this.getValue();
15059         
15060         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15061         
15062         this.inputEl().on("click", this.showTouchView, this);
15063         if (this.triggerEl) {
15064             this.triggerEl.on("click", this.showTouchView, this);
15065         }
15066         
15067         
15068         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15069         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15070         
15071         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15072         
15073         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15074         this.store.on('load', this.onTouchViewLoad, this);
15075         this.store.on('loadexception', this.onTouchViewLoadException, this);
15076         
15077         if(this.hiddenName){
15078             
15079             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15080             
15081             this.hiddenField.dom.value =
15082                 this.hiddenValue !== undefined ? this.hiddenValue :
15083                 this.value !== undefined ? this.value : '';
15084         
15085             this.el.dom.removeAttribute('name');
15086             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15087         }
15088         
15089         if(this.multiple){
15090             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15091             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15092         }
15093         
15094         if(this.removable && !this.multiple){
15095             var close = this.closeTriggerEl();
15096             if(close){
15097                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15098                 close.on('click', this.removeBtnClick, this, close);
15099             }
15100         }
15101         /*
15102          * fix the bug in Safari iOS8
15103          */
15104         this.inputEl().on("focus", function(e){
15105             document.activeElement.blur();
15106         }, this);
15107         
15108         return;
15109         
15110         
15111     },
15112     
15113     renderTouchView : function()
15114     {
15115         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15116         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15117         
15118         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15119         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15120         
15121         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15122         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15123         this.touchViewBodyEl.setStyle('overflow', 'auto');
15124         
15125         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15126         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15127         
15128         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15129         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15130         
15131     },
15132     
15133     showTouchView : function()
15134     {
15135         if(this.disabled){
15136             return;
15137         }
15138         
15139         this.touchViewHeaderEl.hide();
15140
15141         if(this.modalTitle.length){
15142             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15143             this.touchViewHeaderEl.show();
15144         }
15145
15146         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15147         this.touchViewEl.show();
15148
15149         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15150         
15151         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15152         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15153
15154         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15155
15156         if(this.modalTitle.length){
15157             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15158         }
15159         
15160         this.touchViewBodyEl.setHeight(bodyHeight);
15161
15162         if(this.animate){
15163             var _this = this;
15164             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15165         }else{
15166             this.touchViewEl.addClass('in');
15167         }
15168
15169         this.doTouchViewQuery();
15170         
15171     },
15172     
15173     hideTouchView : function()
15174     {
15175         this.touchViewEl.removeClass('in');
15176
15177         if(this.animate){
15178             var _this = this;
15179             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15180         }else{
15181             this.touchViewEl.setStyle('display', 'none');
15182         }
15183         
15184     },
15185     
15186     setTouchViewValue : function()
15187     {
15188         if(this.multiple){
15189             this.clearItem();
15190         
15191             var _this = this;
15192
15193             Roo.each(this.tickItems, function(o){
15194                 this.addItem(o);
15195             }, this);
15196         }
15197         
15198         this.hideTouchView();
15199     },
15200     
15201     doTouchViewQuery : function()
15202     {
15203         var qe = {
15204             query: '',
15205             forceAll: true,
15206             combo: this,
15207             cancel:false
15208         };
15209         
15210         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15211             return false;
15212         }
15213         
15214         if(!this.alwaysQuery || this.mode == 'local'){
15215             this.onTouchViewLoad();
15216             return;
15217         }
15218         
15219         this.store.load();
15220     },
15221     
15222     onTouchViewBeforeLoad : function(combo,opts)
15223     {
15224         return;
15225     },
15226
15227     // private
15228     onTouchViewLoad : function()
15229     {
15230         if(this.store.getCount() < 1){
15231             this.onTouchViewEmptyResults();
15232             return;
15233         }
15234         
15235         this.clearTouchView();
15236         
15237         var rawValue = this.getRawValue();
15238         
15239         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15240         
15241         this.tickItems = [];
15242         
15243         this.store.data.each(function(d, rowIndex){
15244             var row = this.touchViewListGroup.createChild(template);
15245             
15246             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15247                 row.addClass(d.data.cls);
15248             }
15249             
15250             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15251                 var cfg = {
15252                     data : d.data,
15253                     html : d.data[this.displayField]
15254                 };
15255                 
15256                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15257                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15258                 }
15259             }
15260             row.removeClass('selected');
15261             if(!this.multiple && this.valueField &&
15262                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15263             {
15264                 // radio buttons..
15265                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15266                 row.addClass('selected');
15267             }
15268             
15269             if(this.multiple && this.valueField &&
15270                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15271             {
15272                 
15273                 // checkboxes...
15274                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15275                 this.tickItems.push(d.data);
15276             }
15277             
15278             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15279             
15280         }, this);
15281         
15282         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15283         
15284         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15285
15286         if(this.modalTitle.length){
15287             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15288         }
15289
15290         var listHeight = this.touchViewListGroup.getHeight();
15291         
15292         var _this = this;
15293         
15294         if(firstChecked && listHeight > bodyHeight){
15295             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15296         }
15297         
15298     },
15299     
15300     onTouchViewLoadException : function()
15301     {
15302         this.hideTouchView();
15303     },
15304     
15305     onTouchViewEmptyResults : function()
15306     {
15307         this.clearTouchView();
15308         
15309         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15310         
15311         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15312         
15313     },
15314     
15315     clearTouchView : function()
15316     {
15317         this.touchViewListGroup.dom.innerHTML = '';
15318     },
15319     
15320     onTouchViewClick : function(e, el, o)
15321     {
15322         e.preventDefault();
15323         
15324         var row = o.row;
15325         var rowIndex = o.rowIndex;
15326         
15327         var r = this.store.getAt(rowIndex);
15328         
15329         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15330             
15331             if(!this.multiple){
15332                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15333                     c.dom.removeAttribute('checked');
15334                 }, this);
15335
15336                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15337
15338                 this.setFromData(r.data);
15339
15340                 var close = this.closeTriggerEl();
15341
15342                 if(close){
15343                     close.show();
15344                 }
15345
15346                 this.hideTouchView();
15347
15348                 this.fireEvent('select', this, r, rowIndex);
15349
15350                 return;
15351             }
15352
15353             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15354                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15355                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15356                 return;
15357             }
15358
15359             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15360             this.addItem(r.data);
15361             this.tickItems.push(r.data);
15362         }
15363     },
15364     
15365     getAutoCreateNativeIOS : function()
15366     {
15367         var cfg = {
15368             cls: 'form-group' //input-group,
15369         };
15370         
15371         var combobox =  {
15372             tag: 'select',
15373             cls : 'roo-ios-select'
15374         };
15375         
15376         if (this.name) {
15377             combobox.name = this.name;
15378         }
15379         
15380         if (this.disabled) {
15381             combobox.disabled = true;
15382         }
15383         
15384         var settings = this;
15385         
15386         ['xs','sm','md','lg'].map(function(size){
15387             if (settings[size]) {
15388                 cfg.cls += ' col-' + size + '-' + settings[size];
15389             }
15390         });
15391         
15392         cfg.cn = combobox;
15393         
15394         return cfg;
15395         
15396     },
15397     
15398     initIOSView : function()
15399     {
15400         this.store.on('load', this.onIOSViewLoad, this);
15401         
15402         return;
15403     },
15404     
15405     onIOSViewLoad : function()
15406     {
15407         if(this.store.getCount() < 1){
15408             return;
15409         }
15410         
15411         this.clearIOSView();
15412         
15413         if(this.allowBlank) {
15414             
15415             var default_text = '-- SELECT --';
15416             
15417             if(this.placeholder.length){
15418                 default_text = this.placeholder;
15419             }
15420             
15421             if(this.emptyTitle.length){
15422                 default_text += ' - ' + this.emptyTitle + ' -';
15423             }
15424             
15425             var opt = this.inputEl().createChild({
15426                 tag: 'option',
15427                 value : 0,
15428                 html : default_text
15429             });
15430             
15431             var o = {};
15432             o[this.valueField] = 0;
15433             o[this.displayField] = default_text;
15434             
15435             this.ios_options.push({
15436                 data : o,
15437                 el : opt
15438             });
15439             
15440         }
15441         
15442         this.store.data.each(function(d, rowIndex){
15443             
15444             var html = '';
15445             
15446             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15447                 html = d.data[this.displayField];
15448             }
15449             
15450             var value = '';
15451             
15452             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15453                 value = d.data[this.valueField];
15454             }
15455             
15456             var option = {
15457                 tag: 'option',
15458                 value : value,
15459                 html : html
15460             };
15461             
15462             if(this.value == d.data[this.valueField]){
15463                 option['selected'] = true;
15464             }
15465             
15466             var opt = this.inputEl().createChild(option);
15467             
15468             this.ios_options.push({
15469                 data : d.data,
15470                 el : opt
15471             });
15472             
15473         }, this);
15474         
15475         this.inputEl().on('change', function(){
15476            this.fireEvent('select', this);
15477         }, this);
15478         
15479     },
15480     
15481     clearIOSView: function()
15482     {
15483         this.inputEl().dom.innerHTML = '';
15484         
15485         this.ios_options = [];
15486     },
15487     
15488     setIOSValue: function(v)
15489     {
15490         this.value = v;
15491         
15492         if(!this.ios_options){
15493             return;
15494         }
15495         
15496         Roo.each(this.ios_options, function(opts){
15497            
15498            opts.el.dom.removeAttribute('selected');
15499            
15500            if(opts.data[this.valueField] != v){
15501                return;
15502            }
15503            
15504            opts.el.dom.setAttribute('selected', true);
15505            
15506         }, this);
15507     }
15508
15509     /** 
15510     * @cfg {Boolean} grow 
15511     * @hide 
15512     */
15513     /** 
15514     * @cfg {Number} growMin 
15515     * @hide 
15516     */
15517     /** 
15518     * @cfg {Number} growMax 
15519     * @hide 
15520     */
15521     /**
15522      * @hide
15523      * @method autoSize
15524      */
15525 });
15526
15527 Roo.apply(Roo.bootstrap.ComboBox,  {
15528     
15529     header : {
15530         tag: 'div',
15531         cls: 'modal-header',
15532         cn: [
15533             {
15534                 tag: 'h4',
15535                 cls: 'modal-title'
15536             }
15537         ]
15538     },
15539     
15540     body : {
15541         tag: 'div',
15542         cls: 'modal-body',
15543         cn: [
15544             {
15545                 tag: 'ul',
15546                 cls: 'list-group'
15547             }
15548         ]
15549     },
15550     
15551     listItemRadio : {
15552         tag: 'li',
15553         cls: 'list-group-item',
15554         cn: [
15555             {
15556                 tag: 'span',
15557                 cls: 'roo-combobox-list-group-item-value'
15558             },
15559             {
15560                 tag: 'div',
15561                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15562                 cn: [
15563                     {
15564                         tag: 'input',
15565                         type: 'radio'
15566                     },
15567                     {
15568                         tag: 'label'
15569                     }
15570                 ]
15571             }
15572         ]
15573     },
15574     
15575     listItemCheckbox : {
15576         tag: 'li',
15577         cls: 'list-group-item',
15578         cn: [
15579             {
15580                 tag: 'span',
15581                 cls: 'roo-combobox-list-group-item-value'
15582             },
15583             {
15584                 tag: 'div',
15585                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15586                 cn: [
15587                     {
15588                         tag: 'input',
15589                         type: 'checkbox'
15590                     },
15591                     {
15592                         tag: 'label'
15593                     }
15594                 ]
15595             }
15596         ]
15597     },
15598     
15599     emptyResult : {
15600         tag: 'div',
15601         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15602     },
15603     
15604     footer : {
15605         tag: 'div',
15606         cls: 'modal-footer',
15607         cn: [
15608             {
15609                 tag: 'div',
15610                 cls: 'row',
15611                 cn: [
15612                     {
15613                         tag: 'div',
15614                         cls: 'col-xs-6 text-left',
15615                         cn: {
15616                             tag: 'button',
15617                             cls: 'btn btn-danger roo-touch-view-cancel',
15618                             html: 'Cancel'
15619                         }
15620                     },
15621                     {
15622                         tag: 'div',
15623                         cls: 'col-xs-6 text-right',
15624                         cn: {
15625                             tag: 'button',
15626                             cls: 'btn btn-success roo-touch-view-ok',
15627                             html: 'OK'
15628                         }
15629                     }
15630                 ]
15631             }
15632         ]
15633         
15634     }
15635 });
15636
15637 Roo.apply(Roo.bootstrap.ComboBox,  {
15638     
15639     touchViewTemplate : {
15640         tag: 'div',
15641         cls: 'modal fade roo-combobox-touch-view',
15642         cn: [
15643             {
15644                 tag: 'div',
15645                 cls: 'modal-dialog',
15646                 style : 'position:fixed', // we have to fix position....
15647                 cn: [
15648                     {
15649                         tag: 'div',
15650                         cls: 'modal-content',
15651                         cn: [
15652                             Roo.bootstrap.ComboBox.header,
15653                             Roo.bootstrap.ComboBox.body,
15654                             Roo.bootstrap.ComboBox.footer
15655                         ]
15656                     }
15657                 ]
15658             }
15659         ]
15660     }
15661 });/*
15662  * Based on:
15663  * Ext JS Library 1.1.1
15664  * Copyright(c) 2006-2007, Ext JS, LLC.
15665  *
15666  * Originally Released Under LGPL - original licence link has changed is not relivant.
15667  *
15668  * Fork - LGPL
15669  * <script type="text/javascript">
15670  */
15671
15672 /**
15673  * @class Roo.View
15674  * @extends Roo.util.Observable
15675  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15676  * This class also supports single and multi selection modes. <br>
15677  * Create a data model bound view:
15678  <pre><code>
15679  var store = new Roo.data.Store(...);
15680
15681  var view = new Roo.View({
15682     el : "my-element",
15683     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15684  
15685     singleSelect: true,
15686     selectedClass: "ydataview-selected",
15687     store: store
15688  });
15689
15690  // listen for node click?
15691  view.on("click", function(vw, index, node, e){
15692  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15693  });
15694
15695  // load XML data
15696  dataModel.load("foobar.xml");
15697  </code></pre>
15698  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15699  * <br><br>
15700  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15701  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15702  * 
15703  * Note: old style constructor is still suported (container, template, config)
15704  * 
15705  * @constructor
15706  * Create a new View
15707  * @param {Object} config The config object
15708  * 
15709  */
15710 Roo.View = function(config, depreciated_tpl, depreciated_config){
15711     
15712     this.parent = false;
15713     
15714     if (typeof(depreciated_tpl) == 'undefined') {
15715         // new way.. - universal constructor.
15716         Roo.apply(this, config);
15717         this.el  = Roo.get(this.el);
15718     } else {
15719         // old format..
15720         this.el  = Roo.get(config);
15721         this.tpl = depreciated_tpl;
15722         Roo.apply(this, depreciated_config);
15723     }
15724     this.wrapEl  = this.el.wrap().wrap();
15725     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15726     
15727     
15728     if(typeof(this.tpl) == "string"){
15729         this.tpl = new Roo.Template(this.tpl);
15730     } else {
15731         // support xtype ctors..
15732         this.tpl = new Roo.factory(this.tpl, Roo);
15733     }
15734     
15735     
15736     this.tpl.compile();
15737     
15738     /** @private */
15739     this.addEvents({
15740         /**
15741          * @event beforeclick
15742          * Fires before a click is processed. Returns false to cancel the default action.
15743          * @param {Roo.View} this
15744          * @param {Number} index The index of the target node
15745          * @param {HTMLElement} node The target node
15746          * @param {Roo.EventObject} e The raw event object
15747          */
15748             "beforeclick" : true,
15749         /**
15750          * @event click
15751          * Fires when a template node is clicked.
15752          * @param {Roo.View} this
15753          * @param {Number} index The index of the target node
15754          * @param {HTMLElement} node The target node
15755          * @param {Roo.EventObject} e The raw event object
15756          */
15757             "click" : true,
15758         /**
15759          * @event dblclick
15760          * Fires when a template node is double clicked.
15761          * @param {Roo.View} this
15762          * @param {Number} index The index of the target node
15763          * @param {HTMLElement} node The target node
15764          * @param {Roo.EventObject} e The raw event object
15765          */
15766             "dblclick" : true,
15767         /**
15768          * @event contextmenu
15769          * Fires when a template node is right clicked.
15770          * @param {Roo.View} this
15771          * @param {Number} index The index of the target node
15772          * @param {HTMLElement} node The target node
15773          * @param {Roo.EventObject} e The raw event object
15774          */
15775             "contextmenu" : true,
15776         /**
15777          * @event selectionchange
15778          * Fires when the selected nodes change.
15779          * @param {Roo.View} this
15780          * @param {Array} selections Array of the selected nodes
15781          */
15782             "selectionchange" : true,
15783     
15784         /**
15785          * @event beforeselect
15786          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15787          * @param {Roo.View} this
15788          * @param {HTMLElement} node The node to be selected
15789          * @param {Array} selections Array of currently selected nodes
15790          */
15791             "beforeselect" : true,
15792         /**
15793          * @event preparedata
15794          * Fires on every row to render, to allow you to change the data.
15795          * @param {Roo.View} this
15796          * @param {Object} data to be rendered (change this)
15797          */
15798           "preparedata" : true
15799           
15800           
15801         });
15802
15803
15804
15805     this.el.on({
15806         "click": this.onClick,
15807         "dblclick": this.onDblClick,
15808         "contextmenu": this.onContextMenu,
15809         scope:this
15810     });
15811
15812     this.selections = [];
15813     this.nodes = [];
15814     this.cmp = new Roo.CompositeElementLite([]);
15815     if(this.store){
15816         this.store = Roo.factory(this.store, Roo.data);
15817         this.setStore(this.store, true);
15818     }
15819     
15820     if ( this.footer && this.footer.xtype) {
15821            
15822          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15823         
15824         this.footer.dataSource = this.store;
15825         this.footer.container = fctr;
15826         this.footer = Roo.factory(this.footer, Roo);
15827         fctr.insertFirst(this.el);
15828         
15829         // this is a bit insane - as the paging toolbar seems to detach the el..
15830 //        dom.parentNode.parentNode.parentNode
15831          // they get detached?
15832     }
15833     
15834     
15835     Roo.View.superclass.constructor.call(this);
15836     
15837     
15838 };
15839
15840 Roo.extend(Roo.View, Roo.util.Observable, {
15841     
15842      /**
15843      * @cfg {Roo.data.Store} store Data store to load data from.
15844      */
15845     store : false,
15846     
15847     /**
15848      * @cfg {String|Roo.Element} el The container element.
15849      */
15850     el : '',
15851     
15852     /**
15853      * @cfg {String|Roo.Template} tpl The template used by this View 
15854      */
15855     tpl : false,
15856     /**
15857      * @cfg {String} dataName the named area of the template to use as the data area
15858      *                          Works with domtemplates roo-name="name"
15859      */
15860     dataName: false,
15861     /**
15862      * @cfg {String} selectedClass The css class to add to selected nodes
15863      */
15864     selectedClass : "x-view-selected",
15865      /**
15866      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15867      */
15868     emptyText : "",
15869     
15870     /**
15871      * @cfg {String} text to display on mask (default Loading)
15872      */
15873     mask : false,
15874     /**
15875      * @cfg {Boolean} multiSelect Allow multiple selection
15876      */
15877     multiSelect : false,
15878     /**
15879      * @cfg {Boolean} singleSelect Allow single selection
15880      */
15881     singleSelect:  false,
15882     
15883     /**
15884      * @cfg {Boolean} toggleSelect - selecting 
15885      */
15886     toggleSelect : false,
15887     
15888     /**
15889      * @cfg {Boolean} tickable - selecting 
15890      */
15891     tickable : false,
15892     
15893     /**
15894      * Returns the element this view is bound to.
15895      * @return {Roo.Element}
15896      */
15897     getEl : function(){
15898         return this.wrapEl;
15899     },
15900     
15901     
15902
15903     /**
15904      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15905      */
15906     refresh : function(){
15907         //Roo.log('refresh');
15908         var t = this.tpl;
15909         
15910         // if we are using something like 'domtemplate', then
15911         // the what gets used is:
15912         // t.applySubtemplate(NAME, data, wrapping data..)
15913         // the outer template then get' applied with
15914         //     the store 'extra data'
15915         // and the body get's added to the
15916         //      roo-name="data" node?
15917         //      <span class='roo-tpl-{name}'></span> ?????
15918         
15919         
15920         
15921         this.clearSelections();
15922         this.el.update("");
15923         var html = [];
15924         var records = this.store.getRange();
15925         if(records.length < 1) {
15926             
15927             // is this valid??  = should it render a template??
15928             
15929             this.el.update(this.emptyText);
15930             return;
15931         }
15932         var el = this.el;
15933         if (this.dataName) {
15934             this.el.update(t.apply(this.store.meta)); //????
15935             el = this.el.child('.roo-tpl-' + this.dataName);
15936         }
15937         
15938         for(var i = 0, len = records.length; i < len; i++){
15939             var data = this.prepareData(records[i].data, i, records[i]);
15940             this.fireEvent("preparedata", this, data, i, records[i]);
15941             
15942             var d = Roo.apply({}, data);
15943             
15944             if(this.tickable){
15945                 Roo.apply(d, {'roo-id' : Roo.id()});
15946                 
15947                 var _this = this;
15948             
15949                 Roo.each(this.parent.item, function(item){
15950                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15951                         return;
15952                     }
15953                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15954                 });
15955             }
15956             
15957             html[html.length] = Roo.util.Format.trim(
15958                 this.dataName ?
15959                     t.applySubtemplate(this.dataName, d, this.store.meta) :
15960                     t.apply(d)
15961             );
15962         }
15963         
15964         
15965         
15966         el.update(html.join(""));
15967         this.nodes = el.dom.childNodes;
15968         this.updateIndexes(0);
15969     },
15970     
15971
15972     /**
15973      * Function to override to reformat the data that is sent to
15974      * the template for each node.
15975      * DEPRICATED - use the preparedata event handler.
15976      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15977      * a JSON object for an UpdateManager bound view).
15978      */
15979     prepareData : function(data, index, record)
15980     {
15981         this.fireEvent("preparedata", this, data, index, record);
15982         return data;
15983     },
15984
15985     onUpdate : function(ds, record){
15986         // Roo.log('on update');   
15987         this.clearSelections();
15988         var index = this.store.indexOf(record);
15989         var n = this.nodes[index];
15990         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15991         n.parentNode.removeChild(n);
15992         this.updateIndexes(index, index);
15993     },
15994
15995     
15996     
15997 // --------- FIXME     
15998     onAdd : function(ds, records, index)
15999     {
16000         //Roo.log(['on Add', ds, records, index] );        
16001         this.clearSelections();
16002         if(this.nodes.length == 0){
16003             this.refresh();
16004             return;
16005         }
16006         var n = this.nodes[index];
16007         for(var i = 0, len = records.length; i < len; i++){
16008             var d = this.prepareData(records[i].data, i, records[i]);
16009             if(n){
16010                 this.tpl.insertBefore(n, d);
16011             }else{
16012                 
16013                 this.tpl.append(this.el, d);
16014             }
16015         }
16016         this.updateIndexes(index);
16017     },
16018
16019     onRemove : function(ds, record, index){
16020        // Roo.log('onRemove');
16021         this.clearSelections();
16022         var el = this.dataName  ?
16023             this.el.child('.roo-tpl-' + this.dataName) :
16024             this.el; 
16025         
16026         el.dom.removeChild(this.nodes[index]);
16027         this.updateIndexes(index);
16028     },
16029
16030     /**
16031      * Refresh an individual node.
16032      * @param {Number} index
16033      */
16034     refreshNode : function(index){
16035         this.onUpdate(this.store, this.store.getAt(index));
16036     },
16037
16038     updateIndexes : function(startIndex, endIndex){
16039         var ns = this.nodes;
16040         startIndex = startIndex || 0;
16041         endIndex = endIndex || ns.length - 1;
16042         for(var i = startIndex; i <= endIndex; i++){
16043             ns[i].nodeIndex = i;
16044         }
16045     },
16046
16047     /**
16048      * Changes the data store this view uses and refresh the view.
16049      * @param {Store} store
16050      */
16051     setStore : function(store, initial){
16052         if(!initial && this.store){
16053             this.store.un("datachanged", this.refresh);
16054             this.store.un("add", this.onAdd);
16055             this.store.un("remove", this.onRemove);
16056             this.store.un("update", this.onUpdate);
16057             this.store.un("clear", this.refresh);
16058             this.store.un("beforeload", this.onBeforeLoad);
16059             this.store.un("load", this.onLoad);
16060             this.store.un("loadexception", this.onLoad);
16061         }
16062         if(store){
16063           
16064             store.on("datachanged", this.refresh, this);
16065             store.on("add", this.onAdd, this);
16066             store.on("remove", this.onRemove, this);
16067             store.on("update", this.onUpdate, this);
16068             store.on("clear", this.refresh, this);
16069             store.on("beforeload", this.onBeforeLoad, this);
16070             store.on("load", this.onLoad, this);
16071             store.on("loadexception", this.onLoad, this);
16072         }
16073         
16074         if(store){
16075             this.refresh();
16076         }
16077     },
16078     /**
16079      * onbeforeLoad - masks the loading area.
16080      *
16081      */
16082     onBeforeLoad : function(store,opts)
16083     {
16084          //Roo.log('onBeforeLoad');   
16085         if (!opts.add) {
16086             this.el.update("");
16087         }
16088         this.el.mask(this.mask ? this.mask : "Loading" ); 
16089     },
16090     onLoad : function ()
16091     {
16092         this.el.unmask();
16093     },
16094     
16095
16096     /**
16097      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16098      * @param {HTMLElement} node
16099      * @return {HTMLElement} The template node
16100      */
16101     findItemFromChild : function(node){
16102         var el = this.dataName  ?
16103             this.el.child('.roo-tpl-' + this.dataName,true) :
16104             this.el.dom; 
16105         
16106         if(!node || node.parentNode == el){
16107                     return node;
16108             }
16109             var p = node.parentNode;
16110             while(p && p != el){
16111             if(p.parentNode == el){
16112                 return p;
16113             }
16114             p = p.parentNode;
16115         }
16116             return null;
16117     },
16118
16119     /** @ignore */
16120     onClick : function(e){
16121         var item = this.findItemFromChild(e.getTarget());
16122         if(item){
16123             var index = this.indexOf(item);
16124             if(this.onItemClick(item, index, e) !== false){
16125                 this.fireEvent("click", this, index, item, e);
16126             }
16127         }else{
16128             this.clearSelections();
16129         }
16130     },
16131
16132     /** @ignore */
16133     onContextMenu : function(e){
16134         var item = this.findItemFromChild(e.getTarget());
16135         if(item){
16136             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16137         }
16138     },
16139
16140     /** @ignore */
16141     onDblClick : function(e){
16142         var item = this.findItemFromChild(e.getTarget());
16143         if(item){
16144             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16145         }
16146     },
16147
16148     onItemClick : function(item, index, e)
16149     {
16150         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16151             return false;
16152         }
16153         if (this.toggleSelect) {
16154             var m = this.isSelected(item) ? 'unselect' : 'select';
16155             //Roo.log(m);
16156             var _t = this;
16157             _t[m](item, true, false);
16158             return true;
16159         }
16160         if(this.multiSelect || this.singleSelect){
16161             if(this.multiSelect && e.shiftKey && this.lastSelection){
16162                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16163             }else{
16164                 this.select(item, this.multiSelect && e.ctrlKey);
16165                 this.lastSelection = item;
16166             }
16167             
16168             if(!this.tickable){
16169                 e.preventDefault();
16170             }
16171             
16172         }
16173         return true;
16174     },
16175
16176     /**
16177      * Get the number of selected nodes.
16178      * @return {Number}
16179      */
16180     getSelectionCount : function(){
16181         return this.selections.length;
16182     },
16183
16184     /**
16185      * Get the currently selected nodes.
16186      * @return {Array} An array of HTMLElements
16187      */
16188     getSelectedNodes : function(){
16189         return this.selections;
16190     },
16191
16192     /**
16193      * Get the indexes of the selected nodes.
16194      * @return {Array}
16195      */
16196     getSelectedIndexes : function(){
16197         var indexes = [], s = this.selections;
16198         for(var i = 0, len = s.length; i < len; i++){
16199             indexes.push(s[i].nodeIndex);
16200         }
16201         return indexes;
16202     },
16203
16204     /**
16205      * Clear all selections
16206      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16207      */
16208     clearSelections : function(suppressEvent){
16209         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16210             this.cmp.elements = this.selections;
16211             this.cmp.removeClass(this.selectedClass);
16212             this.selections = [];
16213             if(!suppressEvent){
16214                 this.fireEvent("selectionchange", this, this.selections);
16215             }
16216         }
16217     },
16218
16219     /**
16220      * Returns true if the passed node is selected
16221      * @param {HTMLElement/Number} node The node or node index
16222      * @return {Boolean}
16223      */
16224     isSelected : function(node){
16225         var s = this.selections;
16226         if(s.length < 1){
16227             return false;
16228         }
16229         node = this.getNode(node);
16230         return s.indexOf(node) !== -1;
16231     },
16232
16233     /**
16234      * Selects nodes.
16235      * @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
16236      * @param {Boolean} keepExisting (optional) true to keep existing selections
16237      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16238      */
16239     select : function(nodeInfo, keepExisting, suppressEvent){
16240         if(nodeInfo instanceof Array){
16241             if(!keepExisting){
16242                 this.clearSelections(true);
16243             }
16244             for(var i = 0, len = nodeInfo.length; i < len; i++){
16245                 this.select(nodeInfo[i], true, true);
16246             }
16247             return;
16248         } 
16249         var node = this.getNode(nodeInfo);
16250         if(!node || this.isSelected(node)){
16251             return; // already selected.
16252         }
16253         if(!keepExisting){
16254             this.clearSelections(true);
16255         }
16256         
16257         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16258             Roo.fly(node).addClass(this.selectedClass);
16259             this.selections.push(node);
16260             if(!suppressEvent){
16261                 this.fireEvent("selectionchange", this, this.selections);
16262             }
16263         }
16264         
16265         
16266     },
16267       /**
16268      * Unselects nodes.
16269      * @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
16270      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16271      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16272      */
16273     unselect : function(nodeInfo, keepExisting, suppressEvent)
16274     {
16275         if(nodeInfo instanceof Array){
16276             Roo.each(this.selections, function(s) {
16277                 this.unselect(s, nodeInfo);
16278             }, this);
16279             return;
16280         }
16281         var node = this.getNode(nodeInfo);
16282         if(!node || !this.isSelected(node)){
16283             //Roo.log("not selected");
16284             return; // not selected.
16285         }
16286         // fireevent???
16287         var ns = [];
16288         Roo.each(this.selections, function(s) {
16289             if (s == node ) {
16290                 Roo.fly(node).removeClass(this.selectedClass);
16291
16292                 return;
16293             }
16294             ns.push(s);
16295         },this);
16296         
16297         this.selections= ns;
16298         this.fireEvent("selectionchange", this, this.selections);
16299     },
16300
16301     /**
16302      * Gets a template node.
16303      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16304      * @return {HTMLElement} The node or null if it wasn't found
16305      */
16306     getNode : function(nodeInfo){
16307         if(typeof nodeInfo == "string"){
16308             return document.getElementById(nodeInfo);
16309         }else if(typeof nodeInfo == "number"){
16310             return this.nodes[nodeInfo];
16311         }
16312         return nodeInfo;
16313     },
16314
16315     /**
16316      * Gets a range template nodes.
16317      * @param {Number} startIndex
16318      * @param {Number} endIndex
16319      * @return {Array} An array of nodes
16320      */
16321     getNodes : function(start, end){
16322         var ns = this.nodes;
16323         start = start || 0;
16324         end = typeof end == "undefined" ? ns.length - 1 : end;
16325         var nodes = [];
16326         if(start <= end){
16327             for(var i = start; i <= end; i++){
16328                 nodes.push(ns[i]);
16329             }
16330         } else{
16331             for(var i = start; i >= end; i--){
16332                 nodes.push(ns[i]);
16333             }
16334         }
16335         return nodes;
16336     },
16337
16338     /**
16339      * Finds the index of the passed node
16340      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16341      * @return {Number} The index of the node or -1
16342      */
16343     indexOf : function(node){
16344         node = this.getNode(node);
16345         if(typeof node.nodeIndex == "number"){
16346             return node.nodeIndex;
16347         }
16348         var ns = this.nodes;
16349         for(var i = 0, len = ns.length; i < len; i++){
16350             if(ns[i] == node){
16351                 return i;
16352             }
16353         }
16354         return -1;
16355     }
16356 });
16357 /*
16358  * - LGPL
16359  *
16360  * based on jquery fullcalendar
16361  * 
16362  */
16363
16364 Roo.bootstrap = Roo.bootstrap || {};
16365 /**
16366  * @class Roo.bootstrap.Calendar
16367  * @extends Roo.bootstrap.Component
16368  * Bootstrap Calendar class
16369  * @cfg {Boolean} loadMask (true|false) default false
16370  * @cfg {Object} header generate the user specific header of the calendar, default false
16371
16372  * @constructor
16373  * Create a new Container
16374  * @param {Object} config The config object
16375  */
16376
16377
16378
16379 Roo.bootstrap.Calendar = function(config){
16380     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16381      this.addEvents({
16382         /**
16383              * @event select
16384              * Fires when a date is selected
16385              * @param {DatePicker} this
16386              * @param {Date} date The selected date
16387              */
16388         'select': true,
16389         /**
16390              * @event monthchange
16391              * Fires when the displayed month changes 
16392              * @param {DatePicker} this
16393              * @param {Date} date The selected month
16394              */
16395         'monthchange': true,
16396         /**
16397              * @event evententer
16398              * Fires when mouse over an event
16399              * @param {Calendar} this
16400              * @param {event} Event
16401              */
16402         'evententer': true,
16403         /**
16404              * @event eventleave
16405              * Fires when the mouse leaves an
16406              * @param {Calendar} this
16407              * @param {event}
16408              */
16409         'eventleave': true,
16410         /**
16411              * @event eventclick
16412              * Fires when the mouse click an
16413              * @param {Calendar} this
16414              * @param {event}
16415              */
16416         'eventclick': true
16417         
16418     });
16419
16420 };
16421
16422 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16423     
16424      /**
16425      * @cfg {Number} startDay
16426      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16427      */
16428     startDay : 0,
16429     
16430     loadMask : false,
16431     
16432     header : false,
16433       
16434     getAutoCreate : function(){
16435         
16436         
16437         var fc_button = function(name, corner, style, content ) {
16438             return Roo.apply({},{
16439                 tag : 'span',
16440                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16441                          (corner.length ?
16442                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16443                             ''
16444                         ),
16445                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16446                 unselectable: 'on'
16447             });
16448         };
16449         
16450         var header = {};
16451         
16452         if(!this.header){
16453             header = {
16454                 tag : 'table',
16455                 cls : 'fc-header',
16456                 style : 'width:100%',
16457                 cn : [
16458                     {
16459                         tag: 'tr',
16460                         cn : [
16461                             {
16462                                 tag : 'td',
16463                                 cls : 'fc-header-left',
16464                                 cn : [
16465                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16466                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16467                                     { tag: 'span', cls: 'fc-header-space' },
16468                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16469
16470
16471                                 ]
16472                             },
16473
16474                             {
16475                                 tag : 'td',
16476                                 cls : 'fc-header-center',
16477                                 cn : [
16478                                     {
16479                                         tag: 'span',
16480                                         cls: 'fc-header-title',
16481                                         cn : {
16482                                             tag: 'H2',
16483                                             html : 'month / year'
16484                                         }
16485                                     }
16486
16487                                 ]
16488                             },
16489                             {
16490                                 tag : 'td',
16491                                 cls : 'fc-header-right',
16492                                 cn : [
16493                               /*      fc_button('month', 'left', '', 'month' ),
16494                                     fc_button('week', '', '', 'week' ),
16495                                     fc_button('day', 'right', '', 'day' )
16496                                 */    
16497
16498                                 ]
16499                             }
16500
16501                         ]
16502                     }
16503                 ]
16504             };
16505         }
16506         
16507         header = this.header;
16508         
16509        
16510         var cal_heads = function() {
16511             var ret = [];
16512             // fixme - handle this.
16513             
16514             for (var i =0; i < Date.dayNames.length; i++) {
16515                 var d = Date.dayNames[i];
16516                 ret.push({
16517                     tag: 'th',
16518                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16519                     html : d.substring(0,3)
16520                 });
16521                 
16522             }
16523             ret[0].cls += ' fc-first';
16524             ret[6].cls += ' fc-last';
16525             return ret;
16526         };
16527         var cal_cell = function(n) {
16528             return  {
16529                 tag: 'td',
16530                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16531                 cn : [
16532                     {
16533                         cn : [
16534                             {
16535                                 cls: 'fc-day-number',
16536                                 html: 'D'
16537                             },
16538                             {
16539                                 cls: 'fc-day-content',
16540                              
16541                                 cn : [
16542                                      {
16543                                         style: 'position: relative;' // height: 17px;
16544                                     }
16545                                 ]
16546                             }
16547                             
16548                             
16549                         ]
16550                     }
16551                 ]
16552                 
16553             }
16554         };
16555         var cal_rows = function() {
16556             
16557             var ret = [];
16558             for (var r = 0; r < 6; r++) {
16559                 var row= {
16560                     tag : 'tr',
16561                     cls : 'fc-week',
16562                     cn : []
16563                 };
16564                 
16565                 for (var i =0; i < Date.dayNames.length; i++) {
16566                     var d = Date.dayNames[i];
16567                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16568
16569                 }
16570                 row.cn[0].cls+=' fc-first';
16571                 row.cn[0].cn[0].style = 'min-height:90px';
16572                 row.cn[6].cls+=' fc-last';
16573                 ret.push(row);
16574                 
16575             }
16576             ret[0].cls += ' fc-first';
16577             ret[4].cls += ' fc-prev-last';
16578             ret[5].cls += ' fc-last';
16579             return ret;
16580             
16581         };
16582         
16583         var cal_table = {
16584             tag: 'table',
16585             cls: 'fc-border-separate',
16586             style : 'width:100%',
16587             cellspacing  : 0,
16588             cn : [
16589                 { 
16590                     tag: 'thead',
16591                     cn : [
16592                         { 
16593                             tag: 'tr',
16594                             cls : 'fc-first fc-last',
16595                             cn : cal_heads()
16596                         }
16597                     ]
16598                 },
16599                 { 
16600                     tag: 'tbody',
16601                     cn : cal_rows()
16602                 }
16603                   
16604             ]
16605         };
16606          
16607          var cfg = {
16608             cls : 'fc fc-ltr',
16609             cn : [
16610                 header,
16611                 {
16612                     cls : 'fc-content',
16613                     style : "position: relative;",
16614                     cn : [
16615                         {
16616                             cls : 'fc-view fc-view-month fc-grid',
16617                             style : 'position: relative',
16618                             unselectable : 'on',
16619                             cn : [
16620                                 {
16621                                     cls : 'fc-event-container',
16622                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16623                                 },
16624                                 cal_table
16625                             ]
16626                         }
16627                     ]
16628     
16629                 }
16630            ] 
16631             
16632         };
16633         
16634          
16635         
16636         return cfg;
16637     },
16638     
16639     
16640     initEvents : function()
16641     {
16642         if(!this.store){
16643             throw "can not find store for calendar";
16644         }
16645         
16646         var mark = {
16647             tag: "div",
16648             cls:"x-dlg-mask",
16649             style: "text-align:center",
16650             cn: [
16651                 {
16652                     tag: "div",
16653                     style: "background-color:white;width:50%;margin:250 auto",
16654                     cn: [
16655                         {
16656                             tag: "img",
16657                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16658                         },
16659                         {
16660                             tag: "span",
16661                             html: "Loading"
16662                         }
16663                         
16664                     ]
16665                 }
16666             ]
16667         };
16668         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16669         
16670         var size = this.el.select('.fc-content', true).first().getSize();
16671         this.maskEl.setSize(size.width, size.height);
16672         this.maskEl.enableDisplayMode("block");
16673         if(!this.loadMask){
16674             this.maskEl.hide();
16675         }
16676         
16677         this.store = Roo.factory(this.store, Roo.data);
16678         this.store.on('load', this.onLoad, this);
16679         this.store.on('beforeload', this.onBeforeLoad, this);
16680         
16681         this.resize();
16682         
16683         this.cells = this.el.select('.fc-day',true);
16684         //Roo.log(this.cells);
16685         this.textNodes = this.el.query('.fc-day-number');
16686         this.cells.addClassOnOver('fc-state-hover');
16687         
16688         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16689         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16690         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16691         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16692         
16693         this.on('monthchange', this.onMonthChange, this);
16694         
16695         this.update(new Date().clearTime());
16696     },
16697     
16698     resize : function() {
16699         var sz  = this.el.getSize();
16700         
16701         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16702         this.el.select('.fc-day-content div',true).setHeight(34);
16703     },
16704     
16705     
16706     // private
16707     showPrevMonth : function(e){
16708         this.update(this.activeDate.add("mo", -1));
16709     },
16710     showToday : function(e){
16711         this.update(new Date().clearTime());
16712     },
16713     // private
16714     showNextMonth : function(e){
16715         this.update(this.activeDate.add("mo", 1));
16716     },
16717
16718     // private
16719     showPrevYear : function(){
16720         this.update(this.activeDate.add("y", -1));
16721     },
16722
16723     // private
16724     showNextYear : function(){
16725         this.update(this.activeDate.add("y", 1));
16726     },
16727
16728     
16729    // private
16730     update : function(date)
16731     {
16732         var vd = this.activeDate;
16733         this.activeDate = date;
16734 //        if(vd && this.el){
16735 //            var t = date.getTime();
16736 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16737 //                Roo.log('using add remove');
16738 //                
16739 //                this.fireEvent('monthchange', this, date);
16740 //                
16741 //                this.cells.removeClass("fc-state-highlight");
16742 //                this.cells.each(function(c){
16743 //                   if(c.dateValue == t){
16744 //                       c.addClass("fc-state-highlight");
16745 //                       setTimeout(function(){
16746 //                            try{c.dom.firstChild.focus();}catch(e){}
16747 //                       }, 50);
16748 //                       return false;
16749 //                   }
16750 //                   return true;
16751 //                });
16752 //                return;
16753 //            }
16754 //        }
16755         
16756         var days = date.getDaysInMonth();
16757         
16758         var firstOfMonth = date.getFirstDateOfMonth();
16759         var startingPos = firstOfMonth.getDay()-this.startDay;
16760         
16761         if(startingPos < this.startDay){
16762             startingPos += 7;
16763         }
16764         
16765         var pm = date.add(Date.MONTH, -1);
16766         var prevStart = pm.getDaysInMonth()-startingPos;
16767 //        
16768         this.cells = this.el.select('.fc-day',true);
16769         this.textNodes = this.el.query('.fc-day-number');
16770         this.cells.addClassOnOver('fc-state-hover');
16771         
16772         var cells = this.cells.elements;
16773         var textEls = this.textNodes;
16774         
16775         Roo.each(cells, function(cell){
16776             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16777         });
16778         
16779         days += startingPos;
16780
16781         // convert everything to numbers so it's fast
16782         var day = 86400000;
16783         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16784         //Roo.log(d);
16785         //Roo.log(pm);
16786         //Roo.log(prevStart);
16787         
16788         var today = new Date().clearTime().getTime();
16789         var sel = date.clearTime().getTime();
16790         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16791         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16792         var ddMatch = this.disabledDatesRE;
16793         var ddText = this.disabledDatesText;
16794         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16795         var ddaysText = this.disabledDaysText;
16796         var format = this.format;
16797         
16798         var setCellClass = function(cal, cell){
16799             cell.row = 0;
16800             cell.events = [];
16801             cell.more = [];
16802             //Roo.log('set Cell Class');
16803             cell.title = "";
16804             var t = d.getTime();
16805             
16806             //Roo.log(d);
16807             
16808             cell.dateValue = t;
16809             if(t == today){
16810                 cell.className += " fc-today";
16811                 cell.className += " fc-state-highlight";
16812                 cell.title = cal.todayText;
16813             }
16814             if(t == sel){
16815                 // disable highlight in other month..
16816                 //cell.className += " fc-state-highlight";
16817                 
16818             }
16819             // disabling
16820             if(t < min) {
16821                 cell.className = " fc-state-disabled";
16822                 cell.title = cal.minText;
16823                 return;
16824             }
16825             if(t > max) {
16826                 cell.className = " fc-state-disabled";
16827                 cell.title = cal.maxText;
16828                 return;
16829             }
16830             if(ddays){
16831                 if(ddays.indexOf(d.getDay()) != -1){
16832                     cell.title = ddaysText;
16833                     cell.className = " fc-state-disabled";
16834                 }
16835             }
16836             if(ddMatch && format){
16837                 var fvalue = d.dateFormat(format);
16838                 if(ddMatch.test(fvalue)){
16839                     cell.title = ddText.replace("%0", fvalue);
16840                     cell.className = " fc-state-disabled";
16841                 }
16842             }
16843             
16844             if (!cell.initialClassName) {
16845                 cell.initialClassName = cell.dom.className;
16846             }
16847             
16848             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16849         };
16850
16851         var i = 0;
16852         
16853         for(; i < startingPos; i++) {
16854             textEls[i].innerHTML = (++prevStart);
16855             d.setDate(d.getDate()+1);
16856             
16857             cells[i].className = "fc-past fc-other-month";
16858             setCellClass(this, cells[i]);
16859         }
16860         
16861         var intDay = 0;
16862         
16863         for(; i < days; i++){
16864             intDay = i - startingPos + 1;
16865             textEls[i].innerHTML = (intDay);
16866             d.setDate(d.getDate()+1);
16867             
16868             cells[i].className = ''; // "x-date-active";
16869             setCellClass(this, cells[i]);
16870         }
16871         var extraDays = 0;
16872         
16873         for(; i < 42; i++) {
16874             textEls[i].innerHTML = (++extraDays);
16875             d.setDate(d.getDate()+1);
16876             
16877             cells[i].className = "fc-future fc-other-month";
16878             setCellClass(this, cells[i]);
16879         }
16880         
16881         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16882         
16883         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16884         
16885         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16886         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16887         
16888         if(totalRows != 6){
16889             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16890             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16891         }
16892         
16893         this.fireEvent('monthchange', this, date);
16894         
16895         
16896         /*
16897         if(!this.internalRender){
16898             var main = this.el.dom.firstChild;
16899             var w = main.offsetWidth;
16900             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16901             Roo.fly(main).setWidth(w);
16902             this.internalRender = true;
16903             // opera does not respect the auto grow header center column
16904             // then, after it gets a width opera refuses to recalculate
16905             // without a second pass
16906             if(Roo.isOpera && !this.secondPass){
16907                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16908                 this.secondPass = true;
16909                 this.update.defer(10, this, [date]);
16910             }
16911         }
16912         */
16913         
16914     },
16915     
16916     findCell : function(dt) {
16917         dt = dt.clearTime().getTime();
16918         var ret = false;
16919         this.cells.each(function(c){
16920             //Roo.log("check " +c.dateValue + '?=' + dt);
16921             if(c.dateValue == dt){
16922                 ret = c;
16923                 return false;
16924             }
16925             return true;
16926         });
16927         
16928         return ret;
16929     },
16930     
16931     findCells : function(ev) {
16932         var s = ev.start.clone().clearTime().getTime();
16933        // Roo.log(s);
16934         var e= ev.end.clone().clearTime().getTime();
16935        // Roo.log(e);
16936         var ret = [];
16937         this.cells.each(function(c){
16938              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16939             
16940             if(c.dateValue > e){
16941                 return ;
16942             }
16943             if(c.dateValue < s){
16944                 return ;
16945             }
16946             ret.push(c);
16947         });
16948         
16949         return ret;    
16950     },
16951     
16952 //    findBestRow: function(cells)
16953 //    {
16954 //        var ret = 0;
16955 //        
16956 //        for (var i =0 ; i < cells.length;i++) {
16957 //            ret  = Math.max(cells[i].rows || 0,ret);
16958 //        }
16959 //        return ret;
16960 //        
16961 //    },
16962     
16963     
16964     addItem : function(ev)
16965     {
16966         // look for vertical location slot in
16967         var cells = this.findCells(ev);
16968         
16969 //        ev.row = this.findBestRow(cells);
16970         
16971         // work out the location.
16972         
16973         var crow = false;
16974         var rows = [];
16975         for(var i =0; i < cells.length; i++) {
16976             
16977             cells[i].row = cells[0].row;
16978             
16979             if(i == 0){
16980                 cells[i].row = cells[i].row + 1;
16981             }
16982             
16983             if (!crow) {
16984                 crow = {
16985                     start : cells[i],
16986                     end :  cells[i]
16987                 };
16988                 continue;
16989             }
16990             if (crow.start.getY() == cells[i].getY()) {
16991                 // on same row.
16992                 crow.end = cells[i];
16993                 continue;
16994             }
16995             // different row.
16996             rows.push(crow);
16997             crow = {
16998                 start: cells[i],
16999                 end : cells[i]
17000             };
17001             
17002         }
17003         
17004         rows.push(crow);
17005         ev.els = [];
17006         ev.rows = rows;
17007         ev.cells = cells;
17008         
17009         cells[0].events.push(ev);
17010         
17011         this.calevents.push(ev);
17012     },
17013     
17014     clearEvents: function() {
17015         
17016         if(!this.calevents){
17017             return;
17018         }
17019         
17020         Roo.each(this.cells.elements, function(c){
17021             c.row = 0;
17022             c.events = [];
17023             c.more = [];
17024         });
17025         
17026         Roo.each(this.calevents, function(e) {
17027             Roo.each(e.els, function(el) {
17028                 el.un('mouseenter' ,this.onEventEnter, this);
17029                 el.un('mouseleave' ,this.onEventLeave, this);
17030                 el.remove();
17031             },this);
17032         },this);
17033         
17034         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17035             e.remove();
17036         });
17037         
17038     },
17039     
17040     renderEvents: function()
17041     {   
17042         var _this = this;
17043         
17044         this.cells.each(function(c) {
17045             
17046             if(c.row < 5){
17047                 return;
17048             }
17049             
17050             var ev = c.events;
17051             
17052             var r = 4;
17053             if(c.row != c.events.length){
17054                 r = 4 - (4 - (c.row - c.events.length));
17055             }
17056             
17057             c.events = ev.slice(0, r);
17058             c.more = ev.slice(r);
17059             
17060             if(c.more.length && c.more.length == 1){
17061                 c.events.push(c.more.pop());
17062             }
17063             
17064             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17065             
17066         });
17067             
17068         this.cells.each(function(c) {
17069             
17070             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17071             
17072             
17073             for (var e = 0; e < c.events.length; e++){
17074                 var ev = c.events[e];
17075                 var rows = ev.rows;
17076                 
17077                 for(var i = 0; i < rows.length; i++) {
17078                 
17079                     // how many rows should it span..
17080
17081                     var  cfg = {
17082                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17083                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17084
17085                         unselectable : "on",
17086                         cn : [
17087                             {
17088                                 cls: 'fc-event-inner',
17089                                 cn : [
17090     //                                {
17091     //                                  tag:'span',
17092     //                                  cls: 'fc-event-time',
17093     //                                  html : cells.length > 1 ? '' : ev.time
17094     //                                },
17095                                     {
17096                                       tag:'span',
17097                                       cls: 'fc-event-title',
17098                                       html : String.format('{0}', ev.title)
17099                                     }
17100
17101
17102                                 ]
17103                             },
17104                             {
17105                                 cls: 'ui-resizable-handle ui-resizable-e',
17106                                 html : '&nbsp;&nbsp;&nbsp'
17107                             }
17108
17109                         ]
17110                     };
17111
17112                     if (i == 0) {
17113                         cfg.cls += ' fc-event-start';
17114                     }
17115                     if ((i+1) == rows.length) {
17116                         cfg.cls += ' fc-event-end';
17117                     }
17118
17119                     var ctr = _this.el.select('.fc-event-container',true).first();
17120                     var cg = ctr.createChild(cfg);
17121
17122                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17123                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17124
17125                     var r = (c.more.length) ? 1 : 0;
17126                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17127                     cg.setWidth(ebox.right - sbox.x -2);
17128
17129                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17130                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17131                     cg.on('click', _this.onEventClick, _this, ev);
17132
17133                     ev.els.push(cg);
17134                     
17135                 }
17136                 
17137             }
17138             
17139             
17140             if(c.more.length){
17141                 var  cfg = {
17142                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17143                     style : 'position: absolute',
17144                     unselectable : "on",
17145                     cn : [
17146                         {
17147                             cls: 'fc-event-inner',
17148                             cn : [
17149                                 {
17150                                   tag:'span',
17151                                   cls: 'fc-event-title',
17152                                   html : 'More'
17153                                 }
17154
17155
17156                             ]
17157                         },
17158                         {
17159                             cls: 'ui-resizable-handle ui-resizable-e',
17160                             html : '&nbsp;&nbsp;&nbsp'
17161                         }
17162
17163                     ]
17164                 };
17165
17166                 var ctr = _this.el.select('.fc-event-container',true).first();
17167                 var cg = ctr.createChild(cfg);
17168
17169                 var sbox = c.select('.fc-day-content',true).first().getBox();
17170                 var ebox = c.select('.fc-day-content',true).first().getBox();
17171                 //Roo.log(cg);
17172                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17173                 cg.setWidth(ebox.right - sbox.x -2);
17174
17175                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17176                 
17177             }
17178             
17179         });
17180         
17181         
17182         
17183     },
17184     
17185     onEventEnter: function (e, el,event,d) {
17186         this.fireEvent('evententer', this, el, event);
17187     },
17188     
17189     onEventLeave: function (e, el,event,d) {
17190         this.fireEvent('eventleave', this, el, event);
17191     },
17192     
17193     onEventClick: function (e, el,event,d) {
17194         this.fireEvent('eventclick', this, el, event);
17195     },
17196     
17197     onMonthChange: function () {
17198         this.store.load();
17199     },
17200     
17201     onMoreEventClick: function(e, el, more)
17202     {
17203         var _this = this;
17204         
17205         this.calpopover.placement = 'right';
17206         this.calpopover.setTitle('More');
17207         
17208         this.calpopover.setContent('');
17209         
17210         var ctr = this.calpopover.el.select('.popover-content', true).first();
17211         
17212         Roo.each(more, function(m){
17213             var cfg = {
17214                 cls : 'fc-event-hori fc-event-draggable',
17215                 html : m.title
17216             };
17217             var cg = ctr.createChild(cfg);
17218             
17219             cg.on('click', _this.onEventClick, _this, m);
17220         });
17221         
17222         this.calpopover.show(el);
17223         
17224         
17225     },
17226     
17227     onLoad: function () 
17228     {   
17229         this.calevents = [];
17230         var cal = this;
17231         
17232         if(this.store.getCount() > 0){
17233             this.store.data.each(function(d){
17234                cal.addItem({
17235                     id : d.data.id,
17236                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17237                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17238                     time : d.data.start_time,
17239                     title : d.data.title,
17240                     description : d.data.description,
17241                     venue : d.data.venue
17242                 });
17243             });
17244         }
17245         
17246         this.renderEvents();
17247         
17248         if(this.calevents.length && this.loadMask){
17249             this.maskEl.hide();
17250         }
17251     },
17252     
17253     onBeforeLoad: function()
17254     {
17255         this.clearEvents();
17256         if(this.loadMask){
17257             this.maskEl.show();
17258         }
17259     }
17260 });
17261
17262  
17263  /*
17264  * - LGPL
17265  *
17266  * element
17267  * 
17268  */
17269
17270 /**
17271  * @class Roo.bootstrap.Popover
17272  * @extends Roo.bootstrap.Component
17273  * Bootstrap Popover class
17274  * @cfg {String} html contents of the popover   (or false to use children..)
17275  * @cfg {String} title of popover (or false to hide)
17276  * @cfg {String} placement how it is placed
17277  * @cfg {String} trigger click || hover (or false to trigger manually)
17278  * @cfg {String} over what (parent or false to trigger manually.)
17279  * @cfg {Number} delay - delay before showing
17280  
17281  * @constructor
17282  * Create a new Popover
17283  * @param {Object} config The config object
17284  */
17285
17286 Roo.bootstrap.Popover = function(config){
17287     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17288     
17289     this.addEvents({
17290         // raw events
17291          /**
17292          * @event show
17293          * After the popover show
17294          * 
17295          * @param {Roo.bootstrap.Popover} this
17296          */
17297         "show" : true,
17298         /**
17299          * @event hide
17300          * After the popover hide
17301          * 
17302          * @param {Roo.bootstrap.Popover} this
17303          */
17304         "hide" : true
17305     });
17306 };
17307
17308 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17309     
17310     title: 'Fill in a title',
17311     html: false,
17312     
17313     placement : 'right',
17314     trigger : 'hover', // hover
17315     
17316     delay : 0,
17317     
17318     over: 'parent',
17319     
17320     can_build_overlaid : false,
17321     
17322     getChildContainer : function()
17323     {
17324         return this.el.select('.popover-content',true).first();
17325     },
17326     
17327     getAutoCreate : function(){
17328          
17329         var cfg = {
17330            cls : 'popover roo-dynamic',
17331            style: 'display:block',
17332            cn : [
17333                 {
17334                     cls : 'arrow'
17335                 },
17336                 {
17337                     cls : 'popover-inner',
17338                     cn : [
17339                         {
17340                             tag: 'h3',
17341                             cls: 'popover-title',
17342                             html : this.title
17343                         },
17344                         {
17345                             cls : 'popover-content',
17346                             html : this.html
17347                         }
17348                     ]
17349                     
17350                 }
17351            ]
17352         };
17353         
17354         return cfg;
17355     },
17356     setTitle: function(str)
17357     {
17358         this.title = str;
17359         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17360     },
17361     setContent: function(str)
17362     {
17363         this.html = str;
17364         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17365     },
17366     // as it get's added to the bottom of the page.
17367     onRender : function(ct, position)
17368     {
17369         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17370         if(!this.el){
17371             var cfg = Roo.apply({},  this.getAutoCreate());
17372             cfg.id = Roo.id();
17373             
17374             if (this.cls) {
17375                 cfg.cls += ' ' + this.cls;
17376             }
17377             if (this.style) {
17378                 cfg.style = this.style;
17379             }
17380             //Roo.log("adding to ");
17381             this.el = Roo.get(document.body).createChild(cfg, position);
17382 //            Roo.log(this.el);
17383         }
17384         this.initEvents();
17385     },
17386     
17387     initEvents : function()
17388     {
17389         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17390         this.el.enableDisplayMode('block');
17391         this.el.hide();
17392         if (this.over === false) {
17393             return; 
17394         }
17395         if (this.triggers === false) {
17396             return;
17397         }
17398         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17399         var triggers = this.trigger ? this.trigger.split(' ') : [];
17400         Roo.each(triggers, function(trigger) {
17401         
17402             if (trigger == 'click') {
17403                 on_el.on('click', this.toggle, this);
17404             } else if (trigger != 'manual') {
17405                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17406                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17407       
17408                 on_el.on(eventIn  ,this.enter, this);
17409                 on_el.on(eventOut, this.leave, this);
17410             }
17411         }, this);
17412         
17413     },
17414     
17415     
17416     // private
17417     timeout : null,
17418     hoverState : null,
17419     
17420     toggle : function () {
17421         this.hoverState == 'in' ? this.leave() : this.enter();
17422     },
17423     
17424     enter : function () {
17425         
17426         clearTimeout(this.timeout);
17427     
17428         this.hoverState = 'in';
17429     
17430         if (!this.delay || !this.delay.show) {
17431             this.show();
17432             return;
17433         }
17434         var _t = this;
17435         this.timeout = setTimeout(function () {
17436             if (_t.hoverState == 'in') {
17437                 _t.show();
17438             }
17439         }, this.delay.show)
17440     },
17441     
17442     leave : function() {
17443         clearTimeout(this.timeout);
17444     
17445         this.hoverState = 'out';
17446     
17447         if (!this.delay || !this.delay.hide) {
17448             this.hide();
17449             return;
17450         }
17451         var _t = this;
17452         this.timeout = setTimeout(function () {
17453             if (_t.hoverState == 'out') {
17454                 _t.hide();
17455             }
17456         }, this.delay.hide)
17457     },
17458     
17459     show : function (on_el)
17460     {
17461         if (!on_el) {
17462             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17463         }
17464         
17465         // set content.
17466         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17467         if (this.html !== false) {
17468             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17469         }
17470         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17471         if (!this.title.length) {
17472             this.el.select('.popover-title',true).hide();
17473         }
17474         
17475         var placement = typeof this.placement == 'function' ?
17476             this.placement.call(this, this.el, on_el) :
17477             this.placement;
17478             
17479         var autoToken = /\s?auto?\s?/i;
17480         var autoPlace = autoToken.test(placement);
17481         if (autoPlace) {
17482             placement = placement.replace(autoToken, '') || 'top';
17483         }
17484         
17485         //this.el.detach()
17486         //this.el.setXY([0,0]);
17487         this.el.show();
17488         this.el.dom.style.display='block';
17489         this.el.addClass(placement);
17490         
17491         //this.el.appendTo(on_el);
17492         
17493         var p = this.getPosition();
17494         var box = this.el.getBox();
17495         
17496         if (autoPlace) {
17497             // fixme..
17498         }
17499         var align = Roo.bootstrap.Popover.alignment[placement];
17500         
17501 //        Roo.log(align);
17502         this.el.alignTo(on_el, align[0],align[1]);
17503         //var arrow = this.el.select('.arrow',true).first();
17504         //arrow.set(align[2], 
17505         
17506         this.el.addClass('in');
17507         
17508         
17509         if (this.el.hasClass('fade')) {
17510             // fade it?
17511         }
17512         
17513         this.hoverState = 'in';
17514         
17515         this.fireEvent('show', this);
17516         
17517     },
17518     hide : function()
17519     {
17520         this.el.setXY([0,0]);
17521         this.el.removeClass('in');
17522         this.el.hide();
17523         this.hoverState = null;
17524         
17525         this.fireEvent('hide', this);
17526     }
17527     
17528 });
17529
17530 Roo.bootstrap.Popover.alignment = {
17531     'left' : ['r-l', [-10,0], 'right'],
17532     'right' : ['l-r', [10,0], 'left'],
17533     'bottom' : ['t-b', [0,10], 'top'],
17534     'top' : [ 'b-t', [0,-10], 'bottom']
17535 };
17536
17537  /*
17538  * - LGPL
17539  *
17540  * Progress
17541  * 
17542  */
17543
17544 /**
17545  * @class Roo.bootstrap.Progress
17546  * @extends Roo.bootstrap.Component
17547  * Bootstrap Progress class
17548  * @cfg {Boolean} striped striped of the progress bar
17549  * @cfg {Boolean} active animated of the progress bar
17550  * 
17551  * 
17552  * @constructor
17553  * Create a new Progress
17554  * @param {Object} config The config object
17555  */
17556
17557 Roo.bootstrap.Progress = function(config){
17558     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17559 };
17560
17561 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17562     
17563     striped : false,
17564     active: false,
17565     
17566     getAutoCreate : function(){
17567         var cfg = {
17568             tag: 'div',
17569             cls: 'progress'
17570         };
17571         
17572         
17573         if(this.striped){
17574             cfg.cls += ' progress-striped';
17575         }
17576       
17577         if(this.active){
17578             cfg.cls += ' active';
17579         }
17580         
17581         
17582         return cfg;
17583     }
17584    
17585 });
17586
17587  
17588
17589  /*
17590  * - LGPL
17591  *
17592  * ProgressBar
17593  * 
17594  */
17595
17596 /**
17597  * @class Roo.bootstrap.ProgressBar
17598  * @extends Roo.bootstrap.Component
17599  * Bootstrap ProgressBar class
17600  * @cfg {Number} aria_valuenow aria-value now
17601  * @cfg {Number} aria_valuemin aria-value min
17602  * @cfg {Number} aria_valuemax aria-value max
17603  * @cfg {String} label label for the progress bar
17604  * @cfg {String} panel (success | info | warning | danger )
17605  * @cfg {String} role role of the progress bar
17606  * @cfg {String} sr_only text
17607  * 
17608  * 
17609  * @constructor
17610  * Create a new ProgressBar
17611  * @param {Object} config The config object
17612  */
17613
17614 Roo.bootstrap.ProgressBar = function(config){
17615     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17616 };
17617
17618 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17619     
17620     aria_valuenow : 0,
17621     aria_valuemin : 0,
17622     aria_valuemax : 100,
17623     label : false,
17624     panel : false,
17625     role : false,
17626     sr_only: false,
17627     
17628     getAutoCreate : function()
17629     {
17630         
17631         var cfg = {
17632             tag: 'div',
17633             cls: 'progress-bar',
17634             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17635         };
17636         
17637         if(this.sr_only){
17638             cfg.cn = {
17639                 tag: 'span',
17640                 cls: 'sr-only',
17641                 html: this.sr_only
17642             }
17643         }
17644         
17645         if(this.role){
17646             cfg.role = this.role;
17647         }
17648         
17649         if(this.aria_valuenow){
17650             cfg['aria-valuenow'] = this.aria_valuenow;
17651         }
17652         
17653         if(this.aria_valuemin){
17654             cfg['aria-valuemin'] = this.aria_valuemin;
17655         }
17656         
17657         if(this.aria_valuemax){
17658             cfg['aria-valuemax'] = this.aria_valuemax;
17659         }
17660         
17661         if(this.label && !this.sr_only){
17662             cfg.html = this.label;
17663         }
17664         
17665         if(this.panel){
17666             cfg.cls += ' progress-bar-' + this.panel;
17667         }
17668         
17669         return cfg;
17670     },
17671     
17672     update : function(aria_valuenow)
17673     {
17674         this.aria_valuenow = aria_valuenow;
17675         
17676         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17677     }
17678    
17679 });
17680
17681  
17682
17683  /*
17684  * - LGPL
17685  *
17686  * column
17687  * 
17688  */
17689
17690 /**
17691  * @class Roo.bootstrap.TabGroup
17692  * @extends Roo.bootstrap.Column
17693  * Bootstrap Column class
17694  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17695  * @cfg {Boolean} carousel true to make the group behave like a carousel
17696  * @cfg {Boolean} bullets show bullets for the panels
17697  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17698  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17699  * @cfg {Boolean} showarrow (true|false) show arrow default true
17700  * 
17701  * @constructor
17702  * Create a new TabGroup
17703  * @param {Object} config The config object
17704  */
17705
17706 Roo.bootstrap.TabGroup = function(config){
17707     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17708     if (!this.navId) {
17709         this.navId = Roo.id();
17710     }
17711     this.tabs = [];
17712     Roo.bootstrap.TabGroup.register(this);
17713     
17714 };
17715
17716 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17717     
17718     carousel : false,
17719     transition : false,
17720     bullets : 0,
17721     timer : 0,
17722     autoslide : false,
17723     slideFn : false,
17724     slideOnTouch : false,
17725     showarrow : true,
17726     
17727     getAutoCreate : function()
17728     {
17729         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17730         
17731         cfg.cls += ' tab-content';
17732         
17733         if (this.carousel) {
17734             cfg.cls += ' carousel slide';
17735             
17736             cfg.cn = [{
17737                cls : 'carousel-inner',
17738                cn : []
17739             }];
17740         
17741             if(this.bullets  && !Roo.isTouch){
17742                 
17743                 var bullets = {
17744                     cls : 'carousel-bullets',
17745                     cn : []
17746                 };
17747                
17748                 if(this.bullets_cls){
17749                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17750                 }
17751                 
17752                 bullets.cn.push({
17753                     cls : 'clear'
17754                 });
17755                 
17756                 cfg.cn[0].cn.push(bullets);
17757             }
17758             
17759             if(this.showarrow){
17760                 cfg.cn[0].cn.push({
17761                     tag : 'div',
17762                     class : 'carousel-arrow',
17763                     cn : [
17764                         {
17765                             tag : 'div',
17766                             class : 'carousel-prev',
17767                             cn : [
17768                                 {
17769                                     tag : 'i',
17770                                     class : 'fa fa-chevron-left'
17771                                 }
17772                             ]
17773                         },
17774                         {
17775                             tag : 'div',
17776                             class : 'carousel-next',
17777                             cn : [
17778                                 {
17779                                     tag : 'i',
17780                                     class : 'fa fa-chevron-right'
17781                                 }
17782                             ]
17783                         }
17784                     ]
17785                 });
17786             }
17787             
17788         }
17789         
17790         return cfg;
17791     },
17792     
17793     initEvents:  function()
17794     {
17795 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17796 //            this.el.on("touchstart", this.onTouchStart, this);
17797 //        }
17798         
17799         if(this.autoslide){
17800             var _this = this;
17801             
17802             this.slideFn = window.setInterval(function() {
17803                 _this.showPanelNext();
17804             }, this.timer);
17805         }
17806         
17807         if(this.showarrow){
17808             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17809             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17810         }
17811         
17812         
17813     },
17814     
17815 //    onTouchStart : function(e, el, o)
17816 //    {
17817 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17818 //            return;
17819 //        }
17820 //        
17821 //        this.showPanelNext();
17822 //    },
17823     
17824     
17825     getChildContainer : function()
17826     {
17827         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17828     },
17829     
17830     /**
17831     * register a Navigation item
17832     * @param {Roo.bootstrap.NavItem} the navitem to add
17833     */
17834     register : function(item)
17835     {
17836         this.tabs.push( item);
17837         item.navId = this.navId; // not really needed..
17838         this.addBullet();
17839     
17840     },
17841     
17842     getActivePanel : function()
17843     {
17844         var r = false;
17845         Roo.each(this.tabs, function(t) {
17846             if (t.active) {
17847                 r = t;
17848                 return false;
17849             }
17850             return null;
17851         });
17852         return r;
17853         
17854     },
17855     getPanelByName : function(n)
17856     {
17857         var r = false;
17858         Roo.each(this.tabs, function(t) {
17859             if (t.tabId == n) {
17860                 r = t;
17861                 return false;
17862             }
17863             return null;
17864         });
17865         return r;
17866     },
17867     indexOfPanel : function(p)
17868     {
17869         var r = false;
17870         Roo.each(this.tabs, function(t,i) {
17871             if (t.tabId == p.tabId) {
17872                 r = i;
17873                 return false;
17874             }
17875             return null;
17876         });
17877         return r;
17878     },
17879     /**
17880      * show a specific panel
17881      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17882      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17883      */
17884     showPanel : function (pan)
17885     {
17886         if(this.transition || typeof(pan) == 'undefined'){
17887             Roo.log("waiting for the transitionend");
17888             return;
17889         }
17890         
17891         if (typeof(pan) == 'number') {
17892             pan = this.tabs[pan];
17893         }
17894         
17895         if (typeof(pan) == 'string') {
17896             pan = this.getPanelByName(pan);
17897         }
17898         
17899         var cur = this.getActivePanel();
17900         
17901         if(!pan || !cur){
17902             Roo.log('pan or acitve pan is undefined');
17903             return false;
17904         }
17905         
17906         if (pan.tabId == this.getActivePanel().tabId) {
17907             return true;
17908         }
17909         
17910         if (false === cur.fireEvent('beforedeactivate')) {
17911             return false;
17912         }
17913         
17914         if(this.bullets > 0 && !Roo.isTouch){
17915             this.setActiveBullet(this.indexOfPanel(pan));
17916         }
17917         
17918         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17919             
17920             this.transition = true;
17921             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17922             var lr = dir == 'next' ? 'left' : 'right';
17923             pan.el.addClass(dir); // or prev
17924             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17925             cur.el.addClass(lr); // or right
17926             pan.el.addClass(lr);
17927             
17928             var _this = this;
17929             cur.el.on('transitionend', function() {
17930                 Roo.log("trans end?");
17931                 
17932                 pan.el.removeClass([lr,dir]);
17933                 pan.setActive(true);
17934                 
17935                 cur.el.removeClass([lr]);
17936                 cur.setActive(false);
17937                 
17938                 _this.transition = false;
17939                 
17940             }, this, { single:  true } );
17941             
17942             return true;
17943         }
17944         
17945         cur.setActive(false);
17946         pan.setActive(true);
17947         
17948         return true;
17949         
17950     },
17951     showPanelNext : function()
17952     {
17953         var i = this.indexOfPanel(this.getActivePanel());
17954         
17955         if (i >= this.tabs.length - 1 && !this.autoslide) {
17956             return;
17957         }
17958         
17959         if (i >= this.tabs.length - 1 && this.autoslide) {
17960             i = -1;
17961         }
17962         
17963         this.showPanel(this.tabs[i+1]);
17964     },
17965     
17966     showPanelPrev : function()
17967     {
17968         var i = this.indexOfPanel(this.getActivePanel());
17969         
17970         if (i  < 1 && !this.autoslide) {
17971             return;
17972         }
17973         
17974         if (i < 1 && this.autoslide) {
17975             i = this.tabs.length;
17976         }
17977         
17978         this.showPanel(this.tabs[i-1]);
17979     },
17980     
17981     
17982     addBullet: function()
17983     {
17984         if(!this.bullets || Roo.isTouch){
17985             return;
17986         }
17987         var ctr = this.el.select('.carousel-bullets',true).first();
17988         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17989         var bullet = ctr.createChild({
17990             cls : 'bullet bullet-' + i
17991         },ctr.dom.lastChild);
17992         
17993         
17994         var _this = this;
17995         
17996         bullet.on('click', (function(e, el, o, ii, t){
17997
17998             e.preventDefault();
17999
18000             this.showPanel(ii);
18001
18002             if(this.autoslide && this.slideFn){
18003                 clearInterval(this.slideFn);
18004                 this.slideFn = window.setInterval(function() {
18005                     _this.showPanelNext();
18006                 }, this.timer);
18007             }
18008
18009         }).createDelegate(this, [i, bullet], true));
18010                 
18011         
18012     },
18013      
18014     setActiveBullet : function(i)
18015     {
18016         if(Roo.isTouch){
18017             return;
18018         }
18019         
18020         Roo.each(this.el.select('.bullet', true).elements, function(el){
18021             el.removeClass('selected');
18022         });
18023
18024         var bullet = this.el.select('.bullet-' + i, true).first();
18025         
18026         if(!bullet){
18027             return;
18028         }
18029         
18030         bullet.addClass('selected');
18031     }
18032     
18033     
18034   
18035 });
18036
18037  
18038
18039  
18040  
18041 Roo.apply(Roo.bootstrap.TabGroup, {
18042     
18043     groups: {},
18044      /**
18045     * register a Navigation Group
18046     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18047     */
18048     register : function(navgrp)
18049     {
18050         this.groups[navgrp.navId] = navgrp;
18051         
18052     },
18053     /**
18054     * fetch a Navigation Group based on the navigation ID
18055     * if one does not exist , it will get created.
18056     * @param {string} the navgroup to add
18057     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18058     */
18059     get: function(navId) {
18060         if (typeof(this.groups[navId]) == 'undefined') {
18061             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18062         }
18063         return this.groups[navId] ;
18064     }
18065     
18066     
18067     
18068 });
18069
18070  /*
18071  * - LGPL
18072  *
18073  * TabPanel
18074  * 
18075  */
18076
18077 /**
18078  * @class Roo.bootstrap.TabPanel
18079  * @extends Roo.bootstrap.Component
18080  * Bootstrap TabPanel class
18081  * @cfg {Boolean} active panel active
18082  * @cfg {String} html panel content
18083  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18084  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18085  * @cfg {String} href click to link..
18086  * 
18087  * 
18088  * @constructor
18089  * Create a new TabPanel
18090  * @param {Object} config The config object
18091  */
18092
18093 Roo.bootstrap.TabPanel = function(config){
18094     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18095     this.addEvents({
18096         /**
18097              * @event changed
18098              * Fires when the active status changes
18099              * @param {Roo.bootstrap.TabPanel} this
18100              * @param {Boolean} state the new state
18101             
18102          */
18103         'changed': true,
18104         /**
18105              * @event beforedeactivate
18106              * Fires before a tab is de-activated - can be used to do validation on a form.
18107              * @param {Roo.bootstrap.TabPanel} this
18108              * @return {Boolean} false if there is an error
18109             
18110          */
18111         'beforedeactivate': true
18112      });
18113     
18114     this.tabId = this.tabId || Roo.id();
18115   
18116 };
18117
18118 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18119     
18120     active: false,
18121     html: false,
18122     tabId: false,
18123     navId : false,
18124     href : '',
18125     
18126     getAutoCreate : function(){
18127         var cfg = {
18128             tag: 'div',
18129             // item is needed for carousel - not sure if it has any effect otherwise
18130             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18131             html: this.html || ''
18132         };
18133         
18134         if(this.active){
18135             cfg.cls += ' active';
18136         }
18137         
18138         if(this.tabId){
18139             cfg.tabId = this.tabId;
18140         }
18141         
18142         
18143         return cfg;
18144     },
18145     
18146     initEvents:  function()
18147     {
18148         var p = this.parent();
18149         
18150         this.navId = this.navId || p.navId;
18151         
18152         if (typeof(this.navId) != 'undefined') {
18153             // not really needed.. but just in case.. parent should be a NavGroup.
18154             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18155             
18156             tg.register(this);
18157             
18158             var i = tg.tabs.length - 1;
18159             
18160             if(this.active && tg.bullets > 0 && i < tg.bullets){
18161                 tg.setActiveBullet(i);
18162             }
18163         }
18164         
18165         this.el.on('click', this.onClick, this);
18166         
18167         if(Roo.isTouch){
18168             this.el.on("touchstart", this.onTouchStart, this);
18169             this.el.on("touchmove", this.onTouchMove, this);
18170             this.el.on("touchend", this.onTouchEnd, this);
18171         }
18172         
18173     },
18174     
18175     onRender : function(ct, position)
18176     {
18177         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18178     },
18179     
18180     setActive : function(state)
18181     {
18182         Roo.log("panel - set active " + this.tabId + "=" + state);
18183         
18184         this.active = state;
18185         if (!state) {
18186             this.el.removeClass('active');
18187             
18188         } else  if (!this.el.hasClass('active')) {
18189             this.el.addClass('active');
18190         }
18191         
18192         this.fireEvent('changed', this, state);
18193     },
18194     
18195     onClick : function(e)
18196     {
18197         e.preventDefault();
18198         
18199         if(!this.href.length){
18200             return;
18201         }
18202         
18203         window.location.href = this.href;
18204     },
18205     
18206     startX : 0,
18207     startY : 0,
18208     endX : 0,
18209     endY : 0,
18210     swiping : false,
18211     
18212     onTouchStart : function(e)
18213     {
18214         this.swiping = false;
18215         
18216         this.startX = e.browserEvent.touches[0].clientX;
18217         this.startY = e.browserEvent.touches[0].clientY;
18218     },
18219     
18220     onTouchMove : function(e)
18221     {
18222         this.swiping = true;
18223         
18224         this.endX = e.browserEvent.touches[0].clientX;
18225         this.endY = e.browserEvent.touches[0].clientY;
18226     },
18227     
18228     onTouchEnd : function(e)
18229     {
18230         if(!this.swiping){
18231             this.onClick(e);
18232             return;
18233         }
18234         
18235         var tabGroup = this.parent();
18236         
18237         if(this.endX > this.startX){ // swiping right
18238             tabGroup.showPanelPrev();
18239             return;
18240         }
18241         
18242         if(this.startX > this.endX){ // swiping left
18243             tabGroup.showPanelNext();
18244             return;
18245         }
18246     }
18247     
18248     
18249 });
18250  
18251
18252  
18253
18254  /*
18255  * - LGPL
18256  *
18257  * DateField
18258  * 
18259  */
18260
18261 /**
18262  * @class Roo.bootstrap.DateField
18263  * @extends Roo.bootstrap.Input
18264  * Bootstrap DateField class
18265  * @cfg {Number} weekStart default 0
18266  * @cfg {String} viewMode default empty, (months|years)
18267  * @cfg {String} minViewMode default empty, (months|years)
18268  * @cfg {Number} startDate default -Infinity
18269  * @cfg {Number} endDate default Infinity
18270  * @cfg {Boolean} todayHighlight default false
18271  * @cfg {Boolean} todayBtn default false
18272  * @cfg {Boolean} calendarWeeks default false
18273  * @cfg {Object} daysOfWeekDisabled default empty
18274  * @cfg {Boolean} singleMode default false (true | false)
18275  * 
18276  * @cfg {Boolean} keyboardNavigation default true
18277  * @cfg {String} language default en
18278  * 
18279  * @constructor
18280  * Create a new DateField
18281  * @param {Object} config The config object
18282  */
18283
18284 Roo.bootstrap.DateField = function(config){
18285     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18286      this.addEvents({
18287             /**
18288              * @event show
18289              * Fires when this field show.
18290              * @param {Roo.bootstrap.DateField} this
18291              * @param {Mixed} date The date value
18292              */
18293             show : true,
18294             /**
18295              * @event show
18296              * Fires when this field hide.
18297              * @param {Roo.bootstrap.DateField} this
18298              * @param {Mixed} date The date value
18299              */
18300             hide : true,
18301             /**
18302              * @event select
18303              * Fires when select a date.
18304              * @param {Roo.bootstrap.DateField} this
18305              * @param {Mixed} date The date value
18306              */
18307             select : true,
18308             /**
18309              * @event beforeselect
18310              * Fires when before select a date.
18311              * @param {Roo.bootstrap.DateField} this
18312              * @param {Mixed} date The date value
18313              */
18314             beforeselect : true
18315         });
18316 };
18317
18318 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18319     
18320     /**
18321      * @cfg {String} format
18322      * The default date format string which can be overriden for localization support.  The format must be
18323      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18324      */
18325     format : "m/d/y",
18326     /**
18327      * @cfg {String} altFormats
18328      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18329      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18330      */
18331     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18332     
18333     weekStart : 0,
18334     
18335     viewMode : '',
18336     
18337     minViewMode : '',
18338     
18339     todayHighlight : false,
18340     
18341     todayBtn: false,
18342     
18343     language: 'en',
18344     
18345     keyboardNavigation: true,
18346     
18347     calendarWeeks: false,
18348     
18349     startDate: -Infinity,
18350     
18351     endDate: Infinity,
18352     
18353     daysOfWeekDisabled: [],
18354     
18355     _events: [],
18356     
18357     singleMode : false,
18358     
18359     UTCDate: function()
18360     {
18361         return new Date(Date.UTC.apply(Date, arguments));
18362     },
18363     
18364     UTCToday: function()
18365     {
18366         var today = new Date();
18367         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18368     },
18369     
18370     getDate: function() {
18371             var d = this.getUTCDate();
18372             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18373     },
18374     
18375     getUTCDate: function() {
18376             return this.date;
18377     },
18378     
18379     setDate: function(d) {
18380             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18381     },
18382     
18383     setUTCDate: function(d) {
18384             this.date = d;
18385             this.setValue(this.formatDate(this.date));
18386     },
18387         
18388     onRender: function(ct, position)
18389     {
18390         
18391         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18392         
18393         this.language = this.language || 'en';
18394         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18395         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18396         
18397         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18398         this.format = this.format || 'm/d/y';
18399         this.isInline = false;
18400         this.isInput = true;
18401         this.component = this.el.select('.add-on', true).first() || false;
18402         this.component = (this.component && this.component.length === 0) ? false : this.component;
18403         this.hasInput = this.component && this.inputEl().length;
18404         
18405         if (typeof(this.minViewMode === 'string')) {
18406             switch (this.minViewMode) {
18407                 case 'months':
18408                     this.minViewMode = 1;
18409                     break;
18410                 case 'years':
18411                     this.minViewMode = 2;
18412                     break;
18413                 default:
18414                     this.minViewMode = 0;
18415                     break;
18416             }
18417         }
18418         
18419         if (typeof(this.viewMode === 'string')) {
18420             switch (this.viewMode) {
18421                 case 'months':
18422                     this.viewMode = 1;
18423                     break;
18424                 case 'years':
18425                     this.viewMode = 2;
18426                     break;
18427                 default:
18428                     this.viewMode = 0;
18429                     break;
18430             }
18431         }
18432                 
18433         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18434         
18435 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18436         
18437         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18438         
18439         this.picker().on('mousedown', this.onMousedown, this);
18440         this.picker().on('click', this.onClick, this);
18441         
18442         this.picker().addClass('datepicker-dropdown');
18443         
18444         this.startViewMode = this.viewMode;
18445         
18446         if(this.singleMode){
18447             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18448                 v.setVisibilityMode(Roo.Element.DISPLAY);
18449                 v.hide();
18450             });
18451             
18452             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18453                 v.setStyle('width', '189px');
18454             });
18455         }
18456         
18457         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18458             if(!this.calendarWeeks){
18459                 v.remove();
18460                 return;
18461             }
18462             
18463             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18464             v.attr('colspan', function(i, val){
18465                 return parseInt(val) + 1;
18466             });
18467         });
18468                         
18469         
18470         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18471         
18472         this.setStartDate(this.startDate);
18473         this.setEndDate(this.endDate);
18474         
18475         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18476         
18477         this.fillDow();
18478         this.fillMonths();
18479         this.update();
18480         this.showMode();
18481         
18482         if(this.isInline) {
18483             this.show();
18484         }
18485     },
18486     
18487     picker : function()
18488     {
18489         return this.pickerEl;
18490 //        return this.el.select('.datepicker', true).first();
18491     },
18492     
18493     fillDow: function()
18494     {
18495         var dowCnt = this.weekStart;
18496         
18497         var dow = {
18498             tag: 'tr',
18499             cn: [
18500                 
18501             ]
18502         };
18503         
18504         if(this.calendarWeeks){
18505             dow.cn.push({
18506                 tag: 'th',
18507                 cls: 'cw',
18508                 html: '&nbsp;'
18509             })
18510         }
18511         
18512         while (dowCnt < this.weekStart + 7) {
18513             dow.cn.push({
18514                 tag: 'th',
18515                 cls: 'dow',
18516                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18517             });
18518         }
18519         
18520         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18521     },
18522     
18523     fillMonths: function()
18524     {    
18525         var i = 0;
18526         var months = this.picker().select('>.datepicker-months td', true).first();
18527         
18528         months.dom.innerHTML = '';
18529         
18530         while (i < 12) {
18531             var month = {
18532                 tag: 'span',
18533                 cls: 'month',
18534                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18535             };
18536             
18537             months.createChild(month);
18538         }
18539         
18540     },
18541     
18542     update: function()
18543     {
18544         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;
18545         
18546         if (this.date < this.startDate) {
18547             this.viewDate = new Date(this.startDate);
18548         } else if (this.date > this.endDate) {
18549             this.viewDate = new Date(this.endDate);
18550         } else {
18551             this.viewDate = new Date(this.date);
18552         }
18553         
18554         this.fill();
18555     },
18556     
18557     fill: function() 
18558     {
18559         var d = new Date(this.viewDate),
18560                 year = d.getUTCFullYear(),
18561                 month = d.getUTCMonth(),
18562                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18563                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18564                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18565                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18566                 currentDate = this.date && this.date.valueOf(),
18567                 today = this.UTCToday();
18568         
18569         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18570         
18571 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18572         
18573 //        this.picker.select('>tfoot th.today').
18574 //                                              .text(dates[this.language].today)
18575 //                                              .toggle(this.todayBtn !== false);
18576     
18577         this.updateNavArrows();
18578         this.fillMonths();
18579                                                 
18580         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18581         
18582         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18583          
18584         prevMonth.setUTCDate(day);
18585         
18586         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18587         
18588         var nextMonth = new Date(prevMonth);
18589         
18590         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18591         
18592         nextMonth = nextMonth.valueOf();
18593         
18594         var fillMonths = false;
18595         
18596         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18597         
18598         while(prevMonth.valueOf() < nextMonth) {
18599             var clsName = '';
18600             
18601             if (prevMonth.getUTCDay() === this.weekStart) {
18602                 if(fillMonths){
18603                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18604                 }
18605                     
18606                 fillMonths = {
18607                     tag: 'tr',
18608                     cn: []
18609                 };
18610                 
18611                 if(this.calendarWeeks){
18612                     // ISO 8601: First week contains first thursday.
18613                     // ISO also states week starts on Monday, but we can be more abstract here.
18614                     var
18615                     // Start of current week: based on weekstart/current date
18616                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18617                     // Thursday of this week
18618                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18619                     // First Thursday of year, year from thursday
18620                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18621                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18622                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18623                     
18624                     fillMonths.cn.push({
18625                         tag: 'td',
18626                         cls: 'cw',
18627                         html: calWeek
18628                     });
18629                 }
18630             }
18631             
18632             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18633                 clsName += ' old';
18634             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18635                 clsName += ' new';
18636             }
18637             if (this.todayHighlight &&
18638                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18639                 prevMonth.getUTCMonth() == today.getMonth() &&
18640                 prevMonth.getUTCDate() == today.getDate()) {
18641                 clsName += ' today';
18642             }
18643             
18644             if (currentDate && prevMonth.valueOf() === currentDate) {
18645                 clsName += ' active';
18646             }
18647             
18648             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18649                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18650                     clsName += ' disabled';
18651             }
18652             
18653             fillMonths.cn.push({
18654                 tag: 'td',
18655                 cls: 'day ' + clsName,
18656                 html: prevMonth.getDate()
18657             });
18658             
18659             prevMonth.setDate(prevMonth.getDate()+1);
18660         }
18661           
18662         var currentYear = this.date && this.date.getUTCFullYear();
18663         var currentMonth = this.date && this.date.getUTCMonth();
18664         
18665         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18666         
18667         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18668             v.removeClass('active');
18669             
18670             if(currentYear === year && k === currentMonth){
18671                 v.addClass('active');
18672             }
18673             
18674             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18675                 v.addClass('disabled');
18676             }
18677             
18678         });
18679         
18680         
18681         year = parseInt(year/10, 10) * 10;
18682         
18683         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18684         
18685         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18686         
18687         year -= 1;
18688         for (var i = -1; i < 11; i++) {
18689             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18690                 tag: 'span',
18691                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18692                 html: year
18693             });
18694             
18695             year += 1;
18696         }
18697     },
18698     
18699     showMode: function(dir) 
18700     {
18701         if (dir) {
18702             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18703         }
18704         
18705         Roo.each(this.picker().select('>div',true).elements, function(v){
18706             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18707             v.hide();
18708         });
18709         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18710     },
18711     
18712     place: function()
18713     {
18714         if(this.isInline) {
18715             return;
18716         }
18717         
18718         this.picker().removeClass(['bottom', 'top']);
18719         
18720         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18721             /*
18722              * place to the top of element!
18723              *
18724              */
18725             
18726             this.picker().addClass('top');
18727             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18728             
18729             return;
18730         }
18731         
18732         this.picker().addClass('bottom');
18733         
18734         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18735     },
18736     
18737     parseDate : function(value)
18738     {
18739         if(!value || value instanceof Date){
18740             return value;
18741         }
18742         var v = Date.parseDate(value, this.format);
18743         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18744             v = Date.parseDate(value, 'Y-m-d');
18745         }
18746         if(!v && this.altFormats){
18747             if(!this.altFormatsArray){
18748                 this.altFormatsArray = this.altFormats.split("|");
18749             }
18750             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18751                 v = Date.parseDate(value, this.altFormatsArray[i]);
18752             }
18753         }
18754         return v;
18755     },
18756     
18757     formatDate : function(date, fmt)
18758     {   
18759         return (!date || !(date instanceof Date)) ?
18760         date : date.dateFormat(fmt || this.format);
18761     },
18762     
18763     onFocus : function()
18764     {
18765         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18766         this.show();
18767     },
18768     
18769     onBlur : function()
18770     {
18771         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18772         
18773         var d = this.inputEl().getValue();
18774         
18775         this.setValue(d);
18776                 
18777         this.hide();
18778     },
18779     
18780     show : function()
18781     {
18782         this.picker().show();
18783         this.update();
18784         this.place();
18785         
18786         this.fireEvent('show', this, this.date);
18787     },
18788     
18789     hide : function()
18790     {
18791         if(this.isInline) {
18792             return;
18793         }
18794         this.picker().hide();
18795         this.viewMode = this.startViewMode;
18796         this.showMode();
18797         
18798         this.fireEvent('hide', this, this.date);
18799         
18800     },
18801     
18802     onMousedown: function(e)
18803     {
18804         e.stopPropagation();
18805         e.preventDefault();
18806     },
18807     
18808     keyup: function(e)
18809     {
18810         Roo.bootstrap.DateField.superclass.keyup.call(this);
18811         this.update();
18812     },
18813
18814     setValue: function(v)
18815     {
18816         if(this.fireEvent('beforeselect', this, v) !== false){
18817             var d = new Date(this.parseDate(v) ).clearTime();
18818         
18819             if(isNaN(d.getTime())){
18820                 this.date = this.viewDate = '';
18821                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18822                 return;
18823             }
18824
18825             v = this.formatDate(d);
18826
18827             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18828
18829             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18830
18831             this.update();
18832
18833             this.fireEvent('select', this, this.date);
18834         }
18835     },
18836     
18837     getValue: function()
18838     {
18839         return this.formatDate(this.date);
18840     },
18841     
18842     fireKey: function(e)
18843     {
18844         if (!this.picker().isVisible()){
18845             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18846                 this.show();
18847             }
18848             return;
18849         }
18850         
18851         var dateChanged = false,
18852         dir, day, month,
18853         newDate, newViewDate;
18854         
18855         switch(e.keyCode){
18856             case 27: // escape
18857                 this.hide();
18858                 e.preventDefault();
18859                 break;
18860             case 37: // left
18861             case 39: // right
18862                 if (!this.keyboardNavigation) {
18863                     break;
18864                 }
18865                 dir = e.keyCode == 37 ? -1 : 1;
18866                 
18867                 if (e.ctrlKey){
18868                     newDate = this.moveYear(this.date, dir);
18869                     newViewDate = this.moveYear(this.viewDate, dir);
18870                 } else if (e.shiftKey){
18871                     newDate = this.moveMonth(this.date, dir);
18872                     newViewDate = this.moveMonth(this.viewDate, dir);
18873                 } else {
18874                     newDate = new Date(this.date);
18875                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18876                     newViewDate = new Date(this.viewDate);
18877                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18878                 }
18879                 if (this.dateWithinRange(newDate)){
18880                     this.date = newDate;
18881                     this.viewDate = newViewDate;
18882                     this.setValue(this.formatDate(this.date));
18883 //                    this.update();
18884                     e.preventDefault();
18885                     dateChanged = true;
18886                 }
18887                 break;
18888             case 38: // up
18889             case 40: // down
18890                 if (!this.keyboardNavigation) {
18891                     break;
18892                 }
18893                 dir = e.keyCode == 38 ? -1 : 1;
18894                 if (e.ctrlKey){
18895                     newDate = this.moveYear(this.date, dir);
18896                     newViewDate = this.moveYear(this.viewDate, dir);
18897                 } else if (e.shiftKey){
18898                     newDate = this.moveMonth(this.date, dir);
18899                     newViewDate = this.moveMonth(this.viewDate, dir);
18900                 } else {
18901                     newDate = new Date(this.date);
18902                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18903                     newViewDate = new Date(this.viewDate);
18904                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18905                 }
18906                 if (this.dateWithinRange(newDate)){
18907                     this.date = newDate;
18908                     this.viewDate = newViewDate;
18909                     this.setValue(this.formatDate(this.date));
18910 //                    this.update();
18911                     e.preventDefault();
18912                     dateChanged = true;
18913                 }
18914                 break;
18915             case 13: // enter
18916                 this.setValue(this.formatDate(this.date));
18917                 this.hide();
18918                 e.preventDefault();
18919                 break;
18920             case 9: // tab
18921                 this.setValue(this.formatDate(this.date));
18922                 this.hide();
18923                 break;
18924             case 16: // shift
18925             case 17: // ctrl
18926             case 18: // alt
18927                 break;
18928             default :
18929                 this.hide();
18930                 
18931         }
18932     },
18933     
18934     
18935     onClick: function(e) 
18936     {
18937         e.stopPropagation();
18938         e.preventDefault();
18939         
18940         var target = e.getTarget();
18941         
18942         if(target.nodeName.toLowerCase() === 'i'){
18943             target = Roo.get(target).dom.parentNode;
18944         }
18945         
18946         var nodeName = target.nodeName;
18947         var className = target.className;
18948         var html = target.innerHTML;
18949         //Roo.log(nodeName);
18950         
18951         switch(nodeName.toLowerCase()) {
18952             case 'th':
18953                 switch(className) {
18954                     case 'switch':
18955                         this.showMode(1);
18956                         break;
18957                     case 'prev':
18958                     case 'next':
18959                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18960                         switch(this.viewMode){
18961                                 case 0:
18962                                         this.viewDate = this.moveMonth(this.viewDate, dir);
18963                                         break;
18964                                 case 1:
18965                                 case 2:
18966                                         this.viewDate = this.moveYear(this.viewDate, dir);
18967                                         break;
18968                         }
18969                         this.fill();
18970                         break;
18971                     case 'today':
18972                         var date = new Date();
18973                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18974 //                        this.fill()
18975                         this.setValue(this.formatDate(this.date));
18976                         
18977                         this.hide();
18978                         break;
18979                 }
18980                 break;
18981             case 'span':
18982                 if (className.indexOf('disabled') < 0) {
18983                     this.viewDate.setUTCDate(1);
18984                     if (className.indexOf('month') > -1) {
18985                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18986                     } else {
18987                         var year = parseInt(html, 10) || 0;
18988                         this.viewDate.setUTCFullYear(year);
18989                         
18990                     }
18991                     
18992                     if(this.singleMode){
18993                         this.setValue(this.formatDate(this.viewDate));
18994                         this.hide();
18995                         return;
18996                     }
18997                     
18998                     this.showMode(-1);
18999                     this.fill();
19000                 }
19001                 break;
19002                 
19003             case 'td':
19004                 //Roo.log(className);
19005                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19006                     var day = parseInt(html, 10) || 1;
19007                     var year = this.viewDate.getUTCFullYear(),
19008                         month = this.viewDate.getUTCMonth();
19009
19010                     if (className.indexOf('old') > -1) {
19011                         if(month === 0 ){
19012                             month = 11;
19013                             year -= 1;
19014                         }else{
19015                             month -= 1;
19016                         }
19017                     } else if (className.indexOf('new') > -1) {
19018                         if (month == 11) {
19019                             month = 0;
19020                             year += 1;
19021                         } else {
19022                             month += 1;
19023                         }
19024                     }
19025                     //Roo.log([year,month,day]);
19026                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19027                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19028 //                    this.fill();
19029                     //Roo.log(this.formatDate(this.date));
19030                     this.setValue(this.formatDate(this.date));
19031                     this.hide();
19032                 }
19033                 break;
19034         }
19035     },
19036     
19037     setStartDate: function(startDate)
19038     {
19039         this.startDate = startDate || -Infinity;
19040         if (this.startDate !== -Infinity) {
19041             this.startDate = this.parseDate(this.startDate);
19042         }
19043         this.update();
19044         this.updateNavArrows();
19045     },
19046
19047     setEndDate: function(endDate)
19048     {
19049         this.endDate = endDate || Infinity;
19050         if (this.endDate !== Infinity) {
19051             this.endDate = this.parseDate(this.endDate);
19052         }
19053         this.update();
19054         this.updateNavArrows();
19055     },
19056     
19057     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19058     {
19059         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19060         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19061             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19062         }
19063         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19064             return parseInt(d, 10);
19065         });
19066         this.update();
19067         this.updateNavArrows();
19068     },
19069     
19070     updateNavArrows: function() 
19071     {
19072         if(this.singleMode){
19073             return;
19074         }
19075         
19076         var d = new Date(this.viewDate),
19077         year = d.getUTCFullYear(),
19078         month = d.getUTCMonth();
19079         
19080         Roo.each(this.picker().select('.prev', true).elements, function(v){
19081             v.show();
19082             switch (this.viewMode) {
19083                 case 0:
19084
19085                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19086                         v.hide();
19087                     }
19088                     break;
19089                 case 1:
19090                 case 2:
19091                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19092                         v.hide();
19093                     }
19094                     break;
19095             }
19096         });
19097         
19098         Roo.each(this.picker().select('.next', true).elements, function(v){
19099             v.show();
19100             switch (this.viewMode) {
19101                 case 0:
19102
19103                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19104                         v.hide();
19105                     }
19106                     break;
19107                 case 1:
19108                 case 2:
19109                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19110                         v.hide();
19111                     }
19112                     break;
19113             }
19114         })
19115     },
19116     
19117     moveMonth: function(date, dir)
19118     {
19119         if (!dir) {
19120             return date;
19121         }
19122         var new_date = new Date(date.valueOf()),
19123         day = new_date.getUTCDate(),
19124         month = new_date.getUTCMonth(),
19125         mag = Math.abs(dir),
19126         new_month, test;
19127         dir = dir > 0 ? 1 : -1;
19128         if (mag == 1){
19129             test = dir == -1
19130             // If going back one month, make sure month is not current month
19131             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19132             ? function(){
19133                 return new_date.getUTCMonth() == month;
19134             }
19135             // If going forward one month, make sure month is as expected
19136             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19137             : function(){
19138                 return new_date.getUTCMonth() != new_month;
19139             };
19140             new_month = month + dir;
19141             new_date.setUTCMonth(new_month);
19142             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19143             if (new_month < 0 || new_month > 11) {
19144                 new_month = (new_month + 12) % 12;
19145             }
19146         } else {
19147             // For magnitudes >1, move one month at a time...
19148             for (var i=0; i<mag; i++) {
19149                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19150                 new_date = this.moveMonth(new_date, dir);
19151             }
19152             // ...then reset the day, keeping it in the new month
19153             new_month = new_date.getUTCMonth();
19154             new_date.setUTCDate(day);
19155             test = function(){
19156                 return new_month != new_date.getUTCMonth();
19157             };
19158         }
19159         // Common date-resetting loop -- if date is beyond end of month, make it
19160         // end of month
19161         while (test()){
19162             new_date.setUTCDate(--day);
19163             new_date.setUTCMonth(new_month);
19164         }
19165         return new_date;
19166     },
19167
19168     moveYear: function(date, dir)
19169     {
19170         return this.moveMonth(date, dir*12);
19171     },
19172
19173     dateWithinRange: function(date)
19174     {
19175         return date >= this.startDate && date <= this.endDate;
19176     },
19177
19178     
19179     remove: function() 
19180     {
19181         this.picker().remove();
19182     },
19183     
19184     validateValue : function(value)
19185     {
19186         if(this.getEl().hasClass('hidden')){
19187             return true;
19188         }
19189         
19190         if(value.length < 1)  {
19191             if(this.allowBlank){
19192                 return true;
19193             }
19194             return false;
19195         }
19196         
19197         if(value.length < this.minLength){
19198             return false;
19199         }
19200         if(value.length > this.maxLength){
19201             return false;
19202         }
19203         if(this.vtype){
19204             var vt = Roo.form.VTypes;
19205             if(!vt[this.vtype](value, this)){
19206                 return false;
19207             }
19208         }
19209         if(typeof this.validator == "function"){
19210             var msg = this.validator(value);
19211             if(msg !== true){
19212                 return false;
19213             }
19214         }
19215         
19216         if(this.regex && !this.regex.test(value)){
19217             return false;
19218         }
19219         
19220         if(typeof(this.parseDate(value)) == 'undefined'){
19221             return false;
19222         }
19223         
19224         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19225             return false;
19226         }      
19227         
19228         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19229             return false;
19230         } 
19231         
19232         
19233         return true;
19234     },
19235     
19236     setVisible : function(visible)
19237     {
19238         if(!this.getEl()){
19239             return;
19240         }
19241         
19242         this.getEl().removeClass('hidden');
19243         
19244         if(visible){
19245             return;
19246         }
19247         
19248         this.getEl().addClass('hidden');
19249     }
19250    
19251 });
19252
19253 Roo.apply(Roo.bootstrap.DateField,  {
19254     
19255     head : {
19256         tag: 'thead',
19257         cn: [
19258         {
19259             tag: 'tr',
19260             cn: [
19261             {
19262                 tag: 'th',
19263                 cls: 'prev',
19264                 html: '<i class="fa fa-arrow-left"/>'
19265             },
19266             {
19267                 tag: 'th',
19268                 cls: 'switch',
19269                 colspan: '5'
19270             },
19271             {
19272                 tag: 'th',
19273                 cls: 'next',
19274                 html: '<i class="fa fa-arrow-right"/>'
19275             }
19276
19277             ]
19278         }
19279         ]
19280     },
19281     
19282     content : {
19283         tag: 'tbody',
19284         cn: [
19285         {
19286             tag: 'tr',
19287             cn: [
19288             {
19289                 tag: 'td',
19290                 colspan: '7'
19291             }
19292             ]
19293         }
19294         ]
19295     },
19296     
19297     footer : {
19298         tag: 'tfoot',
19299         cn: [
19300         {
19301             tag: 'tr',
19302             cn: [
19303             {
19304                 tag: 'th',
19305                 colspan: '7',
19306                 cls: 'today'
19307             }
19308                     
19309             ]
19310         }
19311         ]
19312     },
19313     
19314     dates:{
19315         en: {
19316             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19317             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19318             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19319             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19320             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19321             today: "Today"
19322         }
19323     },
19324     
19325     modes: [
19326     {
19327         clsName: 'days',
19328         navFnc: 'Month',
19329         navStep: 1
19330     },
19331     {
19332         clsName: 'months',
19333         navFnc: 'FullYear',
19334         navStep: 1
19335     },
19336     {
19337         clsName: 'years',
19338         navFnc: 'FullYear',
19339         navStep: 10
19340     }]
19341 });
19342
19343 Roo.apply(Roo.bootstrap.DateField,  {
19344   
19345     template : {
19346         tag: 'div',
19347         cls: 'datepicker dropdown-menu roo-dynamic',
19348         cn: [
19349         {
19350             tag: 'div',
19351             cls: 'datepicker-days',
19352             cn: [
19353             {
19354                 tag: 'table',
19355                 cls: 'table-condensed',
19356                 cn:[
19357                 Roo.bootstrap.DateField.head,
19358                 {
19359                     tag: 'tbody'
19360                 },
19361                 Roo.bootstrap.DateField.footer
19362                 ]
19363             }
19364             ]
19365         },
19366         {
19367             tag: 'div',
19368             cls: 'datepicker-months',
19369             cn: [
19370             {
19371                 tag: 'table',
19372                 cls: 'table-condensed',
19373                 cn:[
19374                 Roo.bootstrap.DateField.head,
19375                 Roo.bootstrap.DateField.content,
19376                 Roo.bootstrap.DateField.footer
19377                 ]
19378             }
19379             ]
19380         },
19381         {
19382             tag: 'div',
19383             cls: 'datepicker-years',
19384             cn: [
19385             {
19386                 tag: 'table',
19387                 cls: 'table-condensed',
19388                 cn:[
19389                 Roo.bootstrap.DateField.head,
19390                 Roo.bootstrap.DateField.content,
19391                 Roo.bootstrap.DateField.footer
19392                 ]
19393             }
19394             ]
19395         }
19396         ]
19397     }
19398 });
19399
19400  
19401
19402  /*
19403  * - LGPL
19404  *
19405  * TimeField
19406  * 
19407  */
19408
19409 /**
19410  * @class Roo.bootstrap.TimeField
19411  * @extends Roo.bootstrap.Input
19412  * Bootstrap DateField class
19413  * 
19414  * 
19415  * @constructor
19416  * Create a new TimeField
19417  * @param {Object} config The config object
19418  */
19419
19420 Roo.bootstrap.TimeField = function(config){
19421     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19422     this.addEvents({
19423             /**
19424              * @event show
19425              * Fires when this field show.
19426              * @param {Roo.bootstrap.DateField} thisthis
19427              * @param {Mixed} date The date value
19428              */
19429             show : true,
19430             /**
19431              * @event show
19432              * Fires when this field hide.
19433              * @param {Roo.bootstrap.DateField} this
19434              * @param {Mixed} date The date value
19435              */
19436             hide : true,
19437             /**
19438              * @event select
19439              * Fires when select a date.
19440              * @param {Roo.bootstrap.DateField} this
19441              * @param {Mixed} date The date value
19442              */
19443             select : true
19444         });
19445 };
19446
19447 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19448     
19449     /**
19450      * @cfg {String} format
19451      * The default time format string which can be overriden for localization support.  The format must be
19452      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19453      */
19454     format : "H:i",
19455        
19456     onRender: function(ct, position)
19457     {
19458         
19459         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19460                 
19461         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19462         
19463         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19464         
19465         this.pop = this.picker().select('>.datepicker-time',true).first();
19466         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19467         
19468         this.picker().on('mousedown', this.onMousedown, this);
19469         this.picker().on('click', this.onClick, this);
19470         
19471         this.picker().addClass('datepicker-dropdown');
19472     
19473         this.fillTime();
19474         this.update();
19475             
19476         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19477         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19478         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19479         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19480         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19481         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19482
19483     },
19484     
19485     fireKey: function(e){
19486         if (!this.picker().isVisible()){
19487             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19488                 this.show();
19489             }
19490             return;
19491         }
19492
19493         e.preventDefault();
19494         
19495         switch(e.keyCode){
19496             case 27: // escape
19497                 this.hide();
19498                 break;
19499             case 37: // left
19500             case 39: // right
19501                 this.onTogglePeriod();
19502                 break;
19503             case 38: // up
19504                 this.onIncrementMinutes();
19505                 break;
19506             case 40: // down
19507                 this.onDecrementMinutes();
19508                 break;
19509             case 13: // enter
19510             case 9: // tab
19511                 this.setTime();
19512                 break;
19513         }
19514     },
19515     
19516     onClick: function(e) {
19517         e.stopPropagation();
19518         e.preventDefault();
19519     },
19520     
19521     picker : function()
19522     {
19523         return this.el.select('.datepicker', true).first();
19524     },
19525     
19526     fillTime: function()
19527     {    
19528         var time = this.pop.select('tbody', true).first();
19529         
19530         time.dom.innerHTML = '';
19531         
19532         time.createChild({
19533             tag: 'tr',
19534             cn: [
19535                 {
19536                     tag: 'td',
19537                     cn: [
19538                         {
19539                             tag: 'a',
19540                             href: '#',
19541                             cls: 'btn',
19542                             cn: [
19543                                 {
19544                                     tag: 'span',
19545                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19546                                 }
19547                             ]
19548                         } 
19549                     ]
19550                 },
19551                 {
19552                     tag: 'td',
19553                     cls: 'separator'
19554                 },
19555                 {
19556                     tag: 'td',
19557                     cn: [
19558                         {
19559                             tag: 'a',
19560                             href: '#',
19561                             cls: 'btn',
19562                             cn: [
19563                                 {
19564                                     tag: 'span',
19565                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19566                                 }
19567                             ]
19568                         }
19569                     ]
19570                 },
19571                 {
19572                     tag: 'td',
19573                     cls: 'separator'
19574                 }
19575             ]
19576         });
19577         
19578         time.createChild({
19579             tag: 'tr',
19580             cn: [
19581                 {
19582                     tag: 'td',
19583                     cn: [
19584                         {
19585                             tag: 'span',
19586                             cls: 'timepicker-hour',
19587                             html: '00'
19588                         }  
19589                     ]
19590                 },
19591                 {
19592                     tag: 'td',
19593                     cls: 'separator',
19594                     html: ':'
19595                 },
19596                 {
19597                     tag: 'td',
19598                     cn: [
19599                         {
19600                             tag: 'span',
19601                             cls: 'timepicker-minute',
19602                             html: '00'
19603                         }  
19604                     ]
19605                 },
19606                 {
19607                     tag: 'td',
19608                     cls: 'separator'
19609                 },
19610                 {
19611                     tag: 'td',
19612                     cn: [
19613                         {
19614                             tag: 'button',
19615                             type: 'button',
19616                             cls: 'btn btn-primary period',
19617                             html: 'AM'
19618                             
19619                         }
19620                     ]
19621                 }
19622             ]
19623         });
19624         
19625         time.createChild({
19626             tag: 'tr',
19627             cn: [
19628                 {
19629                     tag: 'td',
19630                     cn: [
19631                         {
19632                             tag: 'a',
19633                             href: '#',
19634                             cls: 'btn',
19635                             cn: [
19636                                 {
19637                                     tag: 'span',
19638                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19639                                 }
19640                             ]
19641                         }
19642                     ]
19643                 },
19644                 {
19645                     tag: 'td',
19646                     cls: 'separator'
19647                 },
19648                 {
19649                     tag: 'td',
19650                     cn: [
19651                         {
19652                             tag: 'a',
19653                             href: '#',
19654                             cls: 'btn',
19655                             cn: [
19656                                 {
19657                                     tag: 'span',
19658                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19659                                 }
19660                             ]
19661                         }
19662                     ]
19663                 },
19664                 {
19665                     tag: 'td',
19666                     cls: 'separator'
19667                 }
19668             ]
19669         });
19670         
19671     },
19672     
19673     update: function()
19674     {
19675         
19676         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19677         
19678         this.fill();
19679     },
19680     
19681     fill: function() 
19682     {
19683         var hours = this.time.getHours();
19684         var minutes = this.time.getMinutes();
19685         var period = 'AM';
19686         
19687         if(hours > 11){
19688             period = 'PM';
19689         }
19690         
19691         if(hours == 0){
19692             hours = 12;
19693         }
19694         
19695         
19696         if(hours > 12){
19697             hours = hours - 12;
19698         }
19699         
19700         if(hours < 10){
19701             hours = '0' + hours;
19702         }
19703         
19704         if(minutes < 10){
19705             minutes = '0' + minutes;
19706         }
19707         
19708         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19709         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19710         this.pop.select('button', true).first().dom.innerHTML = period;
19711         
19712     },
19713     
19714     place: function()
19715     {   
19716         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19717         
19718         var cls = ['bottom'];
19719         
19720         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19721             cls.pop();
19722             cls.push('top');
19723         }
19724         
19725         cls.push('right');
19726         
19727         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19728             cls.pop();
19729             cls.push('left');
19730         }
19731         
19732         this.picker().addClass(cls.join('-'));
19733         
19734         var _this = this;
19735         
19736         Roo.each(cls, function(c){
19737             if(c == 'bottom'){
19738                 _this.picker().setTop(_this.inputEl().getHeight());
19739                 return;
19740             }
19741             if(c == 'top'){
19742                 _this.picker().setTop(0 - _this.picker().getHeight());
19743                 return;
19744             }
19745             
19746             if(c == 'left'){
19747                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19748                 return;
19749             }
19750             if(c == 'right'){
19751                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19752                 return;
19753             }
19754         });
19755         
19756     },
19757   
19758     onFocus : function()
19759     {
19760         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19761         this.show();
19762     },
19763     
19764     onBlur : function()
19765     {
19766         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19767         this.hide();
19768     },
19769     
19770     show : function()
19771     {
19772         this.picker().show();
19773         this.pop.show();
19774         this.update();
19775         this.place();
19776         
19777         this.fireEvent('show', this, this.date);
19778     },
19779     
19780     hide : function()
19781     {
19782         this.picker().hide();
19783         this.pop.hide();
19784         
19785         this.fireEvent('hide', this, this.date);
19786     },
19787     
19788     setTime : function()
19789     {
19790         this.hide();
19791         this.setValue(this.time.format(this.format));
19792         
19793         this.fireEvent('select', this, this.date);
19794         
19795         
19796     },
19797     
19798     onMousedown: function(e){
19799         e.stopPropagation();
19800         e.preventDefault();
19801     },
19802     
19803     onIncrementHours: function()
19804     {
19805         Roo.log('onIncrementHours');
19806         this.time = this.time.add(Date.HOUR, 1);
19807         this.update();
19808         
19809     },
19810     
19811     onDecrementHours: function()
19812     {
19813         Roo.log('onDecrementHours');
19814         this.time = this.time.add(Date.HOUR, -1);
19815         this.update();
19816     },
19817     
19818     onIncrementMinutes: function()
19819     {
19820         Roo.log('onIncrementMinutes');
19821         this.time = this.time.add(Date.MINUTE, 1);
19822         this.update();
19823     },
19824     
19825     onDecrementMinutes: function()
19826     {
19827         Roo.log('onDecrementMinutes');
19828         this.time = this.time.add(Date.MINUTE, -1);
19829         this.update();
19830     },
19831     
19832     onTogglePeriod: function()
19833     {
19834         Roo.log('onTogglePeriod');
19835         this.time = this.time.add(Date.HOUR, 12);
19836         this.update();
19837     }
19838     
19839    
19840 });
19841
19842 Roo.apply(Roo.bootstrap.TimeField,  {
19843     
19844     content : {
19845         tag: 'tbody',
19846         cn: [
19847             {
19848                 tag: 'tr',
19849                 cn: [
19850                 {
19851                     tag: 'td',
19852                     colspan: '7'
19853                 }
19854                 ]
19855             }
19856         ]
19857     },
19858     
19859     footer : {
19860         tag: 'tfoot',
19861         cn: [
19862             {
19863                 tag: 'tr',
19864                 cn: [
19865                 {
19866                     tag: 'th',
19867                     colspan: '7',
19868                     cls: '',
19869                     cn: [
19870                         {
19871                             tag: 'button',
19872                             cls: 'btn btn-info ok',
19873                             html: 'OK'
19874                         }
19875                     ]
19876                 }
19877
19878                 ]
19879             }
19880         ]
19881     }
19882 });
19883
19884 Roo.apply(Roo.bootstrap.TimeField,  {
19885   
19886     template : {
19887         tag: 'div',
19888         cls: 'datepicker dropdown-menu',
19889         cn: [
19890             {
19891                 tag: 'div',
19892                 cls: 'datepicker-time',
19893                 cn: [
19894                 {
19895                     tag: 'table',
19896                     cls: 'table-condensed',
19897                     cn:[
19898                     Roo.bootstrap.TimeField.content,
19899                     Roo.bootstrap.TimeField.footer
19900                     ]
19901                 }
19902                 ]
19903             }
19904         ]
19905     }
19906 });
19907
19908  
19909
19910  /*
19911  * - LGPL
19912  *
19913  * MonthField
19914  * 
19915  */
19916
19917 /**
19918  * @class Roo.bootstrap.MonthField
19919  * @extends Roo.bootstrap.Input
19920  * Bootstrap MonthField class
19921  * 
19922  * @cfg {String} language default en
19923  * 
19924  * @constructor
19925  * Create a new MonthField
19926  * @param {Object} config The config object
19927  */
19928
19929 Roo.bootstrap.MonthField = function(config){
19930     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19931     
19932     this.addEvents({
19933         /**
19934          * @event show
19935          * Fires when this field show.
19936          * @param {Roo.bootstrap.MonthField} this
19937          * @param {Mixed} date The date value
19938          */
19939         show : true,
19940         /**
19941          * @event show
19942          * Fires when this field hide.
19943          * @param {Roo.bootstrap.MonthField} this
19944          * @param {Mixed} date The date value
19945          */
19946         hide : true,
19947         /**
19948          * @event select
19949          * Fires when select a date.
19950          * @param {Roo.bootstrap.MonthField} this
19951          * @param {String} oldvalue The old value
19952          * @param {String} newvalue The new value
19953          */
19954         select : true
19955     });
19956 };
19957
19958 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
19959     
19960     onRender: function(ct, position)
19961     {
19962         
19963         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19964         
19965         this.language = this.language || 'en';
19966         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19967         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19968         
19969         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19970         this.isInline = false;
19971         this.isInput = true;
19972         this.component = this.el.select('.add-on', true).first() || false;
19973         this.component = (this.component && this.component.length === 0) ? false : this.component;
19974         this.hasInput = this.component && this.inputEL().length;
19975         
19976         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19977         
19978         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19979         
19980         this.picker().on('mousedown', this.onMousedown, this);
19981         this.picker().on('click', this.onClick, this);
19982         
19983         this.picker().addClass('datepicker-dropdown');
19984         
19985         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19986             v.setStyle('width', '189px');
19987         });
19988         
19989         this.fillMonths();
19990         
19991         this.update();
19992         
19993         if(this.isInline) {
19994             this.show();
19995         }
19996         
19997     },
19998     
19999     setValue: function(v, suppressEvent)
20000     {   
20001         var o = this.getValue();
20002         
20003         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20004         
20005         this.update();
20006
20007         if(suppressEvent !== true){
20008             this.fireEvent('select', this, o, v);
20009         }
20010         
20011     },
20012     
20013     getValue: function()
20014     {
20015         return this.value;
20016     },
20017     
20018     onClick: function(e) 
20019     {
20020         e.stopPropagation();
20021         e.preventDefault();
20022         
20023         var target = e.getTarget();
20024         
20025         if(target.nodeName.toLowerCase() === 'i'){
20026             target = Roo.get(target).dom.parentNode;
20027         }
20028         
20029         var nodeName = target.nodeName;
20030         var className = target.className;
20031         var html = target.innerHTML;
20032         
20033         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20034             return;
20035         }
20036         
20037         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20038         
20039         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20040         
20041         this.hide();
20042                         
20043     },
20044     
20045     picker : function()
20046     {
20047         return this.pickerEl;
20048     },
20049     
20050     fillMonths: function()
20051     {    
20052         var i = 0;
20053         var months = this.picker().select('>.datepicker-months td', true).first();
20054         
20055         months.dom.innerHTML = '';
20056         
20057         while (i < 12) {
20058             var month = {
20059                 tag: 'span',
20060                 cls: 'month',
20061                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20062             };
20063             
20064             months.createChild(month);
20065         }
20066         
20067     },
20068     
20069     update: function()
20070     {
20071         var _this = this;
20072         
20073         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20074             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20075         }
20076         
20077         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20078             e.removeClass('active');
20079             
20080             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20081                 e.addClass('active');
20082             }
20083         })
20084     },
20085     
20086     place: function()
20087     {
20088         if(this.isInline) {
20089             return;
20090         }
20091         
20092         this.picker().removeClass(['bottom', 'top']);
20093         
20094         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20095             /*
20096              * place to the top of element!
20097              *
20098              */
20099             
20100             this.picker().addClass('top');
20101             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20102             
20103             return;
20104         }
20105         
20106         this.picker().addClass('bottom');
20107         
20108         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20109     },
20110     
20111     onFocus : function()
20112     {
20113         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20114         this.show();
20115     },
20116     
20117     onBlur : function()
20118     {
20119         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20120         
20121         var d = this.inputEl().getValue();
20122         
20123         this.setValue(d);
20124                 
20125         this.hide();
20126     },
20127     
20128     show : function()
20129     {
20130         this.picker().show();
20131         this.picker().select('>.datepicker-months', true).first().show();
20132         this.update();
20133         this.place();
20134         
20135         this.fireEvent('show', this, this.date);
20136     },
20137     
20138     hide : function()
20139     {
20140         if(this.isInline) {
20141             return;
20142         }
20143         this.picker().hide();
20144         this.fireEvent('hide', this, this.date);
20145         
20146     },
20147     
20148     onMousedown: function(e)
20149     {
20150         e.stopPropagation();
20151         e.preventDefault();
20152     },
20153     
20154     keyup: function(e)
20155     {
20156         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20157         this.update();
20158     },
20159
20160     fireKey: function(e)
20161     {
20162         if (!this.picker().isVisible()){
20163             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20164                 this.show();
20165             }
20166             return;
20167         }
20168         
20169         var dir;
20170         
20171         switch(e.keyCode){
20172             case 27: // escape
20173                 this.hide();
20174                 e.preventDefault();
20175                 break;
20176             case 37: // left
20177             case 39: // right
20178                 dir = e.keyCode == 37 ? -1 : 1;
20179                 
20180                 this.vIndex = this.vIndex + dir;
20181                 
20182                 if(this.vIndex < 0){
20183                     this.vIndex = 0;
20184                 }
20185                 
20186                 if(this.vIndex > 11){
20187                     this.vIndex = 11;
20188                 }
20189                 
20190                 if(isNaN(this.vIndex)){
20191                     this.vIndex = 0;
20192                 }
20193                 
20194                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20195                 
20196                 break;
20197             case 38: // up
20198             case 40: // down
20199                 
20200                 dir = e.keyCode == 38 ? -1 : 1;
20201                 
20202                 this.vIndex = this.vIndex + dir * 4;
20203                 
20204                 if(this.vIndex < 0){
20205                     this.vIndex = 0;
20206                 }
20207                 
20208                 if(this.vIndex > 11){
20209                     this.vIndex = 11;
20210                 }
20211                 
20212                 if(isNaN(this.vIndex)){
20213                     this.vIndex = 0;
20214                 }
20215                 
20216                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20217                 break;
20218                 
20219             case 13: // enter
20220                 
20221                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20222                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20223                 }
20224                 
20225                 this.hide();
20226                 e.preventDefault();
20227                 break;
20228             case 9: // tab
20229                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20230                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20231                 }
20232                 this.hide();
20233                 break;
20234             case 16: // shift
20235             case 17: // ctrl
20236             case 18: // alt
20237                 break;
20238             default :
20239                 this.hide();
20240                 
20241         }
20242     },
20243     
20244     remove: function() 
20245     {
20246         this.picker().remove();
20247     }
20248    
20249 });
20250
20251 Roo.apply(Roo.bootstrap.MonthField,  {
20252     
20253     content : {
20254         tag: 'tbody',
20255         cn: [
20256         {
20257             tag: 'tr',
20258             cn: [
20259             {
20260                 tag: 'td',
20261                 colspan: '7'
20262             }
20263             ]
20264         }
20265         ]
20266     },
20267     
20268     dates:{
20269         en: {
20270             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20271             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20272         }
20273     }
20274 });
20275
20276 Roo.apply(Roo.bootstrap.MonthField,  {
20277   
20278     template : {
20279         tag: 'div',
20280         cls: 'datepicker dropdown-menu roo-dynamic',
20281         cn: [
20282             {
20283                 tag: 'div',
20284                 cls: 'datepicker-months',
20285                 cn: [
20286                 {
20287                     tag: 'table',
20288                     cls: 'table-condensed',
20289                     cn:[
20290                         Roo.bootstrap.DateField.content
20291                     ]
20292                 }
20293                 ]
20294             }
20295         ]
20296     }
20297 });
20298
20299  
20300
20301  
20302  /*
20303  * - LGPL
20304  *
20305  * CheckBox
20306  * 
20307  */
20308
20309 /**
20310  * @class Roo.bootstrap.CheckBox
20311  * @extends Roo.bootstrap.Input
20312  * Bootstrap CheckBox class
20313  * 
20314  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20315  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20316  * @cfg {String} boxLabel The text that appears beside the checkbox
20317  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20318  * @cfg {Boolean} checked initnal the element
20319  * @cfg {Boolean} inline inline the element (default false)
20320  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20321  * @cfg {String} tooltip label tooltip
20322  * 
20323  * @constructor
20324  * Create a new CheckBox
20325  * @param {Object} config The config object
20326  */
20327
20328 Roo.bootstrap.CheckBox = function(config){
20329     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20330    
20331     this.addEvents({
20332         /**
20333         * @event check
20334         * Fires when the element is checked or unchecked.
20335         * @param {Roo.bootstrap.CheckBox} this This input
20336         * @param {Boolean} checked The new checked value
20337         */
20338        check : true,
20339        /**
20340         * @event click
20341         * Fires when the element is click.
20342         * @param {Roo.bootstrap.CheckBox} this This input
20343         */
20344        click : true
20345     });
20346     
20347 };
20348
20349 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20350   
20351     inputType: 'checkbox',
20352     inputValue: 1,
20353     valueOff: 0,
20354     boxLabel: false,
20355     checked: false,
20356     weight : false,
20357     inline: false,
20358     tooltip : '',
20359     
20360     getAutoCreate : function()
20361     {
20362         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20363         
20364         var id = Roo.id();
20365         
20366         var cfg = {};
20367         
20368         cfg.cls = 'form-group ' + this.inputType; //input-group
20369         
20370         if(this.inline){
20371             cfg.cls += ' ' + this.inputType + '-inline';
20372         }
20373         
20374         var input =  {
20375             tag: 'input',
20376             id : id,
20377             type : this.inputType,
20378             value : this.inputValue,
20379             cls : 'roo-' + this.inputType, //'form-box',
20380             placeholder : this.placeholder || ''
20381             
20382         };
20383         
20384         if(this.inputType != 'radio'){
20385             var hidden =  {
20386                 tag: 'input',
20387                 type : 'hidden',
20388                 cls : 'roo-hidden-value',
20389                 value : this.checked ? this.inputValue : this.valueOff
20390             };
20391         }
20392         
20393             
20394         if (this.weight) { // Validity check?
20395             cfg.cls += " " + this.inputType + "-" + this.weight;
20396         }
20397         
20398         if (this.disabled) {
20399             input.disabled=true;
20400         }
20401         
20402         if(this.checked){
20403             input.checked = this.checked;
20404         }
20405         
20406         if (this.name) {
20407             
20408             input.name = this.name;
20409             
20410             if(this.inputType != 'radio'){
20411                 hidden.name = this.name;
20412                 input.name = '_hidden_' + this.name;
20413             }
20414         }
20415         
20416         if (this.size) {
20417             input.cls += ' input-' + this.size;
20418         }
20419         
20420         var settings=this;
20421         
20422         ['xs','sm','md','lg'].map(function(size){
20423             if (settings[size]) {
20424                 cfg.cls += ' col-' + size + '-' + settings[size];
20425             }
20426         });
20427         
20428         var inputblock = input;
20429          
20430         if (this.before || this.after) {
20431             
20432             inputblock = {
20433                 cls : 'input-group',
20434                 cn :  [] 
20435             };
20436             
20437             if (this.before) {
20438                 inputblock.cn.push({
20439                     tag :'span',
20440                     cls : 'input-group-addon',
20441                     html : this.before
20442                 });
20443             }
20444             
20445             inputblock.cn.push(input);
20446             
20447             if(this.inputType != 'radio'){
20448                 inputblock.cn.push(hidden);
20449             }
20450             
20451             if (this.after) {
20452                 inputblock.cn.push({
20453                     tag :'span',
20454                     cls : 'input-group-addon',
20455                     html : this.after
20456                 });
20457             }
20458             
20459         }
20460         
20461         if (align ==='left' && this.fieldLabel.length) {
20462 //                Roo.log("left and has label");
20463             cfg.cn = [
20464                 {
20465                     tag: 'label',
20466                     'for' :  id,
20467                     cls : 'control-label',
20468                     html : this.fieldLabel
20469                 },
20470                 {
20471                     cls : "", 
20472                     cn: [
20473                         inputblock
20474                     ]
20475                 }
20476             ];
20477             
20478             if(this.labelWidth > 12){
20479                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20480             }
20481             
20482             if(this.labelWidth < 13 && this.labelmd == 0){
20483                 this.labelmd = this.labelWidth;
20484             }
20485             
20486             if(this.labellg > 0){
20487                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20488                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20489             }
20490             
20491             if(this.labelmd > 0){
20492                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20493                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20494             }
20495             
20496             if(this.labelsm > 0){
20497                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20498                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20499             }
20500             
20501             if(this.labelxs > 0){
20502                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20503                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20504             }
20505             
20506         } else if ( this.fieldLabel.length) {
20507 //                Roo.log(" label");
20508                 cfg.cn = [
20509                    
20510                     {
20511                         tag: this.boxLabel ? 'span' : 'label',
20512                         'for': id,
20513                         cls: 'control-label box-input-label',
20514                         //cls : 'input-group-addon',
20515                         html : this.fieldLabel
20516                     },
20517                     
20518                     inputblock
20519                     
20520                 ];
20521
20522         } else {
20523             
20524 //                Roo.log(" no label && no align");
20525                 cfg.cn = [  inputblock ] ;
20526                 
20527                 
20528         }
20529         
20530         if(this.boxLabel){
20531              var boxLabelCfg = {
20532                 tag: 'label',
20533                 //'for': id, // box label is handled by onclick - so no for...
20534                 cls: 'box-label',
20535                 html: this.boxLabel
20536             };
20537             
20538             if(this.tooltip){
20539                 boxLabelCfg.tooltip = this.tooltip;
20540             }
20541              
20542             cfg.cn.push(boxLabelCfg);
20543         }
20544         
20545         if(this.inputType != 'radio'){
20546             cfg.cn.push(hidden);
20547         }
20548         
20549         return cfg;
20550         
20551     },
20552     
20553     /**
20554      * return the real input element.
20555      */
20556     inputEl: function ()
20557     {
20558         return this.el.select('input.roo-' + this.inputType,true).first();
20559     },
20560     hiddenEl: function ()
20561     {
20562         return this.el.select('input.roo-hidden-value',true).first();
20563     },
20564     
20565     labelEl: function()
20566     {
20567         return this.el.select('label.control-label',true).first();
20568     },
20569     /* depricated... */
20570     
20571     label: function()
20572     {
20573         return this.labelEl();
20574     },
20575     
20576     boxLabelEl: function()
20577     {
20578         return this.el.select('label.box-label',true).first();
20579     },
20580     
20581     initEvents : function()
20582     {
20583 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20584         
20585         this.inputEl().on('click', this.onClick,  this);
20586         
20587         if (this.boxLabel) { 
20588             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20589         }
20590         
20591         this.startValue = this.getValue();
20592         
20593         if(this.groupId){
20594             Roo.bootstrap.CheckBox.register(this);
20595         }
20596     },
20597     
20598     onClick : function(e)
20599     {   
20600         if(this.fireEvent('click', this, e) !== false){
20601             this.setChecked(!this.checked);
20602         }
20603         
20604     },
20605     
20606     setChecked : function(state,suppressEvent)
20607     {
20608         this.startValue = this.getValue();
20609
20610         if(this.inputType == 'radio'){
20611             
20612             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20613                 e.dom.checked = false;
20614             });
20615             
20616             this.inputEl().dom.checked = true;
20617             
20618             this.inputEl().dom.value = this.inputValue;
20619             
20620             if(suppressEvent !== true){
20621                 this.fireEvent('check', this, true);
20622             }
20623             
20624             this.validate();
20625             
20626             return;
20627         }
20628         
20629         this.checked = state;
20630         
20631         this.inputEl().dom.checked = state;
20632         
20633         
20634         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20635         
20636         if(suppressEvent !== true){
20637             this.fireEvent('check', this, state);
20638         }
20639         
20640         this.validate();
20641     },
20642     
20643     getValue : function()
20644     {
20645         if(this.inputType == 'radio'){
20646             return this.getGroupValue();
20647         }
20648         
20649         return this.hiddenEl().dom.value;
20650         
20651     },
20652     
20653     getGroupValue : function()
20654     {
20655         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20656             return '';
20657         }
20658         
20659         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20660     },
20661     
20662     setValue : function(v,suppressEvent)
20663     {
20664         if(this.inputType == 'radio'){
20665             this.setGroupValue(v, suppressEvent);
20666             return;
20667         }
20668         
20669         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20670         
20671         this.validate();
20672     },
20673     
20674     setGroupValue : function(v, suppressEvent)
20675     {
20676         this.startValue = this.getValue();
20677         
20678         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20679             e.dom.checked = false;
20680             
20681             if(e.dom.value == v){
20682                 e.dom.checked = true;
20683             }
20684         });
20685         
20686         if(suppressEvent !== true){
20687             this.fireEvent('check', this, true);
20688         }
20689
20690         this.validate();
20691         
20692         return;
20693     },
20694     
20695     validate : function()
20696     {
20697         if(this.getEl().hasClass('hidden')){
20698             return true;
20699         }
20700         
20701         if(
20702                 this.disabled || 
20703                 (this.inputType == 'radio' && this.validateRadio()) ||
20704                 (this.inputType == 'checkbox' && this.validateCheckbox())
20705         ){
20706             this.markValid();
20707             return true;
20708         }
20709         
20710         this.markInvalid();
20711         return false;
20712     },
20713     
20714     validateRadio : function()
20715     {
20716         if(this.getEl().hasClass('hidden')){
20717             return true;
20718         }
20719         
20720         if(this.allowBlank){
20721             return true;
20722         }
20723         
20724         var valid = false;
20725         
20726         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20727             if(!e.dom.checked){
20728                 return;
20729             }
20730             
20731             valid = true;
20732             
20733             return false;
20734         });
20735         
20736         return valid;
20737     },
20738     
20739     validateCheckbox : function()
20740     {
20741         if(!this.groupId){
20742             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20743             //return (this.getValue() == this.inputValue) ? true : false;
20744         }
20745         
20746         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20747         
20748         if(!group){
20749             return false;
20750         }
20751         
20752         var r = false;
20753         
20754         for(var i in group){
20755             if(group[i].el.isVisible(true)){
20756                 r = false;
20757                 break;
20758             }
20759             
20760             r = true;
20761         }
20762         
20763         for(var i in group){
20764             if(r){
20765                 break;
20766             }
20767             
20768             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20769         }
20770         
20771         return r;
20772     },
20773     
20774     /**
20775      * Mark this field as valid
20776      */
20777     markValid : function()
20778     {
20779         var _this = this;
20780         
20781         this.fireEvent('valid', this);
20782         
20783         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20784         
20785         if(this.groupId){
20786             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20787         }
20788         
20789         if(label){
20790             label.markValid();
20791         }
20792
20793         if(this.inputType == 'radio'){
20794             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20795                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20796                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20797             });
20798             
20799             return;
20800         }
20801
20802         if(!this.groupId){
20803             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20804             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20805             return;
20806         }
20807         
20808         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20809         
20810         if(!group){
20811             return;
20812         }
20813         
20814         for(var i in group){
20815             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20816             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20817         }
20818     },
20819     
20820      /**
20821      * Mark this field as invalid
20822      * @param {String} msg The validation message
20823      */
20824     markInvalid : function(msg)
20825     {
20826         if(this.allowBlank){
20827             return;
20828         }
20829         
20830         var _this = this;
20831         
20832         this.fireEvent('invalid', this, msg);
20833         
20834         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20835         
20836         if(this.groupId){
20837             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20838         }
20839         
20840         if(label){
20841             label.markInvalid();
20842         }
20843             
20844         if(this.inputType == 'radio'){
20845             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20846                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20847                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20848             });
20849             
20850             return;
20851         }
20852         
20853         if(!this.groupId){
20854             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20855             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20856             return;
20857         }
20858         
20859         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20860         
20861         if(!group){
20862             return;
20863         }
20864         
20865         for(var i in group){
20866             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20867             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20868         }
20869         
20870     },
20871     
20872     clearInvalid : function()
20873     {
20874         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20875         
20876         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20877         
20878         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20879         
20880         if (label && label.iconEl) {
20881             label.iconEl.removeClass(label.validClass);
20882             label.iconEl.removeClass(label.invalidClass);
20883         }
20884     },
20885     
20886     disable : function()
20887     {
20888         if(this.inputType != 'radio'){
20889             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20890             return;
20891         }
20892         
20893         var _this = this;
20894         
20895         if(this.rendered){
20896             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20897                 _this.getActionEl().addClass(this.disabledClass);
20898                 e.dom.disabled = true;
20899             });
20900         }
20901         
20902         this.disabled = true;
20903         this.fireEvent("disable", this);
20904         return this;
20905     },
20906
20907     enable : function()
20908     {
20909         if(this.inputType != 'radio'){
20910             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20911             return;
20912         }
20913         
20914         var _this = this;
20915         
20916         if(this.rendered){
20917             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20918                 _this.getActionEl().removeClass(this.disabledClass);
20919                 e.dom.disabled = false;
20920             });
20921         }
20922         
20923         this.disabled = false;
20924         this.fireEvent("enable", this);
20925         return this;
20926     },
20927     
20928     setBoxLabel : function(v)
20929     {
20930         this.boxLabel = v;
20931         
20932         if(this.rendered){
20933             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
20934         }
20935     }
20936
20937 });
20938
20939 Roo.apply(Roo.bootstrap.CheckBox, {
20940     
20941     groups: {},
20942     
20943      /**
20944     * register a CheckBox Group
20945     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20946     */
20947     register : function(checkbox)
20948     {
20949         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20950             this.groups[checkbox.groupId] = {};
20951         }
20952         
20953         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20954             return;
20955         }
20956         
20957         this.groups[checkbox.groupId][checkbox.name] = checkbox;
20958         
20959     },
20960     /**
20961     * fetch a CheckBox Group based on the group ID
20962     * @param {string} the group ID
20963     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20964     */
20965     get: function(groupId) {
20966         if (typeof(this.groups[groupId]) == 'undefined') {
20967             return false;
20968         }
20969         
20970         return this.groups[groupId] ;
20971     }
20972     
20973     
20974 });
20975 /*
20976  * - LGPL
20977  *
20978  * RadioItem
20979  * 
20980  */
20981
20982 /**
20983  * @class Roo.bootstrap.Radio
20984  * @extends Roo.bootstrap.Component
20985  * Bootstrap Radio class
20986  * @cfg {String} boxLabel - the label associated
20987  * @cfg {String} value - the value of radio
20988  * 
20989  * @constructor
20990  * Create a new Radio
20991  * @param {Object} config The config object
20992  */
20993 Roo.bootstrap.Radio = function(config){
20994     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20995     
20996 };
20997
20998 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
20999     
21000     boxLabel : '',
21001     
21002     value : '',
21003     
21004     getAutoCreate : function()
21005     {
21006         var cfg = {
21007             tag : 'div',
21008             cls : 'form-group radio',
21009             cn : [
21010                 {
21011                     tag : 'label',
21012                     cls : 'box-label',
21013                     html : this.boxLabel
21014                 }
21015             ]
21016         };
21017         
21018         return cfg;
21019     },
21020     
21021     initEvents : function() 
21022     {
21023         this.parent().register(this);
21024         
21025         this.el.on('click', this.onClick, this);
21026         
21027     },
21028     
21029     onClick : function(e)
21030     {
21031         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21032             this.setChecked(true);
21033         }
21034     },
21035     
21036     setChecked : function(state, suppressEvent)
21037     {
21038         this.parent().setValue(this.value, suppressEvent);
21039         
21040     },
21041     
21042     setBoxLabel : function(v)
21043     {
21044         this.boxLabel = v;
21045         
21046         if(this.rendered){
21047             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21048         }
21049     }
21050     
21051 });
21052  
21053
21054  /*
21055  * - LGPL
21056  *
21057  * Input
21058  * 
21059  */
21060
21061 /**
21062  * @class Roo.bootstrap.SecurePass
21063  * @extends Roo.bootstrap.Input
21064  * Bootstrap SecurePass class
21065  *
21066  * 
21067  * @constructor
21068  * Create a new SecurePass
21069  * @param {Object} config The config object
21070  */
21071  
21072 Roo.bootstrap.SecurePass = function (config) {
21073     // these go here, so the translation tool can replace them..
21074     this.errors = {
21075         PwdEmpty: "Please type a password, and then retype it to confirm.",
21076         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21077         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21078         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21079         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21080         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21081         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21082         TooWeak: "Your password is Too Weak."
21083     },
21084     this.meterLabel = "Password strength:";
21085     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21086     this.meterClass = [
21087         "roo-password-meter-tooweak", 
21088         "roo-password-meter-weak", 
21089         "roo-password-meter-medium", 
21090         "roo-password-meter-strong", 
21091         "roo-password-meter-grey"
21092     ];
21093     
21094     this.errors = {};
21095     
21096     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21097 }
21098
21099 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21100     /**
21101      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21102      * {
21103      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21104      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21105      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21106      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21107      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21108      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21109      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21110      * })
21111      */
21112     // private
21113     
21114     meterWidth: 300,
21115     errorMsg :'',    
21116     errors: false,
21117     imageRoot: '/',
21118     /**
21119      * @cfg {String/Object} Label for the strength meter (defaults to
21120      * 'Password strength:')
21121      */
21122     // private
21123     meterLabel: '',
21124     /**
21125      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21126      * ['Weak', 'Medium', 'Strong'])
21127      */
21128     // private    
21129     pwdStrengths: false,    
21130     // private
21131     strength: 0,
21132     // private
21133     _lastPwd: null,
21134     // private
21135     kCapitalLetter: 0,
21136     kSmallLetter: 1,
21137     kDigit: 2,
21138     kPunctuation: 3,
21139     
21140     insecure: false,
21141     // private
21142     initEvents: function ()
21143     {
21144         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21145
21146         if (this.el.is('input[type=password]') && Roo.isSafari) {
21147             this.el.on('keydown', this.SafariOnKeyDown, this);
21148         }
21149
21150         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21151     },
21152     // private
21153     onRender: function (ct, position)
21154     {
21155         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21156         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21157         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21158
21159         this.trigger.createChild({
21160                    cn: [
21161                     {
21162                     //id: 'PwdMeter',
21163                     tag: 'div',
21164                     cls: 'roo-password-meter-grey col-xs-12',
21165                     style: {
21166                         //width: 0,
21167                         //width: this.meterWidth + 'px'                                                
21168                         }
21169                     },
21170                     {                            
21171                          cls: 'roo-password-meter-text'                          
21172                     }
21173                 ]            
21174         });
21175
21176          
21177         if (this.hideTrigger) {
21178             this.trigger.setDisplayed(false);
21179         }
21180         this.setSize(this.width || '', this.height || '');
21181     },
21182     // private
21183     onDestroy: function ()
21184     {
21185         if (this.trigger) {
21186             this.trigger.removeAllListeners();
21187             this.trigger.remove();
21188         }
21189         if (this.wrap) {
21190             this.wrap.remove();
21191         }
21192         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21193     },
21194     // private
21195     checkStrength: function ()
21196     {
21197         var pwd = this.inputEl().getValue();
21198         if (pwd == this._lastPwd) {
21199             return;
21200         }
21201
21202         var strength;
21203         if (this.ClientSideStrongPassword(pwd)) {
21204             strength = 3;
21205         } else if (this.ClientSideMediumPassword(pwd)) {
21206             strength = 2;
21207         } else if (this.ClientSideWeakPassword(pwd)) {
21208             strength = 1;
21209         } else {
21210             strength = 0;
21211         }
21212         
21213         Roo.log('strength1: ' + strength);
21214         
21215         //var pm = this.trigger.child('div/div/div').dom;
21216         var pm = this.trigger.child('div/div');
21217         pm.removeClass(this.meterClass);
21218         pm.addClass(this.meterClass[strength]);
21219                 
21220         
21221         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21222                 
21223         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21224         
21225         this._lastPwd = pwd;
21226     },
21227     reset: function ()
21228     {
21229         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21230         
21231         this._lastPwd = '';
21232         
21233         var pm = this.trigger.child('div/div');
21234         pm.removeClass(this.meterClass);
21235         pm.addClass('roo-password-meter-grey');        
21236         
21237         
21238         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21239         
21240         pt.innerHTML = '';
21241         this.inputEl().dom.type='password';
21242     },
21243     // private
21244     validateValue: function (value)
21245     {
21246         
21247         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21248             return false;
21249         }
21250         if (value.length == 0) {
21251             if (this.allowBlank) {
21252                 this.clearInvalid();
21253                 return true;
21254             }
21255
21256             this.markInvalid(this.errors.PwdEmpty);
21257             this.errorMsg = this.errors.PwdEmpty;
21258             return false;
21259         }
21260         
21261         if(this.insecure){
21262             return true;
21263         }
21264         
21265         if ('[\x21-\x7e]*'.match(value)) {
21266             this.markInvalid(this.errors.PwdBadChar);
21267             this.errorMsg = this.errors.PwdBadChar;
21268             return false;
21269         }
21270         if (value.length < 6) {
21271             this.markInvalid(this.errors.PwdShort);
21272             this.errorMsg = this.errors.PwdShort;
21273             return false;
21274         }
21275         if (value.length > 16) {
21276             this.markInvalid(this.errors.PwdLong);
21277             this.errorMsg = this.errors.PwdLong;
21278             return false;
21279         }
21280         var strength;
21281         if (this.ClientSideStrongPassword(value)) {
21282             strength = 3;
21283         } else if (this.ClientSideMediumPassword(value)) {
21284             strength = 2;
21285         } else if (this.ClientSideWeakPassword(value)) {
21286             strength = 1;
21287         } else {
21288             strength = 0;
21289         }
21290
21291         
21292         if (strength < 2) {
21293             //this.markInvalid(this.errors.TooWeak);
21294             this.errorMsg = this.errors.TooWeak;
21295             //return false;
21296         }
21297         
21298         
21299         console.log('strength2: ' + strength);
21300         
21301         //var pm = this.trigger.child('div/div/div').dom;
21302         
21303         var pm = this.trigger.child('div/div');
21304         pm.removeClass(this.meterClass);
21305         pm.addClass(this.meterClass[strength]);
21306                 
21307         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21308                 
21309         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21310         
21311         this.errorMsg = ''; 
21312         return true;
21313     },
21314     // private
21315     CharacterSetChecks: function (type)
21316     {
21317         this.type = type;
21318         this.fResult = false;
21319     },
21320     // private
21321     isctype: function (character, type)
21322     {
21323         switch (type) {  
21324             case this.kCapitalLetter:
21325                 if (character >= 'A' && character <= 'Z') {
21326                     return true;
21327                 }
21328                 break;
21329             
21330             case this.kSmallLetter:
21331                 if (character >= 'a' && character <= 'z') {
21332                     return true;
21333                 }
21334                 break;
21335             
21336             case this.kDigit:
21337                 if (character >= '0' && character <= '9') {
21338                     return true;
21339                 }
21340                 break;
21341             
21342             case this.kPunctuation:
21343                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21344                     return true;
21345                 }
21346                 break;
21347             
21348             default:
21349                 return false;
21350         }
21351
21352     },
21353     // private
21354     IsLongEnough: function (pwd, size)
21355     {
21356         return !(pwd == null || isNaN(size) || pwd.length < size);
21357     },
21358     // private
21359     SpansEnoughCharacterSets: function (word, nb)
21360     {
21361         if (!this.IsLongEnough(word, nb))
21362         {
21363             return false;
21364         }
21365
21366         var characterSetChecks = new Array(
21367             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21368             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21369         );
21370         
21371         for (var index = 0; index < word.length; ++index) {
21372             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21373                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21374                     characterSetChecks[nCharSet].fResult = true;
21375                     break;
21376                 }
21377             }
21378         }
21379
21380         var nCharSets = 0;
21381         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21382             if (characterSetChecks[nCharSet].fResult) {
21383                 ++nCharSets;
21384             }
21385         }
21386
21387         if (nCharSets < nb) {
21388             return false;
21389         }
21390         return true;
21391     },
21392     // private
21393     ClientSideStrongPassword: function (pwd)
21394     {
21395         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21396     },
21397     // private
21398     ClientSideMediumPassword: function (pwd)
21399     {
21400         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21401     },
21402     // private
21403     ClientSideWeakPassword: function (pwd)
21404     {
21405         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21406     }
21407           
21408 })//<script type="text/javascript">
21409
21410 /*
21411  * Based  Ext JS Library 1.1.1
21412  * Copyright(c) 2006-2007, Ext JS, LLC.
21413  * LGPL
21414  *
21415  */
21416  
21417 /**
21418  * @class Roo.HtmlEditorCore
21419  * @extends Roo.Component
21420  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21421  *
21422  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21423  */
21424
21425 Roo.HtmlEditorCore = function(config){
21426     
21427     
21428     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21429     
21430     
21431     this.addEvents({
21432         /**
21433          * @event initialize
21434          * Fires when the editor is fully initialized (including the iframe)
21435          * @param {Roo.HtmlEditorCore} this
21436          */
21437         initialize: true,
21438         /**
21439          * @event activate
21440          * Fires when the editor is first receives the focus. Any insertion must wait
21441          * until after this event.
21442          * @param {Roo.HtmlEditorCore} this
21443          */
21444         activate: true,
21445          /**
21446          * @event beforesync
21447          * Fires before the textarea is updated with content from the editor iframe. Return false
21448          * to cancel the sync.
21449          * @param {Roo.HtmlEditorCore} this
21450          * @param {String} html
21451          */
21452         beforesync: true,
21453          /**
21454          * @event beforepush
21455          * Fires before the iframe editor is updated with content from the textarea. Return false
21456          * to cancel the push.
21457          * @param {Roo.HtmlEditorCore} this
21458          * @param {String} html
21459          */
21460         beforepush: true,
21461          /**
21462          * @event sync
21463          * Fires when the textarea is updated with content from the editor iframe.
21464          * @param {Roo.HtmlEditorCore} this
21465          * @param {String} html
21466          */
21467         sync: true,
21468          /**
21469          * @event push
21470          * Fires when the iframe editor is updated with content from the textarea.
21471          * @param {Roo.HtmlEditorCore} this
21472          * @param {String} html
21473          */
21474         push: true,
21475         
21476         /**
21477          * @event editorevent
21478          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21479          * @param {Roo.HtmlEditorCore} this
21480          */
21481         editorevent: true
21482         
21483     });
21484     
21485     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21486     
21487     // defaults : white / black...
21488     this.applyBlacklists();
21489     
21490     
21491     
21492 };
21493
21494
21495 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21496
21497
21498      /**
21499      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21500      */
21501     
21502     owner : false,
21503     
21504      /**
21505      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21506      *                        Roo.resizable.
21507      */
21508     resizable : false,
21509      /**
21510      * @cfg {Number} height (in pixels)
21511      */   
21512     height: 300,
21513    /**
21514      * @cfg {Number} width (in pixels)
21515      */   
21516     width: 500,
21517     
21518     /**
21519      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21520      * 
21521      */
21522     stylesheets: false,
21523     
21524     // id of frame..
21525     frameId: false,
21526     
21527     // private properties
21528     validationEvent : false,
21529     deferHeight: true,
21530     initialized : false,
21531     activated : false,
21532     sourceEditMode : false,
21533     onFocus : Roo.emptyFn,
21534     iframePad:3,
21535     hideMode:'offsets',
21536     
21537     clearUp: true,
21538     
21539     // blacklist + whitelisted elements..
21540     black: false,
21541     white: false,
21542      
21543     bodyCls : '',
21544
21545     /**
21546      * Protected method that will not generally be called directly. It
21547      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21548      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21549      */
21550     getDocMarkup : function(){
21551         // body styles..
21552         var st = '';
21553         
21554         // inherit styels from page...?? 
21555         if (this.stylesheets === false) {
21556             
21557             Roo.get(document.head).select('style').each(function(node) {
21558                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21559             });
21560             
21561             Roo.get(document.head).select('link').each(function(node) { 
21562                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21563             });
21564             
21565         } else if (!this.stylesheets.length) {
21566                 // simple..
21567                 st = '<style type="text/css">' +
21568                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21569                    '</style>';
21570         } else { 
21571             st = '<style type="text/css">' +
21572                     this.stylesheets +
21573                 '</style>';
21574         }
21575         
21576         st +=  '<style type="text/css">' +
21577             'IMG { cursor: pointer } ' +
21578         '</style>';
21579
21580         var cls = 'roo-htmleditor-body';
21581         
21582         if(this.bodyCls.length){
21583             cls += ' ' + this.bodyCls;
21584         }
21585         
21586         return '<html><head>' + st  +
21587             //<style type="text/css">' +
21588             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21589             //'</style>' +
21590             ' </head><body class="' +  cls + '"></body></html>';
21591     },
21592
21593     // private
21594     onRender : function(ct, position)
21595     {
21596         var _t = this;
21597         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21598         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21599         
21600         
21601         this.el.dom.style.border = '0 none';
21602         this.el.dom.setAttribute('tabIndex', -1);
21603         this.el.addClass('x-hidden hide');
21604         
21605         
21606         
21607         if(Roo.isIE){ // fix IE 1px bogus margin
21608             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21609         }
21610        
21611         
21612         this.frameId = Roo.id();
21613         
21614          
21615         
21616         var iframe = this.owner.wrap.createChild({
21617             tag: 'iframe',
21618             cls: 'form-control', // bootstrap..
21619             id: this.frameId,
21620             name: this.frameId,
21621             frameBorder : 'no',
21622             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21623         }, this.el
21624         );
21625         
21626         
21627         this.iframe = iframe.dom;
21628
21629          this.assignDocWin();
21630         
21631         this.doc.designMode = 'on';
21632        
21633         this.doc.open();
21634         this.doc.write(this.getDocMarkup());
21635         this.doc.close();
21636
21637         
21638         var task = { // must defer to wait for browser to be ready
21639             run : function(){
21640                 //console.log("run task?" + this.doc.readyState);
21641                 this.assignDocWin();
21642                 if(this.doc.body || this.doc.readyState == 'complete'){
21643                     try {
21644                         this.doc.designMode="on";
21645                     } catch (e) {
21646                         return;
21647                     }
21648                     Roo.TaskMgr.stop(task);
21649                     this.initEditor.defer(10, this);
21650                 }
21651             },
21652             interval : 10,
21653             duration: 10000,
21654             scope: this
21655         };
21656         Roo.TaskMgr.start(task);
21657
21658     },
21659
21660     // private
21661     onResize : function(w, h)
21662     {
21663          Roo.log('resize: ' +w + ',' + h );
21664         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21665         if(!this.iframe){
21666             return;
21667         }
21668         if(typeof w == 'number'){
21669             
21670             this.iframe.style.width = w + 'px';
21671         }
21672         if(typeof h == 'number'){
21673             
21674             this.iframe.style.height = h + 'px';
21675             if(this.doc){
21676                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21677             }
21678         }
21679         
21680     },
21681
21682     /**
21683      * Toggles the editor between standard and source edit mode.
21684      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21685      */
21686     toggleSourceEdit : function(sourceEditMode){
21687         
21688         this.sourceEditMode = sourceEditMode === true;
21689         
21690         if(this.sourceEditMode){
21691  
21692             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21693             
21694         }else{
21695             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21696             //this.iframe.className = '';
21697             this.deferFocus();
21698         }
21699         //this.setSize(this.owner.wrap.getSize());
21700         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21701     },
21702
21703     
21704   
21705
21706     /**
21707      * Protected method that will not generally be called directly. If you need/want
21708      * custom HTML cleanup, this is the method you should override.
21709      * @param {String} html The HTML to be cleaned
21710      * return {String} The cleaned HTML
21711      */
21712     cleanHtml : function(html){
21713         html = String(html);
21714         if(html.length > 5){
21715             if(Roo.isSafari){ // strip safari nonsense
21716                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21717             }
21718         }
21719         if(html == '&nbsp;'){
21720             html = '';
21721         }
21722         return html;
21723     },
21724
21725     /**
21726      * HTML Editor -> Textarea
21727      * Protected method that will not generally be called directly. Syncs the contents
21728      * of the editor iframe with the textarea.
21729      */
21730     syncValue : function(){
21731         if(this.initialized){
21732             var bd = (this.doc.body || this.doc.documentElement);
21733             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21734             var html = bd.innerHTML;
21735             if(Roo.isSafari){
21736                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21737                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21738                 if(m && m[1]){
21739                     html = '<div style="'+m[0]+'">' + html + '</div>';
21740                 }
21741             }
21742             html = this.cleanHtml(html);
21743             // fix up the special chars.. normaly like back quotes in word...
21744             // however we do not want to do this with chinese..
21745             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21746                 var cc = b.charCodeAt();
21747                 if (
21748                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21749                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21750                     (cc >= 0xf900 && cc < 0xfb00 )
21751                 ) {
21752                         return b;
21753                 }
21754                 return "&#"+cc+";" 
21755             });
21756             if(this.owner.fireEvent('beforesync', this, html) !== false){
21757                 this.el.dom.value = html;
21758                 this.owner.fireEvent('sync', this, html);
21759             }
21760         }
21761     },
21762
21763     /**
21764      * Protected method that will not generally be called directly. Pushes the value of the textarea
21765      * into the iframe editor.
21766      */
21767     pushValue : function(){
21768         if(this.initialized){
21769             var v = this.el.dom.value.trim();
21770             
21771 //            if(v.length < 1){
21772 //                v = '&#160;';
21773 //            }
21774             
21775             if(this.owner.fireEvent('beforepush', this, v) !== false){
21776                 var d = (this.doc.body || this.doc.documentElement);
21777                 d.innerHTML = v;
21778                 this.cleanUpPaste();
21779                 this.el.dom.value = d.innerHTML;
21780                 this.owner.fireEvent('push', this, v);
21781             }
21782         }
21783     },
21784
21785     // private
21786     deferFocus : function(){
21787         this.focus.defer(10, this);
21788     },
21789
21790     // doc'ed in Field
21791     focus : function(){
21792         if(this.win && !this.sourceEditMode){
21793             this.win.focus();
21794         }else{
21795             this.el.focus();
21796         }
21797     },
21798     
21799     assignDocWin: function()
21800     {
21801         var iframe = this.iframe;
21802         
21803          if(Roo.isIE){
21804             this.doc = iframe.contentWindow.document;
21805             this.win = iframe.contentWindow;
21806         } else {
21807 //            if (!Roo.get(this.frameId)) {
21808 //                return;
21809 //            }
21810 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21811 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21812             
21813             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21814                 return;
21815             }
21816             
21817             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21818             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21819         }
21820     },
21821     
21822     // private
21823     initEditor : function(){
21824         //console.log("INIT EDITOR");
21825         this.assignDocWin();
21826         
21827         
21828         
21829         this.doc.designMode="on";
21830         this.doc.open();
21831         this.doc.write(this.getDocMarkup());
21832         this.doc.close();
21833         
21834         var dbody = (this.doc.body || this.doc.documentElement);
21835         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21836         // this copies styles from the containing element into thsi one..
21837         // not sure why we need all of this..
21838         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21839         
21840         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21841         //ss['background-attachment'] = 'fixed'; // w3c
21842         dbody.bgProperties = 'fixed'; // ie
21843         //Roo.DomHelper.applyStyles(dbody, ss);
21844         Roo.EventManager.on(this.doc, {
21845             //'mousedown': this.onEditorEvent,
21846             'mouseup': this.onEditorEvent,
21847             'dblclick': this.onEditorEvent,
21848             'click': this.onEditorEvent,
21849             'keyup': this.onEditorEvent,
21850             buffer:100,
21851             scope: this
21852         });
21853         if(Roo.isGecko){
21854             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21855         }
21856         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21857             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21858         }
21859         this.initialized = true;
21860
21861         this.owner.fireEvent('initialize', this);
21862         this.pushValue();
21863     },
21864
21865     // private
21866     onDestroy : function(){
21867         
21868         
21869         
21870         if(this.rendered){
21871             
21872             //for (var i =0; i < this.toolbars.length;i++) {
21873             //    // fixme - ask toolbars for heights?
21874             //    this.toolbars[i].onDestroy();
21875            // }
21876             
21877             //this.wrap.dom.innerHTML = '';
21878             //this.wrap.remove();
21879         }
21880     },
21881
21882     // private
21883     onFirstFocus : function(){
21884         
21885         this.assignDocWin();
21886         
21887         
21888         this.activated = true;
21889          
21890     
21891         if(Roo.isGecko){ // prevent silly gecko errors
21892             this.win.focus();
21893             var s = this.win.getSelection();
21894             if(!s.focusNode || s.focusNode.nodeType != 3){
21895                 var r = s.getRangeAt(0);
21896                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21897                 r.collapse(true);
21898                 this.deferFocus();
21899             }
21900             try{
21901                 this.execCmd('useCSS', true);
21902                 this.execCmd('styleWithCSS', false);
21903             }catch(e){}
21904         }
21905         this.owner.fireEvent('activate', this);
21906     },
21907
21908     // private
21909     adjustFont: function(btn){
21910         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21911         //if(Roo.isSafari){ // safari
21912         //    adjust *= 2;
21913        // }
21914         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21915         if(Roo.isSafari){ // safari
21916             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21917             v =  (v < 10) ? 10 : v;
21918             v =  (v > 48) ? 48 : v;
21919             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21920             
21921         }
21922         
21923         
21924         v = Math.max(1, v+adjust);
21925         
21926         this.execCmd('FontSize', v  );
21927     },
21928
21929     onEditorEvent : function(e)
21930     {
21931         this.owner.fireEvent('editorevent', this, e);
21932       //  this.updateToolbar();
21933         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21934     },
21935
21936     insertTag : function(tg)
21937     {
21938         // could be a bit smarter... -> wrap the current selected tRoo..
21939         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21940             
21941             range = this.createRange(this.getSelection());
21942             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21943             wrappingNode.appendChild(range.extractContents());
21944             range.insertNode(wrappingNode);
21945
21946             return;
21947             
21948             
21949             
21950         }
21951         this.execCmd("formatblock",   tg);
21952         
21953     },
21954     
21955     insertText : function(txt)
21956     {
21957         
21958         
21959         var range = this.createRange();
21960         range.deleteContents();
21961                //alert(Sender.getAttribute('label'));
21962                
21963         range.insertNode(this.doc.createTextNode(txt));
21964     } ,
21965     
21966      
21967
21968     /**
21969      * Executes a Midas editor command on the editor document and performs necessary focus and
21970      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21971      * @param {String} cmd The Midas command
21972      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21973      */
21974     relayCmd : function(cmd, value){
21975         this.win.focus();
21976         this.execCmd(cmd, value);
21977         this.owner.fireEvent('editorevent', this);
21978         //this.updateToolbar();
21979         this.owner.deferFocus();
21980     },
21981
21982     /**
21983      * Executes a Midas editor command directly on the editor document.
21984      * For visual commands, you should use {@link #relayCmd} instead.
21985      * <b>This should only be called after the editor is initialized.</b>
21986      * @param {String} cmd The Midas command
21987      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21988      */
21989     execCmd : function(cmd, value){
21990         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21991         this.syncValue();
21992     },
21993  
21994  
21995    
21996     /**
21997      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21998      * to insert tRoo.
21999      * @param {String} text | dom node.. 
22000      */
22001     insertAtCursor : function(text)
22002     {
22003         
22004         if(!this.activated){
22005             return;
22006         }
22007         /*
22008         if(Roo.isIE){
22009             this.win.focus();
22010             var r = this.doc.selection.createRange();
22011             if(r){
22012                 r.collapse(true);
22013                 r.pasteHTML(text);
22014                 this.syncValue();
22015                 this.deferFocus();
22016             
22017             }
22018             return;
22019         }
22020         */
22021         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22022             this.win.focus();
22023             
22024             
22025             // from jquery ui (MIT licenced)
22026             var range, node;
22027             var win = this.win;
22028             
22029             if (win.getSelection && win.getSelection().getRangeAt) {
22030                 range = win.getSelection().getRangeAt(0);
22031                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22032                 range.insertNode(node);
22033             } else if (win.document.selection && win.document.selection.createRange) {
22034                 // no firefox support
22035                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22036                 win.document.selection.createRange().pasteHTML(txt);
22037             } else {
22038                 // no firefox support
22039                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22040                 this.execCmd('InsertHTML', txt);
22041             } 
22042             
22043             this.syncValue();
22044             
22045             this.deferFocus();
22046         }
22047     },
22048  // private
22049     mozKeyPress : function(e){
22050         if(e.ctrlKey){
22051             var c = e.getCharCode(), cmd;
22052           
22053             if(c > 0){
22054                 c = String.fromCharCode(c).toLowerCase();
22055                 switch(c){
22056                     case 'b':
22057                         cmd = 'bold';
22058                         break;
22059                     case 'i':
22060                         cmd = 'italic';
22061                         break;
22062                     
22063                     case 'u':
22064                         cmd = 'underline';
22065                         break;
22066                     
22067                     case 'v':
22068                         this.cleanUpPaste.defer(100, this);
22069                         return;
22070                         
22071                 }
22072                 if(cmd){
22073                     this.win.focus();
22074                     this.execCmd(cmd);
22075                     this.deferFocus();
22076                     e.preventDefault();
22077                 }
22078                 
22079             }
22080         }
22081     },
22082
22083     // private
22084     fixKeys : function(){ // load time branching for fastest keydown performance
22085         if(Roo.isIE){
22086             return function(e){
22087                 var k = e.getKey(), r;
22088                 if(k == e.TAB){
22089                     e.stopEvent();
22090                     r = this.doc.selection.createRange();
22091                     if(r){
22092                         r.collapse(true);
22093                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22094                         this.deferFocus();
22095                     }
22096                     return;
22097                 }
22098                 
22099                 if(k == e.ENTER){
22100                     r = this.doc.selection.createRange();
22101                     if(r){
22102                         var target = r.parentElement();
22103                         if(!target || target.tagName.toLowerCase() != 'li'){
22104                             e.stopEvent();
22105                             r.pasteHTML('<br />');
22106                             r.collapse(false);
22107                             r.select();
22108                         }
22109                     }
22110                 }
22111                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22112                     this.cleanUpPaste.defer(100, this);
22113                     return;
22114                 }
22115                 
22116                 
22117             };
22118         }else if(Roo.isOpera){
22119             return function(e){
22120                 var k = e.getKey();
22121                 if(k == e.TAB){
22122                     e.stopEvent();
22123                     this.win.focus();
22124                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22125                     this.deferFocus();
22126                 }
22127                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22128                     this.cleanUpPaste.defer(100, this);
22129                     return;
22130                 }
22131                 
22132             };
22133         }else if(Roo.isSafari){
22134             return function(e){
22135                 var k = e.getKey();
22136                 
22137                 if(k == e.TAB){
22138                     e.stopEvent();
22139                     this.execCmd('InsertText','\t');
22140                     this.deferFocus();
22141                     return;
22142                 }
22143                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22144                     this.cleanUpPaste.defer(100, this);
22145                     return;
22146                 }
22147                 
22148              };
22149         }
22150     }(),
22151     
22152     getAllAncestors: function()
22153     {
22154         var p = this.getSelectedNode();
22155         var a = [];
22156         if (!p) {
22157             a.push(p); // push blank onto stack..
22158             p = this.getParentElement();
22159         }
22160         
22161         
22162         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22163             a.push(p);
22164             p = p.parentNode;
22165         }
22166         a.push(this.doc.body);
22167         return a;
22168     },
22169     lastSel : false,
22170     lastSelNode : false,
22171     
22172     
22173     getSelection : function() 
22174     {
22175         this.assignDocWin();
22176         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22177     },
22178     
22179     getSelectedNode: function() 
22180     {
22181         // this may only work on Gecko!!!
22182         
22183         // should we cache this!!!!
22184         
22185         
22186         
22187          
22188         var range = this.createRange(this.getSelection()).cloneRange();
22189         
22190         if (Roo.isIE) {
22191             var parent = range.parentElement();
22192             while (true) {
22193                 var testRange = range.duplicate();
22194                 testRange.moveToElementText(parent);
22195                 if (testRange.inRange(range)) {
22196                     break;
22197                 }
22198                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22199                     break;
22200                 }
22201                 parent = parent.parentElement;
22202             }
22203             return parent;
22204         }
22205         
22206         // is ancestor a text element.
22207         var ac =  range.commonAncestorContainer;
22208         if (ac.nodeType == 3) {
22209             ac = ac.parentNode;
22210         }
22211         
22212         var ar = ac.childNodes;
22213          
22214         var nodes = [];
22215         var other_nodes = [];
22216         var has_other_nodes = false;
22217         for (var i=0;i<ar.length;i++) {
22218             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22219                 continue;
22220             }
22221             // fullly contained node.
22222             
22223             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22224                 nodes.push(ar[i]);
22225                 continue;
22226             }
22227             
22228             // probably selected..
22229             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22230                 other_nodes.push(ar[i]);
22231                 continue;
22232             }
22233             // outer..
22234             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22235                 continue;
22236             }
22237             
22238             
22239             has_other_nodes = true;
22240         }
22241         if (!nodes.length && other_nodes.length) {
22242             nodes= other_nodes;
22243         }
22244         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22245             return false;
22246         }
22247         
22248         return nodes[0];
22249     },
22250     createRange: function(sel)
22251     {
22252         // this has strange effects when using with 
22253         // top toolbar - not sure if it's a great idea.
22254         //this.editor.contentWindow.focus();
22255         if (typeof sel != "undefined") {
22256             try {
22257                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22258             } catch(e) {
22259                 return this.doc.createRange();
22260             }
22261         } else {
22262             return this.doc.createRange();
22263         }
22264     },
22265     getParentElement: function()
22266     {
22267         
22268         this.assignDocWin();
22269         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22270         
22271         var range = this.createRange(sel);
22272          
22273         try {
22274             var p = range.commonAncestorContainer;
22275             while (p.nodeType == 3) { // text node
22276                 p = p.parentNode;
22277             }
22278             return p;
22279         } catch (e) {
22280             return null;
22281         }
22282     
22283     },
22284     /***
22285      *
22286      * Range intersection.. the hard stuff...
22287      *  '-1' = before
22288      *  '0' = hits..
22289      *  '1' = after.
22290      *         [ -- selected range --- ]
22291      *   [fail]                        [fail]
22292      *
22293      *    basically..
22294      *      if end is before start or  hits it. fail.
22295      *      if start is after end or hits it fail.
22296      *
22297      *   if either hits (but other is outside. - then it's not 
22298      *   
22299      *    
22300      **/
22301     
22302     
22303     // @see http://www.thismuchiknow.co.uk/?p=64.
22304     rangeIntersectsNode : function(range, node)
22305     {
22306         var nodeRange = node.ownerDocument.createRange();
22307         try {
22308             nodeRange.selectNode(node);
22309         } catch (e) {
22310             nodeRange.selectNodeContents(node);
22311         }
22312     
22313         var rangeStartRange = range.cloneRange();
22314         rangeStartRange.collapse(true);
22315     
22316         var rangeEndRange = range.cloneRange();
22317         rangeEndRange.collapse(false);
22318     
22319         var nodeStartRange = nodeRange.cloneRange();
22320         nodeStartRange.collapse(true);
22321     
22322         var nodeEndRange = nodeRange.cloneRange();
22323         nodeEndRange.collapse(false);
22324     
22325         return rangeStartRange.compareBoundaryPoints(
22326                  Range.START_TO_START, nodeEndRange) == -1 &&
22327                rangeEndRange.compareBoundaryPoints(
22328                  Range.START_TO_START, nodeStartRange) == 1;
22329         
22330          
22331     },
22332     rangeCompareNode : function(range, node)
22333     {
22334         var nodeRange = node.ownerDocument.createRange();
22335         try {
22336             nodeRange.selectNode(node);
22337         } catch (e) {
22338             nodeRange.selectNodeContents(node);
22339         }
22340         
22341         
22342         range.collapse(true);
22343     
22344         nodeRange.collapse(true);
22345      
22346         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22347         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22348          
22349         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22350         
22351         var nodeIsBefore   =  ss == 1;
22352         var nodeIsAfter    = ee == -1;
22353         
22354         if (nodeIsBefore && nodeIsAfter) {
22355             return 0; // outer
22356         }
22357         if (!nodeIsBefore && nodeIsAfter) {
22358             return 1; //right trailed.
22359         }
22360         
22361         if (nodeIsBefore && !nodeIsAfter) {
22362             return 2;  // left trailed.
22363         }
22364         // fully contined.
22365         return 3;
22366     },
22367
22368     // private? - in a new class?
22369     cleanUpPaste :  function()
22370     {
22371         // cleans up the whole document..
22372         Roo.log('cleanuppaste');
22373         
22374         this.cleanUpChildren(this.doc.body);
22375         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22376         if (clean != this.doc.body.innerHTML) {
22377             this.doc.body.innerHTML = clean;
22378         }
22379         
22380     },
22381     
22382     cleanWordChars : function(input) {// change the chars to hex code
22383         var he = Roo.HtmlEditorCore;
22384         
22385         var output = input;
22386         Roo.each(he.swapCodes, function(sw) { 
22387             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22388             
22389             output = output.replace(swapper, sw[1]);
22390         });
22391         
22392         return output;
22393     },
22394     
22395     
22396     cleanUpChildren : function (n)
22397     {
22398         if (!n.childNodes.length) {
22399             return;
22400         }
22401         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22402            this.cleanUpChild(n.childNodes[i]);
22403         }
22404     },
22405     
22406     
22407         
22408     
22409     cleanUpChild : function (node)
22410     {
22411         var ed = this;
22412         //console.log(node);
22413         if (node.nodeName == "#text") {
22414             // clean up silly Windows -- stuff?
22415             return; 
22416         }
22417         if (node.nodeName == "#comment") {
22418             node.parentNode.removeChild(node);
22419             // clean up silly Windows -- stuff?
22420             return; 
22421         }
22422         var lcname = node.tagName.toLowerCase();
22423         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22424         // whitelist of tags..
22425         
22426         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22427             // remove node.
22428             node.parentNode.removeChild(node);
22429             return;
22430             
22431         }
22432         
22433         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22434         
22435         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22436         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22437         
22438         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22439         //    remove_keep_children = true;
22440         //}
22441         
22442         if (remove_keep_children) {
22443             this.cleanUpChildren(node);
22444             // inserts everything just before this node...
22445             while (node.childNodes.length) {
22446                 var cn = node.childNodes[0];
22447                 node.removeChild(cn);
22448                 node.parentNode.insertBefore(cn, node);
22449             }
22450             node.parentNode.removeChild(node);
22451             return;
22452         }
22453         
22454         if (!node.attributes || !node.attributes.length) {
22455             this.cleanUpChildren(node);
22456             return;
22457         }
22458         
22459         function cleanAttr(n,v)
22460         {
22461             
22462             if (v.match(/^\./) || v.match(/^\//)) {
22463                 return;
22464             }
22465             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
22466                 return;
22467             }
22468             if (v.match(/^#/)) {
22469                 return;
22470             }
22471 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22472             node.removeAttribute(n);
22473             
22474         }
22475         
22476         var cwhite = this.cwhite;
22477         var cblack = this.cblack;
22478             
22479         function cleanStyle(n,v)
22480         {
22481             if (v.match(/expression/)) { //XSS?? should we even bother..
22482                 node.removeAttribute(n);
22483                 return;
22484             }
22485             
22486             var parts = v.split(/;/);
22487             var clean = [];
22488             
22489             Roo.each(parts, function(p) {
22490                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22491                 if (!p.length) {
22492                     return true;
22493                 }
22494                 var l = p.split(':').shift().replace(/\s+/g,'');
22495                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22496                 
22497                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22498 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22499                     //node.removeAttribute(n);
22500                     return true;
22501                 }
22502                 //Roo.log()
22503                 // only allow 'c whitelisted system attributes'
22504                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22505 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22506                     //node.removeAttribute(n);
22507                     return true;
22508                 }
22509                 
22510                 
22511                  
22512                 
22513                 clean.push(p);
22514                 return true;
22515             });
22516             if (clean.length) { 
22517                 node.setAttribute(n, clean.join(';'));
22518             } else {
22519                 node.removeAttribute(n);
22520             }
22521             
22522         }
22523         
22524         
22525         for (var i = node.attributes.length-1; i > -1 ; i--) {
22526             var a = node.attributes[i];
22527             //console.log(a);
22528             
22529             if (a.name.toLowerCase().substr(0,2)=='on')  {
22530                 node.removeAttribute(a.name);
22531                 continue;
22532             }
22533             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22534                 node.removeAttribute(a.name);
22535                 continue;
22536             }
22537             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22538                 cleanAttr(a.name,a.value); // fixme..
22539                 continue;
22540             }
22541             if (a.name == 'style') {
22542                 cleanStyle(a.name,a.value);
22543                 continue;
22544             }
22545             /// clean up MS crap..
22546             // tecnically this should be a list of valid class'es..
22547             
22548             
22549             if (a.name == 'class') {
22550                 if (a.value.match(/^Mso/)) {
22551                     node.className = '';
22552                 }
22553                 
22554                 if (a.value.match(/^body$/)) {
22555                     node.className = '';
22556                 }
22557                 continue;
22558             }
22559             
22560             // style cleanup!?
22561             // class cleanup?
22562             
22563         }
22564         
22565         
22566         this.cleanUpChildren(node);
22567         
22568         
22569     },
22570     
22571     /**
22572      * Clean up MS wordisms...
22573      */
22574     cleanWord : function(node)
22575     {
22576         
22577         
22578         if (!node) {
22579             this.cleanWord(this.doc.body);
22580             return;
22581         }
22582         if (node.nodeName == "#text") {
22583             // clean up silly Windows -- stuff?
22584             return; 
22585         }
22586         if (node.nodeName == "#comment") {
22587             node.parentNode.removeChild(node);
22588             // clean up silly Windows -- stuff?
22589             return; 
22590         }
22591         
22592         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22593             node.parentNode.removeChild(node);
22594             return;
22595         }
22596         
22597         // remove - but keep children..
22598         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22599             while (node.childNodes.length) {
22600                 var cn = node.childNodes[0];
22601                 node.removeChild(cn);
22602                 node.parentNode.insertBefore(cn, node);
22603             }
22604             node.parentNode.removeChild(node);
22605             this.iterateChildren(node, this.cleanWord);
22606             return;
22607         }
22608         // clean styles
22609         if (node.className.length) {
22610             
22611             var cn = node.className.split(/\W+/);
22612             var cna = [];
22613             Roo.each(cn, function(cls) {
22614                 if (cls.match(/Mso[a-zA-Z]+/)) {
22615                     return;
22616                 }
22617                 cna.push(cls);
22618             });
22619             node.className = cna.length ? cna.join(' ') : '';
22620             if (!cna.length) {
22621                 node.removeAttribute("class");
22622             }
22623         }
22624         
22625         if (node.hasAttribute("lang")) {
22626             node.removeAttribute("lang");
22627         }
22628         
22629         if (node.hasAttribute("style")) {
22630             
22631             var styles = node.getAttribute("style").split(";");
22632             var nstyle = [];
22633             Roo.each(styles, function(s) {
22634                 if (!s.match(/:/)) {
22635                     return;
22636                 }
22637                 var kv = s.split(":");
22638                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22639                     return;
22640                 }
22641                 // what ever is left... we allow.
22642                 nstyle.push(s);
22643             });
22644             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22645             if (!nstyle.length) {
22646                 node.removeAttribute('style');
22647             }
22648         }
22649         this.iterateChildren(node, this.cleanWord);
22650         
22651         
22652         
22653     },
22654     /**
22655      * iterateChildren of a Node, calling fn each time, using this as the scole..
22656      * @param {DomNode} node node to iterate children of.
22657      * @param {Function} fn method of this class to call on each item.
22658      */
22659     iterateChildren : function(node, fn)
22660     {
22661         if (!node.childNodes.length) {
22662                 return;
22663         }
22664         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22665            fn.call(this, node.childNodes[i])
22666         }
22667     },
22668     
22669     
22670     /**
22671      * cleanTableWidths.
22672      *
22673      * Quite often pasting from word etc.. results in tables with column and widths.
22674      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22675      *
22676      */
22677     cleanTableWidths : function(node)
22678     {
22679          
22680          
22681         if (!node) {
22682             this.cleanTableWidths(this.doc.body);
22683             return;
22684         }
22685         
22686         // ignore list...
22687         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22688             return; 
22689         }
22690         Roo.log(node.tagName);
22691         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22692             this.iterateChildren(node, this.cleanTableWidths);
22693             return;
22694         }
22695         if (node.hasAttribute('width')) {
22696             node.removeAttribute('width');
22697         }
22698         
22699          
22700         if (node.hasAttribute("style")) {
22701             // pretty basic...
22702             
22703             var styles = node.getAttribute("style").split(";");
22704             var nstyle = [];
22705             Roo.each(styles, function(s) {
22706                 if (!s.match(/:/)) {
22707                     return;
22708                 }
22709                 var kv = s.split(":");
22710                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22711                     return;
22712                 }
22713                 // what ever is left... we allow.
22714                 nstyle.push(s);
22715             });
22716             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22717             if (!nstyle.length) {
22718                 node.removeAttribute('style');
22719             }
22720         }
22721         
22722         this.iterateChildren(node, this.cleanTableWidths);
22723         
22724         
22725     },
22726     
22727     
22728     
22729     
22730     domToHTML : function(currentElement, depth, nopadtext) {
22731         
22732         depth = depth || 0;
22733         nopadtext = nopadtext || false;
22734     
22735         if (!currentElement) {
22736             return this.domToHTML(this.doc.body);
22737         }
22738         
22739         //Roo.log(currentElement);
22740         var j;
22741         var allText = false;
22742         var nodeName = currentElement.nodeName;
22743         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22744         
22745         if  (nodeName == '#text') {
22746             
22747             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22748         }
22749         
22750         
22751         var ret = '';
22752         if (nodeName != 'BODY') {
22753              
22754             var i = 0;
22755             // Prints the node tagName, such as <A>, <IMG>, etc
22756             if (tagName) {
22757                 var attr = [];
22758                 for(i = 0; i < currentElement.attributes.length;i++) {
22759                     // quoting?
22760                     var aname = currentElement.attributes.item(i).name;
22761                     if (!currentElement.attributes.item(i).value.length) {
22762                         continue;
22763                     }
22764                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22765                 }
22766                 
22767                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22768             } 
22769             else {
22770                 
22771                 // eack
22772             }
22773         } else {
22774             tagName = false;
22775         }
22776         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22777             return ret;
22778         }
22779         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22780             nopadtext = true;
22781         }
22782         
22783         
22784         // Traverse the tree
22785         i = 0;
22786         var currentElementChild = currentElement.childNodes.item(i);
22787         var allText = true;
22788         var innerHTML  = '';
22789         lastnode = '';
22790         while (currentElementChild) {
22791             // Formatting code (indent the tree so it looks nice on the screen)
22792             var nopad = nopadtext;
22793             if (lastnode == 'SPAN') {
22794                 nopad  = true;
22795             }
22796             // text
22797             if  (currentElementChild.nodeName == '#text') {
22798                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22799                 toadd = nopadtext ? toadd : toadd.trim();
22800                 if (!nopad && toadd.length > 80) {
22801                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22802                 }
22803                 innerHTML  += toadd;
22804                 
22805                 i++;
22806                 currentElementChild = currentElement.childNodes.item(i);
22807                 lastNode = '';
22808                 continue;
22809             }
22810             allText = false;
22811             
22812             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22813                 
22814             // Recursively traverse the tree structure of the child node
22815             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22816             lastnode = currentElementChild.nodeName;
22817             i++;
22818             currentElementChild=currentElement.childNodes.item(i);
22819         }
22820         
22821         ret += innerHTML;
22822         
22823         if (!allText) {
22824                 // The remaining code is mostly for formatting the tree
22825             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22826         }
22827         
22828         
22829         if (tagName) {
22830             ret+= "</"+tagName+">";
22831         }
22832         return ret;
22833         
22834     },
22835         
22836     applyBlacklists : function()
22837     {
22838         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22839         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22840         
22841         this.white = [];
22842         this.black = [];
22843         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22844             if (b.indexOf(tag) > -1) {
22845                 return;
22846             }
22847             this.white.push(tag);
22848             
22849         }, this);
22850         
22851         Roo.each(w, function(tag) {
22852             if (b.indexOf(tag) > -1) {
22853                 return;
22854             }
22855             if (this.white.indexOf(tag) > -1) {
22856                 return;
22857             }
22858             this.white.push(tag);
22859             
22860         }, this);
22861         
22862         
22863         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22864             if (w.indexOf(tag) > -1) {
22865                 return;
22866             }
22867             this.black.push(tag);
22868             
22869         }, this);
22870         
22871         Roo.each(b, function(tag) {
22872             if (w.indexOf(tag) > -1) {
22873                 return;
22874             }
22875             if (this.black.indexOf(tag) > -1) {
22876                 return;
22877             }
22878             this.black.push(tag);
22879             
22880         }, this);
22881         
22882         
22883         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22884         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22885         
22886         this.cwhite = [];
22887         this.cblack = [];
22888         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22889             if (b.indexOf(tag) > -1) {
22890                 return;
22891             }
22892             this.cwhite.push(tag);
22893             
22894         }, this);
22895         
22896         Roo.each(w, function(tag) {
22897             if (b.indexOf(tag) > -1) {
22898                 return;
22899             }
22900             if (this.cwhite.indexOf(tag) > -1) {
22901                 return;
22902             }
22903             this.cwhite.push(tag);
22904             
22905         }, this);
22906         
22907         
22908         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22909             if (w.indexOf(tag) > -1) {
22910                 return;
22911             }
22912             this.cblack.push(tag);
22913             
22914         }, this);
22915         
22916         Roo.each(b, function(tag) {
22917             if (w.indexOf(tag) > -1) {
22918                 return;
22919             }
22920             if (this.cblack.indexOf(tag) > -1) {
22921                 return;
22922             }
22923             this.cblack.push(tag);
22924             
22925         }, this);
22926     },
22927     
22928     setStylesheets : function(stylesheets)
22929     {
22930         if(typeof(stylesheets) == 'string'){
22931             Roo.get(this.iframe.contentDocument.head).createChild({
22932                 tag : 'link',
22933                 rel : 'stylesheet',
22934                 type : 'text/css',
22935                 href : stylesheets
22936             });
22937             
22938             return;
22939         }
22940         var _this = this;
22941      
22942         Roo.each(stylesheets, function(s) {
22943             if(!s.length){
22944                 return;
22945             }
22946             
22947             Roo.get(_this.iframe.contentDocument.head).createChild({
22948                 tag : 'link',
22949                 rel : 'stylesheet',
22950                 type : 'text/css',
22951                 href : s
22952             });
22953         });
22954
22955         
22956     },
22957     
22958     removeStylesheets : function()
22959     {
22960         var _this = this;
22961         
22962         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22963             s.remove();
22964         });
22965     },
22966     
22967     setStyle : function(style)
22968     {
22969         Roo.get(this.iframe.contentDocument.head).createChild({
22970             tag : 'style',
22971             type : 'text/css',
22972             html : style
22973         });
22974
22975         return;
22976     }
22977     
22978     // hide stuff that is not compatible
22979     /**
22980      * @event blur
22981      * @hide
22982      */
22983     /**
22984      * @event change
22985      * @hide
22986      */
22987     /**
22988      * @event focus
22989      * @hide
22990      */
22991     /**
22992      * @event specialkey
22993      * @hide
22994      */
22995     /**
22996      * @cfg {String} fieldClass @hide
22997      */
22998     /**
22999      * @cfg {String} focusClass @hide
23000      */
23001     /**
23002      * @cfg {String} autoCreate @hide
23003      */
23004     /**
23005      * @cfg {String} inputType @hide
23006      */
23007     /**
23008      * @cfg {String} invalidClass @hide
23009      */
23010     /**
23011      * @cfg {String} invalidText @hide
23012      */
23013     /**
23014      * @cfg {String} msgFx @hide
23015      */
23016     /**
23017      * @cfg {String} validateOnBlur @hide
23018      */
23019 });
23020
23021 Roo.HtmlEditorCore.white = [
23022         'area', 'br', 'img', 'input', 'hr', 'wbr',
23023         
23024        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23025        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23026        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23027        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23028        'table',   'ul',         'xmp', 
23029        
23030        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23031       'thead',   'tr', 
23032      
23033       'dir', 'menu', 'ol', 'ul', 'dl',
23034        
23035       'embed',  'object'
23036 ];
23037
23038
23039 Roo.HtmlEditorCore.black = [
23040     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23041         'applet', // 
23042         'base',   'basefont', 'bgsound', 'blink',  'body', 
23043         'frame',  'frameset', 'head',    'html',   'ilayer', 
23044         'iframe', 'layer',  'link',     'meta',    'object',   
23045         'script', 'style' ,'title',  'xml' // clean later..
23046 ];
23047 Roo.HtmlEditorCore.clean = [
23048     'script', 'style', 'title', 'xml'
23049 ];
23050 Roo.HtmlEditorCore.remove = [
23051     'font'
23052 ];
23053 // attributes..
23054
23055 Roo.HtmlEditorCore.ablack = [
23056     'on'
23057 ];
23058     
23059 Roo.HtmlEditorCore.aclean = [ 
23060     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23061 ];
23062
23063 // protocols..
23064 Roo.HtmlEditorCore.pwhite= [
23065         'http',  'https',  'mailto'
23066 ];
23067
23068 // white listed style attributes.
23069 Roo.HtmlEditorCore.cwhite= [
23070       //  'text-align', /// default is to allow most things..
23071       
23072          
23073 //        'font-size'//??
23074 ];
23075
23076 // black listed style attributes.
23077 Roo.HtmlEditorCore.cblack= [
23078       //  'font-size' -- this can be set by the project 
23079 ];
23080
23081
23082 Roo.HtmlEditorCore.swapCodes   =[ 
23083     [    8211, "--" ], 
23084     [    8212, "--" ], 
23085     [    8216,  "'" ],  
23086     [    8217, "'" ],  
23087     [    8220, '"' ],  
23088     [    8221, '"' ],  
23089     [    8226, "*" ],  
23090     [    8230, "..." ]
23091 ]; 
23092
23093     /*
23094  * - LGPL
23095  *
23096  * HtmlEditor
23097  * 
23098  */
23099
23100 /**
23101  * @class Roo.bootstrap.HtmlEditor
23102  * @extends Roo.bootstrap.TextArea
23103  * Bootstrap HtmlEditor class
23104
23105  * @constructor
23106  * Create a new HtmlEditor
23107  * @param {Object} config The config object
23108  */
23109
23110 Roo.bootstrap.HtmlEditor = function(config){
23111     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23112     if (!this.toolbars) {
23113         this.toolbars = [];
23114     }
23115     
23116     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23117     this.addEvents({
23118             /**
23119              * @event initialize
23120              * Fires when the editor is fully initialized (including the iframe)
23121              * @param {HtmlEditor} this
23122              */
23123             initialize: true,
23124             /**
23125              * @event activate
23126              * Fires when the editor is first receives the focus. Any insertion must wait
23127              * until after this event.
23128              * @param {HtmlEditor} this
23129              */
23130             activate: true,
23131              /**
23132              * @event beforesync
23133              * Fires before the textarea is updated with content from the editor iframe. Return false
23134              * to cancel the sync.
23135              * @param {HtmlEditor} this
23136              * @param {String} html
23137              */
23138             beforesync: true,
23139              /**
23140              * @event beforepush
23141              * Fires before the iframe editor is updated with content from the textarea. Return false
23142              * to cancel the push.
23143              * @param {HtmlEditor} this
23144              * @param {String} html
23145              */
23146             beforepush: true,
23147              /**
23148              * @event sync
23149              * Fires when the textarea is updated with content from the editor iframe.
23150              * @param {HtmlEditor} this
23151              * @param {String} html
23152              */
23153             sync: true,
23154              /**
23155              * @event push
23156              * Fires when the iframe editor is updated with content from the textarea.
23157              * @param {HtmlEditor} this
23158              * @param {String} html
23159              */
23160             push: true,
23161              /**
23162              * @event editmodechange
23163              * Fires when the editor switches edit modes
23164              * @param {HtmlEditor} this
23165              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23166              */
23167             editmodechange: true,
23168             /**
23169              * @event editorevent
23170              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23171              * @param {HtmlEditor} this
23172              */
23173             editorevent: true,
23174             /**
23175              * @event firstfocus
23176              * Fires when on first focus - needed by toolbars..
23177              * @param {HtmlEditor} this
23178              */
23179             firstfocus: true,
23180             /**
23181              * @event autosave
23182              * Auto save the htmlEditor value as a file into Events
23183              * @param {HtmlEditor} this
23184              */
23185             autosave: true,
23186             /**
23187              * @event savedpreview
23188              * preview the saved version of htmlEditor
23189              * @param {HtmlEditor} this
23190              */
23191             savedpreview: true
23192         });
23193 };
23194
23195
23196 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23197     
23198     
23199       /**
23200      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23201      */
23202     toolbars : false,
23203     
23204      /**
23205     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23206     */
23207     btns : [],
23208    
23209      /**
23210      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23211      *                        Roo.resizable.
23212      */
23213     resizable : false,
23214      /**
23215      * @cfg {Number} height (in pixels)
23216      */   
23217     height: 300,
23218    /**
23219      * @cfg {Number} width (in pixels)
23220      */   
23221     width: false,
23222     
23223     /**
23224      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23225      * 
23226      */
23227     stylesheets: false,
23228     
23229     // id of frame..
23230     frameId: false,
23231     
23232     // private properties
23233     validationEvent : false,
23234     deferHeight: true,
23235     initialized : false,
23236     activated : false,
23237     
23238     onFocus : Roo.emptyFn,
23239     iframePad:3,
23240     hideMode:'offsets',
23241     
23242     tbContainer : false,
23243     
23244     bodyCls : '',
23245     
23246     toolbarContainer :function() {
23247         return this.wrap.select('.x-html-editor-tb',true).first();
23248     },
23249
23250     /**
23251      * Protected method that will not generally be called directly. It
23252      * is called when the editor creates its toolbar. Override this method if you need to
23253      * add custom toolbar buttons.
23254      * @param {HtmlEditor} editor
23255      */
23256     createToolbar : function(){
23257         Roo.log('renewing');
23258         Roo.log("create toolbars");
23259         
23260         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23261         this.toolbars[0].render(this.toolbarContainer());
23262         
23263         return;
23264         
23265 //        if (!editor.toolbars || !editor.toolbars.length) {
23266 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23267 //        }
23268 //        
23269 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23270 //            editor.toolbars[i] = Roo.factory(
23271 //                    typeof(editor.toolbars[i]) == 'string' ?
23272 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23273 //                Roo.bootstrap.HtmlEditor);
23274 //            editor.toolbars[i].init(editor);
23275 //        }
23276     },
23277
23278      
23279     // private
23280     onRender : function(ct, position)
23281     {
23282        // Roo.log("Call onRender: " + this.xtype);
23283         var _t = this;
23284         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23285       
23286         this.wrap = this.inputEl().wrap({
23287             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23288         });
23289         
23290         this.editorcore.onRender(ct, position);
23291          
23292         if (this.resizable) {
23293             this.resizeEl = new Roo.Resizable(this.wrap, {
23294                 pinned : true,
23295                 wrap: true,
23296                 dynamic : true,
23297                 minHeight : this.height,
23298                 height: this.height,
23299                 handles : this.resizable,
23300                 width: this.width,
23301                 listeners : {
23302                     resize : function(r, w, h) {
23303                         _t.onResize(w,h); // -something
23304                     }
23305                 }
23306             });
23307             
23308         }
23309         this.createToolbar(this);
23310        
23311         
23312         if(!this.width && this.resizable){
23313             this.setSize(this.wrap.getSize());
23314         }
23315         if (this.resizeEl) {
23316             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23317             // should trigger onReize..
23318         }
23319         
23320     },
23321
23322     // private
23323     onResize : function(w, h)
23324     {
23325         Roo.log('resize: ' +w + ',' + h );
23326         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23327         var ew = false;
23328         var eh = false;
23329         
23330         if(this.inputEl() ){
23331             if(typeof w == 'number'){
23332                 var aw = w - this.wrap.getFrameWidth('lr');
23333                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23334                 ew = aw;
23335             }
23336             if(typeof h == 'number'){
23337                  var tbh = -11;  // fixme it needs to tool bar size!
23338                 for (var i =0; i < this.toolbars.length;i++) {
23339                     // fixme - ask toolbars for heights?
23340                     tbh += this.toolbars[i].el.getHeight();
23341                     //if (this.toolbars[i].footer) {
23342                     //    tbh += this.toolbars[i].footer.el.getHeight();
23343                     //}
23344                 }
23345               
23346                 
23347                 
23348                 
23349                 
23350                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23351                 ah -= 5; // knock a few pixes off for look..
23352                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23353                 var eh = ah;
23354             }
23355         }
23356         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23357         this.editorcore.onResize(ew,eh);
23358         
23359     },
23360
23361     /**
23362      * Toggles the editor between standard and source edit mode.
23363      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23364      */
23365     toggleSourceEdit : function(sourceEditMode)
23366     {
23367         this.editorcore.toggleSourceEdit(sourceEditMode);
23368         
23369         if(this.editorcore.sourceEditMode){
23370             Roo.log('editor - showing textarea');
23371             
23372 //            Roo.log('in');
23373 //            Roo.log(this.syncValue());
23374             this.syncValue();
23375             this.inputEl().removeClass(['hide', 'x-hidden']);
23376             this.inputEl().dom.removeAttribute('tabIndex');
23377             this.inputEl().focus();
23378         }else{
23379             Roo.log('editor - hiding textarea');
23380 //            Roo.log('out')
23381 //            Roo.log(this.pushValue()); 
23382             this.pushValue();
23383             
23384             this.inputEl().addClass(['hide', 'x-hidden']);
23385             this.inputEl().dom.setAttribute('tabIndex', -1);
23386             //this.deferFocus();
23387         }
23388          
23389         if(this.resizable){
23390             this.setSize(this.wrap.getSize());
23391         }
23392         
23393         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23394     },
23395  
23396     // private (for BoxComponent)
23397     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23398
23399     // private (for BoxComponent)
23400     getResizeEl : function(){
23401         return this.wrap;
23402     },
23403
23404     // private (for BoxComponent)
23405     getPositionEl : function(){
23406         return this.wrap;
23407     },
23408
23409     // private
23410     initEvents : function(){
23411         this.originalValue = this.getValue();
23412     },
23413
23414 //    /**
23415 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23416 //     * @method
23417 //     */
23418 //    markInvalid : Roo.emptyFn,
23419 //    /**
23420 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23421 //     * @method
23422 //     */
23423 //    clearInvalid : Roo.emptyFn,
23424
23425     setValue : function(v){
23426         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23427         this.editorcore.pushValue();
23428     },
23429
23430      
23431     // private
23432     deferFocus : function(){
23433         this.focus.defer(10, this);
23434     },
23435
23436     // doc'ed in Field
23437     focus : function(){
23438         this.editorcore.focus();
23439         
23440     },
23441       
23442
23443     // private
23444     onDestroy : function(){
23445         
23446         
23447         
23448         if(this.rendered){
23449             
23450             for (var i =0; i < this.toolbars.length;i++) {
23451                 // fixme - ask toolbars for heights?
23452                 this.toolbars[i].onDestroy();
23453             }
23454             
23455             this.wrap.dom.innerHTML = '';
23456             this.wrap.remove();
23457         }
23458     },
23459
23460     // private
23461     onFirstFocus : function(){
23462         //Roo.log("onFirstFocus");
23463         this.editorcore.onFirstFocus();
23464          for (var i =0; i < this.toolbars.length;i++) {
23465             this.toolbars[i].onFirstFocus();
23466         }
23467         
23468     },
23469     
23470     // private
23471     syncValue : function()
23472     {   
23473         this.editorcore.syncValue();
23474     },
23475     
23476     pushValue : function()
23477     {   
23478         this.editorcore.pushValue();
23479     }
23480      
23481     
23482     // hide stuff that is not compatible
23483     /**
23484      * @event blur
23485      * @hide
23486      */
23487     /**
23488      * @event change
23489      * @hide
23490      */
23491     /**
23492      * @event focus
23493      * @hide
23494      */
23495     /**
23496      * @event specialkey
23497      * @hide
23498      */
23499     /**
23500      * @cfg {String} fieldClass @hide
23501      */
23502     /**
23503      * @cfg {String} focusClass @hide
23504      */
23505     /**
23506      * @cfg {String} autoCreate @hide
23507      */
23508     /**
23509      * @cfg {String} inputType @hide
23510      */
23511     /**
23512      * @cfg {String} invalidClass @hide
23513      */
23514     /**
23515      * @cfg {String} invalidText @hide
23516      */
23517     /**
23518      * @cfg {String} msgFx @hide
23519      */
23520     /**
23521      * @cfg {String} validateOnBlur @hide
23522      */
23523 });
23524  
23525     
23526    
23527    
23528    
23529       
23530 Roo.namespace('Roo.bootstrap.htmleditor');
23531 /**
23532  * @class Roo.bootstrap.HtmlEditorToolbar1
23533  * Basic Toolbar
23534  * 
23535  * Usage:
23536  *
23537  new Roo.bootstrap.HtmlEditor({
23538     ....
23539     toolbars : [
23540         new Roo.bootstrap.HtmlEditorToolbar1({
23541             disable : { fonts: 1 , format: 1, ..., ... , ...],
23542             btns : [ .... ]
23543         })
23544     }
23545      
23546  * 
23547  * @cfg {Object} disable List of elements to disable..
23548  * @cfg {Array} btns List of additional buttons.
23549  * 
23550  * 
23551  * NEEDS Extra CSS? 
23552  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23553  */
23554  
23555 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23556 {
23557     
23558     Roo.apply(this, config);
23559     
23560     // default disabled, based on 'good practice'..
23561     this.disable = this.disable || {};
23562     Roo.applyIf(this.disable, {
23563         fontSize : true,
23564         colors : true,
23565         specialElements : true
23566     });
23567     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23568     
23569     this.editor = config.editor;
23570     this.editorcore = config.editor.editorcore;
23571     
23572     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23573     
23574     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23575     // dont call parent... till later.
23576 }
23577 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23578      
23579     bar : true,
23580     
23581     editor : false,
23582     editorcore : false,
23583     
23584     
23585     formats : [
23586         "p" ,  
23587         "h1","h2","h3","h4","h5","h6", 
23588         "pre", "code", 
23589         "abbr", "acronym", "address", "cite", "samp", "var",
23590         'div','span'
23591     ],
23592     
23593     onRender : function(ct, position)
23594     {
23595        // Roo.log("Call onRender: " + this.xtype);
23596         
23597        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23598        Roo.log(this.el);
23599        this.el.dom.style.marginBottom = '0';
23600        var _this = this;
23601        var editorcore = this.editorcore;
23602        var editor= this.editor;
23603        
23604        var children = [];
23605        var btn = function(id,cmd , toggle, handler, html){
23606        
23607             var  event = toggle ? 'toggle' : 'click';
23608        
23609             var a = {
23610                 size : 'sm',
23611                 xtype: 'Button',
23612                 xns: Roo.bootstrap,
23613                 glyphicon : id,
23614                 cmd : id || cmd,
23615                 enableToggle:toggle !== false,
23616                 html : html || '',
23617                 pressed : toggle ? false : null,
23618                 listeners : {}
23619             };
23620             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23621                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23622             };
23623             children.push(a);
23624             return a;
23625        }
23626        
23627     //    var cb_box = function...
23628         
23629         var style = {
23630                 xtype: 'Button',
23631                 size : 'sm',
23632                 xns: Roo.bootstrap,
23633                 glyphicon : 'font',
23634                 //html : 'submit'
23635                 menu : {
23636                     xtype: 'Menu',
23637                     xns: Roo.bootstrap,
23638                     items:  []
23639                 }
23640         };
23641         Roo.each(this.formats, function(f) {
23642             style.menu.items.push({
23643                 xtype :'MenuItem',
23644                 xns: Roo.bootstrap,
23645                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23646                 tagname : f,
23647                 listeners : {
23648                     click : function()
23649                     {
23650                         editorcore.insertTag(this.tagname);
23651                         editor.focus();
23652                     }
23653                 }
23654                 
23655             });
23656         });
23657         children.push(style);   
23658         
23659         btn('bold',false,true);
23660         btn('italic',false,true);
23661         btn('align-left', 'justifyleft',true);
23662         btn('align-center', 'justifycenter',true);
23663         btn('align-right' , 'justifyright',true);
23664         btn('link', false, false, function(btn) {
23665             //Roo.log("create link?");
23666             var url = prompt(this.createLinkText, this.defaultLinkValue);
23667             if(url && url != 'http:/'+'/'){
23668                 this.editorcore.relayCmd('createlink', url);
23669             }
23670         }),
23671         btn('list','insertunorderedlist',true);
23672         btn('pencil', false,true, function(btn){
23673                 Roo.log(this);
23674                 this.toggleSourceEdit(btn.pressed);
23675         });
23676         
23677         if (this.editor.btns.length > 0) {
23678             for (var i = 0; i<this.editor.btns.length; i++) {
23679                 children.push(this.editor.btns[i]);
23680             }
23681         }
23682         
23683         /*
23684         var cog = {
23685                 xtype: 'Button',
23686                 size : 'sm',
23687                 xns: Roo.bootstrap,
23688                 glyphicon : 'cog',
23689                 //html : 'submit'
23690                 menu : {
23691                     xtype: 'Menu',
23692                     xns: Roo.bootstrap,
23693                     items:  []
23694                 }
23695         };
23696         
23697         cog.menu.items.push({
23698             xtype :'MenuItem',
23699             xns: Roo.bootstrap,
23700             html : Clean styles,
23701             tagname : f,
23702             listeners : {
23703                 click : function()
23704                 {
23705                     editorcore.insertTag(this.tagname);
23706                     editor.focus();
23707                 }
23708             }
23709             
23710         });
23711        */
23712         
23713          
23714        this.xtype = 'NavSimplebar';
23715         
23716         for(var i=0;i< children.length;i++) {
23717             
23718             this.buttons.add(this.addxtypeChild(children[i]));
23719             
23720         }
23721         
23722         editor.on('editorevent', this.updateToolbar, this);
23723     },
23724     onBtnClick : function(id)
23725     {
23726        this.editorcore.relayCmd(id);
23727        this.editorcore.focus();
23728     },
23729     
23730     /**
23731      * Protected method that will not generally be called directly. It triggers
23732      * a toolbar update by reading the markup state of the current selection in the editor.
23733      */
23734     updateToolbar: function(){
23735
23736         if(!this.editorcore.activated){
23737             this.editor.onFirstFocus(); // is this neeed?
23738             return;
23739         }
23740
23741         var btns = this.buttons; 
23742         var doc = this.editorcore.doc;
23743         btns.get('bold').setActive(doc.queryCommandState('bold'));
23744         btns.get('italic').setActive(doc.queryCommandState('italic'));
23745         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23746         
23747         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23748         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23749         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23750         
23751         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23752         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23753          /*
23754         
23755         var ans = this.editorcore.getAllAncestors();
23756         if (this.formatCombo) {
23757             
23758             
23759             var store = this.formatCombo.store;
23760             this.formatCombo.setValue("");
23761             for (var i =0; i < ans.length;i++) {
23762                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23763                     // select it..
23764                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23765                     break;
23766                 }
23767             }
23768         }
23769         
23770         
23771         
23772         // hides menus... - so this cant be on a menu...
23773         Roo.bootstrap.MenuMgr.hideAll();
23774         */
23775         Roo.bootstrap.MenuMgr.hideAll();
23776         //this.editorsyncValue();
23777     },
23778     onFirstFocus: function() {
23779         this.buttons.each(function(item){
23780            item.enable();
23781         });
23782     },
23783     toggleSourceEdit : function(sourceEditMode){
23784         
23785           
23786         if(sourceEditMode){
23787             Roo.log("disabling buttons");
23788            this.buttons.each( function(item){
23789                 if(item.cmd != 'pencil'){
23790                     item.disable();
23791                 }
23792             });
23793           
23794         }else{
23795             Roo.log("enabling buttons");
23796             if(this.editorcore.initialized){
23797                 this.buttons.each( function(item){
23798                     item.enable();
23799                 });
23800             }
23801             
23802         }
23803         Roo.log("calling toggole on editor");
23804         // tell the editor that it's been pressed..
23805         this.editor.toggleSourceEdit(sourceEditMode);
23806        
23807     }
23808 });
23809
23810
23811
23812
23813
23814 /**
23815  * @class Roo.bootstrap.Table.AbstractSelectionModel
23816  * @extends Roo.util.Observable
23817  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23818  * implemented by descendant classes.  This class should not be directly instantiated.
23819  * @constructor
23820  */
23821 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23822     this.locked = false;
23823     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23824 };
23825
23826
23827 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23828     /** @ignore Called by the grid automatically. Do not call directly. */
23829     init : function(grid){
23830         this.grid = grid;
23831         this.initEvents();
23832     },
23833
23834     /**
23835      * Locks the selections.
23836      */
23837     lock : function(){
23838         this.locked = true;
23839     },
23840
23841     /**
23842      * Unlocks the selections.
23843      */
23844     unlock : function(){
23845         this.locked = false;
23846     },
23847
23848     /**
23849      * Returns true if the selections are locked.
23850      * @return {Boolean}
23851      */
23852     isLocked : function(){
23853         return this.locked;
23854     }
23855 });
23856 /**
23857  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23858  * @class Roo.bootstrap.Table.RowSelectionModel
23859  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23860  * It supports multiple selections and keyboard selection/navigation. 
23861  * @constructor
23862  * @param {Object} config
23863  */
23864
23865 Roo.bootstrap.Table.RowSelectionModel = function(config){
23866     Roo.apply(this, config);
23867     this.selections = new Roo.util.MixedCollection(false, function(o){
23868         return o.id;
23869     });
23870
23871     this.last = false;
23872     this.lastActive = false;
23873
23874     this.addEvents({
23875         /**
23876              * @event selectionchange
23877              * Fires when the selection changes
23878              * @param {SelectionModel} this
23879              */
23880             "selectionchange" : true,
23881         /**
23882              * @event afterselectionchange
23883              * Fires after the selection changes (eg. by key press or clicking)
23884              * @param {SelectionModel} this
23885              */
23886             "afterselectionchange" : true,
23887         /**
23888              * @event beforerowselect
23889              * Fires when a row is selected being selected, return false to cancel.
23890              * @param {SelectionModel} this
23891              * @param {Number} rowIndex The selected index
23892              * @param {Boolean} keepExisting False if other selections will be cleared
23893              */
23894             "beforerowselect" : true,
23895         /**
23896              * @event rowselect
23897              * Fires when a row is selected.
23898              * @param {SelectionModel} this
23899              * @param {Number} rowIndex The selected index
23900              * @param {Roo.data.Record} r The record
23901              */
23902             "rowselect" : true,
23903         /**
23904              * @event rowdeselect
23905              * Fires when a row is deselected.
23906              * @param {SelectionModel} this
23907              * @param {Number} rowIndex The selected index
23908              */
23909         "rowdeselect" : true
23910     });
23911     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23912     this.locked = false;
23913  };
23914
23915 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
23916     /**
23917      * @cfg {Boolean} singleSelect
23918      * True to allow selection of only one row at a time (defaults to false)
23919      */
23920     singleSelect : false,
23921
23922     // private
23923     initEvents : function()
23924     {
23925
23926         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23927         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
23928         //}else{ // allow click to work like normal
23929          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
23930         //}
23931         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23932         this.grid.on("rowclick", this.handleMouseDown, this);
23933         
23934         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23935             "up" : function(e){
23936                 if(!e.shiftKey){
23937                     this.selectPrevious(e.shiftKey);
23938                 }else if(this.last !== false && this.lastActive !== false){
23939                     var last = this.last;
23940                     this.selectRange(this.last,  this.lastActive-1);
23941                     this.grid.getView().focusRow(this.lastActive);
23942                     if(last !== false){
23943                         this.last = last;
23944                     }
23945                 }else{
23946                     this.selectFirstRow();
23947                 }
23948                 this.fireEvent("afterselectionchange", this);
23949             },
23950             "down" : function(e){
23951                 if(!e.shiftKey){
23952                     this.selectNext(e.shiftKey);
23953                 }else if(this.last !== false && this.lastActive !== false){
23954                     var last = this.last;
23955                     this.selectRange(this.last,  this.lastActive+1);
23956                     this.grid.getView().focusRow(this.lastActive);
23957                     if(last !== false){
23958                         this.last = last;
23959                     }
23960                 }else{
23961                     this.selectFirstRow();
23962                 }
23963                 this.fireEvent("afterselectionchange", this);
23964             },
23965             scope: this
23966         });
23967         this.grid.store.on('load', function(){
23968             this.selections.clear();
23969         },this);
23970         /*
23971         var view = this.grid.view;
23972         view.on("refresh", this.onRefresh, this);
23973         view.on("rowupdated", this.onRowUpdated, this);
23974         view.on("rowremoved", this.onRemove, this);
23975         */
23976     },
23977
23978     // private
23979     onRefresh : function()
23980     {
23981         var ds = this.grid.store, i, v = this.grid.view;
23982         var s = this.selections;
23983         s.each(function(r){
23984             if((i = ds.indexOfId(r.id)) != -1){
23985                 v.onRowSelect(i);
23986             }else{
23987                 s.remove(r);
23988             }
23989         });
23990     },
23991
23992     // private
23993     onRemove : function(v, index, r){
23994         this.selections.remove(r);
23995     },
23996
23997     // private
23998     onRowUpdated : function(v, index, r){
23999         if(this.isSelected(r)){
24000             v.onRowSelect(index);
24001         }
24002     },
24003
24004     /**
24005      * Select records.
24006      * @param {Array} records The records to select
24007      * @param {Boolean} keepExisting (optional) True to keep existing selections
24008      */
24009     selectRecords : function(records, keepExisting)
24010     {
24011         if(!keepExisting){
24012             this.clearSelections();
24013         }
24014             var ds = this.grid.store;
24015         for(var i = 0, len = records.length; i < len; i++){
24016             this.selectRow(ds.indexOf(records[i]), true);
24017         }
24018     },
24019
24020     /**
24021      * Gets the number of selected rows.
24022      * @return {Number}
24023      */
24024     getCount : function(){
24025         return this.selections.length;
24026     },
24027
24028     /**
24029      * Selects the first row in the grid.
24030      */
24031     selectFirstRow : function(){
24032         this.selectRow(0);
24033     },
24034
24035     /**
24036      * Select the last row.
24037      * @param {Boolean} keepExisting (optional) True to keep existing selections
24038      */
24039     selectLastRow : function(keepExisting){
24040         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24041         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24042     },
24043
24044     /**
24045      * Selects the row immediately following the last selected row.
24046      * @param {Boolean} keepExisting (optional) True to keep existing selections
24047      */
24048     selectNext : function(keepExisting)
24049     {
24050             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24051             this.selectRow(this.last+1, keepExisting);
24052             this.grid.getView().focusRow(this.last);
24053         }
24054     },
24055
24056     /**
24057      * Selects the row that precedes the last selected row.
24058      * @param {Boolean} keepExisting (optional) True to keep existing selections
24059      */
24060     selectPrevious : function(keepExisting){
24061         if(this.last){
24062             this.selectRow(this.last-1, keepExisting);
24063             this.grid.getView().focusRow(this.last);
24064         }
24065     },
24066
24067     /**
24068      * Returns the selected records
24069      * @return {Array} Array of selected records
24070      */
24071     getSelections : function(){
24072         return [].concat(this.selections.items);
24073     },
24074
24075     /**
24076      * Returns the first selected record.
24077      * @return {Record}
24078      */
24079     getSelected : function(){
24080         return this.selections.itemAt(0);
24081     },
24082
24083
24084     /**
24085      * Clears all selections.
24086      */
24087     clearSelections : function(fast)
24088     {
24089         if(this.locked) {
24090             return;
24091         }
24092         if(fast !== true){
24093                 var ds = this.grid.store;
24094             var s = this.selections;
24095             s.each(function(r){
24096                 this.deselectRow(ds.indexOfId(r.id));
24097             }, this);
24098             s.clear();
24099         }else{
24100             this.selections.clear();
24101         }
24102         this.last = false;
24103     },
24104
24105
24106     /**
24107      * Selects all rows.
24108      */
24109     selectAll : function(){
24110         if(this.locked) {
24111             return;
24112         }
24113         this.selections.clear();
24114         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24115             this.selectRow(i, true);
24116         }
24117     },
24118
24119     /**
24120      * Returns True if there is a selection.
24121      * @return {Boolean}
24122      */
24123     hasSelection : function(){
24124         return this.selections.length > 0;
24125     },
24126
24127     /**
24128      * Returns True if the specified row is selected.
24129      * @param {Number/Record} record The record or index of the record to check
24130      * @return {Boolean}
24131      */
24132     isSelected : function(index){
24133             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24134         return (r && this.selections.key(r.id) ? true : false);
24135     },
24136
24137     /**
24138      * Returns True if the specified record id is selected.
24139      * @param {String} id The id of record to check
24140      * @return {Boolean}
24141      */
24142     isIdSelected : function(id){
24143         return (this.selections.key(id) ? true : false);
24144     },
24145
24146
24147     // private
24148     handleMouseDBClick : function(e, t){
24149         
24150     },
24151     // private
24152     handleMouseDown : function(e, t)
24153     {
24154             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24155         if(this.isLocked() || rowIndex < 0 ){
24156             return;
24157         };
24158         if(e.shiftKey && this.last !== false){
24159             var last = this.last;
24160             this.selectRange(last, rowIndex, e.ctrlKey);
24161             this.last = last; // reset the last
24162             t.focus();
24163     
24164         }else{
24165             var isSelected = this.isSelected(rowIndex);
24166             //Roo.log("select row:" + rowIndex);
24167             if(isSelected){
24168                 this.deselectRow(rowIndex);
24169             } else {
24170                         this.selectRow(rowIndex, true);
24171             }
24172     
24173             /*
24174                 if(e.button !== 0 && isSelected){
24175                 alert('rowIndex 2: ' + rowIndex);
24176                     view.focusRow(rowIndex);
24177                 }else if(e.ctrlKey && isSelected){
24178                     this.deselectRow(rowIndex);
24179                 }else if(!isSelected){
24180                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24181                     view.focusRow(rowIndex);
24182                 }
24183             */
24184         }
24185         this.fireEvent("afterselectionchange", this);
24186     },
24187     // private
24188     handleDragableRowClick :  function(grid, rowIndex, e) 
24189     {
24190         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24191             this.selectRow(rowIndex, false);
24192             grid.view.focusRow(rowIndex);
24193              this.fireEvent("afterselectionchange", this);
24194         }
24195     },
24196     
24197     /**
24198      * Selects multiple rows.
24199      * @param {Array} rows Array of the indexes of the row to select
24200      * @param {Boolean} keepExisting (optional) True to keep existing selections
24201      */
24202     selectRows : function(rows, keepExisting){
24203         if(!keepExisting){
24204             this.clearSelections();
24205         }
24206         for(var i = 0, len = rows.length; i < len; i++){
24207             this.selectRow(rows[i], true);
24208         }
24209     },
24210
24211     /**
24212      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24213      * @param {Number} startRow The index of the first row in the range
24214      * @param {Number} endRow The index of the last row in the range
24215      * @param {Boolean} keepExisting (optional) True to retain existing selections
24216      */
24217     selectRange : function(startRow, endRow, keepExisting){
24218         if(this.locked) {
24219             return;
24220         }
24221         if(!keepExisting){
24222             this.clearSelections();
24223         }
24224         if(startRow <= endRow){
24225             for(var i = startRow; i <= endRow; i++){
24226                 this.selectRow(i, true);
24227             }
24228         }else{
24229             for(var i = startRow; i >= endRow; i--){
24230                 this.selectRow(i, true);
24231             }
24232         }
24233     },
24234
24235     /**
24236      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24237      * @param {Number} startRow The index of the first row in the range
24238      * @param {Number} endRow The index of the last row in the range
24239      */
24240     deselectRange : function(startRow, endRow, preventViewNotify){
24241         if(this.locked) {
24242             return;
24243         }
24244         for(var i = startRow; i <= endRow; i++){
24245             this.deselectRow(i, preventViewNotify);
24246         }
24247     },
24248
24249     /**
24250      * Selects a row.
24251      * @param {Number} row The index of the row to select
24252      * @param {Boolean} keepExisting (optional) True to keep existing selections
24253      */
24254     selectRow : function(index, keepExisting, preventViewNotify)
24255     {
24256             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24257             return;
24258         }
24259         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24260             if(!keepExisting || this.singleSelect){
24261                 this.clearSelections();
24262             }
24263             
24264             var r = this.grid.store.getAt(index);
24265             //console.log('selectRow - record id :' + r.id);
24266             
24267             this.selections.add(r);
24268             this.last = this.lastActive = index;
24269             if(!preventViewNotify){
24270                 var proxy = new Roo.Element(
24271                                 this.grid.getRowDom(index)
24272                 );
24273                 proxy.addClass('bg-info info');
24274             }
24275             this.fireEvent("rowselect", this, index, r);
24276             this.fireEvent("selectionchange", this);
24277         }
24278     },
24279
24280     /**
24281      * Deselects a row.
24282      * @param {Number} row The index of the row to deselect
24283      */
24284     deselectRow : function(index, preventViewNotify)
24285     {
24286         if(this.locked) {
24287             return;
24288         }
24289         if(this.last == index){
24290             this.last = false;
24291         }
24292         if(this.lastActive == index){
24293             this.lastActive = false;
24294         }
24295         
24296         var r = this.grid.store.getAt(index);
24297         if (!r) {
24298             return;
24299         }
24300         
24301         this.selections.remove(r);
24302         //.console.log('deselectRow - record id :' + r.id);
24303         if(!preventViewNotify){
24304         
24305             var proxy = new Roo.Element(
24306                 this.grid.getRowDom(index)
24307             );
24308             proxy.removeClass('bg-info info');
24309         }
24310         this.fireEvent("rowdeselect", this, index);
24311         this.fireEvent("selectionchange", this);
24312     },
24313
24314     // private
24315     restoreLast : function(){
24316         if(this._last){
24317             this.last = this._last;
24318         }
24319     },
24320
24321     // private
24322     acceptsNav : function(row, col, cm){
24323         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24324     },
24325
24326     // private
24327     onEditorKey : function(field, e){
24328         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24329         if(k == e.TAB){
24330             e.stopEvent();
24331             ed.completeEdit();
24332             if(e.shiftKey){
24333                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24334             }else{
24335                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24336             }
24337         }else if(k == e.ENTER && !e.ctrlKey){
24338             e.stopEvent();
24339             ed.completeEdit();
24340             if(e.shiftKey){
24341                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24342             }else{
24343                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24344             }
24345         }else if(k == e.ESC){
24346             ed.cancelEdit();
24347         }
24348         if(newCell){
24349             g.startEditing(newCell[0], newCell[1]);
24350         }
24351     }
24352 });
24353 /*
24354  * Based on:
24355  * Ext JS Library 1.1.1
24356  * Copyright(c) 2006-2007, Ext JS, LLC.
24357  *
24358  * Originally Released Under LGPL - original licence link has changed is not relivant.
24359  *
24360  * Fork - LGPL
24361  * <script type="text/javascript">
24362  */
24363  
24364 /**
24365  * @class Roo.bootstrap.PagingToolbar
24366  * @extends Roo.bootstrap.NavSimplebar
24367  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24368  * @constructor
24369  * Create a new PagingToolbar
24370  * @param {Object} config The config object
24371  * @param {Roo.data.Store} store
24372  */
24373 Roo.bootstrap.PagingToolbar = function(config)
24374 {
24375     // old args format still supported... - xtype is prefered..
24376         // created from xtype...
24377     
24378     this.ds = config.dataSource;
24379     
24380     if (config.store && !this.ds) {
24381         this.store= Roo.factory(config.store, Roo.data);
24382         this.ds = this.store;
24383         this.ds.xmodule = this.xmodule || false;
24384     }
24385     
24386     this.toolbarItems = [];
24387     if (config.items) {
24388         this.toolbarItems = config.items;
24389     }
24390     
24391     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24392     
24393     this.cursor = 0;
24394     
24395     if (this.ds) { 
24396         this.bind(this.ds);
24397     }
24398     
24399     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24400     
24401 };
24402
24403 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24404     /**
24405      * @cfg {Roo.data.Store} dataSource
24406      * The underlying data store providing the paged data
24407      */
24408     /**
24409      * @cfg {String/HTMLElement/Element} container
24410      * container The id or element that will contain the toolbar
24411      */
24412     /**
24413      * @cfg {Boolean} displayInfo
24414      * True to display the displayMsg (defaults to false)
24415      */
24416     /**
24417      * @cfg {Number} pageSize
24418      * The number of records to display per page (defaults to 20)
24419      */
24420     pageSize: 20,
24421     /**
24422      * @cfg {String} displayMsg
24423      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24424      */
24425     displayMsg : 'Displaying {0} - {1} of {2}',
24426     /**
24427      * @cfg {String} emptyMsg
24428      * The message to display when no records are found (defaults to "No data to display")
24429      */
24430     emptyMsg : 'No data to display',
24431     /**
24432      * Customizable piece of the default paging text (defaults to "Page")
24433      * @type String
24434      */
24435     beforePageText : "Page",
24436     /**
24437      * Customizable piece of the default paging text (defaults to "of %0")
24438      * @type String
24439      */
24440     afterPageText : "of {0}",
24441     /**
24442      * Customizable piece of the default paging text (defaults to "First Page")
24443      * @type String
24444      */
24445     firstText : "First Page",
24446     /**
24447      * Customizable piece of the default paging text (defaults to "Previous Page")
24448      * @type String
24449      */
24450     prevText : "Previous Page",
24451     /**
24452      * Customizable piece of the default paging text (defaults to "Next Page")
24453      * @type String
24454      */
24455     nextText : "Next Page",
24456     /**
24457      * Customizable piece of the default paging text (defaults to "Last Page")
24458      * @type String
24459      */
24460     lastText : "Last Page",
24461     /**
24462      * Customizable piece of the default paging text (defaults to "Refresh")
24463      * @type String
24464      */
24465     refreshText : "Refresh",
24466
24467     buttons : false,
24468     // private
24469     onRender : function(ct, position) 
24470     {
24471         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24472         this.navgroup.parentId = this.id;
24473         this.navgroup.onRender(this.el, null);
24474         // add the buttons to the navgroup
24475         
24476         if(this.displayInfo){
24477             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24478             this.displayEl = this.el.select('.x-paging-info', true).first();
24479 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24480 //            this.displayEl = navel.el.select('span',true).first();
24481         }
24482         
24483         var _this = this;
24484         
24485         if(this.buttons){
24486             Roo.each(_this.buttons, function(e){ // this might need to use render????
24487                Roo.factory(e).onRender(_this.el, null);
24488             });
24489         }
24490             
24491         Roo.each(_this.toolbarItems, function(e) {
24492             _this.navgroup.addItem(e);
24493         });
24494         
24495         
24496         this.first = this.navgroup.addItem({
24497             tooltip: this.firstText,
24498             cls: "prev",
24499             icon : 'fa fa-backward',
24500             disabled: true,
24501             preventDefault: true,
24502             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24503         });
24504         
24505         this.prev =  this.navgroup.addItem({
24506             tooltip: this.prevText,
24507             cls: "prev",
24508             icon : 'fa fa-step-backward',
24509             disabled: true,
24510             preventDefault: true,
24511             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24512         });
24513     //this.addSeparator();
24514         
24515         
24516         var field = this.navgroup.addItem( {
24517             tagtype : 'span',
24518             cls : 'x-paging-position',
24519             
24520             html : this.beforePageText  +
24521                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24522                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24523          } ); //?? escaped?
24524         
24525         this.field = field.el.select('input', true).first();
24526         this.field.on("keydown", this.onPagingKeydown, this);
24527         this.field.on("focus", function(){this.dom.select();});
24528     
24529     
24530         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24531         //this.field.setHeight(18);
24532         //this.addSeparator();
24533         this.next = this.navgroup.addItem({
24534             tooltip: this.nextText,
24535             cls: "next",
24536             html : ' <i class="fa fa-step-forward">',
24537             disabled: true,
24538             preventDefault: true,
24539             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24540         });
24541         this.last = this.navgroup.addItem({
24542             tooltip: this.lastText,
24543             icon : 'fa fa-forward',
24544             cls: "next",
24545             disabled: true,
24546             preventDefault: true,
24547             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24548         });
24549     //this.addSeparator();
24550         this.loading = this.navgroup.addItem({
24551             tooltip: this.refreshText,
24552             icon: 'fa fa-refresh',
24553             preventDefault: true,
24554             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24555         });
24556         
24557     },
24558
24559     // private
24560     updateInfo : function(){
24561         if(this.displayEl){
24562             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24563             var msg = count == 0 ?
24564                 this.emptyMsg :
24565                 String.format(
24566                     this.displayMsg,
24567                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24568                 );
24569             this.displayEl.update(msg);
24570         }
24571     },
24572
24573     // private
24574     onLoad : function(ds, r, o)
24575     {
24576         this.cursor = o.params.start ? o.params.start : 0;
24577         
24578         var d = this.getPageData(),
24579             ap = d.activePage,
24580             ps = d.pages;
24581         
24582         
24583         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24584         this.field.dom.value = ap;
24585         this.first.setDisabled(ap == 1);
24586         this.prev.setDisabled(ap == 1);
24587         this.next.setDisabled(ap == ps);
24588         this.last.setDisabled(ap == ps);
24589         this.loading.enable();
24590         this.updateInfo();
24591     },
24592
24593     // private
24594     getPageData : function(){
24595         var total = this.ds.getTotalCount();
24596         return {
24597             total : total,
24598             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24599             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24600         };
24601     },
24602
24603     // private
24604     onLoadError : function(){
24605         this.loading.enable();
24606     },
24607
24608     // private
24609     onPagingKeydown : function(e){
24610         var k = e.getKey();
24611         var d = this.getPageData();
24612         if(k == e.RETURN){
24613             var v = this.field.dom.value, pageNum;
24614             if(!v || isNaN(pageNum = parseInt(v, 10))){
24615                 this.field.dom.value = d.activePage;
24616                 return;
24617             }
24618             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24619             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24620             e.stopEvent();
24621         }
24622         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))
24623         {
24624           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24625           this.field.dom.value = pageNum;
24626           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24627           e.stopEvent();
24628         }
24629         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24630         {
24631           var v = this.field.dom.value, pageNum; 
24632           var increment = (e.shiftKey) ? 10 : 1;
24633           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24634                 increment *= -1;
24635           }
24636           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24637             this.field.dom.value = d.activePage;
24638             return;
24639           }
24640           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24641           {
24642             this.field.dom.value = parseInt(v, 10) + increment;
24643             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24644             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24645           }
24646           e.stopEvent();
24647         }
24648     },
24649
24650     // private
24651     beforeLoad : function(){
24652         if(this.loading){
24653             this.loading.disable();
24654         }
24655     },
24656
24657     // private
24658     onClick : function(which){
24659         
24660         var ds = this.ds;
24661         if (!ds) {
24662             return;
24663         }
24664         
24665         switch(which){
24666             case "first":
24667                 ds.load({params:{start: 0, limit: this.pageSize}});
24668             break;
24669             case "prev":
24670                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24671             break;
24672             case "next":
24673                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24674             break;
24675             case "last":
24676                 var total = ds.getTotalCount();
24677                 var extra = total % this.pageSize;
24678                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24679                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24680             break;
24681             case "refresh":
24682                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24683             break;
24684         }
24685     },
24686
24687     /**
24688      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24689      * @param {Roo.data.Store} store The data store to unbind
24690      */
24691     unbind : function(ds){
24692         ds.un("beforeload", this.beforeLoad, this);
24693         ds.un("load", this.onLoad, this);
24694         ds.un("loadexception", this.onLoadError, this);
24695         ds.un("remove", this.updateInfo, this);
24696         ds.un("add", this.updateInfo, this);
24697         this.ds = undefined;
24698     },
24699
24700     /**
24701      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24702      * @param {Roo.data.Store} store The data store to bind
24703      */
24704     bind : function(ds){
24705         ds.on("beforeload", this.beforeLoad, this);
24706         ds.on("load", this.onLoad, this);
24707         ds.on("loadexception", this.onLoadError, this);
24708         ds.on("remove", this.updateInfo, this);
24709         ds.on("add", this.updateInfo, this);
24710         this.ds = ds;
24711     }
24712 });/*
24713  * - LGPL
24714  *
24715  * element
24716  * 
24717  */
24718
24719 /**
24720  * @class Roo.bootstrap.MessageBar
24721  * @extends Roo.bootstrap.Component
24722  * Bootstrap MessageBar class
24723  * @cfg {String} html contents of the MessageBar
24724  * @cfg {String} weight (info | success | warning | danger) default info
24725  * @cfg {String} beforeClass insert the bar before the given class
24726  * @cfg {Boolean} closable (true | false) default false
24727  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24728  * 
24729  * @constructor
24730  * Create a new Element
24731  * @param {Object} config The config object
24732  */
24733
24734 Roo.bootstrap.MessageBar = function(config){
24735     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24736 };
24737
24738 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24739     
24740     html: '',
24741     weight: 'info',
24742     closable: false,
24743     fixed: false,
24744     beforeClass: 'bootstrap-sticky-wrap',
24745     
24746     getAutoCreate : function(){
24747         
24748         var cfg = {
24749             tag: 'div',
24750             cls: 'alert alert-dismissable alert-' + this.weight,
24751             cn: [
24752                 {
24753                     tag: 'span',
24754                     cls: 'message',
24755                     html: this.html || ''
24756                 }
24757             ]
24758         };
24759         
24760         if(this.fixed){
24761             cfg.cls += ' alert-messages-fixed';
24762         }
24763         
24764         if(this.closable){
24765             cfg.cn.push({
24766                 tag: 'button',
24767                 cls: 'close',
24768                 html: 'x'
24769             });
24770         }
24771         
24772         return cfg;
24773     },
24774     
24775     onRender : function(ct, position)
24776     {
24777         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24778         
24779         if(!this.el){
24780             var cfg = Roo.apply({},  this.getAutoCreate());
24781             cfg.id = Roo.id();
24782             
24783             if (this.cls) {
24784                 cfg.cls += ' ' + this.cls;
24785             }
24786             if (this.style) {
24787                 cfg.style = this.style;
24788             }
24789             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24790             
24791             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24792         }
24793         
24794         this.el.select('>button.close').on('click', this.hide, this);
24795         
24796     },
24797     
24798     show : function()
24799     {
24800         if (!this.rendered) {
24801             this.render();
24802         }
24803         
24804         this.el.show();
24805         
24806         this.fireEvent('show', this);
24807         
24808     },
24809     
24810     hide : function()
24811     {
24812         if (!this.rendered) {
24813             this.render();
24814         }
24815         
24816         this.el.hide();
24817         
24818         this.fireEvent('hide', this);
24819     },
24820     
24821     update : function()
24822     {
24823 //        var e = this.el.dom.firstChild;
24824 //        
24825 //        if(this.closable){
24826 //            e = e.nextSibling;
24827 //        }
24828 //        
24829 //        e.data = this.html || '';
24830
24831         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24832     }
24833    
24834 });
24835
24836  
24837
24838      /*
24839  * - LGPL
24840  *
24841  * Graph
24842  * 
24843  */
24844
24845
24846 /**
24847  * @class Roo.bootstrap.Graph
24848  * @extends Roo.bootstrap.Component
24849  * Bootstrap Graph class
24850 > Prameters
24851  -sm {number} sm 4
24852  -md {number} md 5
24853  @cfg {String} graphtype  bar | vbar | pie
24854  @cfg {number} g_x coodinator | centre x (pie)
24855  @cfg {number} g_y coodinator | centre y (pie)
24856  @cfg {number} g_r radius (pie)
24857  @cfg {number} g_height height of the chart (respected by all elements in the set)
24858  @cfg {number} g_width width of the chart (respected by all elements in the set)
24859  @cfg {Object} title The title of the chart
24860     
24861  -{Array}  values
24862  -opts (object) options for the chart 
24863      o {
24864      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24865      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24866      o vgutter (number)
24867      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.
24868      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24869      o to
24870      o stretch (boolean)
24871      o }
24872  -opts (object) options for the pie
24873      o{
24874      o cut
24875      o startAngle (number)
24876      o endAngle (number)
24877      } 
24878  *
24879  * @constructor
24880  * Create a new Input
24881  * @param {Object} config The config object
24882  */
24883
24884 Roo.bootstrap.Graph = function(config){
24885     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24886     
24887     this.addEvents({
24888         // img events
24889         /**
24890          * @event click
24891          * The img click event for the img.
24892          * @param {Roo.EventObject} e
24893          */
24894         "click" : true
24895     });
24896 };
24897
24898 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24899     
24900     sm: 4,
24901     md: 5,
24902     graphtype: 'bar',
24903     g_height: 250,
24904     g_width: 400,
24905     g_x: 50,
24906     g_y: 50,
24907     g_r: 30,
24908     opts:{
24909         //g_colors: this.colors,
24910         g_type: 'soft',
24911         g_gutter: '20%'
24912
24913     },
24914     title : false,
24915
24916     getAutoCreate : function(){
24917         
24918         var cfg = {
24919             tag: 'div',
24920             html : null
24921         };
24922         
24923         
24924         return  cfg;
24925     },
24926
24927     onRender : function(ct,position){
24928         
24929         
24930         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24931         
24932         if (typeof(Raphael) == 'undefined') {
24933             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24934             return;
24935         }
24936         
24937         this.raphael = Raphael(this.el.dom);
24938         
24939                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24940                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24941                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24942                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24943                 /*
24944                 r.text(160, 10, "Single Series Chart").attr(txtattr);
24945                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24946                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24947                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24948                 
24949                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24950                 r.barchart(330, 10, 300, 220, data1);
24951                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
24952                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
24953                 */
24954                 
24955                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24956                 // r.barchart(30, 30, 560, 250,  xdata, {
24957                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
24958                 //     axis : "0 0 1 1",
24959                 //     axisxlabels :  xdata
24960                 //     //yvalues : cols,
24961                    
24962                 // });
24963 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24964 //        
24965 //        this.load(null,xdata,{
24966 //                axis : "0 0 1 1",
24967 //                axisxlabels :  xdata
24968 //                });
24969
24970     },
24971
24972     load : function(graphtype,xdata,opts)
24973     {
24974         this.raphael.clear();
24975         if(!graphtype) {
24976             graphtype = this.graphtype;
24977         }
24978         if(!opts){
24979             opts = this.opts;
24980         }
24981         var r = this.raphael,
24982             fin = function () {
24983                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
24984             },
24985             fout = function () {
24986                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
24987             },
24988             pfin = function() {
24989                 this.sector.stop();
24990                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
24991
24992                 if (this.label) {
24993                     this.label[0].stop();
24994                     this.label[0].attr({ r: 7.5 });
24995                     this.label[1].attr({ "font-weight": 800 });
24996                 }
24997             },
24998             pfout = function() {
24999                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25000
25001                 if (this.label) {
25002                     this.label[0].animate({ r: 5 }, 500, "bounce");
25003                     this.label[1].attr({ "font-weight": 400 });
25004                 }
25005             };
25006
25007         switch(graphtype){
25008             case 'bar':
25009                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25010                 break;
25011             case 'hbar':
25012                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25013                 break;
25014             case 'pie':
25015 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25016 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25017 //            
25018                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25019                 
25020                 break;
25021
25022         }
25023         
25024         if(this.title){
25025             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25026         }
25027         
25028     },
25029     
25030     setTitle: function(o)
25031     {
25032         this.title = o;
25033     },
25034     
25035     initEvents: function() {
25036         
25037         if(!this.href){
25038             this.el.on('click', this.onClick, this);
25039         }
25040     },
25041     
25042     onClick : function(e)
25043     {
25044         Roo.log('img onclick');
25045         this.fireEvent('click', this, e);
25046     }
25047    
25048 });
25049
25050  
25051 /*
25052  * - LGPL
25053  *
25054  * numberBox
25055  * 
25056  */
25057 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25058
25059 /**
25060  * @class Roo.bootstrap.dash.NumberBox
25061  * @extends Roo.bootstrap.Component
25062  * Bootstrap NumberBox class
25063  * @cfg {String} headline Box headline
25064  * @cfg {String} content Box content
25065  * @cfg {String} icon Box icon
25066  * @cfg {String} footer Footer text
25067  * @cfg {String} fhref Footer href
25068  * 
25069  * @constructor
25070  * Create a new NumberBox
25071  * @param {Object} config The config object
25072  */
25073
25074
25075 Roo.bootstrap.dash.NumberBox = function(config){
25076     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25077     
25078 };
25079
25080 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25081     
25082     headline : '',
25083     content : '',
25084     icon : '',
25085     footer : '',
25086     fhref : '',
25087     ficon : '',
25088     
25089     getAutoCreate : function(){
25090         
25091         var cfg = {
25092             tag : 'div',
25093             cls : 'small-box ',
25094             cn : [
25095                 {
25096                     tag : 'div',
25097                     cls : 'inner',
25098                     cn :[
25099                         {
25100                             tag : 'h3',
25101                             cls : 'roo-headline',
25102                             html : this.headline
25103                         },
25104                         {
25105                             tag : 'p',
25106                             cls : 'roo-content',
25107                             html : this.content
25108                         }
25109                     ]
25110                 }
25111             ]
25112         };
25113         
25114         if(this.icon){
25115             cfg.cn.push({
25116                 tag : 'div',
25117                 cls : 'icon',
25118                 cn :[
25119                     {
25120                         tag : 'i',
25121                         cls : 'ion ' + this.icon
25122                     }
25123                 ]
25124             });
25125         }
25126         
25127         if(this.footer){
25128             var footer = {
25129                 tag : 'a',
25130                 cls : 'small-box-footer',
25131                 href : this.fhref || '#',
25132                 html : this.footer
25133             };
25134             
25135             cfg.cn.push(footer);
25136             
25137         }
25138         
25139         return  cfg;
25140     },
25141
25142     onRender : function(ct,position){
25143         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25144
25145
25146        
25147                 
25148     },
25149
25150     setHeadline: function (value)
25151     {
25152         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25153     },
25154     
25155     setFooter: function (value, href)
25156     {
25157         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25158         
25159         if(href){
25160             this.el.select('a.small-box-footer',true).first().attr('href', href);
25161         }
25162         
25163     },
25164
25165     setContent: function (value)
25166     {
25167         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25168     },
25169
25170     initEvents: function() 
25171     {   
25172         
25173     }
25174     
25175 });
25176
25177  
25178 /*
25179  * - LGPL
25180  *
25181  * TabBox
25182  * 
25183  */
25184 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25185
25186 /**
25187  * @class Roo.bootstrap.dash.TabBox
25188  * @extends Roo.bootstrap.Component
25189  * Bootstrap TabBox class
25190  * @cfg {String} title Title of the TabBox
25191  * @cfg {String} icon Icon of the TabBox
25192  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25193  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25194  * 
25195  * @constructor
25196  * Create a new TabBox
25197  * @param {Object} config The config object
25198  */
25199
25200
25201 Roo.bootstrap.dash.TabBox = function(config){
25202     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25203     this.addEvents({
25204         // raw events
25205         /**
25206          * @event addpane
25207          * When a pane is added
25208          * @param {Roo.bootstrap.dash.TabPane} pane
25209          */
25210         "addpane" : true,
25211         /**
25212          * @event activatepane
25213          * When a pane is activated
25214          * @param {Roo.bootstrap.dash.TabPane} pane
25215          */
25216         "activatepane" : true
25217         
25218          
25219     });
25220     
25221     this.panes = [];
25222 };
25223
25224 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25225
25226     title : '',
25227     icon : false,
25228     showtabs : true,
25229     tabScrollable : false,
25230     
25231     getChildContainer : function()
25232     {
25233         return this.el.select('.tab-content', true).first();
25234     },
25235     
25236     getAutoCreate : function(){
25237         
25238         var header = {
25239             tag: 'li',
25240             cls: 'pull-left header',
25241             html: this.title,
25242             cn : []
25243         };
25244         
25245         if(this.icon){
25246             header.cn.push({
25247                 tag: 'i',
25248                 cls: 'fa ' + this.icon
25249             });
25250         }
25251         
25252         var h = {
25253             tag: 'ul',
25254             cls: 'nav nav-tabs pull-right',
25255             cn: [
25256                 header
25257             ]
25258         };
25259         
25260         if(this.tabScrollable){
25261             h = {
25262                 tag: 'div',
25263                 cls: 'tab-header',
25264                 cn: [
25265                     {
25266                         tag: 'ul',
25267                         cls: 'nav nav-tabs pull-right',
25268                         cn: [
25269                             header
25270                         ]
25271                     }
25272                 ]
25273             };
25274         }
25275         
25276         var cfg = {
25277             tag: 'div',
25278             cls: 'nav-tabs-custom',
25279             cn: [
25280                 h,
25281                 {
25282                     tag: 'div',
25283                     cls: 'tab-content no-padding',
25284                     cn: []
25285                 }
25286             ]
25287         };
25288
25289         return  cfg;
25290     },
25291     initEvents : function()
25292     {
25293         //Roo.log('add add pane handler');
25294         this.on('addpane', this.onAddPane, this);
25295     },
25296      /**
25297      * Updates the box title
25298      * @param {String} html to set the title to.
25299      */
25300     setTitle : function(value)
25301     {
25302         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25303     },
25304     onAddPane : function(pane)
25305     {
25306         this.panes.push(pane);
25307         //Roo.log('addpane');
25308         //Roo.log(pane);
25309         // tabs are rendere left to right..
25310         if(!this.showtabs){
25311             return;
25312         }
25313         
25314         var ctr = this.el.select('.nav-tabs', true).first();
25315          
25316          
25317         var existing = ctr.select('.nav-tab',true);
25318         var qty = existing.getCount();;
25319         
25320         
25321         var tab = ctr.createChild({
25322             tag : 'li',
25323             cls : 'nav-tab' + (qty ? '' : ' active'),
25324             cn : [
25325                 {
25326                     tag : 'a',
25327                     href:'#',
25328                     html : pane.title
25329                 }
25330             ]
25331         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25332         pane.tab = tab;
25333         
25334         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25335         if (!qty) {
25336             pane.el.addClass('active');
25337         }
25338         
25339                 
25340     },
25341     onTabClick : function(ev,un,ob,pane)
25342     {
25343         //Roo.log('tab - prev default');
25344         ev.preventDefault();
25345         
25346         
25347         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25348         pane.tab.addClass('active');
25349         //Roo.log(pane.title);
25350         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25351         // technically we should have a deactivate event.. but maybe add later.
25352         // and it should not de-activate the selected tab...
25353         this.fireEvent('activatepane', pane);
25354         pane.el.addClass('active');
25355         pane.fireEvent('activate');
25356         
25357         
25358     },
25359     
25360     getActivePane : function()
25361     {
25362         var r = false;
25363         Roo.each(this.panes, function(p) {
25364             if(p.el.hasClass('active')){
25365                 r = p;
25366                 return false;
25367             }
25368             
25369             return;
25370         });
25371         
25372         return r;
25373     }
25374     
25375     
25376 });
25377
25378  
25379 /*
25380  * - LGPL
25381  *
25382  * Tab pane
25383  * 
25384  */
25385 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25386 /**
25387  * @class Roo.bootstrap.TabPane
25388  * @extends Roo.bootstrap.Component
25389  * Bootstrap TabPane class
25390  * @cfg {Boolean} active (false | true) Default false
25391  * @cfg {String} title title of panel
25392
25393  * 
25394  * @constructor
25395  * Create a new TabPane
25396  * @param {Object} config The config object
25397  */
25398
25399 Roo.bootstrap.dash.TabPane = function(config){
25400     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25401     
25402     this.addEvents({
25403         // raw events
25404         /**
25405          * @event activate
25406          * When a pane is activated
25407          * @param {Roo.bootstrap.dash.TabPane} pane
25408          */
25409         "activate" : true
25410          
25411     });
25412 };
25413
25414 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25415     
25416     active : false,
25417     title : '',
25418     
25419     // the tabBox that this is attached to.
25420     tab : false,
25421      
25422     getAutoCreate : function() 
25423     {
25424         var cfg = {
25425             tag: 'div',
25426             cls: 'tab-pane'
25427         };
25428         
25429         if(this.active){
25430             cfg.cls += ' active';
25431         }
25432         
25433         return cfg;
25434     },
25435     initEvents  : function()
25436     {
25437         //Roo.log('trigger add pane handler');
25438         this.parent().fireEvent('addpane', this)
25439     },
25440     
25441      /**
25442      * Updates the tab title 
25443      * @param {String} html to set the title to.
25444      */
25445     setTitle: function(str)
25446     {
25447         if (!this.tab) {
25448             return;
25449         }
25450         this.title = str;
25451         this.tab.select('a', true).first().dom.innerHTML = str;
25452         
25453     }
25454     
25455     
25456     
25457 });
25458
25459  
25460
25461
25462  /*
25463  * - LGPL
25464  *
25465  * menu
25466  * 
25467  */
25468 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25469
25470 /**
25471  * @class Roo.bootstrap.menu.Menu
25472  * @extends Roo.bootstrap.Component
25473  * Bootstrap Menu class - container for Menu
25474  * @cfg {String} html Text of the menu
25475  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25476  * @cfg {String} icon Font awesome icon
25477  * @cfg {String} pos Menu align to (top | bottom) default bottom
25478  * 
25479  * 
25480  * @constructor
25481  * Create a new Menu
25482  * @param {Object} config The config object
25483  */
25484
25485
25486 Roo.bootstrap.menu.Menu = function(config){
25487     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25488     
25489     this.addEvents({
25490         /**
25491          * @event beforeshow
25492          * Fires before this menu is displayed
25493          * @param {Roo.bootstrap.menu.Menu} this
25494          */
25495         beforeshow : true,
25496         /**
25497          * @event beforehide
25498          * Fires before this menu is hidden
25499          * @param {Roo.bootstrap.menu.Menu} this
25500          */
25501         beforehide : true,
25502         /**
25503          * @event show
25504          * Fires after this menu is displayed
25505          * @param {Roo.bootstrap.menu.Menu} this
25506          */
25507         show : true,
25508         /**
25509          * @event hide
25510          * Fires after this menu is hidden
25511          * @param {Roo.bootstrap.menu.Menu} this
25512          */
25513         hide : true,
25514         /**
25515          * @event click
25516          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25517          * @param {Roo.bootstrap.menu.Menu} this
25518          * @param {Roo.EventObject} e
25519          */
25520         click : true
25521     });
25522     
25523 };
25524
25525 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25526     
25527     submenu : false,
25528     html : '',
25529     weight : 'default',
25530     icon : false,
25531     pos : 'bottom',
25532     
25533     
25534     getChildContainer : function() {
25535         if(this.isSubMenu){
25536             return this.el;
25537         }
25538         
25539         return this.el.select('ul.dropdown-menu', true).first();  
25540     },
25541     
25542     getAutoCreate : function()
25543     {
25544         var text = [
25545             {
25546                 tag : 'span',
25547                 cls : 'roo-menu-text',
25548                 html : this.html
25549             }
25550         ];
25551         
25552         if(this.icon){
25553             text.unshift({
25554                 tag : 'i',
25555                 cls : 'fa ' + this.icon
25556             })
25557         }
25558         
25559         
25560         var cfg = {
25561             tag : 'div',
25562             cls : 'btn-group',
25563             cn : [
25564                 {
25565                     tag : 'button',
25566                     cls : 'dropdown-button btn btn-' + this.weight,
25567                     cn : text
25568                 },
25569                 {
25570                     tag : 'button',
25571                     cls : 'dropdown-toggle btn btn-' + this.weight,
25572                     cn : [
25573                         {
25574                             tag : 'span',
25575                             cls : 'caret'
25576                         }
25577                     ]
25578                 },
25579                 {
25580                     tag : 'ul',
25581                     cls : 'dropdown-menu'
25582                 }
25583             ]
25584             
25585         };
25586         
25587         if(this.pos == 'top'){
25588             cfg.cls += ' dropup';
25589         }
25590         
25591         if(this.isSubMenu){
25592             cfg = {
25593                 tag : 'ul',
25594                 cls : 'dropdown-menu'
25595             }
25596         }
25597         
25598         return cfg;
25599     },
25600     
25601     onRender : function(ct, position)
25602     {
25603         this.isSubMenu = ct.hasClass('dropdown-submenu');
25604         
25605         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25606     },
25607     
25608     initEvents : function() 
25609     {
25610         if(this.isSubMenu){
25611             return;
25612         }
25613         
25614         this.hidden = true;
25615         
25616         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25617         this.triggerEl.on('click', this.onTriggerPress, this);
25618         
25619         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25620         this.buttonEl.on('click', this.onClick, this);
25621         
25622     },
25623     
25624     list : function()
25625     {
25626         if(this.isSubMenu){
25627             return this.el;
25628         }
25629         
25630         return this.el.select('ul.dropdown-menu', true).first();
25631     },
25632     
25633     onClick : function(e)
25634     {
25635         this.fireEvent("click", this, e);
25636     },
25637     
25638     onTriggerPress  : function(e)
25639     {   
25640         if (this.isVisible()) {
25641             this.hide();
25642         } else {
25643             this.show();
25644         }
25645     },
25646     
25647     isVisible : function(){
25648         return !this.hidden;
25649     },
25650     
25651     show : function()
25652     {
25653         this.fireEvent("beforeshow", this);
25654         
25655         this.hidden = false;
25656         this.el.addClass('open');
25657         
25658         Roo.get(document).on("mouseup", this.onMouseUp, this);
25659         
25660         this.fireEvent("show", this);
25661         
25662         
25663     },
25664     
25665     hide : function()
25666     {
25667         this.fireEvent("beforehide", this);
25668         
25669         this.hidden = true;
25670         this.el.removeClass('open');
25671         
25672         Roo.get(document).un("mouseup", this.onMouseUp);
25673         
25674         this.fireEvent("hide", this);
25675     },
25676     
25677     onMouseUp : function()
25678     {
25679         this.hide();
25680     }
25681     
25682 });
25683
25684  
25685  /*
25686  * - LGPL
25687  *
25688  * menu item
25689  * 
25690  */
25691 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25692
25693 /**
25694  * @class Roo.bootstrap.menu.Item
25695  * @extends Roo.bootstrap.Component
25696  * Bootstrap MenuItem class
25697  * @cfg {Boolean} submenu (true | false) default false
25698  * @cfg {String} html text of the item
25699  * @cfg {String} href the link
25700  * @cfg {Boolean} disable (true | false) default false
25701  * @cfg {Boolean} preventDefault (true | false) default true
25702  * @cfg {String} icon Font awesome icon
25703  * @cfg {String} pos Submenu align to (left | right) default right 
25704  * 
25705  * 
25706  * @constructor
25707  * Create a new Item
25708  * @param {Object} config The config object
25709  */
25710
25711
25712 Roo.bootstrap.menu.Item = function(config){
25713     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25714     this.addEvents({
25715         /**
25716          * @event mouseover
25717          * Fires when the mouse is hovering over this menu
25718          * @param {Roo.bootstrap.menu.Item} this
25719          * @param {Roo.EventObject} e
25720          */
25721         mouseover : true,
25722         /**
25723          * @event mouseout
25724          * Fires when the mouse exits this menu
25725          * @param {Roo.bootstrap.menu.Item} this
25726          * @param {Roo.EventObject} e
25727          */
25728         mouseout : true,
25729         // raw events
25730         /**
25731          * @event click
25732          * The raw click event for the entire grid.
25733          * @param {Roo.EventObject} e
25734          */
25735         click : true
25736     });
25737 };
25738
25739 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25740     
25741     submenu : false,
25742     href : '',
25743     html : '',
25744     preventDefault: true,
25745     disable : false,
25746     icon : false,
25747     pos : 'right',
25748     
25749     getAutoCreate : function()
25750     {
25751         var text = [
25752             {
25753                 tag : 'span',
25754                 cls : 'roo-menu-item-text',
25755                 html : this.html
25756             }
25757         ];
25758         
25759         if(this.icon){
25760             text.unshift({
25761                 tag : 'i',
25762                 cls : 'fa ' + this.icon
25763             })
25764         }
25765         
25766         var cfg = {
25767             tag : 'li',
25768             cn : [
25769                 {
25770                     tag : 'a',
25771                     href : this.href || '#',
25772                     cn : text
25773                 }
25774             ]
25775         };
25776         
25777         if(this.disable){
25778             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25779         }
25780         
25781         if(this.submenu){
25782             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25783             
25784             if(this.pos == 'left'){
25785                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25786             }
25787         }
25788         
25789         return cfg;
25790     },
25791     
25792     initEvents : function() 
25793     {
25794         this.el.on('mouseover', this.onMouseOver, this);
25795         this.el.on('mouseout', this.onMouseOut, this);
25796         
25797         this.el.select('a', true).first().on('click', this.onClick, this);
25798         
25799     },
25800     
25801     onClick : function(e)
25802     {
25803         if(this.preventDefault){
25804             e.preventDefault();
25805         }
25806         
25807         this.fireEvent("click", this, e);
25808     },
25809     
25810     onMouseOver : function(e)
25811     {
25812         if(this.submenu && this.pos == 'left'){
25813             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25814         }
25815         
25816         this.fireEvent("mouseover", this, e);
25817     },
25818     
25819     onMouseOut : function(e)
25820     {
25821         this.fireEvent("mouseout", this, e);
25822     }
25823 });
25824
25825  
25826
25827  /*
25828  * - LGPL
25829  *
25830  * menu separator
25831  * 
25832  */
25833 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25834
25835 /**
25836  * @class Roo.bootstrap.menu.Separator
25837  * @extends Roo.bootstrap.Component
25838  * Bootstrap Separator class
25839  * 
25840  * @constructor
25841  * Create a new Separator
25842  * @param {Object} config The config object
25843  */
25844
25845
25846 Roo.bootstrap.menu.Separator = function(config){
25847     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25848 };
25849
25850 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25851     
25852     getAutoCreate : function(){
25853         var cfg = {
25854             tag : 'li',
25855             cls: 'divider'
25856         };
25857         
25858         return cfg;
25859     }
25860    
25861 });
25862
25863  
25864
25865  /*
25866  * - LGPL
25867  *
25868  * Tooltip
25869  * 
25870  */
25871
25872 /**
25873  * @class Roo.bootstrap.Tooltip
25874  * Bootstrap Tooltip class
25875  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25876  * to determine which dom element triggers the tooltip.
25877  * 
25878  * It needs to add support for additional attributes like tooltip-position
25879  * 
25880  * @constructor
25881  * Create a new Toolti
25882  * @param {Object} config The config object
25883  */
25884
25885 Roo.bootstrap.Tooltip = function(config){
25886     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25887     
25888     this.alignment = Roo.bootstrap.Tooltip.alignment;
25889     
25890     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25891         this.alignment = config.alignment;
25892     }
25893     
25894 };
25895
25896 Roo.apply(Roo.bootstrap.Tooltip, {
25897     /**
25898      * @function init initialize tooltip monitoring.
25899      * @static
25900      */
25901     currentEl : false,
25902     currentTip : false,
25903     currentRegion : false,
25904     
25905     //  init : delay?
25906     
25907     init : function()
25908     {
25909         Roo.get(document).on('mouseover', this.enter ,this);
25910         Roo.get(document).on('mouseout', this.leave, this);
25911          
25912         
25913         this.currentTip = new Roo.bootstrap.Tooltip();
25914     },
25915     
25916     enter : function(ev)
25917     {
25918         var dom = ev.getTarget();
25919         
25920         //Roo.log(['enter',dom]);
25921         var el = Roo.fly(dom);
25922         if (this.currentEl) {
25923             //Roo.log(dom);
25924             //Roo.log(this.currentEl);
25925             //Roo.log(this.currentEl.contains(dom));
25926             if (this.currentEl == el) {
25927                 return;
25928             }
25929             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25930                 return;
25931             }
25932
25933         }
25934         
25935         if (this.currentTip.el) {
25936             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25937         }    
25938         //Roo.log(ev);
25939         
25940         if(!el || el.dom == document){
25941             return;
25942         }
25943         
25944         var bindEl = el;
25945         
25946         // you can not look for children, as if el is the body.. then everythign is the child..
25947         if (!el.attr('tooltip')) { //
25948             if (!el.select("[tooltip]").elements.length) {
25949                 return;
25950             }
25951             // is the mouse over this child...?
25952             bindEl = el.select("[tooltip]").first();
25953             var xy = ev.getXY();
25954             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
25955                 //Roo.log("not in region.");
25956                 return;
25957             }
25958             //Roo.log("child element over..");
25959             
25960         }
25961         this.currentEl = bindEl;
25962         this.currentTip.bind(bindEl);
25963         this.currentRegion = Roo.lib.Region.getRegion(dom);
25964         this.currentTip.enter();
25965         
25966     },
25967     leave : function(ev)
25968     {
25969         var dom = ev.getTarget();
25970         //Roo.log(['leave',dom]);
25971         if (!this.currentEl) {
25972             return;
25973         }
25974         
25975         
25976         if (dom != this.currentEl.dom) {
25977             return;
25978         }
25979         var xy = ev.getXY();
25980         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
25981             return;
25982         }
25983         // only activate leave if mouse cursor is outside... bounding box..
25984         
25985         
25986         
25987         
25988         if (this.currentTip) {
25989             this.currentTip.leave();
25990         }
25991         //Roo.log('clear currentEl');
25992         this.currentEl = false;
25993         
25994         
25995     },
25996     alignment : {
25997         'left' : ['r-l', [-2,0], 'right'],
25998         'right' : ['l-r', [2,0], 'left'],
25999         'bottom' : ['t-b', [0,2], 'top'],
26000         'top' : [ 'b-t', [0,-2], 'bottom']
26001     }
26002     
26003 });
26004
26005
26006 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26007     
26008     
26009     bindEl : false,
26010     
26011     delay : null, // can be { show : 300 , hide: 500}
26012     
26013     timeout : null,
26014     
26015     hoverState : null, //???
26016     
26017     placement : 'bottom', 
26018     
26019     alignment : false,
26020     
26021     getAutoCreate : function(){
26022     
26023         var cfg = {
26024            cls : 'tooltip',
26025            role : 'tooltip',
26026            cn : [
26027                 {
26028                     cls : 'tooltip-arrow'
26029                 },
26030                 {
26031                     cls : 'tooltip-inner'
26032                 }
26033            ]
26034         };
26035         
26036         return cfg;
26037     },
26038     bind : function(el)
26039     {
26040         this.bindEl = el;
26041     },
26042       
26043     
26044     enter : function () {
26045        
26046         if (this.timeout != null) {
26047             clearTimeout(this.timeout);
26048         }
26049         
26050         this.hoverState = 'in';
26051          //Roo.log("enter - show");
26052         if (!this.delay || !this.delay.show) {
26053             this.show();
26054             return;
26055         }
26056         var _t = this;
26057         this.timeout = setTimeout(function () {
26058             if (_t.hoverState == 'in') {
26059                 _t.show();
26060             }
26061         }, this.delay.show);
26062     },
26063     leave : function()
26064     {
26065         clearTimeout(this.timeout);
26066     
26067         this.hoverState = 'out';
26068          if (!this.delay || !this.delay.hide) {
26069             this.hide();
26070             return;
26071         }
26072        
26073         var _t = this;
26074         this.timeout = setTimeout(function () {
26075             //Roo.log("leave - timeout");
26076             
26077             if (_t.hoverState == 'out') {
26078                 _t.hide();
26079                 Roo.bootstrap.Tooltip.currentEl = false;
26080             }
26081         }, delay);
26082     },
26083     
26084     show : function (msg)
26085     {
26086         if (!this.el) {
26087             this.render(document.body);
26088         }
26089         // set content.
26090         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26091         
26092         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26093         
26094         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26095         
26096         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26097         
26098         var placement = typeof this.placement == 'function' ?
26099             this.placement.call(this, this.el, on_el) :
26100             this.placement;
26101             
26102         var autoToken = /\s?auto?\s?/i;
26103         var autoPlace = autoToken.test(placement);
26104         if (autoPlace) {
26105             placement = placement.replace(autoToken, '') || 'top';
26106         }
26107         
26108         //this.el.detach()
26109         //this.el.setXY([0,0]);
26110         this.el.show();
26111         //this.el.dom.style.display='block';
26112         
26113         //this.el.appendTo(on_el);
26114         
26115         var p = this.getPosition();
26116         var box = this.el.getBox();
26117         
26118         if (autoPlace) {
26119             // fixme..
26120         }
26121         
26122         var align = this.alignment[placement];
26123         
26124         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26125         
26126         if(placement == 'top' || placement == 'bottom'){
26127             if(xy[0] < 0){
26128                 placement = 'right';
26129             }
26130             
26131             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26132                 placement = 'left';
26133             }
26134             
26135             var scroll = Roo.select('body', true).first().getScroll();
26136             
26137             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26138                 placement = 'top';
26139             }
26140             
26141         }
26142         
26143         this.el.alignTo(this.bindEl, align[0],align[1]);
26144         //var arrow = this.el.select('.arrow',true).first();
26145         //arrow.set(align[2], 
26146         
26147         this.el.addClass(placement);
26148         
26149         this.el.addClass('in fade');
26150         
26151         this.hoverState = null;
26152         
26153         if (this.el.hasClass('fade')) {
26154             // fade it?
26155         }
26156         
26157     },
26158     hide : function()
26159     {
26160          
26161         if (!this.el) {
26162             return;
26163         }
26164         //this.el.setXY([0,0]);
26165         this.el.removeClass('in');
26166         //this.el.hide();
26167         
26168     }
26169     
26170 });
26171  
26172
26173  /*
26174  * - LGPL
26175  *
26176  * Location Picker
26177  * 
26178  */
26179
26180 /**
26181  * @class Roo.bootstrap.LocationPicker
26182  * @extends Roo.bootstrap.Component
26183  * Bootstrap LocationPicker class
26184  * @cfg {Number} latitude Position when init default 0
26185  * @cfg {Number} longitude Position when init default 0
26186  * @cfg {Number} zoom default 15
26187  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26188  * @cfg {Boolean} mapTypeControl default false
26189  * @cfg {Boolean} disableDoubleClickZoom default false
26190  * @cfg {Boolean} scrollwheel default true
26191  * @cfg {Boolean} streetViewControl default false
26192  * @cfg {Number} radius default 0
26193  * @cfg {String} locationName
26194  * @cfg {Boolean} draggable default true
26195  * @cfg {Boolean} enableAutocomplete default false
26196  * @cfg {Boolean} enableReverseGeocode default true
26197  * @cfg {String} markerTitle
26198  * 
26199  * @constructor
26200  * Create a new LocationPicker
26201  * @param {Object} config The config object
26202  */
26203
26204
26205 Roo.bootstrap.LocationPicker = function(config){
26206     
26207     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26208     
26209     this.addEvents({
26210         /**
26211          * @event initial
26212          * Fires when the picker initialized.
26213          * @param {Roo.bootstrap.LocationPicker} this
26214          * @param {Google Location} location
26215          */
26216         initial : true,
26217         /**
26218          * @event positionchanged
26219          * Fires when the picker position changed.
26220          * @param {Roo.bootstrap.LocationPicker} this
26221          * @param {Google Location} location
26222          */
26223         positionchanged : true,
26224         /**
26225          * @event resize
26226          * Fires when the map resize.
26227          * @param {Roo.bootstrap.LocationPicker} this
26228          */
26229         resize : true,
26230         /**
26231          * @event show
26232          * Fires when the map show.
26233          * @param {Roo.bootstrap.LocationPicker} this
26234          */
26235         show : true,
26236         /**
26237          * @event hide
26238          * Fires when the map hide.
26239          * @param {Roo.bootstrap.LocationPicker} this
26240          */
26241         hide : true,
26242         /**
26243          * @event mapClick
26244          * Fires when click the map.
26245          * @param {Roo.bootstrap.LocationPicker} this
26246          * @param {Map event} e
26247          */
26248         mapClick : true,
26249         /**
26250          * @event mapRightClick
26251          * Fires when right click the map.
26252          * @param {Roo.bootstrap.LocationPicker} this
26253          * @param {Map event} e
26254          */
26255         mapRightClick : true,
26256         /**
26257          * @event markerClick
26258          * Fires when click the marker.
26259          * @param {Roo.bootstrap.LocationPicker} this
26260          * @param {Map event} e
26261          */
26262         markerClick : true,
26263         /**
26264          * @event markerRightClick
26265          * Fires when right click the marker.
26266          * @param {Roo.bootstrap.LocationPicker} this
26267          * @param {Map event} e
26268          */
26269         markerRightClick : true,
26270         /**
26271          * @event OverlayViewDraw
26272          * Fires when OverlayView Draw
26273          * @param {Roo.bootstrap.LocationPicker} this
26274          */
26275         OverlayViewDraw : true,
26276         /**
26277          * @event OverlayViewOnAdd
26278          * Fires when OverlayView Draw
26279          * @param {Roo.bootstrap.LocationPicker} this
26280          */
26281         OverlayViewOnAdd : true,
26282         /**
26283          * @event OverlayViewOnRemove
26284          * Fires when OverlayView Draw
26285          * @param {Roo.bootstrap.LocationPicker} this
26286          */
26287         OverlayViewOnRemove : true,
26288         /**
26289          * @event OverlayViewShow
26290          * Fires when OverlayView Draw
26291          * @param {Roo.bootstrap.LocationPicker} this
26292          * @param {Pixel} cpx
26293          */
26294         OverlayViewShow : true,
26295         /**
26296          * @event OverlayViewHide
26297          * Fires when OverlayView Draw
26298          * @param {Roo.bootstrap.LocationPicker} this
26299          */
26300         OverlayViewHide : true,
26301         /**
26302          * @event loadexception
26303          * Fires when load google lib failed.
26304          * @param {Roo.bootstrap.LocationPicker} this
26305          */
26306         loadexception : true
26307     });
26308         
26309 };
26310
26311 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26312     
26313     gMapContext: false,
26314     
26315     latitude: 0,
26316     longitude: 0,
26317     zoom: 15,
26318     mapTypeId: false,
26319     mapTypeControl: false,
26320     disableDoubleClickZoom: false,
26321     scrollwheel: true,
26322     streetViewControl: false,
26323     radius: 0,
26324     locationName: '',
26325     draggable: true,
26326     enableAutocomplete: false,
26327     enableReverseGeocode: true,
26328     markerTitle: '',
26329     
26330     getAutoCreate: function()
26331     {
26332
26333         var cfg = {
26334             tag: 'div',
26335             cls: 'roo-location-picker'
26336         };
26337         
26338         return cfg
26339     },
26340     
26341     initEvents: function(ct, position)
26342     {       
26343         if(!this.el.getWidth() || this.isApplied()){
26344             return;
26345         }
26346         
26347         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26348         
26349         this.initial();
26350     },
26351     
26352     initial: function()
26353     {
26354         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26355             this.fireEvent('loadexception', this);
26356             return;
26357         }
26358         
26359         if(!this.mapTypeId){
26360             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26361         }
26362         
26363         this.gMapContext = this.GMapContext();
26364         
26365         this.initOverlayView();
26366         
26367         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26368         
26369         var _this = this;
26370                 
26371         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26372             _this.setPosition(_this.gMapContext.marker.position);
26373         });
26374         
26375         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26376             _this.fireEvent('mapClick', this, event);
26377             
26378         });
26379
26380         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26381             _this.fireEvent('mapRightClick', this, event);
26382             
26383         });
26384         
26385         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26386             _this.fireEvent('markerClick', this, event);
26387             
26388         });
26389
26390         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26391             _this.fireEvent('markerRightClick', this, event);
26392             
26393         });
26394         
26395         this.setPosition(this.gMapContext.location);
26396         
26397         this.fireEvent('initial', this, this.gMapContext.location);
26398     },
26399     
26400     initOverlayView: function()
26401     {
26402         var _this = this;
26403         
26404         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26405             
26406             draw: function()
26407             {
26408                 _this.fireEvent('OverlayViewDraw', _this);
26409             },
26410             
26411             onAdd: function()
26412             {
26413                 _this.fireEvent('OverlayViewOnAdd', _this);
26414             },
26415             
26416             onRemove: function()
26417             {
26418                 _this.fireEvent('OverlayViewOnRemove', _this);
26419             },
26420             
26421             show: function(cpx)
26422             {
26423                 _this.fireEvent('OverlayViewShow', _this, cpx);
26424             },
26425             
26426             hide: function()
26427             {
26428                 _this.fireEvent('OverlayViewHide', _this);
26429             }
26430             
26431         });
26432     },
26433     
26434     fromLatLngToContainerPixel: function(event)
26435     {
26436         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26437     },
26438     
26439     isApplied: function() 
26440     {
26441         return this.getGmapContext() == false ? false : true;
26442     },
26443     
26444     getGmapContext: function() 
26445     {
26446         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26447     },
26448     
26449     GMapContext: function() 
26450     {
26451         var position = new google.maps.LatLng(this.latitude, this.longitude);
26452         
26453         var _map = new google.maps.Map(this.el.dom, {
26454             center: position,
26455             zoom: this.zoom,
26456             mapTypeId: this.mapTypeId,
26457             mapTypeControl: this.mapTypeControl,
26458             disableDoubleClickZoom: this.disableDoubleClickZoom,
26459             scrollwheel: this.scrollwheel,
26460             streetViewControl: this.streetViewControl,
26461             locationName: this.locationName,
26462             draggable: this.draggable,
26463             enableAutocomplete: this.enableAutocomplete,
26464             enableReverseGeocode: this.enableReverseGeocode
26465         });
26466         
26467         var _marker = new google.maps.Marker({
26468             position: position,
26469             map: _map,
26470             title: this.markerTitle,
26471             draggable: this.draggable
26472         });
26473         
26474         return {
26475             map: _map,
26476             marker: _marker,
26477             circle: null,
26478             location: position,
26479             radius: this.radius,
26480             locationName: this.locationName,
26481             addressComponents: {
26482                 formatted_address: null,
26483                 addressLine1: null,
26484                 addressLine2: null,
26485                 streetName: null,
26486                 streetNumber: null,
26487                 city: null,
26488                 district: null,
26489                 state: null,
26490                 stateOrProvince: null
26491             },
26492             settings: this,
26493             domContainer: this.el.dom,
26494             geodecoder: new google.maps.Geocoder()
26495         };
26496     },
26497     
26498     drawCircle: function(center, radius, options) 
26499     {
26500         if (this.gMapContext.circle != null) {
26501             this.gMapContext.circle.setMap(null);
26502         }
26503         if (radius > 0) {
26504             radius *= 1;
26505             options = Roo.apply({}, options, {
26506                 strokeColor: "#0000FF",
26507                 strokeOpacity: .35,
26508                 strokeWeight: 2,
26509                 fillColor: "#0000FF",
26510                 fillOpacity: .2
26511             });
26512             
26513             options.map = this.gMapContext.map;
26514             options.radius = radius;
26515             options.center = center;
26516             this.gMapContext.circle = new google.maps.Circle(options);
26517             return this.gMapContext.circle;
26518         }
26519         
26520         return null;
26521     },
26522     
26523     setPosition: function(location) 
26524     {
26525         this.gMapContext.location = location;
26526         this.gMapContext.marker.setPosition(location);
26527         this.gMapContext.map.panTo(location);
26528         this.drawCircle(location, this.gMapContext.radius, {});
26529         
26530         var _this = this;
26531         
26532         if (this.gMapContext.settings.enableReverseGeocode) {
26533             this.gMapContext.geodecoder.geocode({
26534                 latLng: this.gMapContext.location
26535             }, function(results, status) {
26536                 
26537                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26538                     _this.gMapContext.locationName = results[0].formatted_address;
26539                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26540                     
26541                     _this.fireEvent('positionchanged', this, location);
26542                 }
26543             });
26544             
26545             return;
26546         }
26547         
26548         this.fireEvent('positionchanged', this, location);
26549     },
26550     
26551     resize: function()
26552     {
26553         google.maps.event.trigger(this.gMapContext.map, "resize");
26554         
26555         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26556         
26557         this.fireEvent('resize', this);
26558     },
26559     
26560     setPositionByLatLng: function(latitude, longitude)
26561     {
26562         this.setPosition(new google.maps.LatLng(latitude, longitude));
26563     },
26564     
26565     getCurrentPosition: function() 
26566     {
26567         return {
26568             latitude: this.gMapContext.location.lat(),
26569             longitude: this.gMapContext.location.lng()
26570         };
26571     },
26572     
26573     getAddressName: function() 
26574     {
26575         return this.gMapContext.locationName;
26576     },
26577     
26578     getAddressComponents: function() 
26579     {
26580         return this.gMapContext.addressComponents;
26581     },
26582     
26583     address_component_from_google_geocode: function(address_components) 
26584     {
26585         var result = {};
26586         
26587         for (var i = 0; i < address_components.length; i++) {
26588             var component = address_components[i];
26589             if (component.types.indexOf("postal_code") >= 0) {
26590                 result.postalCode = component.short_name;
26591             } else if (component.types.indexOf("street_number") >= 0) {
26592                 result.streetNumber = component.short_name;
26593             } else if (component.types.indexOf("route") >= 0) {
26594                 result.streetName = component.short_name;
26595             } else if (component.types.indexOf("neighborhood") >= 0) {
26596                 result.city = component.short_name;
26597             } else if (component.types.indexOf("locality") >= 0) {
26598                 result.city = component.short_name;
26599             } else if (component.types.indexOf("sublocality") >= 0) {
26600                 result.district = component.short_name;
26601             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26602                 result.stateOrProvince = component.short_name;
26603             } else if (component.types.indexOf("country") >= 0) {
26604                 result.country = component.short_name;
26605             }
26606         }
26607         
26608         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26609         result.addressLine2 = "";
26610         return result;
26611     },
26612     
26613     setZoomLevel: function(zoom)
26614     {
26615         this.gMapContext.map.setZoom(zoom);
26616     },
26617     
26618     show: function()
26619     {
26620         if(!this.el){
26621             return;
26622         }
26623         
26624         this.el.show();
26625         
26626         this.resize();
26627         
26628         this.fireEvent('show', this);
26629     },
26630     
26631     hide: function()
26632     {
26633         if(!this.el){
26634             return;
26635         }
26636         
26637         this.el.hide();
26638         
26639         this.fireEvent('hide', this);
26640     }
26641     
26642 });
26643
26644 Roo.apply(Roo.bootstrap.LocationPicker, {
26645     
26646     OverlayView : function(map, options)
26647     {
26648         options = options || {};
26649         
26650         this.setMap(map);
26651     }
26652     
26653     
26654 });/*
26655  * - LGPL
26656  *
26657  * Alert
26658  * 
26659  */
26660
26661 /**
26662  * @class Roo.bootstrap.Alert
26663  * @extends Roo.bootstrap.Component
26664  * Bootstrap Alert class
26665  * @cfg {String} title The title of alert
26666  * @cfg {String} html The content of alert
26667  * @cfg {String} weight (  success | info | warning | danger )
26668  * @cfg {String} faicon font-awesomeicon
26669  * 
26670  * @constructor
26671  * Create a new alert
26672  * @param {Object} config The config object
26673  */
26674
26675
26676 Roo.bootstrap.Alert = function(config){
26677     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26678     
26679 };
26680
26681 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26682     
26683     title: '',
26684     html: '',
26685     weight: false,
26686     faicon: false,
26687     
26688     getAutoCreate : function()
26689     {
26690         
26691         var cfg = {
26692             tag : 'div',
26693             cls : 'alert',
26694             cn : [
26695                 {
26696                     tag : 'i',
26697                     cls : 'roo-alert-icon'
26698                     
26699                 },
26700                 {
26701                     tag : 'b',
26702                     cls : 'roo-alert-title',
26703                     html : this.title
26704                 },
26705                 {
26706                     tag : 'span',
26707                     cls : 'roo-alert-text',
26708                     html : this.html
26709                 }
26710             ]
26711         };
26712         
26713         if(this.faicon){
26714             cfg.cn[0].cls += ' fa ' + this.faicon;
26715         }
26716         
26717         if(this.weight){
26718             cfg.cls += ' alert-' + this.weight;
26719         }
26720         
26721         return cfg;
26722     },
26723     
26724     initEvents: function() 
26725     {
26726         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26727     },
26728     
26729     setTitle : function(str)
26730     {
26731         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26732     },
26733     
26734     setText : function(str)
26735     {
26736         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26737     },
26738     
26739     setWeight : function(weight)
26740     {
26741         if(this.weight){
26742             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26743         }
26744         
26745         this.weight = weight;
26746         
26747         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26748     },
26749     
26750     setIcon : function(icon)
26751     {
26752         if(this.faicon){
26753             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26754         }
26755         
26756         this.faicon = icon;
26757         
26758         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26759     },
26760     
26761     hide: function() 
26762     {
26763         this.el.hide();   
26764     },
26765     
26766     show: function() 
26767     {  
26768         this.el.show();   
26769     }
26770     
26771 });
26772
26773  
26774 /*
26775 * Licence: LGPL
26776 */
26777
26778 /**
26779  * @class Roo.bootstrap.UploadCropbox
26780  * @extends Roo.bootstrap.Component
26781  * Bootstrap UploadCropbox class
26782  * @cfg {String} emptyText show when image has been loaded
26783  * @cfg {String} rotateNotify show when image too small to rotate
26784  * @cfg {Number} errorTimeout default 3000
26785  * @cfg {Number} minWidth default 300
26786  * @cfg {Number} minHeight default 300
26787  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26788  * @cfg {Boolean} isDocument (true|false) default false
26789  * @cfg {String} url action url
26790  * @cfg {String} paramName default 'imageUpload'
26791  * @cfg {String} method default POST
26792  * @cfg {Boolean} loadMask (true|false) default true
26793  * @cfg {Boolean} loadingText default 'Loading...'
26794  * 
26795  * @constructor
26796  * Create a new UploadCropbox
26797  * @param {Object} config The config object
26798  */
26799
26800 Roo.bootstrap.UploadCropbox = function(config){
26801     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26802     
26803     this.addEvents({
26804         /**
26805          * @event beforeselectfile
26806          * Fire before select file
26807          * @param {Roo.bootstrap.UploadCropbox} this
26808          */
26809         "beforeselectfile" : true,
26810         /**
26811          * @event initial
26812          * Fire after initEvent
26813          * @param {Roo.bootstrap.UploadCropbox} this
26814          */
26815         "initial" : true,
26816         /**
26817          * @event crop
26818          * Fire after initEvent
26819          * @param {Roo.bootstrap.UploadCropbox} this
26820          * @param {String} data
26821          */
26822         "crop" : true,
26823         /**
26824          * @event prepare
26825          * Fire when preparing the file data
26826          * @param {Roo.bootstrap.UploadCropbox} this
26827          * @param {Object} file
26828          */
26829         "prepare" : true,
26830         /**
26831          * @event exception
26832          * Fire when get exception
26833          * @param {Roo.bootstrap.UploadCropbox} this
26834          * @param {XMLHttpRequest} xhr
26835          */
26836         "exception" : true,
26837         /**
26838          * @event beforeloadcanvas
26839          * Fire before load the canvas
26840          * @param {Roo.bootstrap.UploadCropbox} this
26841          * @param {String} src
26842          */
26843         "beforeloadcanvas" : true,
26844         /**
26845          * @event trash
26846          * Fire when trash image
26847          * @param {Roo.bootstrap.UploadCropbox} this
26848          */
26849         "trash" : true,
26850         /**
26851          * @event download
26852          * Fire when download the image
26853          * @param {Roo.bootstrap.UploadCropbox} this
26854          */
26855         "download" : true,
26856         /**
26857          * @event footerbuttonclick
26858          * Fire when footerbuttonclick
26859          * @param {Roo.bootstrap.UploadCropbox} this
26860          * @param {String} type
26861          */
26862         "footerbuttonclick" : true,
26863         /**
26864          * @event resize
26865          * Fire when resize
26866          * @param {Roo.bootstrap.UploadCropbox} this
26867          */
26868         "resize" : true,
26869         /**
26870          * @event rotate
26871          * Fire when rotate the image
26872          * @param {Roo.bootstrap.UploadCropbox} this
26873          * @param {String} pos
26874          */
26875         "rotate" : true,
26876         /**
26877          * @event inspect
26878          * Fire when inspect the file
26879          * @param {Roo.bootstrap.UploadCropbox} this
26880          * @param {Object} file
26881          */
26882         "inspect" : true,
26883         /**
26884          * @event upload
26885          * Fire when xhr upload the file
26886          * @param {Roo.bootstrap.UploadCropbox} this
26887          * @param {Object} data
26888          */
26889         "upload" : true,
26890         /**
26891          * @event arrange
26892          * Fire when arrange the file data
26893          * @param {Roo.bootstrap.UploadCropbox} this
26894          * @param {Object} formData
26895          */
26896         "arrange" : true
26897     });
26898     
26899     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26900 };
26901
26902 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
26903     
26904     emptyText : 'Click to upload image',
26905     rotateNotify : 'Image is too small to rotate',
26906     errorTimeout : 3000,
26907     scale : 0,
26908     baseScale : 1,
26909     rotate : 0,
26910     dragable : false,
26911     pinching : false,
26912     mouseX : 0,
26913     mouseY : 0,
26914     cropData : false,
26915     minWidth : 300,
26916     minHeight : 300,
26917     file : false,
26918     exif : {},
26919     baseRotate : 1,
26920     cropType : 'image/jpeg',
26921     buttons : false,
26922     canvasLoaded : false,
26923     isDocument : false,
26924     method : 'POST',
26925     paramName : 'imageUpload',
26926     loadMask : true,
26927     loadingText : 'Loading...',
26928     maskEl : false,
26929     
26930     getAutoCreate : function()
26931     {
26932         var cfg = {
26933             tag : 'div',
26934             cls : 'roo-upload-cropbox',
26935             cn : [
26936                 {
26937                     tag : 'input',
26938                     cls : 'roo-upload-cropbox-selector',
26939                     type : 'file'
26940                 },
26941                 {
26942                     tag : 'div',
26943                     cls : 'roo-upload-cropbox-body',
26944                     style : 'cursor:pointer',
26945                     cn : [
26946                         {
26947                             tag : 'div',
26948                             cls : 'roo-upload-cropbox-preview'
26949                         },
26950                         {
26951                             tag : 'div',
26952                             cls : 'roo-upload-cropbox-thumb'
26953                         },
26954                         {
26955                             tag : 'div',
26956                             cls : 'roo-upload-cropbox-empty-notify',
26957                             html : this.emptyText
26958                         },
26959                         {
26960                             tag : 'div',
26961                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
26962                             html : this.rotateNotify
26963                         }
26964                     ]
26965                 },
26966                 {
26967                     tag : 'div',
26968                     cls : 'roo-upload-cropbox-footer',
26969                     cn : {
26970                         tag : 'div',
26971                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
26972                         cn : []
26973                     }
26974                 }
26975             ]
26976         };
26977         
26978         return cfg;
26979     },
26980     
26981     onRender : function(ct, position)
26982     {
26983         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
26984         
26985         if (this.buttons.length) {
26986             
26987             Roo.each(this.buttons, function(bb) {
26988                 
26989                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
26990                 
26991                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
26992                 
26993             }, this);
26994         }
26995         
26996         if(this.loadMask){
26997             this.maskEl = this.el;
26998         }
26999     },
27000     
27001     initEvents : function()
27002     {
27003         this.urlAPI = (window.createObjectURL && window) || 
27004                                 (window.URL && URL.revokeObjectURL && URL) || 
27005                                 (window.webkitURL && webkitURL);
27006                         
27007         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27008         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27009         
27010         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27011         this.selectorEl.hide();
27012         
27013         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27014         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27015         
27016         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27017         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27018         this.thumbEl.hide();
27019         
27020         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27021         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27022         
27023         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27024         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27025         this.errorEl.hide();
27026         
27027         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27028         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27029         this.footerEl.hide();
27030         
27031         this.setThumbBoxSize();
27032         
27033         this.bind();
27034         
27035         this.resize();
27036         
27037         this.fireEvent('initial', this);
27038     },
27039
27040     bind : function()
27041     {
27042         var _this = this;
27043         
27044         window.addEventListener("resize", function() { _this.resize(); } );
27045         
27046         this.bodyEl.on('click', this.beforeSelectFile, this);
27047         
27048         if(Roo.isTouch){
27049             this.bodyEl.on('touchstart', this.onTouchStart, this);
27050             this.bodyEl.on('touchmove', this.onTouchMove, this);
27051             this.bodyEl.on('touchend', this.onTouchEnd, this);
27052         }
27053         
27054         if(!Roo.isTouch){
27055             this.bodyEl.on('mousedown', this.onMouseDown, this);
27056             this.bodyEl.on('mousemove', this.onMouseMove, this);
27057             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27058             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27059             Roo.get(document).on('mouseup', this.onMouseUp, this);
27060         }
27061         
27062         this.selectorEl.on('change', this.onFileSelected, this);
27063     },
27064     
27065     reset : function()
27066     {    
27067         this.scale = 0;
27068         this.baseScale = 1;
27069         this.rotate = 0;
27070         this.baseRotate = 1;
27071         this.dragable = false;
27072         this.pinching = false;
27073         this.mouseX = 0;
27074         this.mouseY = 0;
27075         this.cropData = false;
27076         this.notifyEl.dom.innerHTML = this.emptyText;
27077         
27078         this.selectorEl.dom.value = '';
27079         
27080     },
27081     
27082     resize : function()
27083     {
27084         if(this.fireEvent('resize', this) != false){
27085             this.setThumbBoxPosition();
27086             this.setCanvasPosition();
27087         }
27088     },
27089     
27090     onFooterButtonClick : function(e, el, o, type)
27091     {
27092         switch (type) {
27093             case 'rotate-left' :
27094                 this.onRotateLeft(e);
27095                 break;
27096             case 'rotate-right' :
27097                 this.onRotateRight(e);
27098                 break;
27099             case 'picture' :
27100                 this.beforeSelectFile(e);
27101                 break;
27102             case 'trash' :
27103                 this.trash(e);
27104                 break;
27105             case 'crop' :
27106                 this.crop(e);
27107                 break;
27108             case 'download' :
27109                 this.download(e);
27110                 break;
27111             default :
27112                 break;
27113         }
27114         
27115         this.fireEvent('footerbuttonclick', this, type);
27116     },
27117     
27118     beforeSelectFile : function(e)
27119     {
27120         e.preventDefault();
27121         
27122         if(this.fireEvent('beforeselectfile', this) != false){
27123             this.selectorEl.dom.click();
27124         }
27125     },
27126     
27127     onFileSelected : function(e)
27128     {
27129         e.preventDefault();
27130         
27131         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27132             return;
27133         }
27134         
27135         var file = this.selectorEl.dom.files[0];
27136         
27137         if(this.fireEvent('inspect', this, file) != false){
27138             this.prepare(file);
27139         }
27140         
27141     },
27142     
27143     trash : function(e)
27144     {
27145         this.fireEvent('trash', this);
27146     },
27147     
27148     download : function(e)
27149     {
27150         this.fireEvent('download', this);
27151     },
27152     
27153     loadCanvas : function(src)
27154     {   
27155         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27156             
27157             this.reset();
27158             
27159             this.imageEl = document.createElement('img');
27160             
27161             var _this = this;
27162             
27163             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27164             
27165             this.imageEl.src = src;
27166         }
27167     },
27168     
27169     onLoadCanvas : function()
27170     {   
27171         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27172         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27173         
27174         this.bodyEl.un('click', this.beforeSelectFile, this);
27175         
27176         this.notifyEl.hide();
27177         this.thumbEl.show();
27178         this.footerEl.show();
27179         
27180         this.baseRotateLevel();
27181         
27182         if(this.isDocument){
27183             this.setThumbBoxSize();
27184         }
27185         
27186         this.setThumbBoxPosition();
27187         
27188         this.baseScaleLevel();
27189         
27190         this.draw();
27191         
27192         this.resize();
27193         
27194         this.canvasLoaded = true;
27195         
27196         if(this.loadMask){
27197             this.maskEl.unmask();
27198         }
27199         
27200     },
27201     
27202     setCanvasPosition : function()
27203     {   
27204         if(!this.canvasEl){
27205             return;
27206         }
27207         
27208         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27209         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27210         
27211         this.previewEl.setLeft(pw);
27212         this.previewEl.setTop(ph);
27213         
27214     },
27215     
27216     onMouseDown : function(e)
27217     {   
27218         e.stopEvent();
27219         
27220         this.dragable = true;
27221         this.pinching = false;
27222         
27223         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27224             this.dragable = false;
27225             return;
27226         }
27227         
27228         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27229         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27230         
27231     },
27232     
27233     onMouseMove : function(e)
27234     {   
27235         e.stopEvent();
27236         
27237         if(!this.canvasLoaded){
27238             return;
27239         }
27240         
27241         if (!this.dragable){
27242             return;
27243         }
27244         
27245         var minX = Math.ceil(this.thumbEl.getLeft(true));
27246         var minY = Math.ceil(this.thumbEl.getTop(true));
27247         
27248         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27249         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27250         
27251         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27252         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27253         
27254         x = x - this.mouseX;
27255         y = y - this.mouseY;
27256         
27257         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27258         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27259         
27260         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27261         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27262         
27263         this.previewEl.setLeft(bgX);
27264         this.previewEl.setTop(bgY);
27265         
27266         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27267         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27268     },
27269     
27270     onMouseUp : function(e)
27271     {   
27272         e.stopEvent();
27273         
27274         this.dragable = false;
27275     },
27276     
27277     onMouseWheel : function(e)
27278     {   
27279         e.stopEvent();
27280         
27281         this.startScale = this.scale;
27282         
27283         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27284         
27285         if(!this.zoomable()){
27286             this.scale = this.startScale;
27287             return;
27288         }
27289         
27290         this.draw();
27291         
27292         return;
27293     },
27294     
27295     zoomable : function()
27296     {
27297         var minScale = this.thumbEl.getWidth() / this.minWidth;
27298         
27299         if(this.minWidth < this.minHeight){
27300             minScale = this.thumbEl.getHeight() / this.minHeight;
27301         }
27302         
27303         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27304         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27305         
27306         if(
27307                 this.isDocument &&
27308                 (this.rotate == 0 || this.rotate == 180) && 
27309                 (
27310                     width > this.imageEl.OriginWidth || 
27311                     height > this.imageEl.OriginHeight ||
27312                     (width < this.minWidth && height < this.minHeight)
27313                 )
27314         ){
27315             return false;
27316         }
27317         
27318         if(
27319                 this.isDocument &&
27320                 (this.rotate == 90 || this.rotate == 270) && 
27321                 (
27322                     width > this.imageEl.OriginWidth || 
27323                     height > this.imageEl.OriginHeight ||
27324                     (width < this.minHeight && height < this.minWidth)
27325                 )
27326         ){
27327             return false;
27328         }
27329         
27330         if(
27331                 !this.isDocument &&
27332                 (this.rotate == 0 || this.rotate == 180) && 
27333                 (
27334                     width < this.minWidth || 
27335                     width > this.imageEl.OriginWidth || 
27336                     height < this.minHeight || 
27337                     height > this.imageEl.OriginHeight
27338                 )
27339         ){
27340             return false;
27341         }
27342         
27343         if(
27344                 !this.isDocument &&
27345                 (this.rotate == 90 || this.rotate == 270) && 
27346                 (
27347                     width < this.minHeight || 
27348                     width > this.imageEl.OriginWidth || 
27349                     height < this.minWidth || 
27350                     height > this.imageEl.OriginHeight
27351                 )
27352         ){
27353             return false;
27354         }
27355         
27356         return true;
27357         
27358     },
27359     
27360     onRotateLeft : function(e)
27361     {   
27362         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27363             
27364             var minScale = this.thumbEl.getWidth() / this.minWidth;
27365             
27366             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27367             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27368             
27369             this.startScale = this.scale;
27370             
27371             while (this.getScaleLevel() < minScale){
27372             
27373                 this.scale = this.scale + 1;
27374                 
27375                 if(!this.zoomable()){
27376                     break;
27377                 }
27378                 
27379                 if(
27380                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27381                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27382                 ){
27383                     continue;
27384                 }
27385                 
27386                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27387
27388                 this.draw();
27389                 
27390                 return;
27391             }
27392             
27393             this.scale = this.startScale;
27394             
27395             this.onRotateFail();
27396             
27397             return false;
27398         }
27399         
27400         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27401
27402         if(this.isDocument){
27403             this.setThumbBoxSize();
27404             this.setThumbBoxPosition();
27405             this.setCanvasPosition();
27406         }
27407         
27408         this.draw();
27409         
27410         this.fireEvent('rotate', this, 'left');
27411         
27412     },
27413     
27414     onRotateRight : function(e)
27415     {
27416         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27417             
27418             var minScale = this.thumbEl.getWidth() / this.minWidth;
27419         
27420             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27421             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27422             
27423             this.startScale = this.scale;
27424             
27425             while (this.getScaleLevel() < minScale){
27426             
27427                 this.scale = this.scale + 1;
27428                 
27429                 if(!this.zoomable()){
27430                     break;
27431                 }
27432                 
27433                 if(
27434                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27435                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27436                 ){
27437                     continue;
27438                 }
27439                 
27440                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27441
27442                 this.draw();
27443                 
27444                 return;
27445             }
27446             
27447             this.scale = this.startScale;
27448             
27449             this.onRotateFail();
27450             
27451             return false;
27452         }
27453         
27454         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27455
27456         if(this.isDocument){
27457             this.setThumbBoxSize();
27458             this.setThumbBoxPosition();
27459             this.setCanvasPosition();
27460         }
27461         
27462         this.draw();
27463         
27464         this.fireEvent('rotate', this, 'right');
27465     },
27466     
27467     onRotateFail : function()
27468     {
27469         this.errorEl.show(true);
27470         
27471         var _this = this;
27472         
27473         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27474     },
27475     
27476     draw : function()
27477     {
27478         this.previewEl.dom.innerHTML = '';
27479         
27480         var canvasEl = document.createElement("canvas");
27481         
27482         var contextEl = canvasEl.getContext("2d");
27483         
27484         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27485         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27486         var center = this.imageEl.OriginWidth / 2;
27487         
27488         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27489             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27490             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27491             center = this.imageEl.OriginHeight / 2;
27492         }
27493         
27494         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27495         
27496         contextEl.translate(center, center);
27497         contextEl.rotate(this.rotate * Math.PI / 180);
27498
27499         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27500         
27501         this.canvasEl = document.createElement("canvas");
27502         
27503         this.contextEl = this.canvasEl.getContext("2d");
27504         
27505         switch (this.rotate) {
27506             case 0 :
27507                 
27508                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27509                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27510                 
27511                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27512                 
27513                 break;
27514             case 90 : 
27515                 
27516                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27517                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27518                 
27519                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27520                     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);
27521                     break;
27522                 }
27523                 
27524                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27525                 
27526                 break;
27527             case 180 :
27528                 
27529                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27530                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27531                 
27532                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27533                     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);
27534                     break;
27535                 }
27536                 
27537                 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);
27538                 
27539                 break;
27540             case 270 :
27541                 
27542                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27543                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27544         
27545                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27546                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27547                     break;
27548                 }
27549                 
27550                 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);
27551                 
27552                 break;
27553             default : 
27554                 break;
27555         }
27556         
27557         this.previewEl.appendChild(this.canvasEl);
27558         
27559         this.setCanvasPosition();
27560     },
27561     
27562     crop : function()
27563     {
27564         if(!this.canvasLoaded){
27565             return;
27566         }
27567         
27568         var imageCanvas = document.createElement("canvas");
27569         
27570         var imageContext = imageCanvas.getContext("2d");
27571         
27572         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27573         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27574         
27575         var center = imageCanvas.width / 2;
27576         
27577         imageContext.translate(center, center);
27578         
27579         imageContext.rotate(this.rotate * Math.PI / 180);
27580         
27581         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27582         
27583         var canvas = document.createElement("canvas");
27584         
27585         var context = canvas.getContext("2d");
27586                 
27587         canvas.width = this.minWidth;
27588         canvas.height = this.minHeight;
27589
27590         switch (this.rotate) {
27591             case 0 :
27592                 
27593                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27594                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27595                 
27596                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27597                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27598                 
27599                 var targetWidth = this.minWidth - 2 * x;
27600                 var targetHeight = this.minHeight - 2 * y;
27601                 
27602                 var scale = 1;
27603                 
27604                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27605                     scale = targetWidth / width;
27606                 }
27607                 
27608                 if(x > 0 && y == 0){
27609                     scale = targetHeight / height;
27610                 }
27611                 
27612                 if(x > 0 && y > 0){
27613                     scale = targetWidth / width;
27614                     
27615                     if(width < height){
27616                         scale = targetHeight / height;
27617                     }
27618                 }
27619                 
27620                 context.scale(scale, scale);
27621                 
27622                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27623                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27624
27625                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27626                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27627
27628                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27629                 
27630                 break;
27631             case 90 : 
27632                 
27633                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27634                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27635                 
27636                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27637                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27638                 
27639                 var targetWidth = this.minWidth - 2 * x;
27640                 var targetHeight = this.minHeight - 2 * y;
27641                 
27642                 var scale = 1;
27643                 
27644                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27645                     scale = targetWidth / width;
27646                 }
27647                 
27648                 if(x > 0 && y == 0){
27649                     scale = targetHeight / height;
27650                 }
27651                 
27652                 if(x > 0 && y > 0){
27653                     scale = targetWidth / width;
27654                     
27655                     if(width < height){
27656                         scale = targetHeight / height;
27657                     }
27658                 }
27659                 
27660                 context.scale(scale, scale);
27661                 
27662                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27663                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27664
27665                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27666                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27667                 
27668                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27669                 
27670                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27671                 
27672                 break;
27673             case 180 :
27674                 
27675                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27676                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27677                 
27678                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27679                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27680                 
27681                 var targetWidth = this.minWidth - 2 * x;
27682                 var targetHeight = this.minHeight - 2 * y;
27683                 
27684                 var scale = 1;
27685                 
27686                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27687                     scale = targetWidth / width;
27688                 }
27689                 
27690                 if(x > 0 && y == 0){
27691                     scale = targetHeight / height;
27692                 }
27693                 
27694                 if(x > 0 && y > 0){
27695                     scale = targetWidth / width;
27696                     
27697                     if(width < height){
27698                         scale = targetHeight / height;
27699                     }
27700                 }
27701                 
27702                 context.scale(scale, scale);
27703                 
27704                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27705                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27706
27707                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27708                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27709
27710                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27711                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27712                 
27713                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27714                 
27715                 break;
27716             case 270 :
27717                 
27718                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27719                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27720                 
27721                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27722                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27723                 
27724                 var targetWidth = this.minWidth - 2 * x;
27725                 var targetHeight = this.minHeight - 2 * y;
27726                 
27727                 var scale = 1;
27728                 
27729                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27730                     scale = targetWidth / width;
27731                 }
27732                 
27733                 if(x > 0 && y == 0){
27734                     scale = targetHeight / height;
27735                 }
27736                 
27737                 if(x > 0 && y > 0){
27738                     scale = targetWidth / width;
27739                     
27740                     if(width < height){
27741                         scale = targetHeight / height;
27742                     }
27743                 }
27744                 
27745                 context.scale(scale, scale);
27746                 
27747                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27748                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27749
27750                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27751                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27752                 
27753                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27754                 
27755                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27756                 
27757                 break;
27758             default : 
27759                 break;
27760         }
27761         
27762         this.cropData = canvas.toDataURL(this.cropType);
27763         
27764         if(this.fireEvent('crop', this, this.cropData) !== false){
27765             this.process(this.file, this.cropData);
27766         }
27767         
27768         return;
27769         
27770     },
27771     
27772     setThumbBoxSize : function()
27773     {
27774         var width, height;
27775         
27776         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27777             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27778             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27779             
27780             this.minWidth = width;
27781             this.minHeight = height;
27782             
27783             if(this.rotate == 90 || this.rotate == 270){
27784                 this.minWidth = height;
27785                 this.minHeight = width;
27786             }
27787         }
27788         
27789         height = 300;
27790         width = Math.ceil(this.minWidth * height / this.minHeight);
27791         
27792         if(this.minWidth > this.minHeight){
27793             width = 300;
27794             height = Math.ceil(this.minHeight * width / this.minWidth);
27795         }
27796         
27797         this.thumbEl.setStyle({
27798             width : width + 'px',
27799             height : height + 'px'
27800         });
27801
27802         return;
27803             
27804     },
27805     
27806     setThumbBoxPosition : function()
27807     {
27808         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27809         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27810         
27811         this.thumbEl.setLeft(x);
27812         this.thumbEl.setTop(y);
27813         
27814     },
27815     
27816     baseRotateLevel : function()
27817     {
27818         this.baseRotate = 1;
27819         
27820         if(
27821                 typeof(this.exif) != 'undefined' &&
27822                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27823                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27824         ){
27825             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27826         }
27827         
27828         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27829         
27830     },
27831     
27832     baseScaleLevel : function()
27833     {
27834         var width, height;
27835         
27836         if(this.isDocument){
27837             
27838             if(this.baseRotate == 6 || this.baseRotate == 8){
27839             
27840                 height = this.thumbEl.getHeight();
27841                 this.baseScale = height / this.imageEl.OriginWidth;
27842
27843                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27844                     width = this.thumbEl.getWidth();
27845                     this.baseScale = width / this.imageEl.OriginHeight;
27846                 }
27847
27848                 return;
27849             }
27850
27851             height = this.thumbEl.getHeight();
27852             this.baseScale = height / this.imageEl.OriginHeight;
27853
27854             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27855                 width = this.thumbEl.getWidth();
27856                 this.baseScale = width / this.imageEl.OriginWidth;
27857             }
27858
27859             return;
27860         }
27861         
27862         if(this.baseRotate == 6 || this.baseRotate == 8){
27863             
27864             width = this.thumbEl.getHeight();
27865             this.baseScale = width / this.imageEl.OriginHeight;
27866             
27867             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27868                 height = this.thumbEl.getWidth();
27869                 this.baseScale = height / this.imageEl.OriginHeight;
27870             }
27871             
27872             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27873                 height = this.thumbEl.getWidth();
27874                 this.baseScale = height / this.imageEl.OriginHeight;
27875                 
27876                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27877                     width = this.thumbEl.getHeight();
27878                     this.baseScale = width / this.imageEl.OriginWidth;
27879                 }
27880             }
27881             
27882             return;
27883         }
27884         
27885         width = this.thumbEl.getWidth();
27886         this.baseScale = width / this.imageEl.OriginWidth;
27887         
27888         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27889             height = this.thumbEl.getHeight();
27890             this.baseScale = height / this.imageEl.OriginHeight;
27891         }
27892         
27893         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27894             
27895             height = this.thumbEl.getHeight();
27896             this.baseScale = height / this.imageEl.OriginHeight;
27897             
27898             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27899                 width = this.thumbEl.getWidth();
27900                 this.baseScale = width / this.imageEl.OriginWidth;
27901             }
27902             
27903         }
27904         
27905         return;
27906     },
27907     
27908     getScaleLevel : function()
27909     {
27910         return this.baseScale * Math.pow(1.1, this.scale);
27911     },
27912     
27913     onTouchStart : function(e)
27914     {
27915         if(!this.canvasLoaded){
27916             this.beforeSelectFile(e);
27917             return;
27918         }
27919         
27920         var touches = e.browserEvent.touches;
27921         
27922         if(!touches){
27923             return;
27924         }
27925         
27926         if(touches.length == 1){
27927             this.onMouseDown(e);
27928             return;
27929         }
27930         
27931         if(touches.length != 2){
27932             return;
27933         }
27934         
27935         var coords = [];
27936         
27937         for(var i = 0, finger; finger = touches[i]; i++){
27938             coords.push(finger.pageX, finger.pageY);
27939         }
27940         
27941         var x = Math.pow(coords[0] - coords[2], 2);
27942         var y = Math.pow(coords[1] - coords[3], 2);
27943         
27944         this.startDistance = Math.sqrt(x + y);
27945         
27946         this.startScale = this.scale;
27947         
27948         this.pinching = true;
27949         this.dragable = false;
27950         
27951     },
27952     
27953     onTouchMove : function(e)
27954     {
27955         if(!this.pinching && !this.dragable){
27956             return;
27957         }
27958         
27959         var touches = e.browserEvent.touches;
27960         
27961         if(!touches){
27962             return;
27963         }
27964         
27965         if(this.dragable){
27966             this.onMouseMove(e);
27967             return;
27968         }
27969         
27970         var coords = [];
27971         
27972         for(var i = 0, finger; finger = touches[i]; i++){
27973             coords.push(finger.pageX, finger.pageY);
27974         }
27975         
27976         var x = Math.pow(coords[0] - coords[2], 2);
27977         var y = Math.pow(coords[1] - coords[3], 2);
27978         
27979         this.endDistance = Math.sqrt(x + y);
27980         
27981         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
27982         
27983         if(!this.zoomable()){
27984             this.scale = this.startScale;
27985             return;
27986         }
27987         
27988         this.draw();
27989         
27990     },
27991     
27992     onTouchEnd : function(e)
27993     {
27994         this.pinching = false;
27995         this.dragable = false;
27996         
27997     },
27998     
27999     process : function(file, crop)
28000     {
28001         if(this.loadMask){
28002             this.maskEl.mask(this.loadingText);
28003         }
28004         
28005         this.xhr = new XMLHttpRequest();
28006         
28007         file.xhr = this.xhr;
28008
28009         this.xhr.open(this.method, this.url, true);
28010         
28011         var headers = {
28012             "Accept": "application/json",
28013             "Cache-Control": "no-cache",
28014             "X-Requested-With": "XMLHttpRequest"
28015         };
28016         
28017         for (var headerName in headers) {
28018             var headerValue = headers[headerName];
28019             if (headerValue) {
28020                 this.xhr.setRequestHeader(headerName, headerValue);
28021             }
28022         }
28023         
28024         var _this = this;
28025         
28026         this.xhr.onload = function()
28027         {
28028             _this.xhrOnLoad(_this.xhr);
28029         }
28030         
28031         this.xhr.onerror = function()
28032         {
28033             _this.xhrOnError(_this.xhr);
28034         }
28035         
28036         var formData = new FormData();
28037
28038         formData.append('returnHTML', 'NO');
28039         
28040         if(crop){
28041             formData.append('crop', crop);
28042         }
28043         
28044         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28045             formData.append(this.paramName, file, file.name);
28046         }
28047         
28048         if(typeof(file.filename) != 'undefined'){
28049             formData.append('filename', file.filename);
28050         }
28051         
28052         if(typeof(file.mimetype) != 'undefined'){
28053             formData.append('mimetype', file.mimetype);
28054         }
28055         
28056         if(this.fireEvent('arrange', this, formData) != false){
28057             this.xhr.send(formData);
28058         };
28059     },
28060     
28061     xhrOnLoad : function(xhr)
28062     {
28063         if(this.loadMask){
28064             this.maskEl.unmask();
28065         }
28066         
28067         if (xhr.readyState !== 4) {
28068             this.fireEvent('exception', this, xhr);
28069             return;
28070         }
28071
28072         var response = Roo.decode(xhr.responseText);
28073         
28074         if(!response.success){
28075             this.fireEvent('exception', this, xhr);
28076             return;
28077         }
28078         
28079         var response = Roo.decode(xhr.responseText);
28080         
28081         this.fireEvent('upload', this, response);
28082         
28083     },
28084     
28085     xhrOnError : function()
28086     {
28087         if(this.loadMask){
28088             this.maskEl.unmask();
28089         }
28090         
28091         Roo.log('xhr on error');
28092         
28093         var response = Roo.decode(xhr.responseText);
28094           
28095         Roo.log(response);
28096         
28097     },
28098     
28099     prepare : function(file)
28100     {   
28101         if(this.loadMask){
28102             this.maskEl.mask(this.loadingText);
28103         }
28104         
28105         this.file = false;
28106         this.exif = {};
28107         
28108         if(typeof(file) === 'string'){
28109             this.loadCanvas(file);
28110             return;
28111         }
28112         
28113         if(!file || !this.urlAPI){
28114             return;
28115         }
28116         
28117         this.file = file;
28118         this.cropType = file.type;
28119         
28120         var _this = this;
28121         
28122         if(this.fireEvent('prepare', this, this.file) != false){
28123             
28124             var reader = new FileReader();
28125             
28126             reader.onload = function (e) {
28127                 if (e.target.error) {
28128                     Roo.log(e.target.error);
28129                     return;
28130                 }
28131                 
28132                 var buffer = e.target.result,
28133                     dataView = new DataView(buffer),
28134                     offset = 2,
28135                     maxOffset = dataView.byteLength - 4,
28136                     markerBytes,
28137                     markerLength;
28138                 
28139                 if (dataView.getUint16(0) === 0xffd8) {
28140                     while (offset < maxOffset) {
28141                         markerBytes = dataView.getUint16(offset);
28142                         
28143                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28144                             markerLength = dataView.getUint16(offset + 2) + 2;
28145                             if (offset + markerLength > dataView.byteLength) {
28146                                 Roo.log('Invalid meta data: Invalid segment size.');
28147                                 break;
28148                             }
28149                             
28150                             if(markerBytes == 0xffe1){
28151                                 _this.parseExifData(
28152                                     dataView,
28153                                     offset,
28154                                     markerLength
28155                                 );
28156                             }
28157                             
28158                             offset += markerLength;
28159                             
28160                             continue;
28161                         }
28162                         
28163                         break;
28164                     }
28165                     
28166                 }
28167                 
28168                 var url = _this.urlAPI.createObjectURL(_this.file);
28169                 
28170                 _this.loadCanvas(url);
28171                 
28172                 return;
28173             }
28174             
28175             reader.readAsArrayBuffer(this.file);
28176             
28177         }
28178         
28179     },
28180     
28181     parseExifData : function(dataView, offset, length)
28182     {
28183         var tiffOffset = offset + 10,
28184             littleEndian,
28185             dirOffset;
28186     
28187         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28188             // No Exif data, might be XMP data instead
28189             return;
28190         }
28191         
28192         // Check for the ASCII code for "Exif" (0x45786966):
28193         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28194             // No Exif data, might be XMP data instead
28195             return;
28196         }
28197         if (tiffOffset + 8 > dataView.byteLength) {
28198             Roo.log('Invalid Exif data: Invalid segment size.');
28199             return;
28200         }
28201         // Check for the two null bytes:
28202         if (dataView.getUint16(offset + 8) !== 0x0000) {
28203             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28204             return;
28205         }
28206         // Check the byte alignment:
28207         switch (dataView.getUint16(tiffOffset)) {
28208         case 0x4949:
28209             littleEndian = true;
28210             break;
28211         case 0x4D4D:
28212             littleEndian = false;
28213             break;
28214         default:
28215             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28216             return;
28217         }
28218         // Check for the TIFF tag marker (0x002A):
28219         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28220             Roo.log('Invalid Exif data: Missing TIFF marker.');
28221             return;
28222         }
28223         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28224         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28225         
28226         this.parseExifTags(
28227             dataView,
28228             tiffOffset,
28229             tiffOffset + dirOffset,
28230             littleEndian
28231         );
28232     },
28233     
28234     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28235     {
28236         var tagsNumber,
28237             dirEndOffset,
28238             i;
28239         if (dirOffset + 6 > dataView.byteLength) {
28240             Roo.log('Invalid Exif data: Invalid directory offset.');
28241             return;
28242         }
28243         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28244         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28245         if (dirEndOffset + 4 > dataView.byteLength) {
28246             Roo.log('Invalid Exif data: Invalid directory size.');
28247             return;
28248         }
28249         for (i = 0; i < tagsNumber; i += 1) {
28250             this.parseExifTag(
28251                 dataView,
28252                 tiffOffset,
28253                 dirOffset + 2 + 12 * i, // tag offset
28254                 littleEndian
28255             );
28256         }
28257         // Return the offset to the next directory:
28258         return dataView.getUint32(dirEndOffset, littleEndian);
28259     },
28260     
28261     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28262     {
28263         var tag = dataView.getUint16(offset, littleEndian);
28264         
28265         this.exif[tag] = this.getExifValue(
28266             dataView,
28267             tiffOffset,
28268             offset,
28269             dataView.getUint16(offset + 2, littleEndian), // tag type
28270             dataView.getUint32(offset + 4, littleEndian), // tag length
28271             littleEndian
28272         );
28273     },
28274     
28275     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28276     {
28277         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28278             tagSize,
28279             dataOffset,
28280             values,
28281             i,
28282             str,
28283             c;
28284     
28285         if (!tagType) {
28286             Roo.log('Invalid Exif data: Invalid tag type.');
28287             return;
28288         }
28289         
28290         tagSize = tagType.size * length;
28291         // Determine if the value is contained in the dataOffset bytes,
28292         // or if the value at the dataOffset is a pointer to the actual data:
28293         dataOffset = tagSize > 4 ?
28294                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28295         if (dataOffset + tagSize > dataView.byteLength) {
28296             Roo.log('Invalid Exif data: Invalid data offset.');
28297             return;
28298         }
28299         if (length === 1) {
28300             return tagType.getValue(dataView, dataOffset, littleEndian);
28301         }
28302         values = [];
28303         for (i = 0; i < length; i += 1) {
28304             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28305         }
28306         
28307         if (tagType.ascii) {
28308             str = '';
28309             // Concatenate the chars:
28310             for (i = 0; i < values.length; i += 1) {
28311                 c = values[i];
28312                 // Ignore the terminating NULL byte(s):
28313                 if (c === '\u0000') {
28314                     break;
28315                 }
28316                 str += c;
28317             }
28318             return str;
28319         }
28320         return values;
28321     }
28322     
28323 });
28324
28325 Roo.apply(Roo.bootstrap.UploadCropbox, {
28326     tags : {
28327         'Orientation': 0x0112
28328     },
28329     
28330     Orientation: {
28331             1: 0, //'top-left',
28332 //            2: 'top-right',
28333             3: 180, //'bottom-right',
28334 //            4: 'bottom-left',
28335 //            5: 'left-top',
28336             6: 90, //'right-top',
28337 //            7: 'right-bottom',
28338             8: 270 //'left-bottom'
28339     },
28340     
28341     exifTagTypes : {
28342         // byte, 8-bit unsigned int:
28343         1: {
28344             getValue: function (dataView, dataOffset) {
28345                 return dataView.getUint8(dataOffset);
28346             },
28347             size: 1
28348         },
28349         // ascii, 8-bit byte:
28350         2: {
28351             getValue: function (dataView, dataOffset) {
28352                 return String.fromCharCode(dataView.getUint8(dataOffset));
28353             },
28354             size: 1,
28355             ascii: true
28356         },
28357         // short, 16 bit int:
28358         3: {
28359             getValue: function (dataView, dataOffset, littleEndian) {
28360                 return dataView.getUint16(dataOffset, littleEndian);
28361             },
28362             size: 2
28363         },
28364         // long, 32 bit int:
28365         4: {
28366             getValue: function (dataView, dataOffset, littleEndian) {
28367                 return dataView.getUint32(dataOffset, littleEndian);
28368             },
28369             size: 4
28370         },
28371         // rational = two long values, first is numerator, second is denominator:
28372         5: {
28373             getValue: function (dataView, dataOffset, littleEndian) {
28374                 return dataView.getUint32(dataOffset, littleEndian) /
28375                     dataView.getUint32(dataOffset + 4, littleEndian);
28376             },
28377             size: 8
28378         },
28379         // slong, 32 bit signed int:
28380         9: {
28381             getValue: function (dataView, dataOffset, littleEndian) {
28382                 return dataView.getInt32(dataOffset, littleEndian);
28383             },
28384             size: 4
28385         },
28386         // srational, two slongs, first is numerator, second is denominator:
28387         10: {
28388             getValue: function (dataView, dataOffset, littleEndian) {
28389                 return dataView.getInt32(dataOffset, littleEndian) /
28390                     dataView.getInt32(dataOffset + 4, littleEndian);
28391             },
28392             size: 8
28393         }
28394     },
28395     
28396     footer : {
28397         STANDARD : [
28398             {
28399                 tag : 'div',
28400                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28401                 action : 'rotate-left',
28402                 cn : [
28403                     {
28404                         tag : 'button',
28405                         cls : 'btn btn-default',
28406                         html : '<i class="fa fa-undo"></i>'
28407                     }
28408                 ]
28409             },
28410             {
28411                 tag : 'div',
28412                 cls : 'btn-group roo-upload-cropbox-picture',
28413                 action : 'picture',
28414                 cn : [
28415                     {
28416                         tag : 'button',
28417                         cls : 'btn btn-default',
28418                         html : '<i class="fa fa-picture-o"></i>'
28419                     }
28420                 ]
28421             },
28422             {
28423                 tag : 'div',
28424                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28425                 action : 'rotate-right',
28426                 cn : [
28427                     {
28428                         tag : 'button',
28429                         cls : 'btn btn-default',
28430                         html : '<i class="fa fa-repeat"></i>'
28431                     }
28432                 ]
28433             }
28434         ],
28435         DOCUMENT : [
28436             {
28437                 tag : 'div',
28438                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28439                 action : 'rotate-left',
28440                 cn : [
28441                     {
28442                         tag : 'button',
28443                         cls : 'btn btn-default',
28444                         html : '<i class="fa fa-undo"></i>'
28445                     }
28446                 ]
28447             },
28448             {
28449                 tag : 'div',
28450                 cls : 'btn-group roo-upload-cropbox-download',
28451                 action : 'download',
28452                 cn : [
28453                     {
28454                         tag : 'button',
28455                         cls : 'btn btn-default',
28456                         html : '<i class="fa fa-download"></i>'
28457                     }
28458                 ]
28459             },
28460             {
28461                 tag : 'div',
28462                 cls : 'btn-group roo-upload-cropbox-crop',
28463                 action : 'crop',
28464                 cn : [
28465                     {
28466                         tag : 'button',
28467                         cls : 'btn btn-default',
28468                         html : '<i class="fa fa-crop"></i>'
28469                     }
28470                 ]
28471             },
28472             {
28473                 tag : 'div',
28474                 cls : 'btn-group roo-upload-cropbox-trash',
28475                 action : 'trash',
28476                 cn : [
28477                     {
28478                         tag : 'button',
28479                         cls : 'btn btn-default',
28480                         html : '<i class="fa fa-trash"></i>'
28481                     }
28482                 ]
28483             },
28484             {
28485                 tag : 'div',
28486                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28487                 action : 'rotate-right',
28488                 cn : [
28489                     {
28490                         tag : 'button',
28491                         cls : 'btn btn-default',
28492                         html : '<i class="fa fa-repeat"></i>'
28493                     }
28494                 ]
28495             }
28496         ],
28497         ROTATOR : [
28498             {
28499                 tag : 'div',
28500                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28501                 action : 'rotate-left',
28502                 cn : [
28503                     {
28504                         tag : 'button',
28505                         cls : 'btn btn-default',
28506                         html : '<i class="fa fa-undo"></i>'
28507                     }
28508                 ]
28509             },
28510             {
28511                 tag : 'div',
28512                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28513                 action : 'rotate-right',
28514                 cn : [
28515                     {
28516                         tag : 'button',
28517                         cls : 'btn btn-default',
28518                         html : '<i class="fa fa-repeat"></i>'
28519                     }
28520                 ]
28521             }
28522         ]
28523     }
28524 });
28525
28526 /*
28527 * Licence: LGPL
28528 */
28529
28530 /**
28531  * @class Roo.bootstrap.DocumentManager
28532  * @extends Roo.bootstrap.Component
28533  * Bootstrap DocumentManager class
28534  * @cfg {String} paramName default 'imageUpload'
28535  * @cfg {String} toolTipName default 'filename'
28536  * @cfg {String} method default POST
28537  * @cfg {String} url action url
28538  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28539  * @cfg {Boolean} multiple multiple upload default true
28540  * @cfg {Number} thumbSize default 300
28541  * @cfg {String} fieldLabel
28542  * @cfg {Number} labelWidth default 4
28543  * @cfg {String} labelAlign (left|top) default left
28544  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28545 * @cfg {Number} labellg set the width of label (1-12)
28546  * @cfg {Number} labelmd set the width of label (1-12)
28547  * @cfg {Number} labelsm set the width of label (1-12)
28548  * @cfg {Number} labelxs set the width of label (1-12)
28549  * 
28550  * @constructor
28551  * Create a new DocumentManager
28552  * @param {Object} config The config object
28553  */
28554
28555 Roo.bootstrap.DocumentManager = function(config){
28556     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28557     
28558     this.files = [];
28559     this.delegates = [];
28560     
28561     this.addEvents({
28562         /**
28563          * @event initial
28564          * Fire when initial the DocumentManager
28565          * @param {Roo.bootstrap.DocumentManager} this
28566          */
28567         "initial" : true,
28568         /**
28569          * @event inspect
28570          * inspect selected file
28571          * @param {Roo.bootstrap.DocumentManager} this
28572          * @param {File} file
28573          */
28574         "inspect" : true,
28575         /**
28576          * @event exception
28577          * Fire when xhr load exception
28578          * @param {Roo.bootstrap.DocumentManager} this
28579          * @param {XMLHttpRequest} xhr
28580          */
28581         "exception" : true,
28582         /**
28583          * @event afterupload
28584          * Fire when xhr load exception
28585          * @param {Roo.bootstrap.DocumentManager} this
28586          * @param {XMLHttpRequest} xhr
28587          */
28588         "afterupload" : true,
28589         /**
28590          * @event prepare
28591          * prepare the form data
28592          * @param {Roo.bootstrap.DocumentManager} this
28593          * @param {Object} formData
28594          */
28595         "prepare" : true,
28596         /**
28597          * @event remove
28598          * Fire when remove the file
28599          * @param {Roo.bootstrap.DocumentManager} this
28600          * @param {Object} file
28601          */
28602         "remove" : true,
28603         /**
28604          * @event refresh
28605          * Fire after refresh the file
28606          * @param {Roo.bootstrap.DocumentManager} this
28607          */
28608         "refresh" : true,
28609         /**
28610          * @event click
28611          * Fire after click the image
28612          * @param {Roo.bootstrap.DocumentManager} this
28613          * @param {Object} file
28614          */
28615         "click" : true,
28616         /**
28617          * @event edit
28618          * Fire when upload a image and editable set to true
28619          * @param {Roo.bootstrap.DocumentManager} this
28620          * @param {Object} file
28621          */
28622         "edit" : true,
28623         /**
28624          * @event beforeselectfile
28625          * Fire before select file
28626          * @param {Roo.bootstrap.DocumentManager} this
28627          */
28628         "beforeselectfile" : true,
28629         /**
28630          * @event process
28631          * Fire before process file
28632          * @param {Roo.bootstrap.DocumentManager} this
28633          * @param {Object} file
28634          */
28635         "process" : true,
28636         /**
28637          * @event previewrendered
28638          * Fire when preview rendered
28639          * @param {Roo.bootstrap.DocumentManager} this
28640          * @param {Object} file
28641          */
28642         "previewrendered" : true
28643         
28644     });
28645 };
28646
28647 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28648     
28649     boxes : 0,
28650     inputName : '',
28651     thumbSize : 300,
28652     multiple : true,
28653     files : false,
28654     method : 'POST',
28655     url : '',
28656     paramName : 'imageUpload',
28657     toolTipName : 'filename',
28658     fieldLabel : '',
28659     labelWidth : 4,
28660     labelAlign : 'left',
28661     editable : true,
28662     delegates : false,
28663     xhr : false, 
28664     
28665     labellg : 0,
28666     labelmd : 0,
28667     labelsm : 0,
28668     labelxs : 0,
28669     
28670     getAutoCreate : function()
28671     {   
28672         var managerWidget = {
28673             tag : 'div',
28674             cls : 'roo-document-manager',
28675             cn : [
28676                 {
28677                     tag : 'input',
28678                     cls : 'roo-document-manager-selector',
28679                     type : 'file'
28680                 },
28681                 {
28682                     tag : 'div',
28683                     cls : 'roo-document-manager-uploader',
28684                     cn : [
28685                         {
28686                             tag : 'div',
28687                             cls : 'roo-document-manager-upload-btn',
28688                             html : '<i class="fa fa-plus"></i>'
28689                         }
28690                     ]
28691                     
28692                 }
28693             ]
28694         };
28695         
28696         var content = [
28697             {
28698                 tag : 'div',
28699                 cls : 'column col-md-12',
28700                 cn : managerWidget
28701             }
28702         ];
28703         
28704         if(this.fieldLabel.length){
28705             
28706             content = [
28707                 {
28708                     tag : 'div',
28709                     cls : 'column col-md-12',
28710                     html : this.fieldLabel
28711                 },
28712                 {
28713                     tag : 'div',
28714                     cls : 'column col-md-12',
28715                     cn : managerWidget
28716                 }
28717             ];
28718
28719             if(this.labelAlign == 'left'){
28720                 content = [
28721                     {
28722                         tag : 'div',
28723                         cls : 'column',
28724                         html : this.fieldLabel
28725                     },
28726                     {
28727                         tag : 'div',
28728                         cls : 'column',
28729                         cn : managerWidget
28730                     }
28731                 ];
28732                 
28733                 if(this.labelWidth > 12){
28734                     content[0].style = "width: " + this.labelWidth + 'px';
28735                 }
28736
28737                 if(this.labelWidth < 13 && this.labelmd == 0){
28738                     this.labelmd = this.labelWidth;
28739                 }
28740
28741                 if(this.labellg > 0){
28742                     content[0].cls += ' col-lg-' + this.labellg;
28743                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28744                 }
28745
28746                 if(this.labelmd > 0){
28747                     content[0].cls += ' col-md-' + this.labelmd;
28748                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28749                 }
28750
28751                 if(this.labelsm > 0){
28752                     content[0].cls += ' col-sm-' + this.labelsm;
28753                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28754                 }
28755
28756                 if(this.labelxs > 0){
28757                     content[0].cls += ' col-xs-' + this.labelxs;
28758                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28759                 }
28760                 
28761             }
28762         }
28763         
28764         var cfg = {
28765             tag : 'div',
28766             cls : 'row clearfix',
28767             cn : content
28768         };
28769         
28770         return cfg;
28771         
28772     },
28773     
28774     initEvents : function()
28775     {
28776         this.managerEl = this.el.select('.roo-document-manager', true).first();
28777         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28778         
28779         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28780         this.selectorEl.hide();
28781         
28782         if(this.multiple){
28783             this.selectorEl.attr('multiple', 'multiple');
28784         }
28785         
28786         this.selectorEl.on('change', this.onFileSelected, this);
28787         
28788         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28789         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28790         
28791         this.uploader.on('click', this.onUploaderClick, this);
28792         
28793         this.renderProgressDialog();
28794         
28795         var _this = this;
28796         
28797         window.addEventListener("resize", function() { _this.refresh(); } );
28798         
28799         this.fireEvent('initial', this);
28800     },
28801     
28802     renderProgressDialog : function()
28803     {
28804         var _this = this;
28805         
28806         this.progressDialog = new Roo.bootstrap.Modal({
28807             cls : 'roo-document-manager-progress-dialog',
28808             allow_close : false,
28809             title : '',
28810             buttons : [
28811                 {
28812                     name  :'cancel',
28813                     weight : 'danger',
28814                     html : 'Cancel'
28815                 }
28816             ], 
28817             listeners : { 
28818                 btnclick : function() {
28819                     _this.uploadCancel();
28820                     this.hide();
28821                 }
28822             }
28823         });
28824          
28825         this.progressDialog.render(Roo.get(document.body));
28826          
28827         this.progress = new Roo.bootstrap.Progress({
28828             cls : 'roo-document-manager-progress',
28829             active : true,
28830             striped : true
28831         });
28832         
28833         this.progress.render(this.progressDialog.getChildContainer());
28834         
28835         this.progressBar = new Roo.bootstrap.ProgressBar({
28836             cls : 'roo-document-manager-progress-bar',
28837             aria_valuenow : 0,
28838             aria_valuemin : 0,
28839             aria_valuemax : 12,
28840             panel : 'success'
28841         });
28842         
28843         this.progressBar.render(this.progress.getChildContainer());
28844     },
28845     
28846     onUploaderClick : function(e)
28847     {
28848         e.preventDefault();
28849      
28850         if(this.fireEvent('beforeselectfile', this) != false){
28851             this.selectorEl.dom.click();
28852         }
28853         
28854     },
28855     
28856     onFileSelected : function(e)
28857     {
28858         e.preventDefault();
28859         
28860         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28861             return;
28862         }
28863         
28864         Roo.each(this.selectorEl.dom.files, function(file){
28865             if(this.fireEvent('inspect', this, file) != false){
28866                 this.files.push(file);
28867             }
28868         }, this);
28869         
28870         this.queue();
28871         
28872     },
28873     
28874     queue : function()
28875     {
28876         this.selectorEl.dom.value = '';
28877         
28878         if(!this.files || !this.files.length){
28879             return;
28880         }
28881         
28882         if(this.boxes > 0 && this.files.length > this.boxes){
28883             this.files = this.files.slice(0, this.boxes);
28884         }
28885         
28886         this.uploader.show();
28887         
28888         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28889             this.uploader.hide();
28890         }
28891         
28892         var _this = this;
28893         
28894         var files = [];
28895         
28896         var docs = [];
28897         
28898         Roo.each(this.files, function(file){
28899             
28900             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28901                 var f = this.renderPreview(file);
28902                 files.push(f);
28903                 return;
28904             }
28905             
28906             if(file.type.indexOf('image') != -1){
28907                 this.delegates.push(
28908                     (function(){
28909                         _this.process(file);
28910                     }).createDelegate(this)
28911                 );
28912         
28913                 return;
28914             }
28915             
28916             docs.push(
28917                 (function(){
28918                     _this.process(file);
28919                 }).createDelegate(this)
28920             );
28921             
28922         }, this);
28923         
28924         this.files = files;
28925         
28926         this.delegates = this.delegates.concat(docs);
28927         
28928         if(!this.delegates.length){
28929             this.refresh();
28930             return;
28931         }
28932         
28933         this.progressBar.aria_valuemax = this.delegates.length;
28934         
28935         this.arrange();
28936         
28937         return;
28938     },
28939     
28940     arrange : function()
28941     {
28942         if(!this.delegates.length){
28943             this.progressDialog.hide();
28944             this.refresh();
28945             return;
28946         }
28947         
28948         var delegate = this.delegates.shift();
28949         
28950         this.progressDialog.show();
28951         
28952         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
28953         
28954         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
28955         
28956         delegate();
28957     },
28958     
28959     refresh : function()
28960     {
28961         this.uploader.show();
28962         
28963         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28964             this.uploader.hide();
28965         }
28966         
28967         Roo.isTouch ? this.closable(false) : this.closable(true);
28968         
28969         this.fireEvent('refresh', this);
28970     },
28971     
28972     onRemove : function(e, el, o)
28973     {
28974         e.preventDefault();
28975         
28976         this.fireEvent('remove', this, o);
28977         
28978     },
28979     
28980     remove : function(o)
28981     {
28982         var files = [];
28983         
28984         Roo.each(this.files, function(file){
28985             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
28986                 files.push(file);
28987                 return;
28988             }
28989
28990             o.target.remove();
28991
28992         }, this);
28993         
28994         this.files = files;
28995         
28996         this.refresh();
28997     },
28998     
28999     clear : function()
29000     {
29001         Roo.each(this.files, function(file){
29002             if(!file.target){
29003                 return;
29004             }
29005             
29006             file.target.remove();
29007
29008         }, this);
29009         
29010         this.files = [];
29011         
29012         this.refresh();
29013     },
29014     
29015     onClick : function(e, el, o)
29016     {
29017         e.preventDefault();
29018         
29019         this.fireEvent('click', this, o);
29020         
29021     },
29022     
29023     closable : function(closable)
29024     {
29025         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29026             
29027             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29028             
29029             if(closable){
29030                 el.show();
29031                 return;
29032             }
29033             
29034             el.hide();
29035             
29036         }, this);
29037     },
29038     
29039     xhrOnLoad : function(xhr)
29040     {
29041         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29042             el.remove();
29043         }, this);
29044         
29045         if (xhr.readyState !== 4) {
29046             this.arrange();
29047             this.fireEvent('exception', this, xhr);
29048             return;
29049         }
29050
29051         var response = Roo.decode(xhr.responseText);
29052         
29053         if(!response.success){
29054             this.arrange();
29055             this.fireEvent('exception', this, xhr);
29056             return;
29057         }
29058         
29059         var file = this.renderPreview(response.data);
29060         
29061         this.files.push(file);
29062         
29063         this.arrange();
29064         
29065         this.fireEvent('afterupload', this, xhr);
29066         
29067     },
29068     
29069     xhrOnError : function(xhr)
29070     {
29071         Roo.log('xhr on error');
29072         
29073         var response = Roo.decode(xhr.responseText);
29074           
29075         Roo.log(response);
29076         
29077         this.arrange();
29078     },
29079     
29080     process : function(file)
29081     {
29082         if(this.fireEvent('process', this, file) !== false){
29083             if(this.editable && file.type.indexOf('image') != -1){
29084                 this.fireEvent('edit', this, file);
29085                 return;
29086             }
29087
29088             this.uploadStart(file, false);
29089
29090             return;
29091         }
29092         
29093     },
29094     
29095     uploadStart : function(file, crop)
29096     {
29097         this.xhr = new XMLHttpRequest();
29098         
29099         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29100             this.arrange();
29101             return;
29102         }
29103         
29104         file.xhr = this.xhr;
29105             
29106         this.managerEl.createChild({
29107             tag : 'div',
29108             cls : 'roo-document-manager-loading',
29109             cn : [
29110                 {
29111                     tag : 'div',
29112                     tooltip : file.name,
29113                     cls : 'roo-document-manager-thumb',
29114                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29115                 }
29116             ]
29117
29118         });
29119
29120         this.xhr.open(this.method, this.url, true);
29121         
29122         var headers = {
29123             "Accept": "application/json",
29124             "Cache-Control": "no-cache",
29125             "X-Requested-With": "XMLHttpRequest"
29126         };
29127         
29128         for (var headerName in headers) {
29129             var headerValue = headers[headerName];
29130             if (headerValue) {
29131                 this.xhr.setRequestHeader(headerName, headerValue);
29132             }
29133         }
29134         
29135         var _this = this;
29136         
29137         this.xhr.onload = function()
29138         {
29139             _this.xhrOnLoad(_this.xhr);
29140         }
29141         
29142         this.xhr.onerror = function()
29143         {
29144             _this.xhrOnError(_this.xhr);
29145         }
29146         
29147         var formData = new FormData();
29148
29149         formData.append('returnHTML', 'NO');
29150         
29151         if(crop){
29152             formData.append('crop', crop);
29153         }
29154         
29155         formData.append(this.paramName, file, file.name);
29156         
29157         var options = {
29158             file : file, 
29159             manually : false
29160         };
29161         
29162         if(this.fireEvent('prepare', this, formData, options) != false){
29163             
29164             if(options.manually){
29165                 return;
29166             }
29167             
29168             this.xhr.send(formData);
29169             return;
29170         };
29171         
29172         this.uploadCancel();
29173     },
29174     
29175     uploadCancel : function()
29176     {
29177         if (this.xhr) {
29178             this.xhr.abort();
29179         }
29180         
29181         this.delegates = [];
29182         
29183         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29184             el.remove();
29185         }, this);
29186         
29187         this.arrange();
29188     },
29189     
29190     renderPreview : function(file)
29191     {
29192         if(typeof(file.target) != 'undefined' && file.target){
29193             return file;
29194         }
29195         
29196         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29197         
29198         var previewEl = this.managerEl.createChild({
29199             tag : 'div',
29200             cls : 'roo-document-manager-preview',
29201             cn : [
29202                 {
29203                     tag : 'div',
29204                     tooltip : file[this.toolTipName],
29205                     cls : 'roo-document-manager-thumb',
29206                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29207                 },
29208                 {
29209                     tag : 'button',
29210                     cls : 'close',
29211                     html : '<i class="fa fa-times-circle"></i>'
29212                 }
29213             ]
29214         });
29215
29216         var close = previewEl.select('button.close', true).first();
29217
29218         close.on('click', this.onRemove, this, file);
29219
29220         file.target = previewEl;
29221
29222         var image = previewEl.select('img', true).first();
29223         
29224         var _this = this;
29225         
29226         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29227         
29228         image.on('click', this.onClick, this, file);
29229         
29230         this.fireEvent('previewrendered', this, file);
29231         
29232         return file;
29233         
29234     },
29235     
29236     onPreviewLoad : function(file, image)
29237     {
29238         if(typeof(file.target) == 'undefined' || !file.target){
29239             return;
29240         }
29241         
29242         var width = image.dom.naturalWidth || image.dom.width;
29243         var height = image.dom.naturalHeight || image.dom.height;
29244         
29245         if(width > height){
29246             file.target.addClass('wide');
29247             return;
29248         }
29249         
29250         file.target.addClass('tall');
29251         return;
29252         
29253     },
29254     
29255     uploadFromSource : function(file, crop)
29256     {
29257         this.xhr = new XMLHttpRequest();
29258         
29259         this.managerEl.createChild({
29260             tag : 'div',
29261             cls : 'roo-document-manager-loading',
29262             cn : [
29263                 {
29264                     tag : 'div',
29265                     tooltip : file.name,
29266                     cls : 'roo-document-manager-thumb',
29267                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29268                 }
29269             ]
29270
29271         });
29272
29273         this.xhr.open(this.method, this.url, true);
29274         
29275         var headers = {
29276             "Accept": "application/json",
29277             "Cache-Control": "no-cache",
29278             "X-Requested-With": "XMLHttpRequest"
29279         };
29280         
29281         for (var headerName in headers) {
29282             var headerValue = headers[headerName];
29283             if (headerValue) {
29284                 this.xhr.setRequestHeader(headerName, headerValue);
29285             }
29286         }
29287         
29288         var _this = this;
29289         
29290         this.xhr.onload = function()
29291         {
29292             _this.xhrOnLoad(_this.xhr);
29293         }
29294         
29295         this.xhr.onerror = function()
29296         {
29297             _this.xhrOnError(_this.xhr);
29298         }
29299         
29300         var formData = new FormData();
29301
29302         formData.append('returnHTML', 'NO');
29303         
29304         formData.append('crop', crop);
29305         
29306         if(typeof(file.filename) != 'undefined'){
29307             formData.append('filename', file.filename);
29308         }
29309         
29310         if(typeof(file.mimetype) != 'undefined'){
29311             formData.append('mimetype', file.mimetype);
29312         }
29313         
29314         Roo.log(formData);
29315         
29316         if(this.fireEvent('prepare', this, formData) != false){
29317             this.xhr.send(formData);
29318         };
29319     }
29320 });
29321
29322 /*
29323 * Licence: LGPL
29324 */
29325
29326 /**
29327  * @class Roo.bootstrap.DocumentViewer
29328  * @extends Roo.bootstrap.Component
29329  * Bootstrap DocumentViewer class
29330  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29331  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29332  * 
29333  * @constructor
29334  * Create a new DocumentViewer
29335  * @param {Object} config The config object
29336  */
29337
29338 Roo.bootstrap.DocumentViewer = function(config){
29339     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29340     
29341     this.addEvents({
29342         /**
29343          * @event initial
29344          * Fire after initEvent
29345          * @param {Roo.bootstrap.DocumentViewer} this
29346          */
29347         "initial" : true,
29348         /**
29349          * @event click
29350          * Fire after click
29351          * @param {Roo.bootstrap.DocumentViewer} this
29352          */
29353         "click" : true,
29354         /**
29355          * @event download
29356          * Fire after download button
29357          * @param {Roo.bootstrap.DocumentViewer} this
29358          */
29359         "download" : true,
29360         /**
29361          * @event trash
29362          * Fire after trash button
29363          * @param {Roo.bootstrap.DocumentViewer} this
29364          */
29365         "trash" : true
29366         
29367     });
29368 };
29369
29370 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29371     
29372     showDownload : true,
29373     
29374     showTrash : true,
29375     
29376     getAutoCreate : function()
29377     {
29378         var cfg = {
29379             tag : 'div',
29380             cls : 'roo-document-viewer',
29381             cn : [
29382                 {
29383                     tag : 'div',
29384                     cls : 'roo-document-viewer-body',
29385                     cn : [
29386                         {
29387                             tag : 'div',
29388                             cls : 'roo-document-viewer-thumb',
29389                             cn : [
29390                                 {
29391                                     tag : 'img',
29392                                     cls : 'roo-document-viewer-image'
29393                                 }
29394                             ]
29395                         }
29396                     ]
29397                 },
29398                 {
29399                     tag : 'div',
29400                     cls : 'roo-document-viewer-footer',
29401                     cn : {
29402                         tag : 'div',
29403                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29404                         cn : [
29405                             {
29406                                 tag : 'div',
29407                                 cls : 'btn-group roo-document-viewer-download',
29408                                 cn : [
29409                                     {
29410                                         tag : 'button',
29411                                         cls : 'btn btn-default',
29412                                         html : '<i class="fa fa-download"></i>'
29413                                     }
29414                                 ]
29415                             },
29416                             {
29417                                 tag : 'div',
29418                                 cls : 'btn-group roo-document-viewer-trash',
29419                                 cn : [
29420                                     {
29421                                         tag : 'button',
29422                                         cls : 'btn btn-default',
29423                                         html : '<i class="fa fa-trash"></i>'
29424                                     }
29425                                 ]
29426                             }
29427                         ]
29428                     }
29429                 }
29430             ]
29431         };
29432         
29433         return cfg;
29434     },
29435     
29436     initEvents : function()
29437     {
29438         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29439         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29440         
29441         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29442         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29443         
29444         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29445         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29446         
29447         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29448         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29449         
29450         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29451         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29452         
29453         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29454         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29455         
29456         this.bodyEl.on('click', this.onClick, this);
29457         this.downloadBtn.on('click', this.onDownload, this);
29458         this.trashBtn.on('click', this.onTrash, this);
29459         
29460         this.downloadBtn.hide();
29461         this.trashBtn.hide();
29462         
29463         if(this.showDownload){
29464             this.downloadBtn.show();
29465         }
29466         
29467         if(this.showTrash){
29468             this.trashBtn.show();
29469         }
29470         
29471         if(!this.showDownload && !this.showTrash) {
29472             this.footerEl.hide();
29473         }
29474         
29475     },
29476     
29477     initial : function()
29478     {
29479         this.fireEvent('initial', this);
29480         
29481     },
29482     
29483     onClick : function(e)
29484     {
29485         e.preventDefault();
29486         
29487         this.fireEvent('click', this);
29488     },
29489     
29490     onDownload : function(e)
29491     {
29492         e.preventDefault();
29493         
29494         this.fireEvent('download', this);
29495     },
29496     
29497     onTrash : function(e)
29498     {
29499         e.preventDefault();
29500         
29501         this.fireEvent('trash', this);
29502     }
29503     
29504 });
29505 /*
29506  * - LGPL
29507  *
29508  * nav progress bar
29509  * 
29510  */
29511
29512 /**
29513  * @class Roo.bootstrap.NavProgressBar
29514  * @extends Roo.bootstrap.Component
29515  * Bootstrap NavProgressBar class
29516  * 
29517  * @constructor
29518  * Create a new nav progress bar
29519  * @param {Object} config The config object
29520  */
29521
29522 Roo.bootstrap.NavProgressBar = function(config){
29523     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29524
29525     this.bullets = this.bullets || [];
29526    
29527 //    Roo.bootstrap.NavProgressBar.register(this);
29528      this.addEvents({
29529         /**
29530              * @event changed
29531              * Fires when the active item changes
29532              * @param {Roo.bootstrap.NavProgressBar} this
29533              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29534              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29535          */
29536         'changed': true
29537      });
29538     
29539 };
29540
29541 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29542     
29543     bullets : [],
29544     barItems : [],
29545     
29546     getAutoCreate : function()
29547     {
29548         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29549         
29550         cfg = {
29551             tag : 'div',
29552             cls : 'roo-navigation-bar-group',
29553             cn : [
29554                 {
29555                     tag : 'div',
29556                     cls : 'roo-navigation-top-bar'
29557                 },
29558                 {
29559                     tag : 'div',
29560                     cls : 'roo-navigation-bullets-bar',
29561                     cn : [
29562                         {
29563                             tag : 'ul',
29564                             cls : 'roo-navigation-bar'
29565                         }
29566                     ]
29567                 },
29568                 
29569                 {
29570                     tag : 'div',
29571                     cls : 'roo-navigation-bottom-bar'
29572                 }
29573             ]
29574             
29575         };
29576         
29577         return cfg;
29578         
29579     },
29580     
29581     initEvents: function() 
29582     {
29583         
29584     },
29585     
29586     onRender : function(ct, position) 
29587     {
29588         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29589         
29590         if(this.bullets.length){
29591             Roo.each(this.bullets, function(b){
29592                this.addItem(b);
29593             }, this);
29594         }
29595         
29596         this.format();
29597         
29598     },
29599     
29600     addItem : function(cfg)
29601     {
29602         var item = new Roo.bootstrap.NavProgressItem(cfg);
29603         
29604         item.parentId = this.id;
29605         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29606         
29607         if(cfg.html){
29608             var top = new Roo.bootstrap.Element({
29609                 tag : 'div',
29610                 cls : 'roo-navigation-bar-text'
29611             });
29612             
29613             var bottom = new Roo.bootstrap.Element({
29614                 tag : 'div',
29615                 cls : 'roo-navigation-bar-text'
29616             });
29617             
29618             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29619             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29620             
29621             var topText = new Roo.bootstrap.Element({
29622                 tag : 'span',
29623                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29624             });
29625             
29626             var bottomText = new Roo.bootstrap.Element({
29627                 tag : 'span',
29628                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29629             });
29630             
29631             topText.onRender(top.el, null);
29632             bottomText.onRender(bottom.el, null);
29633             
29634             item.topEl = top;
29635             item.bottomEl = bottom;
29636         }
29637         
29638         this.barItems.push(item);
29639         
29640         return item;
29641     },
29642     
29643     getActive : function()
29644     {
29645         var active = false;
29646         
29647         Roo.each(this.barItems, function(v){
29648             
29649             if (!v.isActive()) {
29650                 return;
29651             }
29652             
29653             active = v;
29654             return false;
29655             
29656         });
29657         
29658         return active;
29659     },
29660     
29661     setActiveItem : function(item)
29662     {
29663         var prev = false;
29664         
29665         Roo.each(this.barItems, function(v){
29666             if (v.rid == item.rid) {
29667                 return ;
29668             }
29669             
29670             if (v.isActive()) {
29671                 v.setActive(false);
29672                 prev = v;
29673             }
29674         });
29675
29676         item.setActive(true);
29677         
29678         this.fireEvent('changed', this, item, prev);
29679     },
29680     
29681     getBarItem: function(rid)
29682     {
29683         var ret = false;
29684         
29685         Roo.each(this.barItems, function(e) {
29686             if (e.rid != rid) {
29687                 return;
29688             }
29689             
29690             ret =  e;
29691             return false;
29692         });
29693         
29694         return ret;
29695     },
29696     
29697     indexOfItem : function(item)
29698     {
29699         var index = false;
29700         
29701         Roo.each(this.barItems, function(v, i){
29702             
29703             if (v.rid != item.rid) {
29704                 return;
29705             }
29706             
29707             index = i;
29708             return false
29709         });
29710         
29711         return index;
29712     },
29713     
29714     setActiveNext : function()
29715     {
29716         var i = this.indexOfItem(this.getActive());
29717         
29718         if (i > this.barItems.length) {
29719             return;
29720         }
29721         
29722         this.setActiveItem(this.barItems[i+1]);
29723     },
29724     
29725     setActivePrev : function()
29726     {
29727         var i = this.indexOfItem(this.getActive());
29728         
29729         if (i  < 1) {
29730             return;
29731         }
29732         
29733         this.setActiveItem(this.barItems[i-1]);
29734     },
29735     
29736     format : function()
29737     {
29738         if(!this.barItems.length){
29739             return;
29740         }
29741      
29742         var width = 100 / this.barItems.length;
29743         
29744         Roo.each(this.barItems, function(i){
29745             i.el.setStyle('width', width + '%');
29746             i.topEl.el.setStyle('width', width + '%');
29747             i.bottomEl.el.setStyle('width', width + '%');
29748         }, this);
29749         
29750     }
29751     
29752 });
29753 /*
29754  * - LGPL
29755  *
29756  * Nav Progress Item
29757  * 
29758  */
29759
29760 /**
29761  * @class Roo.bootstrap.NavProgressItem
29762  * @extends Roo.bootstrap.Component
29763  * Bootstrap NavProgressItem class
29764  * @cfg {String} rid the reference id
29765  * @cfg {Boolean} active (true|false) Is item active default false
29766  * @cfg {Boolean} disabled (true|false) Is item active default false
29767  * @cfg {String} html
29768  * @cfg {String} position (top|bottom) text position default bottom
29769  * @cfg {String} icon show icon instead of number
29770  * 
29771  * @constructor
29772  * Create a new NavProgressItem
29773  * @param {Object} config The config object
29774  */
29775 Roo.bootstrap.NavProgressItem = function(config){
29776     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29777     this.addEvents({
29778         // raw events
29779         /**
29780          * @event click
29781          * The raw click event for the entire grid.
29782          * @param {Roo.bootstrap.NavProgressItem} this
29783          * @param {Roo.EventObject} e
29784          */
29785         "click" : true
29786     });
29787    
29788 };
29789
29790 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29791     
29792     rid : '',
29793     active : false,
29794     disabled : false,
29795     html : '',
29796     position : 'bottom',
29797     icon : false,
29798     
29799     getAutoCreate : function()
29800     {
29801         var iconCls = 'roo-navigation-bar-item-icon';
29802         
29803         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29804         
29805         var cfg = {
29806             tag: 'li',
29807             cls: 'roo-navigation-bar-item',
29808             cn : [
29809                 {
29810                     tag : 'i',
29811                     cls : iconCls
29812                 }
29813             ]
29814         };
29815         
29816         if(this.active){
29817             cfg.cls += ' active';
29818         }
29819         if(this.disabled){
29820             cfg.cls += ' disabled';
29821         }
29822         
29823         return cfg;
29824     },
29825     
29826     disable : function()
29827     {
29828         this.setDisabled(true);
29829     },
29830     
29831     enable : function()
29832     {
29833         this.setDisabled(false);
29834     },
29835     
29836     initEvents: function() 
29837     {
29838         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29839         
29840         this.iconEl.on('click', this.onClick, this);
29841     },
29842     
29843     onClick : function(e)
29844     {
29845         e.preventDefault();
29846         
29847         if(this.disabled){
29848             return;
29849         }
29850         
29851         if(this.fireEvent('click', this, e) === false){
29852             return;
29853         };
29854         
29855         this.parent().setActiveItem(this);
29856     },
29857     
29858     isActive: function () 
29859     {
29860         return this.active;
29861     },
29862     
29863     setActive : function(state)
29864     {
29865         if(this.active == state){
29866             return;
29867         }
29868         
29869         this.active = state;
29870         
29871         if (state) {
29872             this.el.addClass('active');
29873             return;
29874         }
29875         
29876         this.el.removeClass('active');
29877         
29878         return;
29879     },
29880     
29881     setDisabled : function(state)
29882     {
29883         if(this.disabled == state){
29884             return;
29885         }
29886         
29887         this.disabled = state;
29888         
29889         if (state) {
29890             this.el.addClass('disabled');
29891             return;
29892         }
29893         
29894         this.el.removeClass('disabled');
29895     },
29896     
29897     tooltipEl : function()
29898     {
29899         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29900     }
29901 });
29902  
29903
29904  /*
29905  * - LGPL
29906  *
29907  * FieldLabel
29908  * 
29909  */
29910
29911 /**
29912  * @class Roo.bootstrap.FieldLabel
29913  * @extends Roo.bootstrap.Component
29914  * Bootstrap FieldLabel class
29915  * @cfg {String} html contents of the element
29916  * @cfg {String} tag tag of the element default label
29917  * @cfg {String} cls class of the element
29918  * @cfg {String} target label target 
29919  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29920  * @cfg {String} invalidClass default "text-warning"
29921  * @cfg {String} validClass default "text-success"
29922  * @cfg {String} iconTooltip default "This field is required"
29923  * @cfg {String} indicatorpos (left|right) default left
29924  * 
29925  * @constructor
29926  * Create a new FieldLabel
29927  * @param {Object} config The config object
29928  */
29929
29930 Roo.bootstrap.FieldLabel = function(config){
29931     Roo.bootstrap.Element.superclass.constructor.call(this, config);
29932     
29933     this.addEvents({
29934             /**
29935              * @event invalid
29936              * Fires after the field has been marked as invalid.
29937              * @param {Roo.form.FieldLabel} this
29938              * @param {String} msg The validation message
29939              */
29940             invalid : true,
29941             /**
29942              * @event valid
29943              * Fires after the field has been validated with no errors.
29944              * @param {Roo.form.FieldLabel} this
29945              */
29946             valid : true
29947         });
29948 };
29949
29950 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
29951     
29952     tag: 'label',
29953     cls: '',
29954     html: '',
29955     target: '',
29956     allowBlank : true,
29957     invalidClass : 'has-warning',
29958     validClass : 'has-success',
29959     iconTooltip : 'This field is required',
29960     indicatorpos : 'left',
29961     
29962     getAutoCreate : function(){
29963         
29964         var cfg = {
29965             tag : this.tag,
29966             cls : 'roo-bootstrap-field-label ' + this.cls,
29967             for : this.target,
29968             cn : [
29969                 {
29970                     tag : 'i',
29971                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
29972                     tooltip : this.iconTooltip
29973                 },
29974                 {
29975                     tag : 'span',
29976                     html : this.html
29977                 }
29978             ] 
29979         };
29980         
29981         if(this.indicatorpos == 'right'){
29982             var cfg = {
29983                 tag : this.tag,
29984                 cls : 'roo-bootstrap-field-label ' + this.cls,
29985                 for : this.target,
29986                 cn : [
29987                     {
29988                         tag : 'span',
29989                         html : this.html
29990                     },
29991                     {
29992                         tag : 'i',
29993                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
29994                         tooltip : this.iconTooltip
29995                     }
29996                 ] 
29997             };
29998         }
29999         
30000         return cfg;
30001     },
30002     
30003     initEvents: function() 
30004     {
30005         Roo.bootstrap.Element.superclass.initEvents.call(this);
30006         
30007         this.indicator = this.indicatorEl();
30008         
30009         if(this.indicator){
30010             this.indicator.removeClass('visible');
30011             this.indicator.addClass('invisible');
30012         }
30013         
30014         Roo.bootstrap.FieldLabel.register(this);
30015     },
30016     
30017     indicatorEl : function()
30018     {
30019         var indicator = this.el.select('i.roo-required-indicator',true).first();
30020         
30021         if(!indicator){
30022             return false;
30023         }
30024         
30025         return indicator;
30026         
30027     },
30028     
30029     /**
30030      * Mark this field as valid
30031      */
30032     markValid : function()
30033     {
30034         if(this.indicator){
30035             this.indicator.removeClass('visible');
30036             this.indicator.addClass('invisible');
30037         }
30038         
30039         this.el.removeClass(this.invalidClass);
30040         
30041         this.el.addClass(this.validClass);
30042         
30043         this.fireEvent('valid', this);
30044     },
30045     
30046     /**
30047      * Mark this field as invalid
30048      * @param {String} msg The validation message
30049      */
30050     markInvalid : function(msg)
30051     {
30052         if(this.indicator){
30053             this.indicator.removeClass('invisible');
30054             this.indicator.addClass('visible');
30055         }
30056         
30057         this.el.removeClass(this.validClass);
30058         
30059         this.el.addClass(this.invalidClass);
30060         
30061         this.fireEvent('invalid', this, msg);
30062     }
30063     
30064    
30065 });
30066
30067 Roo.apply(Roo.bootstrap.FieldLabel, {
30068     
30069     groups: {},
30070     
30071      /**
30072     * register a FieldLabel Group
30073     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30074     */
30075     register : function(label)
30076     {
30077         if(this.groups.hasOwnProperty(label.target)){
30078             return;
30079         }
30080      
30081         this.groups[label.target] = label;
30082         
30083     },
30084     /**
30085     * fetch a FieldLabel Group based on the target
30086     * @param {string} target
30087     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30088     */
30089     get: function(target) {
30090         if (typeof(this.groups[target]) == 'undefined') {
30091             return false;
30092         }
30093         
30094         return this.groups[target] ;
30095     }
30096 });
30097
30098  
30099
30100  /*
30101  * - LGPL
30102  *
30103  * page DateSplitField.
30104  * 
30105  */
30106
30107
30108 /**
30109  * @class Roo.bootstrap.DateSplitField
30110  * @extends Roo.bootstrap.Component
30111  * Bootstrap DateSplitField class
30112  * @cfg {string} fieldLabel - the label associated
30113  * @cfg {Number} labelWidth set the width of label (0-12)
30114  * @cfg {String} labelAlign (top|left)
30115  * @cfg {Boolean} dayAllowBlank (true|false) default false
30116  * @cfg {Boolean} monthAllowBlank (true|false) default false
30117  * @cfg {Boolean} yearAllowBlank (true|false) default false
30118  * @cfg {string} dayPlaceholder 
30119  * @cfg {string} monthPlaceholder
30120  * @cfg {string} yearPlaceholder
30121  * @cfg {string} dayFormat default 'd'
30122  * @cfg {string} monthFormat default 'm'
30123  * @cfg {string} yearFormat default 'Y'
30124  * @cfg {Number} labellg set the width of label (1-12)
30125  * @cfg {Number} labelmd set the width of label (1-12)
30126  * @cfg {Number} labelsm set the width of label (1-12)
30127  * @cfg {Number} labelxs set the width of label (1-12)
30128
30129  *     
30130  * @constructor
30131  * Create a new DateSplitField
30132  * @param {Object} config The config object
30133  */
30134
30135 Roo.bootstrap.DateSplitField = function(config){
30136     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30137     
30138     this.addEvents({
30139         // raw events
30140          /**
30141          * @event years
30142          * getting the data of years
30143          * @param {Roo.bootstrap.DateSplitField} this
30144          * @param {Object} years
30145          */
30146         "years" : true,
30147         /**
30148          * @event days
30149          * getting the data of days
30150          * @param {Roo.bootstrap.DateSplitField} this
30151          * @param {Object} days
30152          */
30153         "days" : true,
30154         /**
30155          * @event invalid
30156          * Fires after the field has been marked as invalid.
30157          * @param {Roo.form.Field} this
30158          * @param {String} msg The validation message
30159          */
30160         invalid : true,
30161        /**
30162          * @event valid
30163          * Fires after the field has been validated with no errors.
30164          * @param {Roo.form.Field} this
30165          */
30166         valid : true
30167     });
30168 };
30169
30170 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30171     
30172     fieldLabel : '',
30173     labelAlign : 'top',
30174     labelWidth : 3,
30175     dayAllowBlank : false,
30176     monthAllowBlank : false,
30177     yearAllowBlank : false,
30178     dayPlaceholder : '',
30179     monthPlaceholder : '',
30180     yearPlaceholder : '',
30181     dayFormat : 'd',
30182     monthFormat : 'm',
30183     yearFormat : 'Y',
30184     isFormField : true,
30185     labellg : 0,
30186     labelmd : 0,
30187     labelsm : 0,
30188     labelxs : 0,
30189     
30190     getAutoCreate : function()
30191     {
30192         var cfg = {
30193             tag : 'div',
30194             cls : 'row roo-date-split-field-group',
30195             cn : [
30196                 {
30197                     tag : 'input',
30198                     type : 'hidden',
30199                     cls : 'form-hidden-field roo-date-split-field-group-value',
30200                     name : this.name
30201                 }
30202             ]
30203         };
30204         
30205         var labelCls = 'col-md-12';
30206         var contentCls = 'col-md-4';
30207         
30208         if(this.fieldLabel){
30209             
30210             var label = {
30211                 tag : 'div',
30212                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30213                 cn : [
30214                     {
30215                         tag : 'label',
30216                         html : this.fieldLabel
30217                     }
30218                 ]
30219             };
30220             
30221             if(this.labelAlign == 'left'){
30222             
30223                 if(this.labelWidth > 12){
30224                     label.style = "width: " + this.labelWidth + 'px';
30225                 }
30226
30227                 if(this.labelWidth < 13 && this.labelmd == 0){
30228                     this.labelmd = this.labelWidth;
30229                 }
30230
30231                 if(this.labellg > 0){
30232                     labelCls = ' col-lg-' + this.labellg;
30233                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30234                 }
30235
30236                 if(this.labelmd > 0){
30237                     labelCls = ' col-md-' + this.labelmd;
30238                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30239                 }
30240
30241                 if(this.labelsm > 0){
30242                     labelCls = ' col-sm-' + this.labelsm;
30243                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30244                 }
30245
30246                 if(this.labelxs > 0){
30247                     labelCls = ' col-xs-' + this.labelxs;
30248                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30249                 }
30250             }
30251             
30252             label.cls += ' ' + labelCls;
30253             
30254             cfg.cn.push(label);
30255         }
30256         
30257         Roo.each(['day', 'month', 'year'], function(t){
30258             cfg.cn.push({
30259                 tag : 'div',
30260                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30261             });
30262         }, this);
30263         
30264         return cfg;
30265     },
30266     
30267     inputEl: function ()
30268     {
30269         return this.el.select('.roo-date-split-field-group-value', true).first();
30270     },
30271     
30272     onRender : function(ct, position) 
30273     {
30274         var _this = this;
30275         
30276         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30277         
30278         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30279         
30280         this.dayField = new Roo.bootstrap.ComboBox({
30281             allowBlank : this.dayAllowBlank,
30282             alwaysQuery : true,
30283             displayField : 'value',
30284             editable : false,
30285             fieldLabel : '',
30286             forceSelection : true,
30287             mode : 'local',
30288             placeholder : this.dayPlaceholder,
30289             selectOnFocus : true,
30290             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30291             triggerAction : 'all',
30292             typeAhead : true,
30293             valueField : 'value',
30294             store : new Roo.data.SimpleStore({
30295                 data : (function() {    
30296                     var days = [];
30297                     _this.fireEvent('days', _this, days);
30298                     return days;
30299                 })(),
30300                 fields : [ 'value' ]
30301             }),
30302             listeners : {
30303                 select : function (_self, record, index)
30304                 {
30305                     _this.setValue(_this.getValue());
30306                 }
30307             }
30308         });
30309
30310         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30311         
30312         this.monthField = new Roo.bootstrap.MonthField({
30313             after : '<i class=\"fa fa-calendar\"></i>',
30314             allowBlank : this.monthAllowBlank,
30315             placeholder : this.monthPlaceholder,
30316             readOnly : true,
30317             listeners : {
30318                 render : function (_self)
30319                 {
30320                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30321                         e.preventDefault();
30322                         _self.focus();
30323                     });
30324                 },
30325                 select : function (_self, oldvalue, newvalue)
30326                 {
30327                     _this.setValue(_this.getValue());
30328                 }
30329             }
30330         });
30331         
30332         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30333         
30334         this.yearField = new Roo.bootstrap.ComboBox({
30335             allowBlank : this.yearAllowBlank,
30336             alwaysQuery : true,
30337             displayField : 'value',
30338             editable : false,
30339             fieldLabel : '',
30340             forceSelection : true,
30341             mode : 'local',
30342             placeholder : this.yearPlaceholder,
30343             selectOnFocus : true,
30344             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30345             triggerAction : 'all',
30346             typeAhead : true,
30347             valueField : 'value',
30348             store : new Roo.data.SimpleStore({
30349                 data : (function() {
30350                     var years = [];
30351                     _this.fireEvent('years', _this, years);
30352                     return years;
30353                 })(),
30354                 fields : [ 'value' ]
30355             }),
30356             listeners : {
30357                 select : function (_self, record, index)
30358                 {
30359                     _this.setValue(_this.getValue());
30360                 }
30361             }
30362         });
30363
30364         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30365     },
30366     
30367     setValue : function(v, format)
30368     {
30369         this.inputEl.dom.value = v;
30370         
30371         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30372         
30373         var d = Date.parseDate(v, f);
30374         
30375         if(!d){
30376             this.validate();
30377             return;
30378         }
30379         
30380         this.setDay(d.format(this.dayFormat));
30381         this.setMonth(d.format(this.monthFormat));
30382         this.setYear(d.format(this.yearFormat));
30383         
30384         this.validate();
30385         
30386         return;
30387     },
30388     
30389     setDay : function(v)
30390     {
30391         this.dayField.setValue(v);
30392         this.inputEl.dom.value = this.getValue();
30393         this.validate();
30394         return;
30395     },
30396     
30397     setMonth : function(v)
30398     {
30399         this.monthField.setValue(v, true);
30400         this.inputEl.dom.value = this.getValue();
30401         this.validate();
30402         return;
30403     },
30404     
30405     setYear : function(v)
30406     {
30407         this.yearField.setValue(v);
30408         this.inputEl.dom.value = this.getValue();
30409         this.validate();
30410         return;
30411     },
30412     
30413     getDay : function()
30414     {
30415         return this.dayField.getValue();
30416     },
30417     
30418     getMonth : function()
30419     {
30420         return this.monthField.getValue();
30421     },
30422     
30423     getYear : function()
30424     {
30425         return this.yearField.getValue();
30426     },
30427     
30428     getValue : function()
30429     {
30430         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30431         
30432         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30433         
30434         return date;
30435     },
30436     
30437     reset : function()
30438     {
30439         this.setDay('');
30440         this.setMonth('');
30441         this.setYear('');
30442         this.inputEl.dom.value = '';
30443         this.validate();
30444         return;
30445     },
30446     
30447     validate : function()
30448     {
30449         var d = this.dayField.validate();
30450         var m = this.monthField.validate();
30451         var y = this.yearField.validate();
30452         
30453         var valid = true;
30454         
30455         if(
30456                 (!this.dayAllowBlank && !d) ||
30457                 (!this.monthAllowBlank && !m) ||
30458                 (!this.yearAllowBlank && !y)
30459         ){
30460             valid = false;
30461         }
30462         
30463         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30464             return valid;
30465         }
30466         
30467         if(valid){
30468             this.markValid();
30469             return valid;
30470         }
30471         
30472         this.markInvalid();
30473         
30474         return valid;
30475     },
30476     
30477     markValid : function()
30478     {
30479         
30480         var label = this.el.select('label', true).first();
30481         var icon = this.el.select('i.fa-star', true).first();
30482
30483         if(label && icon){
30484             icon.remove();
30485         }
30486         
30487         this.fireEvent('valid', this);
30488     },
30489     
30490      /**
30491      * Mark this field as invalid
30492      * @param {String} msg The validation message
30493      */
30494     markInvalid : function(msg)
30495     {
30496         
30497         var label = this.el.select('label', true).first();
30498         var icon = this.el.select('i.fa-star', true).first();
30499
30500         if(label && !icon){
30501             this.el.select('.roo-date-split-field-label', true).createChild({
30502                 tag : 'i',
30503                 cls : 'text-danger fa fa-lg fa-star',
30504                 tooltip : 'This field is required',
30505                 style : 'margin-right:5px;'
30506             }, label, true);
30507         }
30508         
30509         this.fireEvent('invalid', this, msg);
30510     },
30511     
30512     clearInvalid : function()
30513     {
30514         var label = this.el.select('label', true).first();
30515         var icon = this.el.select('i.fa-star', true).first();
30516
30517         if(label && icon){
30518             icon.remove();
30519         }
30520         
30521         this.fireEvent('valid', this);
30522     },
30523     
30524     getName: function()
30525     {
30526         return this.name;
30527     }
30528     
30529 });
30530
30531  /**
30532  *
30533  * This is based on 
30534  * http://masonry.desandro.com
30535  *
30536  * The idea is to render all the bricks based on vertical width...
30537  *
30538  * The original code extends 'outlayer' - we might need to use that....
30539  * 
30540  */
30541
30542
30543 /**
30544  * @class Roo.bootstrap.LayoutMasonry
30545  * @extends Roo.bootstrap.Component
30546  * Bootstrap Layout Masonry class
30547  * 
30548  * @constructor
30549  * Create a new Element
30550  * @param {Object} config The config object
30551  */
30552
30553 Roo.bootstrap.LayoutMasonry = function(config){
30554     
30555     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30556     
30557     this.bricks = [];
30558     
30559     Roo.bootstrap.LayoutMasonry.register(this);
30560     
30561     this.addEvents({
30562         // raw events
30563         /**
30564          * @event layout
30565          * Fire after layout the items
30566          * @param {Roo.bootstrap.LayoutMasonry} this
30567          * @param {Roo.EventObject} e
30568          */
30569         "layout" : true
30570     });
30571     
30572 };
30573
30574 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30575     
30576     /**
30577      * @cfg {Boolean} isLayoutInstant = no animation?
30578      */   
30579     isLayoutInstant : false, // needed?
30580    
30581     /**
30582      * @cfg {Number} boxWidth  width of the columns
30583      */   
30584     boxWidth : 450,
30585     
30586       /**
30587      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30588      */   
30589     boxHeight : 0,
30590     
30591     /**
30592      * @cfg {Number} padWidth padding below box..
30593      */   
30594     padWidth : 10, 
30595     
30596     /**
30597      * @cfg {Number} gutter gutter width..
30598      */   
30599     gutter : 10,
30600     
30601      /**
30602      * @cfg {Number} maxCols maximum number of columns
30603      */   
30604     
30605     maxCols: 0,
30606     
30607     /**
30608      * @cfg {Boolean} isAutoInitial defalut true
30609      */   
30610     isAutoInitial : true, 
30611     
30612     containerWidth: 0,
30613     
30614     /**
30615      * @cfg {Boolean} isHorizontal defalut false
30616      */   
30617     isHorizontal : false, 
30618
30619     currentSize : null,
30620     
30621     tag: 'div',
30622     
30623     cls: '',
30624     
30625     bricks: null, //CompositeElement
30626     
30627     cols : 1,
30628     
30629     _isLayoutInited : false,
30630     
30631 //    isAlternative : false, // only use for vertical layout...
30632     
30633     /**
30634      * @cfg {Number} alternativePadWidth padding below box..
30635      */   
30636     alternativePadWidth : 50,
30637     
30638     selectedBrick : [],
30639     
30640     getAutoCreate : function(){
30641         
30642         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30643         
30644         var cfg = {
30645             tag: this.tag,
30646             cls: 'blog-masonary-wrapper ' + this.cls,
30647             cn : {
30648                 cls : 'mas-boxes masonary'
30649             }
30650         };
30651         
30652         return cfg;
30653     },
30654     
30655     getChildContainer: function( )
30656     {
30657         if (this.boxesEl) {
30658             return this.boxesEl;
30659         }
30660         
30661         this.boxesEl = this.el.select('.mas-boxes').first();
30662         
30663         return this.boxesEl;
30664     },
30665     
30666     
30667     initEvents : function()
30668     {
30669         var _this = this;
30670         
30671         if(this.isAutoInitial){
30672             Roo.log('hook children rendered');
30673             this.on('childrenrendered', function() {
30674                 Roo.log('children rendered');
30675                 _this.initial();
30676             } ,this);
30677         }
30678     },
30679     
30680     initial : function()
30681     {
30682         this.selectedBrick = [];
30683         
30684         this.currentSize = this.el.getBox(true);
30685         
30686         Roo.EventManager.onWindowResize(this.resize, this); 
30687
30688         if(!this.isAutoInitial){
30689             this.layout();
30690             return;
30691         }
30692         
30693         this.layout();
30694         
30695         return;
30696         //this.layout.defer(500,this);
30697         
30698     },
30699     
30700     resize : function()
30701     {
30702         var cs = this.el.getBox(true);
30703         
30704         if (
30705                 this.currentSize.width == cs.width && 
30706                 this.currentSize.x == cs.x && 
30707                 this.currentSize.height == cs.height && 
30708                 this.currentSize.y == cs.y 
30709         ) {
30710             Roo.log("no change in with or X or Y");
30711             return;
30712         }
30713         
30714         this.currentSize = cs;
30715         
30716         this.layout();
30717         
30718     },
30719     
30720     layout : function()
30721     {   
30722         this._resetLayout();
30723         
30724         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30725         
30726         this.layoutItems( isInstant );
30727       
30728         this._isLayoutInited = true;
30729         
30730         this.fireEvent('layout', this);
30731         
30732     },
30733     
30734     _resetLayout : function()
30735     {
30736         if(this.isHorizontal){
30737             this.horizontalMeasureColumns();
30738             return;
30739         }
30740         
30741         this.verticalMeasureColumns();
30742         
30743     },
30744     
30745     verticalMeasureColumns : function()
30746     {
30747         this.getContainerWidth();
30748         
30749 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30750 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30751 //            return;
30752 //        }
30753         
30754         var boxWidth = this.boxWidth + this.padWidth;
30755         
30756         if(this.containerWidth < this.boxWidth){
30757             boxWidth = this.containerWidth
30758         }
30759         
30760         var containerWidth = this.containerWidth;
30761         
30762         var cols = Math.floor(containerWidth / boxWidth);
30763         
30764         this.cols = Math.max( cols, 1 );
30765         
30766         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30767         
30768         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30769         
30770         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30771         
30772         this.colWidth = boxWidth + avail - this.padWidth;
30773         
30774         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30775         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30776     },
30777     
30778     horizontalMeasureColumns : function()
30779     {
30780         this.getContainerWidth();
30781         
30782         var boxWidth = this.boxWidth;
30783         
30784         if(this.containerWidth < boxWidth){
30785             boxWidth = this.containerWidth;
30786         }
30787         
30788         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30789         
30790         this.el.setHeight(boxWidth);
30791         
30792     },
30793     
30794     getContainerWidth : function()
30795     {
30796         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30797     },
30798     
30799     layoutItems : function( isInstant )
30800     {
30801         Roo.log(this.bricks);
30802         
30803         var items = Roo.apply([], this.bricks);
30804         
30805         if(this.isHorizontal){
30806             this._horizontalLayoutItems( items , isInstant );
30807             return;
30808         }
30809         
30810 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30811 //            this._verticalAlternativeLayoutItems( items , isInstant );
30812 //            return;
30813 //        }
30814         
30815         this._verticalLayoutItems( items , isInstant );
30816         
30817     },
30818     
30819     _verticalLayoutItems : function ( items , isInstant)
30820     {
30821         if ( !items || !items.length ) {
30822             return;
30823         }
30824         
30825         var standard = [
30826             ['xs', 'xs', 'xs', 'tall'],
30827             ['xs', 'xs', 'tall'],
30828             ['xs', 'xs', 'sm'],
30829             ['xs', 'xs', 'xs'],
30830             ['xs', 'tall'],
30831             ['xs', 'sm'],
30832             ['xs', 'xs'],
30833             ['xs'],
30834             
30835             ['sm', 'xs', 'xs'],
30836             ['sm', 'xs'],
30837             ['sm'],
30838             
30839             ['tall', 'xs', 'xs', 'xs'],
30840             ['tall', 'xs', 'xs'],
30841             ['tall', 'xs'],
30842             ['tall']
30843             
30844         ];
30845         
30846         var queue = [];
30847         
30848         var boxes = [];
30849         
30850         var box = [];
30851         
30852         Roo.each(items, function(item, k){
30853             
30854             switch (item.size) {
30855                 // these layouts take up a full box,
30856                 case 'md' :
30857                 case 'md-left' :
30858                 case 'md-right' :
30859                 case 'wide' :
30860                     
30861                     if(box.length){
30862                         boxes.push(box);
30863                         box = [];
30864                     }
30865                     
30866                     boxes.push([item]);
30867                     
30868                     break;
30869                     
30870                 case 'xs' :
30871                 case 'sm' :
30872                 case 'tall' :
30873                     
30874                     box.push(item);
30875                     
30876                     break;
30877                 default :
30878                     break;
30879                     
30880             }
30881             
30882         }, this);
30883         
30884         if(box.length){
30885             boxes.push(box);
30886             box = [];
30887         }
30888         
30889         var filterPattern = function(box, length)
30890         {
30891             if(!box.length){
30892                 return;
30893             }
30894             
30895             var match = false;
30896             
30897             var pattern = box.slice(0, length);
30898             
30899             var format = [];
30900             
30901             Roo.each(pattern, function(i){
30902                 format.push(i.size);
30903             }, this);
30904             
30905             Roo.each(standard, function(s){
30906                 
30907                 if(String(s) != String(format)){
30908                     return;
30909                 }
30910                 
30911                 match = true;
30912                 return false;
30913                 
30914             }, this);
30915             
30916             if(!match && length == 1){
30917                 return;
30918             }
30919             
30920             if(!match){
30921                 filterPattern(box, length - 1);
30922                 return;
30923             }
30924                 
30925             queue.push(pattern);
30926
30927             box = box.slice(length, box.length);
30928
30929             filterPattern(box, 4);
30930
30931             return;
30932             
30933         }
30934         
30935         Roo.each(boxes, function(box, k){
30936             
30937             if(!box.length){
30938                 return;
30939             }
30940             
30941             if(box.length == 1){
30942                 queue.push(box);
30943                 return;
30944             }
30945             
30946             filterPattern(box, 4);
30947             
30948         }, this);
30949         
30950         this._processVerticalLayoutQueue( queue, isInstant );
30951         
30952     },
30953     
30954 //    _verticalAlternativeLayoutItems : function( items , isInstant )
30955 //    {
30956 //        if ( !items || !items.length ) {
30957 //            return;
30958 //        }
30959 //
30960 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
30961 //        
30962 //    },
30963     
30964     _horizontalLayoutItems : function ( items , isInstant)
30965     {
30966         if ( !items || !items.length || items.length < 3) {
30967             return;
30968         }
30969         
30970         items.reverse();
30971         
30972         var eItems = items.slice(0, 3);
30973         
30974         items = items.slice(3, items.length);
30975         
30976         var standard = [
30977             ['xs', 'xs', 'xs', 'wide'],
30978             ['xs', 'xs', 'wide'],
30979             ['xs', 'xs', 'sm'],
30980             ['xs', 'xs', 'xs'],
30981             ['xs', 'wide'],
30982             ['xs', 'sm'],
30983             ['xs', 'xs'],
30984             ['xs'],
30985             
30986             ['sm', 'xs', 'xs'],
30987             ['sm', 'xs'],
30988             ['sm'],
30989             
30990             ['wide', 'xs', 'xs', 'xs'],
30991             ['wide', 'xs', 'xs'],
30992             ['wide', 'xs'],
30993             ['wide'],
30994             
30995             ['wide-thin']
30996         ];
30997         
30998         var queue = [];
30999         
31000         var boxes = [];
31001         
31002         var box = [];
31003         
31004         Roo.each(items, function(item, k){
31005             
31006             switch (item.size) {
31007                 case 'md' :
31008                 case 'md-left' :
31009                 case 'md-right' :
31010                 case 'tall' :
31011                     
31012                     if(box.length){
31013                         boxes.push(box);
31014                         box = [];
31015                     }
31016                     
31017                     boxes.push([item]);
31018                     
31019                     break;
31020                     
31021                 case 'xs' :
31022                 case 'sm' :
31023                 case 'wide' :
31024                 case 'wide-thin' :
31025                     
31026                     box.push(item);
31027                     
31028                     break;
31029                 default :
31030                     break;
31031                     
31032             }
31033             
31034         }, this);
31035         
31036         if(box.length){
31037             boxes.push(box);
31038             box = [];
31039         }
31040         
31041         var filterPattern = function(box, length)
31042         {
31043             if(!box.length){
31044                 return;
31045             }
31046             
31047             var match = false;
31048             
31049             var pattern = box.slice(0, length);
31050             
31051             var format = [];
31052             
31053             Roo.each(pattern, function(i){
31054                 format.push(i.size);
31055             }, this);
31056             
31057             Roo.each(standard, function(s){
31058                 
31059                 if(String(s) != String(format)){
31060                     return;
31061                 }
31062                 
31063                 match = true;
31064                 return false;
31065                 
31066             }, this);
31067             
31068             if(!match && length == 1){
31069                 return;
31070             }
31071             
31072             if(!match){
31073                 filterPattern(box, length - 1);
31074                 return;
31075             }
31076                 
31077             queue.push(pattern);
31078
31079             box = box.slice(length, box.length);
31080
31081             filterPattern(box, 4);
31082
31083             return;
31084             
31085         }
31086         
31087         Roo.each(boxes, function(box, k){
31088             
31089             if(!box.length){
31090                 return;
31091             }
31092             
31093             if(box.length == 1){
31094                 queue.push(box);
31095                 return;
31096             }
31097             
31098             filterPattern(box, 4);
31099             
31100         }, this);
31101         
31102         
31103         var prune = [];
31104         
31105         var pos = this.el.getBox(true);
31106         
31107         var minX = pos.x;
31108         
31109         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31110         
31111         var hit_end = false;
31112         
31113         Roo.each(queue, function(box){
31114             
31115             if(hit_end){
31116                 
31117                 Roo.each(box, function(b){
31118                 
31119                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31120                     b.el.hide();
31121
31122                 }, this);
31123
31124                 return;
31125             }
31126             
31127             var mx = 0;
31128             
31129             Roo.each(box, function(b){
31130                 
31131                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31132                 b.el.show();
31133
31134                 mx = Math.max(mx, b.x);
31135                 
31136             }, this);
31137             
31138             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31139             
31140             if(maxX < minX){
31141                 
31142                 Roo.each(box, function(b){
31143                 
31144                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31145                     b.el.hide();
31146                     
31147                 }, this);
31148                 
31149                 hit_end = true;
31150                 
31151                 return;
31152             }
31153             
31154             prune.push(box);
31155             
31156         }, this);
31157         
31158         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31159     },
31160     
31161     /** Sets position of item in DOM
31162     * @param {Element} item
31163     * @param {Number} x - horizontal position
31164     * @param {Number} y - vertical position
31165     * @param {Boolean} isInstant - disables transitions
31166     */
31167     _processVerticalLayoutQueue : function( queue, isInstant )
31168     {
31169         var pos = this.el.getBox(true);
31170         var x = pos.x;
31171         var y = pos.y;
31172         var maxY = [];
31173         
31174         for (var i = 0; i < this.cols; i++){
31175             maxY[i] = pos.y;
31176         }
31177         
31178         Roo.each(queue, function(box, k){
31179             
31180             var col = k % this.cols;
31181             
31182             Roo.each(box, function(b,kk){
31183                 
31184                 b.el.position('absolute');
31185                 
31186                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31187                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31188                 
31189                 if(b.size == 'md-left' || b.size == 'md-right'){
31190                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31191                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31192                 }
31193                 
31194                 b.el.setWidth(width);
31195                 b.el.setHeight(height);
31196                 // iframe?
31197                 b.el.select('iframe',true).setSize(width,height);
31198                 
31199             }, this);
31200             
31201             for (var i = 0; i < this.cols; i++){
31202                 
31203                 if(maxY[i] < maxY[col]){
31204                     col = i;
31205                     continue;
31206                 }
31207                 
31208                 col = Math.min(col, i);
31209                 
31210             }
31211             
31212             x = pos.x + col * (this.colWidth + this.padWidth);
31213             
31214             y = maxY[col];
31215             
31216             var positions = [];
31217             
31218             switch (box.length){
31219                 case 1 :
31220                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31221                     break;
31222                 case 2 :
31223                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31224                     break;
31225                 case 3 :
31226                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31227                     break;
31228                 case 4 :
31229                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31230                     break;
31231                 default :
31232                     break;
31233             }
31234             
31235             Roo.each(box, function(b,kk){
31236                 
31237                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31238                 
31239                 var sz = b.el.getSize();
31240                 
31241                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31242                 
31243             }, this);
31244             
31245         }, this);
31246         
31247         var mY = 0;
31248         
31249         for (var i = 0; i < this.cols; i++){
31250             mY = Math.max(mY, maxY[i]);
31251         }
31252         
31253         this.el.setHeight(mY - pos.y);
31254         
31255     },
31256     
31257 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31258 //    {
31259 //        var pos = this.el.getBox(true);
31260 //        var x = pos.x;
31261 //        var y = pos.y;
31262 //        var maxX = pos.right;
31263 //        
31264 //        var maxHeight = 0;
31265 //        
31266 //        Roo.each(items, function(item, k){
31267 //            
31268 //            var c = k % 2;
31269 //            
31270 //            item.el.position('absolute');
31271 //                
31272 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31273 //
31274 //            item.el.setWidth(width);
31275 //
31276 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31277 //
31278 //            item.el.setHeight(height);
31279 //            
31280 //            if(c == 0){
31281 //                item.el.setXY([x, y], isInstant ? false : true);
31282 //            } else {
31283 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31284 //            }
31285 //            
31286 //            y = y + height + this.alternativePadWidth;
31287 //            
31288 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31289 //            
31290 //        }, this);
31291 //        
31292 //        this.el.setHeight(maxHeight);
31293 //        
31294 //    },
31295     
31296     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31297     {
31298         var pos = this.el.getBox(true);
31299         
31300         var minX = pos.x;
31301         var minY = pos.y;
31302         
31303         var maxX = pos.right;
31304         
31305         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31306         
31307         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31308         
31309         Roo.each(queue, function(box, k){
31310             
31311             Roo.each(box, function(b, kk){
31312                 
31313                 b.el.position('absolute');
31314                 
31315                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31316                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31317                 
31318                 if(b.size == 'md-left' || b.size == 'md-right'){
31319                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31320                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31321                 }
31322                 
31323                 b.el.setWidth(width);
31324                 b.el.setHeight(height);
31325                 
31326             }, this);
31327             
31328             if(!box.length){
31329                 return;
31330             }
31331             
31332             var positions = [];
31333             
31334             switch (box.length){
31335                 case 1 :
31336                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31337                     break;
31338                 case 2 :
31339                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31340                     break;
31341                 case 3 :
31342                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31343                     break;
31344                 case 4 :
31345                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31346                     break;
31347                 default :
31348                     break;
31349             }
31350             
31351             Roo.each(box, function(b,kk){
31352                 
31353                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31354                 
31355                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31356                 
31357             }, this);
31358             
31359         }, this);
31360         
31361     },
31362     
31363     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31364     {
31365         Roo.each(eItems, function(b,k){
31366             
31367             b.size = (k == 0) ? 'sm' : 'xs';
31368             b.x = (k == 0) ? 2 : 1;
31369             b.y = (k == 0) ? 2 : 1;
31370             
31371             b.el.position('absolute');
31372             
31373             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31374                 
31375             b.el.setWidth(width);
31376             
31377             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31378             
31379             b.el.setHeight(height);
31380             
31381         }, this);
31382
31383         var positions = [];
31384         
31385         positions.push({
31386             x : maxX - this.unitWidth * 2 - this.gutter,
31387             y : minY
31388         });
31389         
31390         positions.push({
31391             x : maxX - this.unitWidth,
31392             y : minY + (this.unitWidth + this.gutter) * 2
31393         });
31394         
31395         positions.push({
31396             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31397             y : minY
31398         });
31399         
31400         Roo.each(eItems, function(b,k){
31401             
31402             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31403
31404         }, this);
31405         
31406     },
31407     
31408     getVerticalOneBoxColPositions : function(x, y, box)
31409     {
31410         var pos = [];
31411         
31412         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31413         
31414         if(box[0].size == 'md-left'){
31415             rand = 0;
31416         }
31417         
31418         if(box[0].size == 'md-right'){
31419             rand = 1;
31420         }
31421         
31422         pos.push({
31423             x : x + (this.unitWidth + this.gutter) * rand,
31424             y : y
31425         });
31426         
31427         return pos;
31428     },
31429     
31430     getVerticalTwoBoxColPositions : function(x, y, box)
31431     {
31432         var pos = [];
31433         
31434         if(box[0].size == 'xs'){
31435             
31436             pos.push({
31437                 x : x,
31438                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31439             });
31440
31441             pos.push({
31442                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31443                 y : y
31444             });
31445             
31446             return pos;
31447             
31448         }
31449         
31450         pos.push({
31451             x : x,
31452             y : y
31453         });
31454
31455         pos.push({
31456             x : x + (this.unitWidth + this.gutter) * 2,
31457             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31458         });
31459         
31460         return pos;
31461         
31462     },
31463     
31464     getVerticalThreeBoxColPositions : function(x, y, box)
31465     {
31466         var pos = [];
31467         
31468         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31469             
31470             pos.push({
31471                 x : x,
31472                 y : y
31473             });
31474
31475             pos.push({
31476                 x : x + (this.unitWidth + this.gutter) * 1,
31477                 y : y
31478             });
31479             
31480             pos.push({
31481                 x : x + (this.unitWidth + this.gutter) * 2,
31482                 y : y
31483             });
31484             
31485             return pos;
31486             
31487         }
31488         
31489         if(box[0].size == 'xs' && box[1].size == 'xs'){
31490             
31491             pos.push({
31492                 x : x,
31493                 y : y
31494             });
31495
31496             pos.push({
31497                 x : x,
31498                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31499             });
31500             
31501             pos.push({
31502                 x : x + (this.unitWidth + this.gutter) * 1,
31503                 y : y
31504             });
31505             
31506             return pos;
31507             
31508         }
31509         
31510         pos.push({
31511             x : x,
31512             y : y
31513         });
31514
31515         pos.push({
31516             x : x + (this.unitWidth + this.gutter) * 2,
31517             y : y
31518         });
31519
31520         pos.push({
31521             x : x + (this.unitWidth + this.gutter) * 2,
31522             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31523         });
31524             
31525         return pos;
31526         
31527     },
31528     
31529     getVerticalFourBoxColPositions : function(x, y, box)
31530     {
31531         var pos = [];
31532         
31533         if(box[0].size == 'xs'){
31534             
31535             pos.push({
31536                 x : x,
31537                 y : y
31538             });
31539
31540             pos.push({
31541                 x : x,
31542                 y : y + (this.unitHeight + this.gutter) * 1
31543             });
31544             
31545             pos.push({
31546                 x : x,
31547                 y : y + (this.unitHeight + this.gutter) * 2
31548             });
31549             
31550             pos.push({
31551                 x : x + (this.unitWidth + this.gutter) * 1,
31552                 y : y
31553             });
31554             
31555             return pos;
31556             
31557         }
31558         
31559         pos.push({
31560             x : x,
31561             y : y
31562         });
31563
31564         pos.push({
31565             x : x + (this.unitWidth + this.gutter) * 2,
31566             y : y
31567         });
31568
31569         pos.push({
31570             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31571             y : y + (this.unitHeight + this.gutter) * 1
31572         });
31573
31574         pos.push({
31575             x : x + (this.unitWidth + this.gutter) * 2,
31576             y : y + (this.unitWidth + this.gutter) * 2
31577         });
31578
31579         return pos;
31580         
31581     },
31582     
31583     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31584     {
31585         var pos = [];
31586         
31587         if(box[0].size == 'md-left'){
31588             pos.push({
31589                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31590                 y : minY
31591             });
31592             
31593             return pos;
31594         }
31595         
31596         if(box[0].size == 'md-right'){
31597             pos.push({
31598                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31599                 y : minY + (this.unitWidth + this.gutter) * 1
31600             });
31601             
31602             return pos;
31603         }
31604         
31605         var rand = Math.floor(Math.random() * (4 - box[0].y));
31606         
31607         pos.push({
31608             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31609             y : minY + (this.unitWidth + this.gutter) * rand
31610         });
31611         
31612         return pos;
31613         
31614     },
31615     
31616     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31617     {
31618         var pos = [];
31619         
31620         if(box[0].size == 'xs'){
31621             
31622             pos.push({
31623                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31624                 y : minY
31625             });
31626
31627             pos.push({
31628                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31629                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31630             });
31631             
31632             return pos;
31633             
31634         }
31635         
31636         pos.push({
31637             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31638             y : minY
31639         });
31640
31641         pos.push({
31642             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31643             y : minY + (this.unitWidth + this.gutter) * 2
31644         });
31645         
31646         return pos;
31647         
31648     },
31649     
31650     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31651     {
31652         var pos = [];
31653         
31654         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31655             
31656             pos.push({
31657                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31658                 y : minY
31659             });
31660
31661             pos.push({
31662                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31663                 y : minY + (this.unitWidth + this.gutter) * 1
31664             });
31665             
31666             pos.push({
31667                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31668                 y : minY + (this.unitWidth + this.gutter) * 2
31669             });
31670             
31671             return pos;
31672             
31673         }
31674         
31675         if(box[0].size == 'xs' && box[1].size == 'xs'){
31676             
31677             pos.push({
31678                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31679                 y : minY
31680             });
31681
31682             pos.push({
31683                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31684                 y : minY
31685             });
31686             
31687             pos.push({
31688                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31689                 y : minY + (this.unitWidth + this.gutter) * 1
31690             });
31691             
31692             return pos;
31693             
31694         }
31695         
31696         pos.push({
31697             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31698             y : minY
31699         });
31700
31701         pos.push({
31702             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31703             y : minY + (this.unitWidth + this.gutter) * 2
31704         });
31705
31706         pos.push({
31707             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31708             y : minY + (this.unitWidth + this.gutter) * 2
31709         });
31710             
31711         return pos;
31712         
31713     },
31714     
31715     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31716     {
31717         var pos = [];
31718         
31719         if(box[0].size == 'xs'){
31720             
31721             pos.push({
31722                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31723                 y : minY
31724             });
31725
31726             pos.push({
31727                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31728                 y : minY
31729             });
31730             
31731             pos.push({
31732                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31733                 y : minY
31734             });
31735             
31736             pos.push({
31737                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31738                 y : minY + (this.unitWidth + this.gutter) * 1
31739             });
31740             
31741             return pos;
31742             
31743         }
31744         
31745         pos.push({
31746             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31747             y : minY
31748         });
31749         
31750         pos.push({
31751             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31752             y : minY + (this.unitWidth + this.gutter) * 2
31753         });
31754         
31755         pos.push({
31756             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31757             y : minY + (this.unitWidth + this.gutter) * 2
31758         });
31759         
31760         pos.push({
31761             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1) - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31762             y : minY + (this.unitWidth + this.gutter) * 2
31763         });
31764
31765         return pos;
31766         
31767     },
31768     
31769     /**
31770     * remove a Masonry Brick
31771     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31772     */
31773     removeBrick : function(brick_id)
31774     {
31775         if (!brick_id) {
31776             return;
31777         }
31778         
31779         for (var i = 0; i<this.bricks.length; i++) {
31780             if (this.bricks[i].id == brick_id) {
31781                 this.bricks.splice(i,1);
31782                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31783                 this.initial();
31784             }
31785         }
31786     },
31787     
31788     /**
31789     * adds a Masonry Brick
31790     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31791     */
31792     addBrick : function(cfg)
31793     {
31794         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31795         //this.register(cn);
31796         cn.parentId = this.id;
31797         cn.onRender(this.el, null);
31798         return cn;
31799     },
31800     
31801     /**
31802     * register a Masonry Brick
31803     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31804     */
31805     
31806     register : function(brick)
31807     {
31808         this.bricks.push(brick);
31809         brick.masonryId = this.id;
31810     },
31811     
31812     /**
31813     * clear all the Masonry Brick
31814     */
31815     clearAll : function()
31816     {
31817         this.bricks = [];
31818         //this.getChildContainer().dom.innerHTML = "";
31819         this.el.dom.innerHTML = '';
31820     },
31821     
31822     getSelected : function()
31823     {
31824         if (!this.selectedBrick) {
31825             return false;
31826         }
31827         
31828         return this.selectedBrick;
31829     }
31830 });
31831
31832 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31833     
31834     groups: {},
31835      /**
31836     * register a Masonry Layout
31837     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31838     */
31839     
31840     register : function(layout)
31841     {
31842         this.groups[layout.id] = layout;
31843     },
31844     /**
31845     * fetch a  Masonry Layout based on the masonry layout ID
31846     * @param {string} the masonry layout to add
31847     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31848     */
31849     
31850     get: function(layout_id) {
31851         if (typeof(this.groups[layout_id]) == 'undefined') {
31852             return false;
31853         }
31854         return this.groups[layout_id] ;
31855     }
31856     
31857     
31858     
31859 });
31860
31861  
31862
31863  /**
31864  *
31865  * This is based on 
31866  * http://masonry.desandro.com
31867  *
31868  * The idea is to render all the bricks based on vertical width...
31869  *
31870  * The original code extends 'outlayer' - we might need to use that....
31871  * 
31872  */
31873
31874
31875 /**
31876  * @class Roo.bootstrap.LayoutMasonryAuto
31877  * @extends Roo.bootstrap.Component
31878  * Bootstrap Layout Masonry class
31879  * 
31880  * @constructor
31881  * Create a new Element
31882  * @param {Object} config The config object
31883  */
31884
31885 Roo.bootstrap.LayoutMasonryAuto = function(config){
31886     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31887 };
31888
31889 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
31890     
31891       /**
31892      * @cfg {Boolean} isFitWidth  - resize the width..
31893      */   
31894     isFitWidth : false,  // options..
31895     /**
31896      * @cfg {Boolean} isOriginLeft = left align?
31897      */   
31898     isOriginLeft : true,
31899     /**
31900      * @cfg {Boolean} isOriginTop = top align?
31901      */   
31902     isOriginTop : false,
31903     /**
31904      * @cfg {Boolean} isLayoutInstant = no animation?
31905      */   
31906     isLayoutInstant : false, // needed?
31907     /**
31908      * @cfg {Boolean} isResizingContainer = not sure if this is used..
31909      */   
31910     isResizingContainer : true,
31911     /**
31912      * @cfg {Number} columnWidth  width of the columns 
31913      */   
31914     
31915     columnWidth : 0,
31916     
31917     /**
31918      * @cfg {Number} maxCols maximum number of columns
31919      */   
31920     
31921     maxCols: 0,
31922     /**
31923      * @cfg {Number} padHeight padding below box..
31924      */   
31925     
31926     padHeight : 10, 
31927     
31928     /**
31929      * @cfg {Boolean} isAutoInitial defalut true
31930      */   
31931     
31932     isAutoInitial : true, 
31933     
31934     // private?
31935     gutter : 0,
31936     
31937     containerWidth: 0,
31938     initialColumnWidth : 0,
31939     currentSize : null,
31940     
31941     colYs : null, // array.
31942     maxY : 0,
31943     padWidth: 10,
31944     
31945     
31946     tag: 'div',
31947     cls: '',
31948     bricks: null, //CompositeElement
31949     cols : 0, // array?
31950     // element : null, // wrapped now this.el
31951     _isLayoutInited : null, 
31952     
31953     
31954     getAutoCreate : function(){
31955         
31956         var cfg = {
31957             tag: this.tag,
31958             cls: 'blog-masonary-wrapper ' + this.cls,
31959             cn : {
31960                 cls : 'mas-boxes masonary'
31961             }
31962         };
31963         
31964         return cfg;
31965     },
31966     
31967     getChildContainer: function( )
31968     {
31969         if (this.boxesEl) {
31970             return this.boxesEl;
31971         }
31972         
31973         this.boxesEl = this.el.select('.mas-boxes').first();
31974         
31975         return this.boxesEl;
31976     },
31977     
31978     
31979     initEvents : function()
31980     {
31981         var _this = this;
31982         
31983         if(this.isAutoInitial){
31984             Roo.log('hook children rendered');
31985             this.on('childrenrendered', function() {
31986                 Roo.log('children rendered');
31987                 _this.initial();
31988             } ,this);
31989         }
31990         
31991     },
31992     
31993     initial : function()
31994     {
31995         this.reloadItems();
31996
31997         this.currentSize = this.el.getBox(true);
31998
31999         /// was window resize... - let's see if this works..
32000         Roo.EventManager.onWindowResize(this.resize, this); 
32001
32002         if(!this.isAutoInitial){
32003             this.layout();
32004             return;
32005         }
32006         
32007         this.layout.defer(500,this);
32008     },
32009     
32010     reloadItems: function()
32011     {
32012         this.bricks = this.el.select('.masonry-brick', true);
32013         
32014         this.bricks.each(function(b) {
32015             //Roo.log(b.getSize());
32016             if (!b.attr('originalwidth')) {
32017                 b.attr('originalwidth',  b.getSize().width);
32018             }
32019             
32020         });
32021         
32022         Roo.log(this.bricks.elements.length);
32023     },
32024     
32025     resize : function()
32026     {
32027         Roo.log('resize');
32028         var cs = this.el.getBox(true);
32029         
32030         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32031             Roo.log("no change in with or X");
32032             return;
32033         }
32034         this.currentSize = cs;
32035         this.layout();
32036     },
32037     
32038     layout : function()
32039     {
32040          Roo.log('layout');
32041         this._resetLayout();
32042         //this._manageStamps();
32043       
32044         // don't animate first layout
32045         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32046         this.layoutItems( isInstant );
32047       
32048         // flag for initalized
32049         this._isLayoutInited = true;
32050     },
32051     
32052     layoutItems : function( isInstant )
32053     {
32054         //var items = this._getItemsForLayout( this.items );
32055         // original code supports filtering layout items.. we just ignore it..
32056         
32057         this._layoutItems( this.bricks , isInstant );
32058       
32059         this._postLayout();
32060     },
32061     _layoutItems : function ( items , isInstant)
32062     {
32063        //this.fireEvent( 'layout', this, items );
32064     
32065
32066         if ( !items || !items.elements.length ) {
32067           // no items, emit event with empty array
32068             return;
32069         }
32070
32071         var queue = [];
32072         items.each(function(item) {
32073             Roo.log("layout item");
32074             Roo.log(item);
32075             // get x/y object from method
32076             var position = this._getItemLayoutPosition( item );
32077             // enqueue
32078             position.item = item;
32079             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32080             queue.push( position );
32081         }, this);
32082       
32083         this._processLayoutQueue( queue );
32084     },
32085     /** Sets position of item in DOM
32086     * @param {Element} item
32087     * @param {Number} x - horizontal position
32088     * @param {Number} y - vertical position
32089     * @param {Boolean} isInstant - disables transitions
32090     */
32091     _processLayoutQueue : function( queue )
32092     {
32093         for ( var i=0, len = queue.length; i < len; i++ ) {
32094             var obj = queue[i];
32095             obj.item.position('absolute');
32096             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32097         }
32098     },
32099       
32100     
32101     /**
32102     * Any logic you want to do after each layout,
32103     * i.e. size the container
32104     */
32105     _postLayout : function()
32106     {
32107         this.resizeContainer();
32108     },
32109     
32110     resizeContainer : function()
32111     {
32112         if ( !this.isResizingContainer ) {
32113             return;
32114         }
32115         var size = this._getContainerSize();
32116         if ( size ) {
32117             this.el.setSize(size.width,size.height);
32118             this.boxesEl.setSize(size.width,size.height);
32119         }
32120     },
32121     
32122     
32123     
32124     _resetLayout : function()
32125     {
32126         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32127         this.colWidth = this.el.getWidth();
32128         //this.gutter = this.el.getWidth(); 
32129         
32130         this.measureColumns();
32131
32132         // reset column Y
32133         var i = this.cols;
32134         this.colYs = [];
32135         while (i--) {
32136             this.colYs.push( 0 );
32137         }
32138     
32139         this.maxY = 0;
32140     },
32141
32142     measureColumns : function()
32143     {
32144         this.getContainerWidth();
32145       // if columnWidth is 0, default to outerWidth of first item
32146         if ( !this.columnWidth ) {
32147             var firstItem = this.bricks.first();
32148             Roo.log(firstItem);
32149             this.columnWidth  = this.containerWidth;
32150             if (firstItem && firstItem.attr('originalwidth') ) {
32151                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32152             }
32153             // columnWidth fall back to item of first element
32154             Roo.log("set column width?");
32155                         this.initialColumnWidth = this.columnWidth  ;
32156
32157             // if first elem has no width, default to size of container
32158             
32159         }
32160         
32161         
32162         if (this.initialColumnWidth) {
32163             this.columnWidth = this.initialColumnWidth;
32164         }
32165         
32166         
32167             
32168         // column width is fixed at the top - however if container width get's smaller we should
32169         // reduce it...
32170         
32171         // this bit calcs how man columns..
32172             
32173         var columnWidth = this.columnWidth += this.gutter;
32174       
32175         // calculate columns
32176         var containerWidth = this.containerWidth + this.gutter;
32177         
32178         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32179         // fix rounding errors, typically with gutters
32180         var excess = columnWidth - containerWidth % columnWidth;
32181         
32182         
32183         // if overshoot is less than a pixel, round up, otherwise floor it
32184         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32185         cols = Math[ mathMethod ]( cols );
32186         this.cols = Math.max( cols, 1 );
32187         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32188         
32189          // padding positioning..
32190         var totalColWidth = this.cols * this.columnWidth;
32191         var padavail = this.containerWidth - totalColWidth;
32192         // so for 2 columns - we need 3 'pads'
32193         
32194         var padNeeded = (1+this.cols) * this.padWidth;
32195         
32196         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32197         
32198         this.columnWidth += padExtra
32199         //this.padWidth = Math.floor(padavail /  ( this.cols));
32200         
32201         // adjust colum width so that padding is fixed??
32202         
32203         // we have 3 columns ... total = width * 3
32204         // we have X left over... that should be used by 
32205         
32206         //if (this.expandC) {
32207             
32208         //}
32209         
32210         
32211         
32212     },
32213     
32214     getContainerWidth : function()
32215     {
32216        /* // container is parent if fit width
32217         var container = this.isFitWidth ? this.element.parentNode : this.element;
32218         // check that this.size and size are there
32219         // IE8 triggers resize on body size change, so they might not be
32220         
32221         var size = getSize( container );  //FIXME
32222         this.containerWidth = size && size.innerWidth; //FIXME
32223         */
32224          
32225         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32226         
32227     },
32228     
32229     _getItemLayoutPosition : function( item )  // what is item?
32230     {
32231         // we resize the item to our columnWidth..
32232       
32233         item.setWidth(this.columnWidth);
32234         item.autoBoxAdjust  = false;
32235         
32236         var sz = item.getSize();
32237  
32238         // how many columns does this brick span
32239         var remainder = this.containerWidth % this.columnWidth;
32240         
32241         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32242         // round if off by 1 pixel, otherwise use ceil
32243         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32244         colSpan = Math.min( colSpan, this.cols );
32245         
32246         // normally this should be '1' as we dont' currently allow multi width columns..
32247         
32248         var colGroup = this._getColGroup( colSpan );
32249         // get the minimum Y value from the columns
32250         var minimumY = Math.min.apply( Math, colGroup );
32251         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32252         
32253         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32254          
32255         // position the brick
32256         var position = {
32257             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32258             y: this.currentSize.y + minimumY + this.padHeight
32259         };
32260         
32261         Roo.log(position);
32262         // apply setHeight to necessary columns
32263         var setHeight = minimumY + sz.height + this.padHeight;
32264         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32265         
32266         var setSpan = this.cols + 1 - colGroup.length;
32267         for ( var i = 0; i < setSpan; i++ ) {
32268           this.colYs[ shortColIndex + i ] = setHeight ;
32269         }
32270       
32271         return position;
32272     },
32273     
32274     /**
32275      * @param {Number} colSpan - number of columns the element spans
32276      * @returns {Array} colGroup
32277      */
32278     _getColGroup : function( colSpan )
32279     {
32280         if ( colSpan < 2 ) {
32281           // if brick spans only one column, use all the column Ys
32282           return this.colYs;
32283         }
32284       
32285         var colGroup = [];
32286         // how many different places could this brick fit horizontally
32287         var groupCount = this.cols + 1 - colSpan;
32288         // for each group potential horizontal position
32289         for ( var i = 0; i < groupCount; i++ ) {
32290           // make an array of colY values for that one group
32291           var groupColYs = this.colYs.slice( i, i + colSpan );
32292           // and get the max value of the array
32293           colGroup[i] = Math.max.apply( Math, groupColYs );
32294         }
32295         return colGroup;
32296     },
32297     /*
32298     _manageStamp : function( stamp )
32299     {
32300         var stampSize =  stamp.getSize();
32301         var offset = stamp.getBox();
32302         // get the columns that this stamp affects
32303         var firstX = this.isOriginLeft ? offset.x : offset.right;
32304         var lastX = firstX + stampSize.width;
32305         var firstCol = Math.floor( firstX / this.columnWidth );
32306         firstCol = Math.max( 0, firstCol );
32307         
32308         var lastCol = Math.floor( lastX / this.columnWidth );
32309         // lastCol should not go over if multiple of columnWidth #425
32310         lastCol -= lastX % this.columnWidth ? 0 : 1;
32311         lastCol = Math.min( this.cols - 1, lastCol );
32312         
32313         // set colYs to bottom of the stamp
32314         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32315             stampSize.height;
32316             
32317         for ( var i = firstCol; i <= lastCol; i++ ) {
32318           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32319         }
32320     },
32321     */
32322     
32323     _getContainerSize : function()
32324     {
32325         this.maxY = Math.max.apply( Math, this.colYs );
32326         var size = {
32327             height: this.maxY
32328         };
32329       
32330         if ( this.isFitWidth ) {
32331             size.width = this._getContainerFitWidth();
32332         }
32333       
32334         return size;
32335     },
32336     
32337     _getContainerFitWidth : function()
32338     {
32339         var unusedCols = 0;
32340         // count unused columns
32341         var i = this.cols;
32342         while ( --i ) {
32343           if ( this.colYs[i] !== 0 ) {
32344             break;
32345           }
32346           unusedCols++;
32347         }
32348         // fit container to columns that have been used
32349         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32350     },
32351     
32352     needsResizeLayout : function()
32353     {
32354         var previousWidth = this.containerWidth;
32355         this.getContainerWidth();
32356         return previousWidth !== this.containerWidth;
32357     }
32358  
32359 });
32360
32361  
32362
32363  /*
32364  * - LGPL
32365  *
32366  * element
32367  * 
32368  */
32369
32370 /**
32371  * @class Roo.bootstrap.MasonryBrick
32372  * @extends Roo.bootstrap.Component
32373  * Bootstrap MasonryBrick class
32374  * 
32375  * @constructor
32376  * Create a new MasonryBrick
32377  * @param {Object} config The config object
32378  */
32379
32380 Roo.bootstrap.MasonryBrick = function(config){
32381     
32382     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32383     
32384     Roo.bootstrap.MasonryBrick.register(this);
32385     
32386     this.addEvents({
32387         // raw events
32388         /**
32389          * @event click
32390          * When a MasonryBrick is clcik
32391          * @param {Roo.bootstrap.MasonryBrick} this
32392          * @param {Roo.EventObject} e
32393          */
32394         "click" : true
32395     });
32396 };
32397
32398 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32399     
32400     /**
32401      * @cfg {String} title
32402      */   
32403     title : '',
32404     /**
32405      * @cfg {String} html
32406      */   
32407     html : '',
32408     /**
32409      * @cfg {String} bgimage
32410      */   
32411     bgimage : '',
32412     /**
32413      * @cfg {String} videourl
32414      */   
32415     videourl : '',
32416     /**
32417      * @cfg {String} cls
32418      */   
32419     cls : '',
32420     /**
32421      * @cfg {String} href
32422      */   
32423     href : '',
32424     /**
32425      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32426      */   
32427     size : 'xs',
32428     
32429     /**
32430      * @cfg {String} placetitle (center|bottom)
32431      */   
32432     placetitle : '',
32433     
32434     /**
32435      * @cfg {Boolean} isFitContainer defalut true
32436      */   
32437     isFitContainer : true, 
32438     
32439     /**
32440      * @cfg {Boolean} preventDefault defalut false
32441      */   
32442     preventDefault : false, 
32443     
32444     /**
32445      * @cfg {Boolean} inverse defalut false
32446      */   
32447     maskInverse : false, 
32448     
32449     getAutoCreate : function()
32450     {
32451         if(!this.isFitContainer){
32452             return this.getSplitAutoCreate();
32453         }
32454         
32455         var cls = 'masonry-brick masonry-brick-full';
32456         
32457         if(this.href.length){
32458             cls += ' masonry-brick-link';
32459         }
32460         
32461         if(this.bgimage.length){
32462             cls += ' masonry-brick-image';
32463         }
32464         
32465         if(this.maskInverse){
32466             cls += ' mask-inverse';
32467         }
32468         
32469         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32470             cls += ' enable-mask';
32471         }
32472         
32473         if(this.size){
32474             cls += ' masonry-' + this.size + '-brick';
32475         }
32476         
32477         if(this.placetitle.length){
32478             
32479             switch (this.placetitle) {
32480                 case 'center' :
32481                     cls += ' masonry-center-title';
32482                     break;
32483                 case 'bottom' :
32484                     cls += ' masonry-bottom-title';
32485                     break;
32486                 default:
32487                     break;
32488             }
32489             
32490         } else {
32491             if(!this.html.length && !this.bgimage.length){
32492                 cls += ' masonry-center-title';
32493             }
32494
32495             if(!this.html.length && this.bgimage.length){
32496                 cls += ' masonry-bottom-title';
32497             }
32498         }
32499         
32500         if(this.cls){
32501             cls += ' ' + this.cls;
32502         }
32503         
32504         var cfg = {
32505             tag: (this.href.length) ? 'a' : 'div',
32506             cls: cls,
32507             cn: [
32508                 {
32509                     tag: 'div',
32510                     cls: 'masonry-brick-mask'
32511                 },
32512                 {
32513                     tag: 'div',
32514                     cls: 'masonry-brick-paragraph',
32515                     cn: []
32516                 }
32517             ]
32518         };
32519         
32520         if(this.href.length){
32521             cfg.href = this.href;
32522         }
32523         
32524         var cn = cfg.cn[1].cn;
32525         
32526         if(this.title.length){
32527             cn.push({
32528                 tag: 'h4',
32529                 cls: 'masonry-brick-title',
32530                 html: this.title
32531             });
32532         }
32533         
32534         if(this.html.length){
32535             cn.push({
32536                 tag: 'p',
32537                 cls: 'masonry-brick-text',
32538                 html: this.html
32539             });
32540         }
32541         
32542         if (!this.title.length && !this.html.length) {
32543             cfg.cn[1].cls += ' hide';
32544         }
32545         
32546         if(this.bgimage.length){
32547             cfg.cn.push({
32548                 tag: 'img',
32549                 cls: 'masonry-brick-image-view',
32550                 src: this.bgimage
32551             });
32552         }
32553         
32554         if(this.videourl.length){
32555             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32556             // youtube support only?
32557             cfg.cn.push({
32558                 tag: 'iframe',
32559                 cls: 'masonry-brick-image-view',
32560                 src: vurl,
32561                 frameborder : 0,
32562                 allowfullscreen : true
32563             });
32564         }
32565         
32566         return cfg;
32567         
32568     },
32569     
32570     getSplitAutoCreate : function()
32571     {
32572         var cls = 'masonry-brick masonry-brick-split';
32573         
32574         if(this.href.length){
32575             cls += ' masonry-brick-link';
32576         }
32577         
32578         if(this.bgimage.length){
32579             cls += ' masonry-brick-image';
32580         }
32581         
32582         if(this.size){
32583             cls += ' masonry-' + this.size + '-brick';
32584         }
32585         
32586         switch (this.placetitle) {
32587             case 'center' :
32588                 cls += ' masonry-center-title';
32589                 break;
32590             case 'bottom' :
32591                 cls += ' masonry-bottom-title';
32592                 break;
32593             default:
32594                 if(!this.bgimage.length){
32595                     cls += ' masonry-center-title';
32596                 }
32597
32598                 if(this.bgimage.length){
32599                     cls += ' masonry-bottom-title';
32600                 }
32601                 break;
32602         }
32603         
32604         if(this.cls){
32605             cls += ' ' + this.cls;
32606         }
32607         
32608         var cfg = {
32609             tag: (this.href.length) ? 'a' : 'div',
32610             cls: cls,
32611             cn: [
32612                 {
32613                     tag: 'div',
32614                     cls: 'masonry-brick-split-head',
32615                     cn: [
32616                         {
32617                             tag: 'div',
32618                             cls: 'masonry-brick-paragraph',
32619                             cn: []
32620                         }
32621                     ]
32622                 },
32623                 {
32624                     tag: 'div',
32625                     cls: 'masonry-brick-split-body',
32626                     cn: []
32627                 }
32628             ]
32629         };
32630         
32631         if(this.href.length){
32632             cfg.href = this.href;
32633         }
32634         
32635         if(this.title.length){
32636             cfg.cn[0].cn[0].cn.push({
32637                 tag: 'h4',
32638                 cls: 'masonry-brick-title',
32639                 html: this.title
32640             });
32641         }
32642         
32643         if(this.html.length){
32644             cfg.cn[1].cn.push({
32645                 tag: 'p',
32646                 cls: 'masonry-brick-text',
32647                 html: this.html
32648             });
32649         }
32650
32651         if(this.bgimage.length){
32652             cfg.cn[0].cn.push({
32653                 tag: 'img',
32654                 cls: 'masonry-brick-image-view',
32655                 src: this.bgimage
32656             });
32657         }
32658         
32659         if(this.videourl.length){
32660             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32661             // youtube support only?
32662             cfg.cn[0].cn.cn.push({
32663                 tag: 'iframe',
32664                 cls: 'masonry-brick-image-view',
32665                 src: vurl,
32666                 frameborder : 0,
32667                 allowfullscreen : true
32668             });
32669         }
32670         
32671         return cfg;
32672     },
32673     
32674     initEvents: function() 
32675     {
32676         switch (this.size) {
32677             case 'xs' :
32678                 this.x = 1;
32679                 this.y = 1;
32680                 break;
32681             case 'sm' :
32682                 this.x = 2;
32683                 this.y = 2;
32684                 break;
32685             case 'md' :
32686             case 'md-left' :
32687             case 'md-right' :
32688                 this.x = 3;
32689                 this.y = 3;
32690                 break;
32691             case 'tall' :
32692                 this.x = 2;
32693                 this.y = 3;
32694                 break;
32695             case 'wide' :
32696                 this.x = 3;
32697                 this.y = 2;
32698                 break;
32699             case 'wide-thin' :
32700                 this.x = 3;
32701                 this.y = 1;
32702                 break;
32703                         
32704             default :
32705                 break;
32706         }
32707         
32708         if(Roo.isTouch){
32709             this.el.on('touchstart', this.onTouchStart, this);
32710             this.el.on('touchmove', this.onTouchMove, this);
32711             this.el.on('touchend', this.onTouchEnd, this);
32712             this.el.on('contextmenu', this.onContextMenu, this);
32713         } else {
32714             this.el.on('mouseenter'  ,this.enter, this);
32715             this.el.on('mouseleave', this.leave, this);
32716             this.el.on('click', this.onClick, this);
32717         }
32718         
32719         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32720             this.parent().bricks.push(this);   
32721         }
32722         
32723     },
32724     
32725     onClick: function(e, el)
32726     {
32727         var time = this.endTimer - this.startTimer;
32728         // Roo.log(e.preventDefault());
32729         if(Roo.isTouch){
32730             if(time > 1000){
32731                 e.preventDefault();
32732                 return;
32733             }
32734         }
32735         
32736         if(!this.preventDefault){
32737             return;
32738         }
32739         
32740         e.preventDefault();
32741         
32742         if (this.activcClass != '') {
32743             this.selectBrick();
32744         }
32745         
32746         this.fireEvent('click', this);
32747     },
32748     
32749     enter: function(e, el)
32750     {
32751         e.preventDefault();
32752         
32753         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32754             return;
32755         }
32756         
32757         if(this.bgimage.length && this.html.length){
32758             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32759         }
32760     },
32761     
32762     leave: function(e, el)
32763     {
32764         e.preventDefault();
32765         
32766         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32767             return;
32768         }
32769         
32770         if(this.bgimage.length && this.html.length){
32771             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32772         }
32773     },
32774     
32775     onTouchStart: function(e, el)
32776     {
32777 //        e.preventDefault();
32778         
32779         this.touchmoved = false;
32780         
32781         if(!this.isFitContainer){
32782             return;
32783         }
32784         
32785         if(!this.bgimage.length || !this.html.length){
32786             return;
32787         }
32788         
32789         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32790         
32791         this.timer = new Date().getTime();
32792         
32793     },
32794     
32795     onTouchMove: function(e, el)
32796     {
32797         this.touchmoved = true;
32798     },
32799     
32800     onContextMenu : function(e,el)
32801     {
32802         e.preventDefault();
32803         e.stopPropagation();
32804         return false;
32805     },
32806     
32807     onTouchEnd: function(e, el)
32808     {
32809 //        e.preventDefault();
32810         
32811         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32812         
32813             this.leave(e,el);
32814             
32815             return;
32816         }
32817         
32818         if(!this.bgimage.length || !this.html.length){
32819             
32820             if(this.href.length){
32821                 window.location.href = this.href;
32822             }
32823             
32824             return;
32825         }
32826         
32827         if(!this.isFitContainer){
32828             return;
32829         }
32830         
32831         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32832         
32833         window.location.href = this.href;
32834     },
32835     
32836     //selection on single brick only
32837     selectBrick : function() {
32838         
32839         if (!this.parentId) {
32840             return;
32841         }
32842         
32843         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32844         var index = m.selectedBrick.indexOf(this.id);
32845         
32846         if ( index > -1) {
32847             m.selectedBrick.splice(index,1);
32848             this.el.removeClass(this.activeClass);
32849             return;
32850         }
32851         
32852         for(var i = 0; i < m.selectedBrick.length; i++) {
32853             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32854             b.el.removeClass(b.activeClass);
32855         }
32856         
32857         m.selectedBrick = [];
32858         
32859         m.selectedBrick.push(this.id);
32860         this.el.addClass(this.activeClass);
32861         return;
32862     }
32863     
32864 });
32865
32866 Roo.apply(Roo.bootstrap.MasonryBrick, {
32867     
32868     //groups: {},
32869     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32870      /**
32871     * register a Masonry Brick
32872     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32873     */
32874     
32875     register : function(brick)
32876     {
32877         //this.groups[brick.id] = brick;
32878         this.groups.add(brick.id, brick);
32879     },
32880     /**
32881     * fetch a  masonry brick based on the masonry brick ID
32882     * @param {string} the masonry brick to add
32883     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32884     */
32885     
32886     get: function(brick_id) 
32887     {
32888         // if (typeof(this.groups[brick_id]) == 'undefined') {
32889         //     return false;
32890         // }
32891         // return this.groups[brick_id] ;
32892         
32893         if(this.groups.key(brick_id)) {
32894             return this.groups.key(brick_id);
32895         }
32896         
32897         return false;
32898     }
32899     
32900     
32901     
32902 });
32903
32904  /*
32905  * - LGPL
32906  *
32907  * element
32908  * 
32909  */
32910
32911 /**
32912  * @class Roo.bootstrap.Brick
32913  * @extends Roo.bootstrap.Component
32914  * Bootstrap Brick class
32915  * 
32916  * @constructor
32917  * Create a new Brick
32918  * @param {Object} config The config object
32919  */
32920
32921 Roo.bootstrap.Brick = function(config){
32922     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32923     
32924     this.addEvents({
32925         // raw events
32926         /**
32927          * @event click
32928          * When a Brick is click
32929          * @param {Roo.bootstrap.Brick} this
32930          * @param {Roo.EventObject} e
32931          */
32932         "click" : true
32933     });
32934 };
32935
32936 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
32937     
32938     /**
32939      * @cfg {String} title
32940      */   
32941     title : '',
32942     /**
32943      * @cfg {String} html
32944      */   
32945     html : '',
32946     /**
32947      * @cfg {String} bgimage
32948      */   
32949     bgimage : '',
32950     /**
32951      * @cfg {String} cls
32952      */   
32953     cls : '',
32954     /**
32955      * @cfg {String} href
32956      */   
32957     href : '',
32958     /**
32959      * @cfg {String} video
32960      */   
32961     video : '',
32962     /**
32963      * @cfg {Boolean} square
32964      */   
32965     square : true,
32966     
32967     getAutoCreate : function()
32968     {
32969         var cls = 'roo-brick';
32970         
32971         if(this.href.length){
32972             cls += ' roo-brick-link';
32973         }
32974         
32975         if(this.bgimage.length){
32976             cls += ' roo-brick-image';
32977         }
32978         
32979         if(!this.html.length && !this.bgimage.length){
32980             cls += ' roo-brick-center-title';
32981         }
32982         
32983         if(!this.html.length && this.bgimage.length){
32984             cls += ' roo-brick-bottom-title';
32985         }
32986         
32987         if(this.cls){
32988             cls += ' ' + this.cls;
32989         }
32990         
32991         var cfg = {
32992             tag: (this.href.length) ? 'a' : 'div',
32993             cls: cls,
32994             cn: [
32995                 {
32996                     tag: 'div',
32997                     cls: 'roo-brick-paragraph',
32998                     cn: []
32999                 }
33000             ]
33001         };
33002         
33003         if(this.href.length){
33004             cfg.href = this.href;
33005         }
33006         
33007         var cn = cfg.cn[0].cn;
33008         
33009         if(this.title.length){
33010             cn.push({
33011                 tag: 'h4',
33012                 cls: 'roo-brick-title',
33013                 html: this.title
33014             });
33015         }
33016         
33017         if(this.html.length){
33018             cn.push({
33019                 tag: 'p',
33020                 cls: 'roo-brick-text',
33021                 html: this.html
33022             });
33023         } else {
33024             cn.cls += ' hide';
33025         }
33026         
33027         if(this.bgimage.length){
33028             cfg.cn.push({
33029                 tag: 'img',
33030                 cls: 'roo-brick-image-view',
33031                 src: this.bgimage
33032             });
33033         }
33034         
33035         return cfg;
33036     },
33037     
33038     initEvents: function() 
33039     {
33040         if(this.title.length || this.html.length){
33041             this.el.on('mouseenter'  ,this.enter, this);
33042             this.el.on('mouseleave', this.leave, this);
33043         }
33044         
33045         Roo.EventManager.onWindowResize(this.resize, this); 
33046         
33047         if(this.bgimage.length){
33048             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33049             this.imageEl.on('load', this.onImageLoad, this);
33050             return;
33051         }
33052         
33053         this.resize();
33054     },
33055     
33056     onImageLoad : function()
33057     {
33058         this.resize();
33059     },
33060     
33061     resize : function()
33062     {
33063         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33064         
33065         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33066         
33067         if(this.bgimage.length){
33068             var image = this.el.select('.roo-brick-image-view', true).first();
33069             
33070             image.setWidth(paragraph.getWidth());
33071             
33072             if(this.square){
33073                 image.setHeight(paragraph.getWidth());
33074             }
33075             
33076             this.el.setHeight(image.getHeight());
33077             paragraph.setHeight(image.getHeight());
33078             
33079         }
33080         
33081     },
33082     
33083     enter: function(e, el)
33084     {
33085         e.preventDefault();
33086         
33087         if(this.bgimage.length){
33088             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33089             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33090         }
33091     },
33092     
33093     leave: function(e, el)
33094     {
33095         e.preventDefault();
33096         
33097         if(this.bgimage.length){
33098             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33099             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33100         }
33101     }
33102     
33103 });
33104
33105  
33106
33107  /*
33108  * - LGPL
33109  *
33110  * Number field 
33111  */
33112
33113 /**
33114  * @class Roo.bootstrap.NumberField
33115  * @extends Roo.bootstrap.Input
33116  * Bootstrap NumberField class
33117  * 
33118  * 
33119  * 
33120  * 
33121  * @constructor
33122  * Create a new NumberField
33123  * @param {Object} config The config object
33124  */
33125
33126 Roo.bootstrap.NumberField = function(config){
33127     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33128 };
33129
33130 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33131     
33132     /**
33133      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33134      */
33135     allowDecimals : true,
33136     /**
33137      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33138      */
33139     decimalSeparator : ".",
33140     /**
33141      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33142      */
33143     decimalPrecision : 2,
33144     /**
33145      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33146      */
33147     allowNegative : true,
33148     
33149     /**
33150      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33151      */
33152     allowZero: true,
33153     /**
33154      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33155      */
33156     minValue : Number.NEGATIVE_INFINITY,
33157     /**
33158      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33159      */
33160     maxValue : Number.MAX_VALUE,
33161     /**
33162      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33163      */
33164     minText : "The minimum value for this field is {0}",
33165     /**
33166      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33167      */
33168     maxText : "The maximum value for this field is {0}",
33169     /**
33170      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33171      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33172      */
33173     nanText : "{0} is not a valid number",
33174     /**
33175      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
33176      */
33177     castInt : true,
33178     /**
33179      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33180      */
33181     thousandsDelimiter : false,
33182     /**
33183      * @cfg {String} valueAlign alignment of value
33184      */
33185     valueAlign : "left",
33186
33187     getAutoCreate : function()
33188     {
33189         var hiddenInput = {
33190             tag: 'input',
33191             type: 'hidden',
33192             id: Roo.id(),
33193             cls: 'hidden-number-input'
33194         };
33195         
33196         if (this.name) {
33197             hiddenInput.name = this.name;
33198         }
33199         
33200         this.name = '';
33201         
33202         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33203         
33204         this.name = hiddenInput.name;
33205         
33206         if(cfg.cn.length > 0) {
33207             cfg.cn.push(hiddenInput);
33208         }
33209         
33210         return cfg;
33211     },
33212
33213     // private
33214     initEvents : function()
33215     {   
33216         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33217         
33218         var allowed = "0123456789";
33219         
33220         if(this.allowDecimals){
33221             allowed += this.decimalSeparator;
33222         }
33223         
33224         if(this.allowNegative){
33225             allowed += "-";
33226         }
33227         
33228         if(this.thousandsDelimiter) {
33229             allowed += ",";
33230         }
33231         
33232         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33233         
33234         var keyPress = function(e){
33235             
33236             var k = e.getKey();
33237             
33238             var c = e.getCharCode();
33239             
33240             if(
33241                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33242                     allowed.indexOf(String.fromCharCode(c)) === -1
33243             ){
33244                 e.stopEvent();
33245                 return;
33246             }
33247             
33248             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33249                 return;
33250             }
33251             
33252             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33253                 e.stopEvent();
33254             }
33255         };
33256         
33257         this.el.on("keypress", keyPress, this);
33258     },
33259     
33260     validateValue : function(value)
33261     {
33262         
33263         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33264             return false;
33265         }
33266         
33267         var num = this.parseValue(value);
33268         
33269         if(isNaN(num)){
33270             this.markInvalid(String.format(this.nanText, value));
33271             return false;
33272         }
33273         
33274         if(num < this.minValue){
33275             this.markInvalid(String.format(this.minText, this.minValue));
33276             return false;
33277         }
33278         
33279         if(num > this.maxValue){
33280             this.markInvalid(String.format(this.maxText, this.maxValue));
33281             return false;
33282         }
33283         
33284         return true;
33285     },
33286
33287     getValue : function()
33288     {
33289         var v = this.hiddenEl().getValue();
33290         
33291         return this.fixPrecision(this.parseValue(v));
33292     },
33293
33294     parseValue : function(value)
33295     {
33296         if(this.thousandsDelimiter) {
33297             value += "";
33298             r = new RegExp(",", "g");
33299             value = value.replace(r, "");
33300         }
33301         
33302         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33303         return isNaN(value) ? '' : value;
33304     },
33305
33306     fixPrecision : function(value)
33307     {
33308         if(this.thousandsDelimiter) {
33309             value += "";
33310             r = new RegExp(",", "g");
33311             value = value.replace(r, "");
33312         }
33313         
33314         var nan = isNaN(value);
33315         
33316         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33317             return nan ? '' : value;
33318         }
33319         return parseFloat(value).toFixed(this.decimalPrecision);
33320     },
33321
33322     setValue : function(v)
33323     {
33324         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33325         
33326         this.value = v;
33327         
33328         if(this.rendered){
33329             
33330             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33331             
33332             this.inputEl().dom.value = (v == '') ? '' :
33333                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33334             
33335             if(!this.allowZero && v === '0') {
33336                 this.hiddenEl().dom.value = '';
33337                 this.inputEl().dom.value = '';
33338             }
33339             
33340             this.validate();
33341         }
33342     },
33343
33344     decimalPrecisionFcn : function(v)
33345     {
33346         return Math.floor(v);
33347     },
33348
33349     beforeBlur : function()
33350     {
33351         if(!this.castInt){
33352             return;
33353         }
33354         
33355         var v = this.parseValue(this.getRawValue());
33356         
33357         if(v || v === 0){
33358             this.setValue(v);
33359         }
33360     },
33361     
33362     hiddenEl : function()
33363     {
33364         return this.el.select('input.hidden-number-input',true).first();
33365     }
33366     
33367 });
33368
33369  
33370
33371 /*
33372 * Licence: LGPL
33373 */
33374
33375 /**
33376  * @class Roo.bootstrap.DocumentSlider
33377  * @extends Roo.bootstrap.Component
33378  * Bootstrap DocumentSlider class
33379  * 
33380  * @constructor
33381  * Create a new DocumentViewer
33382  * @param {Object} config The config object
33383  */
33384
33385 Roo.bootstrap.DocumentSlider = function(config){
33386     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33387     
33388     this.files = [];
33389     
33390     this.addEvents({
33391         /**
33392          * @event initial
33393          * Fire after initEvent
33394          * @param {Roo.bootstrap.DocumentSlider} this
33395          */
33396         "initial" : true,
33397         /**
33398          * @event update
33399          * Fire after update
33400          * @param {Roo.bootstrap.DocumentSlider} this
33401          */
33402         "update" : true,
33403         /**
33404          * @event click
33405          * Fire after click
33406          * @param {Roo.bootstrap.DocumentSlider} this
33407          */
33408         "click" : true
33409     });
33410 };
33411
33412 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33413     
33414     files : false,
33415     
33416     indicator : 0,
33417     
33418     getAutoCreate : function()
33419     {
33420         var cfg = {
33421             tag : 'div',
33422             cls : 'roo-document-slider',
33423             cn : [
33424                 {
33425                     tag : 'div',
33426                     cls : 'roo-document-slider-header',
33427                     cn : [
33428                         {
33429                             tag : 'div',
33430                             cls : 'roo-document-slider-header-title'
33431                         }
33432                     ]
33433                 },
33434                 {
33435                     tag : 'div',
33436                     cls : 'roo-document-slider-body',
33437                     cn : [
33438                         {
33439                             tag : 'div',
33440                             cls : 'roo-document-slider-prev',
33441                             cn : [
33442                                 {
33443                                     tag : 'i',
33444                                     cls : 'fa fa-chevron-left'
33445                                 }
33446                             ]
33447                         },
33448                         {
33449                             tag : 'div',
33450                             cls : 'roo-document-slider-thumb',
33451                             cn : [
33452                                 {
33453                                     tag : 'img',
33454                                     cls : 'roo-document-slider-image'
33455                                 }
33456                             ]
33457                         },
33458                         {
33459                             tag : 'div',
33460                             cls : 'roo-document-slider-next',
33461                             cn : [
33462                                 {
33463                                     tag : 'i',
33464                                     cls : 'fa fa-chevron-right'
33465                                 }
33466                             ]
33467                         }
33468                     ]
33469                 }
33470             ]
33471         };
33472         
33473         return cfg;
33474     },
33475     
33476     initEvents : function()
33477     {
33478         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33479         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33480         
33481         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33482         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33483         
33484         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33485         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33486         
33487         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33488         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33489         
33490         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33491         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33492         
33493         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33494         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33495         
33496         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33497         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33498         
33499         this.thumbEl.on('click', this.onClick, this);
33500         
33501         this.prevIndicator.on('click', this.prev, this);
33502         
33503         this.nextIndicator.on('click', this.next, this);
33504         
33505     },
33506     
33507     initial : function()
33508     {
33509         if(this.files.length){
33510             this.indicator = 1;
33511             this.update()
33512         }
33513         
33514         this.fireEvent('initial', this);
33515     },
33516     
33517     update : function()
33518     {
33519         this.imageEl.attr('src', this.files[this.indicator - 1]);
33520         
33521         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33522         
33523         this.prevIndicator.show();
33524         
33525         if(this.indicator == 1){
33526             this.prevIndicator.hide();
33527         }
33528         
33529         this.nextIndicator.show();
33530         
33531         if(this.indicator == this.files.length){
33532             this.nextIndicator.hide();
33533         }
33534         
33535         this.thumbEl.scrollTo('top');
33536         
33537         this.fireEvent('update', this);
33538     },
33539     
33540     onClick : function(e)
33541     {
33542         e.preventDefault();
33543         
33544         this.fireEvent('click', this);
33545     },
33546     
33547     prev : function(e)
33548     {
33549         e.preventDefault();
33550         
33551         this.indicator = Math.max(1, this.indicator - 1);
33552         
33553         this.update();
33554     },
33555     
33556     next : function(e)
33557     {
33558         e.preventDefault();
33559         
33560         this.indicator = Math.min(this.files.length, this.indicator + 1);
33561         
33562         this.update();
33563     }
33564 });
33565 /*
33566  * - LGPL
33567  *
33568  * RadioSet
33569  *
33570  *
33571  */
33572
33573 /**
33574  * @class Roo.bootstrap.RadioSet
33575  * @extends Roo.bootstrap.Input
33576  * Bootstrap RadioSet class
33577  * @cfg {String} indicatorpos (left|right) default left
33578  * @cfg {Boolean} inline (true|false) inline the element (default true)
33579  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33580  * @constructor
33581  * Create a new RadioSet
33582  * @param {Object} config The config object
33583  */
33584
33585 Roo.bootstrap.RadioSet = function(config){
33586     
33587     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33588     
33589     this.radioes = [];
33590     
33591     Roo.bootstrap.RadioSet.register(this);
33592     
33593     this.addEvents({
33594         /**
33595         * @event check
33596         * Fires when the element is checked or unchecked.
33597         * @param {Roo.bootstrap.RadioSet} this This radio
33598         * @param {Roo.bootstrap.Radio} item The checked item
33599         */
33600        check : true,
33601        /**
33602         * @event click
33603         * Fires when the element is click.
33604         * @param {Roo.bootstrap.RadioSet} this This radio set
33605         * @param {Roo.bootstrap.Radio} item The checked item
33606         * @param {Roo.EventObject} e The event object
33607         */
33608        click : true
33609     });
33610     
33611 };
33612
33613 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33614
33615     radioes : false,
33616     
33617     inline : true,
33618     
33619     weight : '',
33620     
33621     indicatorpos : 'left',
33622     
33623     getAutoCreate : function()
33624     {
33625         var label = {
33626             tag : 'label',
33627             cls : 'roo-radio-set-label',
33628             cn : [
33629                 {
33630                     tag : 'span',
33631                     html : this.fieldLabel
33632                 }
33633             ]
33634         };
33635         
33636         if(this.indicatorpos == 'left'){
33637             label.cn.unshift({
33638                 tag : 'i',
33639                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33640                 tooltip : 'This field is required'
33641             });
33642         } else {
33643             label.cn.push({
33644                 tag : 'i',
33645                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33646                 tooltip : 'This field is required'
33647             });
33648         }
33649         
33650         var items = {
33651             tag : 'div',
33652             cls : 'roo-radio-set-items'
33653         };
33654         
33655         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33656         
33657         if (align === 'left' && this.fieldLabel.length) {
33658             
33659             items = {
33660                 cls : "roo-radio-set-right", 
33661                 cn: [
33662                     items
33663                 ]
33664             };
33665             
33666             if(this.labelWidth > 12){
33667                 label.style = "width: " + this.labelWidth + 'px';
33668             }
33669             
33670             if(this.labelWidth < 13 && this.labelmd == 0){
33671                 this.labelmd = this.labelWidth;
33672             }
33673             
33674             if(this.labellg > 0){
33675                 label.cls += ' col-lg-' + this.labellg;
33676                 items.cls += ' col-lg-' + (12 - this.labellg);
33677             }
33678             
33679             if(this.labelmd > 0){
33680                 label.cls += ' col-md-' + this.labelmd;
33681                 items.cls += ' col-md-' + (12 - this.labelmd);
33682             }
33683             
33684             if(this.labelsm > 0){
33685                 label.cls += ' col-sm-' + this.labelsm;
33686                 items.cls += ' col-sm-' + (12 - this.labelsm);
33687             }
33688             
33689             if(this.labelxs > 0){
33690                 label.cls += ' col-xs-' + this.labelxs;
33691                 items.cls += ' col-xs-' + (12 - this.labelxs);
33692             }
33693         }
33694         
33695         var cfg = {
33696             tag : 'div',
33697             cls : 'roo-radio-set',
33698             cn : [
33699                 {
33700                     tag : 'input',
33701                     cls : 'roo-radio-set-input',
33702                     type : 'hidden',
33703                     name : this.name,
33704                     value : this.value ? this.value :  ''
33705                 },
33706                 label,
33707                 items
33708             ]
33709         };
33710         
33711         if(this.weight.length){
33712             cfg.cls += ' roo-radio-' + this.weight;
33713         }
33714         
33715         if(this.inline) {
33716             cfg.cls += ' roo-radio-set-inline';
33717         }
33718         
33719         var settings=this;
33720         ['xs','sm','md','lg'].map(function(size){
33721             if (settings[size]) {
33722                 cfg.cls += ' col-' + size + '-' + settings[size];
33723             }
33724         });
33725         
33726         return cfg;
33727         
33728     },
33729
33730     initEvents : function()
33731     {
33732         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33733         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33734         
33735         if(!this.fieldLabel.length){
33736             this.labelEl.hide();
33737         }
33738         
33739         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33740         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33741         
33742         this.indicatorEl().addClass('invisible');
33743         
33744         this.originalValue = this.getValue();
33745         
33746     },
33747     
33748     inputEl: function ()
33749     {
33750         return this.el.select('.roo-radio-set-input', true).first();
33751     },
33752     
33753     getChildContainer : function()
33754     {
33755         return this.itemsEl;
33756     },
33757     
33758     register : function(item)
33759     {
33760         this.radioes.push(item);
33761         
33762     },
33763     
33764     validate : function()
33765     {   
33766         if(this.getEl().hasClass('hidden')){
33767             return true;
33768         }
33769         
33770         var valid = false;
33771         
33772         Roo.each(this.radioes, function(i){
33773             if(!i.checked){
33774                 return;
33775             }
33776             
33777             valid = true;
33778             return false;
33779         });
33780         
33781         if(this.allowBlank) {
33782             return true;
33783         }
33784         
33785         if(this.disabled || valid){
33786             this.markValid();
33787             return true;
33788         }
33789         
33790         this.markInvalid();
33791         return false;
33792         
33793     },
33794     
33795     markValid : function()
33796     {
33797         if(this.labelEl.isVisible(true)){
33798             this.indicatorEl().removeClass('visible');
33799             this.indicatorEl().addClass('invisible');
33800         }
33801         
33802         this.el.removeClass([this.invalidClass, this.validClass]);
33803         this.el.addClass(this.validClass);
33804         
33805         this.fireEvent('valid', this);
33806     },
33807     
33808     markInvalid : function(msg)
33809     {
33810         if(this.allowBlank || this.disabled){
33811             return;
33812         }
33813         
33814         if(this.labelEl.isVisible(true)){
33815             this.indicatorEl().removeClass('invisible');
33816             this.indicatorEl().addClass('visible');
33817         }
33818         
33819         this.el.removeClass([this.invalidClass, this.validClass]);
33820         this.el.addClass(this.invalidClass);
33821         
33822         this.fireEvent('invalid', this, msg);
33823         
33824     },
33825     
33826     setValue : function(v, suppressEvent)
33827     {   
33828         if(this.value === v){
33829             return;
33830         }
33831         
33832         this.value = v;
33833         
33834         if(this.rendered){
33835             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33836         }
33837         
33838         Roo.each(this.radioes, function(i){
33839             i.checked = false;
33840             i.el.removeClass('checked');
33841         });
33842         
33843         Roo.each(this.radioes, function(i){
33844             
33845             if(i.value === v || i.value.toString() === v.toString()){
33846                 i.checked = true;
33847                 i.el.addClass('checked');
33848                 
33849                 if(suppressEvent !== true){
33850                     this.fireEvent('check', this, i);
33851                 }
33852                 
33853                 return false;
33854             }
33855             
33856         }, this);
33857         
33858         this.validate();
33859     },
33860     
33861     clearInvalid : function(){
33862         
33863         if(!this.el || this.preventMark){
33864             return;
33865         }
33866         
33867         this.el.removeClass([this.invalidClass]);
33868         
33869         this.fireEvent('valid', this);
33870     }
33871     
33872 });
33873
33874 Roo.apply(Roo.bootstrap.RadioSet, {
33875     
33876     groups: {},
33877     
33878     register : function(set)
33879     {
33880         this.groups[set.name] = set;
33881     },
33882     
33883     get: function(name) 
33884     {
33885         if (typeof(this.groups[name]) == 'undefined') {
33886             return false;
33887         }
33888         
33889         return this.groups[name] ;
33890     }
33891     
33892 });
33893 /*
33894  * Based on:
33895  * Ext JS Library 1.1.1
33896  * Copyright(c) 2006-2007, Ext JS, LLC.
33897  *
33898  * Originally Released Under LGPL - original licence link has changed is not relivant.
33899  *
33900  * Fork - LGPL
33901  * <script type="text/javascript">
33902  */
33903
33904
33905 /**
33906  * @class Roo.bootstrap.SplitBar
33907  * @extends Roo.util.Observable
33908  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33909  * <br><br>
33910  * Usage:
33911  * <pre><code>
33912 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33913                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33914 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33915 split.minSize = 100;
33916 split.maxSize = 600;
33917 split.animate = true;
33918 split.on('moved', splitterMoved);
33919 </code></pre>
33920  * @constructor
33921  * Create a new SplitBar
33922  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
33923  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
33924  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33925  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
33926                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33927                         position of the SplitBar).
33928  */
33929 Roo.bootstrap.SplitBar = function(cfg){
33930     
33931     /** @private */
33932     
33933     //{
33934     //  dragElement : elm
33935     //  resizingElement: el,
33936         // optional..
33937     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
33938     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
33939         // existingProxy ???
33940     //}
33941     
33942     this.el = Roo.get(cfg.dragElement, true);
33943     this.el.dom.unselectable = "on";
33944     /** @private */
33945     this.resizingEl = Roo.get(cfg.resizingElement, true);
33946
33947     /**
33948      * @private
33949      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33950      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
33951      * @type Number
33952      */
33953     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
33954     
33955     /**
33956      * The minimum size of the resizing element. (Defaults to 0)
33957      * @type Number
33958      */
33959     this.minSize = 0;
33960     
33961     /**
33962      * The maximum size of the resizing element. (Defaults to 2000)
33963      * @type Number
33964      */
33965     this.maxSize = 2000;
33966     
33967     /**
33968      * Whether to animate the transition to the new size
33969      * @type Boolean
33970      */
33971     this.animate = false;
33972     
33973     /**
33974      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
33975      * @type Boolean
33976      */
33977     this.useShim = false;
33978     
33979     /** @private */
33980     this.shim = null;
33981     
33982     if(!cfg.existingProxy){
33983         /** @private */
33984         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
33985     }else{
33986         this.proxy = Roo.get(cfg.existingProxy).dom;
33987     }
33988     /** @private */
33989     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
33990     
33991     /** @private */
33992     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
33993     
33994     /** @private */
33995     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
33996     
33997     /** @private */
33998     this.dragSpecs = {};
33999     
34000     /**
34001      * @private The adapter to use to positon and resize elements
34002      */
34003     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34004     this.adapter.init(this);
34005     
34006     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34007         /** @private */
34008         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34009         this.el.addClass("roo-splitbar-h");
34010     }else{
34011         /** @private */
34012         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34013         this.el.addClass("roo-splitbar-v");
34014     }
34015     
34016     this.addEvents({
34017         /**
34018          * @event resize
34019          * Fires when the splitter is moved (alias for {@link #event-moved})
34020          * @param {Roo.bootstrap.SplitBar} this
34021          * @param {Number} newSize the new width or height
34022          */
34023         "resize" : true,
34024         /**
34025          * @event moved
34026          * Fires when the splitter is moved
34027          * @param {Roo.bootstrap.SplitBar} this
34028          * @param {Number} newSize the new width or height
34029          */
34030         "moved" : true,
34031         /**
34032          * @event beforeresize
34033          * Fires before the splitter is dragged
34034          * @param {Roo.bootstrap.SplitBar} this
34035          */
34036         "beforeresize" : true,
34037
34038         "beforeapply" : true
34039     });
34040
34041     Roo.util.Observable.call(this);
34042 };
34043
34044 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34045     onStartProxyDrag : function(x, y){
34046         this.fireEvent("beforeresize", this);
34047         if(!this.overlay){
34048             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34049             o.unselectable();
34050             o.enableDisplayMode("block");
34051             // all splitbars share the same overlay
34052             Roo.bootstrap.SplitBar.prototype.overlay = o;
34053         }
34054         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34055         this.overlay.show();
34056         Roo.get(this.proxy).setDisplayed("block");
34057         var size = this.adapter.getElementSize(this);
34058         this.activeMinSize = this.getMinimumSize();;
34059         this.activeMaxSize = this.getMaximumSize();;
34060         var c1 = size - this.activeMinSize;
34061         var c2 = Math.max(this.activeMaxSize - size, 0);
34062         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34063             this.dd.resetConstraints();
34064             this.dd.setXConstraint(
34065                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34066                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34067             );
34068             this.dd.setYConstraint(0, 0);
34069         }else{
34070             this.dd.resetConstraints();
34071             this.dd.setXConstraint(0, 0);
34072             this.dd.setYConstraint(
34073                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34074                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34075             );
34076          }
34077         this.dragSpecs.startSize = size;
34078         this.dragSpecs.startPoint = [x, y];
34079         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34080     },
34081     
34082     /** 
34083      * @private Called after the drag operation by the DDProxy
34084      */
34085     onEndProxyDrag : function(e){
34086         Roo.get(this.proxy).setDisplayed(false);
34087         var endPoint = Roo.lib.Event.getXY(e);
34088         if(this.overlay){
34089             this.overlay.hide();
34090         }
34091         var newSize;
34092         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34093             newSize = this.dragSpecs.startSize + 
34094                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34095                     endPoint[0] - this.dragSpecs.startPoint[0] :
34096                     this.dragSpecs.startPoint[0] - endPoint[0]
34097                 );
34098         }else{
34099             newSize = this.dragSpecs.startSize + 
34100                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34101                     endPoint[1] - this.dragSpecs.startPoint[1] :
34102                     this.dragSpecs.startPoint[1] - endPoint[1]
34103                 );
34104         }
34105         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34106         if(newSize != this.dragSpecs.startSize){
34107             if(this.fireEvent('beforeapply', this, newSize) !== false){
34108                 this.adapter.setElementSize(this, newSize);
34109                 this.fireEvent("moved", this, newSize);
34110                 this.fireEvent("resize", this, newSize);
34111             }
34112         }
34113     },
34114     
34115     /**
34116      * Get the adapter this SplitBar uses
34117      * @return The adapter object
34118      */
34119     getAdapter : function(){
34120         return this.adapter;
34121     },
34122     
34123     /**
34124      * Set the adapter this SplitBar uses
34125      * @param {Object} adapter A SplitBar adapter object
34126      */
34127     setAdapter : function(adapter){
34128         this.adapter = adapter;
34129         this.adapter.init(this);
34130     },
34131     
34132     /**
34133      * Gets the minimum size for the resizing element
34134      * @return {Number} The minimum size
34135      */
34136     getMinimumSize : function(){
34137         return this.minSize;
34138     },
34139     
34140     /**
34141      * Sets the minimum size for the resizing element
34142      * @param {Number} minSize The minimum size
34143      */
34144     setMinimumSize : function(minSize){
34145         this.minSize = minSize;
34146     },
34147     
34148     /**
34149      * Gets the maximum size for the resizing element
34150      * @return {Number} The maximum size
34151      */
34152     getMaximumSize : function(){
34153         return this.maxSize;
34154     },
34155     
34156     /**
34157      * Sets the maximum size for the resizing element
34158      * @param {Number} maxSize The maximum size
34159      */
34160     setMaximumSize : function(maxSize){
34161         this.maxSize = maxSize;
34162     },
34163     
34164     /**
34165      * Sets the initialize size for the resizing element
34166      * @param {Number} size The initial size
34167      */
34168     setCurrentSize : function(size){
34169         var oldAnimate = this.animate;
34170         this.animate = false;
34171         this.adapter.setElementSize(this, size);
34172         this.animate = oldAnimate;
34173     },
34174     
34175     /**
34176      * Destroy this splitbar. 
34177      * @param {Boolean} removeEl True to remove the element
34178      */
34179     destroy : function(removeEl){
34180         if(this.shim){
34181             this.shim.remove();
34182         }
34183         this.dd.unreg();
34184         this.proxy.parentNode.removeChild(this.proxy);
34185         if(removeEl){
34186             this.el.remove();
34187         }
34188     }
34189 });
34190
34191 /**
34192  * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
34193  */
34194 Roo.bootstrap.SplitBar.createProxy = function(dir){
34195     var proxy = new Roo.Element(document.createElement("div"));
34196     proxy.unselectable();
34197     var cls = 'roo-splitbar-proxy';
34198     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34199     document.body.appendChild(proxy.dom);
34200     return proxy.dom;
34201 };
34202
34203 /** 
34204  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34205  * Default Adapter. It assumes the splitter and resizing element are not positioned
34206  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34207  */
34208 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34209 };
34210
34211 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34212     // do nothing for now
34213     init : function(s){
34214     
34215     },
34216     /**
34217      * Called before drag operations to get the current size of the resizing element. 
34218      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34219      */
34220      getElementSize : function(s){
34221         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34222             return s.resizingEl.getWidth();
34223         }else{
34224             return s.resizingEl.getHeight();
34225         }
34226     },
34227     
34228     /**
34229      * Called after drag operations to set the size of the resizing element.
34230      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34231      * @param {Number} newSize The new size to set
34232      * @param {Function} onComplete A function to be invoked when resizing is complete
34233      */
34234     setElementSize : function(s, newSize, onComplete){
34235         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34236             if(!s.animate){
34237                 s.resizingEl.setWidth(newSize);
34238                 if(onComplete){
34239                     onComplete(s, newSize);
34240                 }
34241             }else{
34242                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34243             }
34244         }else{
34245             
34246             if(!s.animate){
34247                 s.resizingEl.setHeight(newSize);
34248                 if(onComplete){
34249                     onComplete(s, newSize);
34250                 }
34251             }else{
34252                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34253             }
34254         }
34255     }
34256 };
34257
34258 /** 
34259  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34260  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34261  * Adapter that  moves the splitter element to align with the resized sizing element. 
34262  * Used with an absolute positioned SplitBar.
34263  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34264  * document.body, make sure you assign an id to the body element.
34265  */
34266 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34267     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34268     this.container = Roo.get(container);
34269 };
34270
34271 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34272     init : function(s){
34273         this.basic.init(s);
34274     },
34275     
34276     getElementSize : function(s){
34277         return this.basic.getElementSize(s);
34278     },
34279     
34280     setElementSize : function(s, newSize, onComplete){
34281         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34282     },
34283     
34284     moveSplitter : function(s){
34285         var yes = Roo.bootstrap.SplitBar;
34286         switch(s.placement){
34287             case yes.LEFT:
34288                 s.el.setX(s.resizingEl.getRight());
34289                 break;
34290             case yes.RIGHT:
34291                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34292                 break;
34293             case yes.TOP:
34294                 s.el.setY(s.resizingEl.getBottom());
34295                 break;
34296             case yes.BOTTOM:
34297                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34298                 break;
34299         }
34300     }
34301 };
34302
34303 /**
34304  * Orientation constant - Create a vertical SplitBar
34305  * @static
34306  * @type Number
34307  */
34308 Roo.bootstrap.SplitBar.VERTICAL = 1;
34309
34310 /**
34311  * Orientation constant - Create a horizontal SplitBar
34312  * @static
34313  * @type Number
34314  */
34315 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34316
34317 /**
34318  * Placement constant - The resizing element is to the left of the splitter element
34319  * @static
34320  * @type Number
34321  */
34322 Roo.bootstrap.SplitBar.LEFT = 1;
34323
34324 /**
34325  * Placement constant - The resizing element is to the right of the splitter element
34326  * @static
34327  * @type Number
34328  */
34329 Roo.bootstrap.SplitBar.RIGHT = 2;
34330
34331 /**
34332  * Placement constant - The resizing element is positioned above the splitter element
34333  * @static
34334  * @type Number
34335  */
34336 Roo.bootstrap.SplitBar.TOP = 3;
34337
34338 /**
34339  * Placement constant - The resizing element is positioned under splitter element
34340  * @static
34341  * @type Number
34342  */
34343 Roo.bootstrap.SplitBar.BOTTOM = 4;
34344 Roo.namespace("Roo.bootstrap.layout");/*
34345  * Based on:
34346  * Ext JS Library 1.1.1
34347  * Copyright(c) 2006-2007, Ext JS, LLC.
34348  *
34349  * Originally Released Under LGPL - original licence link has changed is not relivant.
34350  *
34351  * Fork - LGPL
34352  * <script type="text/javascript">
34353  */
34354
34355 /**
34356  * @class Roo.bootstrap.layout.Manager
34357  * @extends Roo.bootstrap.Component
34358  * Base class for layout managers.
34359  */
34360 Roo.bootstrap.layout.Manager = function(config)
34361 {
34362     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34363
34364
34365
34366
34367
34368     /** false to disable window resize monitoring @type Boolean */
34369     this.monitorWindowResize = true;
34370     this.regions = {};
34371     this.addEvents({
34372         /**
34373          * @event layout
34374          * Fires when a layout is performed.
34375          * @param {Roo.LayoutManager} this
34376          */
34377         "layout" : true,
34378         /**
34379          * @event regionresized
34380          * Fires when the user resizes a region.
34381          * @param {Roo.LayoutRegion} region The resized region
34382          * @param {Number} newSize The new size (width for east/west, height for north/south)
34383          */
34384         "regionresized" : true,
34385         /**
34386          * @event regioncollapsed
34387          * Fires when a region is collapsed.
34388          * @param {Roo.LayoutRegion} region The collapsed region
34389          */
34390         "regioncollapsed" : true,
34391         /**
34392          * @event regionexpanded
34393          * Fires when a region is expanded.
34394          * @param {Roo.LayoutRegion} region The expanded region
34395          */
34396         "regionexpanded" : true
34397     });
34398     this.updating = false;
34399
34400     if (config.el) {
34401         this.el = Roo.get(config.el);
34402         this.initEvents();
34403     }
34404
34405 };
34406
34407 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34408
34409
34410     regions : null,
34411
34412     monitorWindowResize : true,
34413
34414
34415     updating : false,
34416
34417
34418     onRender : function(ct, position)
34419     {
34420         if(!this.el){
34421             this.el = Roo.get(ct);
34422             this.initEvents();
34423         }
34424         //this.fireEvent('render',this);
34425     },
34426
34427
34428     initEvents: function()
34429     {
34430
34431
34432         // ie scrollbar fix
34433         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34434             document.body.scroll = "no";
34435         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34436             this.el.position('relative');
34437         }
34438         this.id = this.el.id;
34439         this.el.addClass("roo-layout-container");
34440         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34441         if(this.el.dom != document.body ) {
34442             this.el.on('resize', this.layout,this);
34443             this.el.on('show', this.layout,this);
34444         }
34445
34446     },
34447
34448     /**
34449      * Returns true if this layout is currently being updated
34450      * @return {Boolean}
34451      */
34452     isUpdating : function(){
34453         return this.updating;
34454     },
34455
34456     /**
34457      * Suspend the LayoutManager from doing auto-layouts while
34458      * making multiple add or remove calls
34459      */
34460     beginUpdate : function(){
34461         this.updating = true;
34462     },
34463
34464     /**
34465      * Restore auto-layouts and optionally disable the manager from performing a layout
34466      * @param {Boolean} noLayout true to disable a layout update
34467      */
34468     endUpdate : function(noLayout){
34469         this.updating = false;
34470         if(!noLayout){
34471             this.layout();
34472         }
34473     },
34474
34475     layout: function(){
34476         // abstract...
34477     },
34478
34479     onRegionResized : function(region, newSize){
34480         this.fireEvent("regionresized", region, newSize);
34481         this.layout();
34482     },
34483
34484     onRegionCollapsed : function(region){
34485         this.fireEvent("regioncollapsed", region);
34486     },
34487
34488     onRegionExpanded : function(region){
34489         this.fireEvent("regionexpanded", region);
34490     },
34491
34492     /**
34493      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34494      * performs box-model adjustments.
34495      * @return {Object} The size as an object {width: (the width), height: (the height)}
34496      */
34497     getViewSize : function()
34498     {
34499         var size;
34500         if(this.el.dom != document.body){
34501             size = this.el.getSize();
34502         }else{
34503             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34504         }
34505         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34506         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34507         return size;
34508     },
34509
34510     /**
34511      * Returns the Element this layout is bound to.
34512      * @return {Roo.Element}
34513      */
34514     getEl : function(){
34515         return this.el;
34516     },
34517
34518     /**
34519      * Returns the specified region.
34520      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34521      * @return {Roo.LayoutRegion}
34522      */
34523     getRegion : function(target){
34524         return this.regions[target.toLowerCase()];
34525     },
34526
34527     onWindowResize : function(){
34528         if(this.monitorWindowResize){
34529             this.layout();
34530         }
34531     }
34532 });
34533 /*
34534  * Based on:
34535  * Ext JS Library 1.1.1
34536  * Copyright(c) 2006-2007, Ext JS, LLC.
34537  *
34538  * Originally Released Under LGPL - original licence link has changed is not relivant.
34539  *
34540  * Fork - LGPL
34541  * <script type="text/javascript">
34542  */
34543 /**
34544  * @class Roo.bootstrap.layout.Border
34545  * @extends Roo.bootstrap.layout.Manager
34546  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34547  * please see: examples/bootstrap/nested.html<br><br>
34548  
34549 <b>The container the layout is rendered into can be either the body element or any other element.
34550 If it is not the body element, the container needs to either be an absolute positioned element,
34551 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34552 the container size if it is not the body element.</b>
34553
34554 * @constructor
34555 * Create a new Border
34556 * @param {Object} config Configuration options
34557  */
34558 Roo.bootstrap.layout.Border = function(config){
34559     config = config || {};
34560     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34561     
34562     
34563     
34564     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34565         if(config[region]){
34566             config[region].region = region;
34567             this.addRegion(config[region]);
34568         }
34569     },this);
34570     
34571 };
34572
34573 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34574
34575 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34576     /**
34577      * Creates and adds a new region if it doesn't already exist.
34578      * @param {String} target The target region key (north, south, east, west or center).
34579      * @param {Object} config The regions config object
34580      * @return {BorderLayoutRegion} The new region
34581      */
34582     addRegion : function(config)
34583     {
34584         if(!this.regions[config.region]){
34585             var r = this.factory(config);
34586             this.bindRegion(r);
34587         }
34588         return this.regions[config.region];
34589     },
34590
34591     // private (kinda)
34592     bindRegion : function(r){
34593         this.regions[r.config.region] = r;
34594         
34595         r.on("visibilitychange",    this.layout, this);
34596         r.on("paneladded",          this.layout, this);
34597         r.on("panelremoved",        this.layout, this);
34598         r.on("invalidated",         this.layout, this);
34599         r.on("resized",             this.onRegionResized, this);
34600         r.on("collapsed",           this.onRegionCollapsed, this);
34601         r.on("expanded",            this.onRegionExpanded, this);
34602     },
34603
34604     /**
34605      * Performs a layout update.
34606      */
34607     layout : function()
34608     {
34609         if(this.updating) {
34610             return;
34611         }
34612         
34613         // render all the rebions if they have not been done alreayd?
34614         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34615             if(this.regions[region] && !this.regions[region].bodyEl){
34616                 this.regions[region].onRender(this.el)
34617             }
34618         },this);
34619         
34620         var size = this.getViewSize();
34621         var w = size.width;
34622         var h = size.height;
34623         var centerW = w;
34624         var centerH = h;
34625         var centerY = 0;
34626         var centerX = 0;
34627         //var x = 0, y = 0;
34628
34629         var rs = this.regions;
34630         var north = rs["north"];
34631         var south = rs["south"]; 
34632         var west = rs["west"];
34633         var east = rs["east"];
34634         var center = rs["center"];
34635         //if(this.hideOnLayout){ // not supported anymore
34636             //c.el.setStyle("display", "none");
34637         //}
34638         if(north && north.isVisible()){
34639             var b = north.getBox();
34640             var m = north.getMargins();
34641             b.width = w - (m.left+m.right);
34642             b.x = m.left;
34643             b.y = m.top;
34644             centerY = b.height + b.y + m.bottom;
34645             centerH -= centerY;
34646             north.updateBox(this.safeBox(b));
34647         }
34648         if(south && south.isVisible()){
34649             var b = south.getBox();
34650             var m = south.getMargins();
34651             b.width = w - (m.left+m.right);
34652             b.x = m.left;
34653             var totalHeight = (b.height + m.top + m.bottom);
34654             b.y = h - totalHeight + m.top;
34655             centerH -= totalHeight;
34656             south.updateBox(this.safeBox(b));
34657         }
34658         if(west && west.isVisible()){
34659             var b = west.getBox();
34660             var m = west.getMargins();
34661             b.height = centerH - (m.top+m.bottom);
34662             b.x = m.left;
34663             b.y = centerY + m.top;
34664             var totalWidth = (b.width + m.left + m.right);
34665             centerX += totalWidth;
34666             centerW -= totalWidth;
34667             west.updateBox(this.safeBox(b));
34668         }
34669         if(east && east.isVisible()){
34670             var b = east.getBox();
34671             var m = east.getMargins();
34672             b.height = centerH - (m.top+m.bottom);
34673             var totalWidth = (b.width + m.left + m.right);
34674             b.x = w - totalWidth + m.left;
34675             b.y = centerY + m.top;
34676             centerW -= totalWidth;
34677             east.updateBox(this.safeBox(b));
34678         }
34679         if(center){
34680             var m = center.getMargins();
34681             var centerBox = {
34682                 x: centerX + m.left,
34683                 y: centerY + m.top,
34684                 width: centerW - (m.left+m.right),
34685                 height: centerH - (m.top+m.bottom)
34686             };
34687             //if(this.hideOnLayout){
34688                 //center.el.setStyle("display", "block");
34689             //}
34690             center.updateBox(this.safeBox(centerBox));
34691         }
34692         this.el.repaint();
34693         this.fireEvent("layout", this);
34694     },
34695
34696     // private
34697     safeBox : function(box){
34698         box.width = Math.max(0, box.width);
34699         box.height = Math.max(0, box.height);
34700         return box;
34701     },
34702
34703     /**
34704      * Adds a ContentPanel (or subclass) to this layout.
34705      * @param {String} target The target region key (north, south, east, west or center).
34706      * @param {Roo.ContentPanel} panel The panel to add
34707      * @return {Roo.ContentPanel} The added panel
34708      */
34709     add : function(target, panel){
34710          
34711         target = target.toLowerCase();
34712         return this.regions[target].add(panel);
34713     },
34714
34715     /**
34716      * Remove a ContentPanel (or subclass) to this layout.
34717      * @param {String} target The target region key (north, south, east, west or center).
34718      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34719      * @return {Roo.ContentPanel} The removed panel
34720      */
34721     remove : function(target, panel){
34722         target = target.toLowerCase();
34723         return this.regions[target].remove(panel);
34724     },
34725
34726     /**
34727      * Searches all regions for a panel with the specified id
34728      * @param {String} panelId
34729      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34730      */
34731     findPanel : function(panelId){
34732         var rs = this.regions;
34733         for(var target in rs){
34734             if(typeof rs[target] != "function"){
34735                 var p = rs[target].getPanel(panelId);
34736                 if(p){
34737                     return p;
34738                 }
34739             }
34740         }
34741         return null;
34742     },
34743
34744     /**
34745      * Searches all regions for a panel with the specified id and activates (shows) it.
34746      * @param {String/ContentPanel} panelId The panels id or the panel itself
34747      * @return {Roo.ContentPanel} The shown panel or null
34748      */
34749     showPanel : function(panelId) {
34750       var rs = this.regions;
34751       for(var target in rs){
34752          var r = rs[target];
34753          if(typeof r != "function"){
34754             if(r.hasPanel(panelId)){
34755                return r.showPanel(panelId);
34756             }
34757          }
34758       }
34759       return null;
34760    },
34761
34762    /**
34763      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34764      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34765      */
34766    /*
34767     restoreState : function(provider){
34768         if(!provider){
34769             provider = Roo.state.Manager;
34770         }
34771         var sm = new Roo.LayoutStateManager();
34772         sm.init(this, provider);
34773     },
34774 */
34775  
34776  
34777     /**
34778      * Adds a xtype elements to the layout.
34779      * <pre><code>
34780
34781 layout.addxtype({
34782        xtype : 'ContentPanel',
34783        region: 'west',
34784        items: [ .... ]
34785    }
34786 );
34787
34788 layout.addxtype({
34789         xtype : 'NestedLayoutPanel',
34790         region: 'west',
34791         layout: {
34792            center: { },
34793            west: { }   
34794         },
34795         items : [ ... list of content panels or nested layout panels.. ]
34796    }
34797 );
34798 </code></pre>
34799      * @param {Object} cfg Xtype definition of item to add.
34800      */
34801     addxtype : function(cfg)
34802     {
34803         // basically accepts a pannel...
34804         // can accept a layout region..!?!?
34805         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34806         
34807         
34808         // theory?  children can only be panels??
34809         
34810         //if (!cfg.xtype.match(/Panel$/)) {
34811         //    return false;
34812         //}
34813         var ret = false;
34814         
34815         if (typeof(cfg.region) == 'undefined') {
34816             Roo.log("Failed to add Panel, region was not set");
34817             Roo.log(cfg);
34818             return false;
34819         }
34820         var region = cfg.region;
34821         delete cfg.region;
34822         
34823           
34824         var xitems = [];
34825         if (cfg.items) {
34826             xitems = cfg.items;
34827             delete cfg.items;
34828         }
34829         var nb = false;
34830         
34831         switch(cfg.xtype) 
34832         {
34833             case 'Content':  // ContentPanel (el, cfg)
34834             case 'Scroll':  // ContentPanel (el, cfg)
34835             case 'View': 
34836                 cfg.autoCreate = true;
34837                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34838                 //} else {
34839                 //    var el = this.el.createChild();
34840                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34841                 //}
34842                 
34843                 this.add(region, ret);
34844                 break;
34845             
34846             /*
34847             case 'TreePanel': // our new panel!
34848                 cfg.el = this.el.createChild();
34849                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34850                 this.add(region, ret);
34851                 break;
34852             */
34853             
34854             case 'Nest': 
34855                 // create a new Layout (which is  a Border Layout...
34856                 
34857                 var clayout = cfg.layout;
34858                 clayout.el  = this.el.createChild();
34859                 clayout.items   = clayout.items  || [];
34860                 
34861                 delete cfg.layout;
34862                 
34863                 // replace this exitems with the clayout ones..
34864                 xitems = clayout.items;
34865                  
34866                 // force background off if it's in center...
34867                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34868                     cfg.background = false;
34869                 }
34870                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
34871                 
34872                 
34873                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34874                 //console.log('adding nested layout panel '  + cfg.toSource());
34875                 this.add(region, ret);
34876                 nb = {}; /// find first...
34877                 break;
34878             
34879             case 'Grid':
34880                 
34881                 // needs grid and region
34882                 
34883                 //var el = this.getRegion(region).el.createChild();
34884                 /*
34885                  *var el = this.el.createChild();
34886                 // create the grid first...
34887                 cfg.grid.container = el;
34888                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34889                 */
34890                 
34891                 if (region == 'center' && this.active ) {
34892                     cfg.background = false;
34893                 }
34894                 
34895                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34896                 
34897                 this.add(region, ret);
34898                 /*
34899                 if (cfg.background) {
34900                     // render grid on panel activation (if panel background)
34901                     ret.on('activate', function(gp) {
34902                         if (!gp.grid.rendered) {
34903                     //        gp.grid.render(el);
34904                         }
34905                     });
34906                 } else {
34907                   //  cfg.grid.render(el);
34908                 }
34909                 */
34910                 break;
34911            
34912            
34913             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34914                 // it was the old xcomponent building that caused this before.
34915                 // espeically if border is the top element in the tree.
34916                 ret = this;
34917                 break; 
34918                 
34919                     
34920                 
34921                 
34922                 
34923             default:
34924                 /*
34925                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34926                     
34927                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34928                     this.add(region, ret);
34929                 } else {
34930                 */
34931                     Roo.log(cfg);
34932                     throw "Can not add '" + cfg.xtype + "' to Border";
34933                     return null;
34934              
34935                                 
34936              
34937         }
34938         this.beginUpdate();
34939         // add children..
34940         var region = '';
34941         var abn = {};
34942         Roo.each(xitems, function(i)  {
34943             region = nb && i.region ? i.region : false;
34944             
34945             var add = ret.addxtype(i);
34946            
34947             if (region) {
34948                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
34949                 if (!i.background) {
34950                     abn[region] = nb[region] ;
34951                 }
34952             }
34953             
34954         });
34955         this.endUpdate();
34956
34957         // make the last non-background panel active..
34958         //if (nb) { Roo.log(abn); }
34959         if (nb) {
34960             
34961             for(var r in abn) {
34962                 region = this.getRegion(r);
34963                 if (region) {
34964                     // tried using nb[r], but it does not work..
34965                      
34966                     region.showPanel(abn[r]);
34967                    
34968                 }
34969             }
34970         }
34971         return ret;
34972         
34973     },
34974     
34975     
34976 // private
34977     factory : function(cfg)
34978     {
34979         
34980         var validRegions = Roo.bootstrap.layout.Border.regions;
34981
34982         var target = cfg.region;
34983         cfg.mgr = this;
34984         
34985         var r = Roo.bootstrap.layout;
34986         Roo.log(target);
34987         switch(target){
34988             case "north":
34989                 return new r.North(cfg);
34990             case "south":
34991                 return new r.South(cfg);
34992             case "east":
34993                 return new r.East(cfg);
34994             case "west":
34995                 return new r.West(cfg);
34996             case "center":
34997                 return new r.Center(cfg);
34998         }
34999         throw 'Layout region "'+target+'" not supported.';
35000     }
35001     
35002     
35003 });
35004  /*
35005  * Based on:
35006  * Ext JS Library 1.1.1
35007  * Copyright(c) 2006-2007, Ext JS, LLC.
35008  *
35009  * Originally Released Under LGPL - original licence link has changed is not relivant.
35010  *
35011  * Fork - LGPL
35012  * <script type="text/javascript">
35013  */
35014  
35015 /**
35016  * @class Roo.bootstrap.layout.Basic
35017  * @extends Roo.util.Observable
35018  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35019  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35020  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35021  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35022  * @cfg {string}   region  the region that it inhabits..
35023  * @cfg {bool}   skipConfig skip config?
35024  * 
35025
35026  */
35027 Roo.bootstrap.layout.Basic = function(config){
35028     
35029     this.mgr = config.mgr;
35030     
35031     this.position = config.region;
35032     
35033     var skipConfig = config.skipConfig;
35034     
35035     this.events = {
35036         /**
35037          * @scope Roo.BasicLayoutRegion
35038          */
35039         
35040         /**
35041          * @event beforeremove
35042          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35043          * @param {Roo.LayoutRegion} this
35044          * @param {Roo.ContentPanel} panel The panel
35045          * @param {Object} e The cancel event object
35046          */
35047         "beforeremove" : true,
35048         /**
35049          * @event invalidated
35050          * Fires when the layout for this region is changed.
35051          * @param {Roo.LayoutRegion} this
35052          */
35053         "invalidated" : true,
35054         /**
35055          * @event visibilitychange
35056          * Fires when this region is shown or hidden 
35057          * @param {Roo.LayoutRegion} this
35058          * @param {Boolean} visibility true or false
35059          */
35060         "visibilitychange" : true,
35061         /**
35062          * @event paneladded
35063          * Fires when a panel is added. 
35064          * @param {Roo.LayoutRegion} this
35065          * @param {Roo.ContentPanel} panel The panel
35066          */
35067         "paneladded" : true,
35068         /**
35069          * @event panelremoved
35070          * Fires when a panel is removed. 
35071          * @param {Roo.LayoutRegion} this
35072          * @param {Roo.ContentPanel} panel The panel
35073          */
35074         "panelremoved" : true,
35075         /**
35076          * @event beforecollapse
35077          * Fires when this region before collapse.
35078          * @param {Roo.LayoutRegion} this
35079          */
35080         "beforecollapse" : true,
35081         /**
35082          * @event collapsed
35083          * Fires when this region is collapsed.
35084          * @param {Roo.LayoutRegion} this
35085          */
35086         "collapsed" : true,
35087         /**
35088          * @event expanded
35089          * Fires when this region is expanded.
35090          * @param {Roo.LayoutRegion} this
35091          */
35092         "expanded" : true,
35093         /**
35094          * @event slideshow
35095          * Fires when this region is slid into view.
35096          * @param {Roo.LayoutRegion} this
35097          */
35098         "slideshow" : true,
35099         /**
35100          * @event slidehide
35101          * Fires when this region slides out of view. 
35102          * @param {Roo.LayoutRegion} this
35103          */
35104         "slidehide" : true,
35105         /**
35106          * @event panelactivated
35107          * Fires when a panel is activated. 
35108          * @param {Roo.LayoutRegion} this
35109          * @param {Roo.ContentPanel} panel The activated panel
35110          */
35111         "panelactivated" : true,
35112         /**
35113          * @event resized
35114          * Fires when the user resizes this region. 
35115          * @param {Roo.LayoutRegion} this
35116          * @param {Number} newSize The new size (width for east/west, height for north/south)
35117          */
35118         "resized" : true
35119     };
35120     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35121     this.panels = new Roo.util.MixedCollection();
35122     this.panels.getKey = this.getPanelId.createDelegate(this);
35123     this.box = null;
35124     this.activePanel = null;
35125     // ensure listeners are added...
35126     
35127     if (config.listeners || config.events) {
35128         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35129             listeners : config.listeners || {},
35130             events : config.events || {}
35131         });
35132     }
35133     
35134     if(skipConfig !== true){
35135         this.applyConfig(config);
35136     }
35137 };
35138
35139 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35140 {
35141     getPanelId : function(p){
35142         return p.getId();
35143     },
35144     
35145     applyConfig : function(config){
35146         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35147         this.config = config;
35148         
35149     },
35150     
35151     /**
35152      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35153      * the width, for horizontal (north, south) the height.
35154      * @param {Number} newSize The new width or height
35155      */
35156     resizeTo : function(newSize){
35157         var el = this.el ? this.el :
35158                  (this.activePanel ? this.activePanel.getEl() : null);
35159         if(el){
35160             switch(this.position){
35161                 case "east":
35162                 case "west":
35163                     el.setWidth(newSize);
35164                     this.fireEvent("resized", this, newSize);
35165                 break;
35166                 case "north":
35167                 case "south":
35168                     el.setHeight(newSize);
35169                     this.fireEvent("resized", this, newSize);
35170                 break;                
35171             }
35172         }
35173     },
35174     
35175     getBox : function(){
35176         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35177     },
35178     
35179     getMargins : function(){
35180         return this.margins;
35181     },
35182     
35183     updateBox : function(box){
35184         this.box = box;
35185         var el = this.activePanel.getEl();
35186         el.dom.style.left = box.x + "px";
35187         el.dom.style.top = box.y + "px";
35188         this.activePanel.setSize(box.width, box.height);
35189     },
35190     
35191     /**
35192      * Returns the container element for this region.
35193      * @return {Roo.Element}
35194      */
35195     getEl : function(){
35196         return this.activePanel;
35197     },
35198     
35199     /**
35200      * Returns true if this region is currently visible.
35201      * @return {Boolean}
35202      */
35203     isVisible : function(){
35204         return this.activePanel ? true : false;
35205     },
35206     
35207     setActivePanel : function(panel){
35208         panel = this.getPanel(panel);
35209         if(this.activePanel && this.activePanel != panel){
35210             this.activePanel.setActiveState(false);
35211             this.activePanel.getEl().setLeftTop(-10000,-10000);
35212         }
35213         this.activePanel = panel;
35214         panel.setActiveState(true);
35215         if(this.box){
35216             panel.setSize(this.box.width, this.box.height);
35217         }
35218         this.fireEvent("panelactivated", this, panel);
35219         this.fireEvent("invalidated");
35220     },
35221     
35222     /**
35223      * Show the specified panel.
35224      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35225      * @return {Roo.ContentPanel} The shown panel or null
35226      */
35227     showPanel : function(panel){
35228         panel = this.getPanel(panel);
35229         if(panel){
35230             this.setActivePanel(panel);
35231         }
35232         return panel;
35233     },
35234     
35235     /**
35236      * Get the active panel for this region.
35237      * @return {Roo.ContentPanel} The active panel or null
35238      */
35239     getActivePanel : function(){
35240         return this.activePanel;
35241     },
35242     
35243     /**
35244      * Add the passed ContentPanel(s)
35245      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35246      * @return {Roo.ContentPanel} The panel added (if only one was added)
35247      */
35248     add : function(panel){
35249         if(arguments.length > 1){
35250             for(var i = 0, len = arguments.length; i < len; i++) {
35251                 this.add(arguments[i]);
35252             }
35253             return null;
35254         }
35255         if(this.hasPanel(panel)){
35256             this.showPanel(panel);
35257             return panel;
35258         }
35259         var el = panel.getEl();
35260         if(el.dom.parentNode != this.mgr.el.dom){
35261             this.mgr.el.dom.appendChild(el.dom);
35262         }
35263         if(panel.setRegion){
35264             panel.setRegion(this);
35265         }
35266         this.panels.add(panel);
35267         el.setStyle("position", "absolute");
35268         if(!panel.background){
35269             this.setActivePanel(panel);
35270             if(this.config.initialSize && this.panels.getCount()==1){
35271                 this.resizeTo(this.config.initialSize);
35272             }
35273         }
35274         this.fireEvent("paneladded", this, panel);
35275         return panel;
35276     },
35277     
35278     /**
35279      * Returns true if the panel is in this region.
35280      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35281      * @return {Boolean}
35282      */
35283     hasPanel : function(panel){
35284         if(typeof panel == "object"){ // must be panel obj
35285             panel = panel.getId();
35286         }
35287         return this.getPanel(panel) ? true : false;
35288     },
35289     
35290     /**
35291      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35292      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35293      * @param {Boolean} preservePanel Overrides the config preservePanel option
35294      * @return {Roo.ContentPanel} The panel that was removed
35295      */
35296     remove : function(panel, preservePanel){
35297         panel = this.getPanel(panel);
35298         if(!panel){
35299             return null;
35300         }
35301         var e = {};
35302         this.fireEvent("beforeremove", this, panel, e);
35303         if(e.cancel === true){
35304             return null;
35305         }
35306         var panelId = panel.getId();
35307         this.panels.removeKey(panelId);
35308         return panel;
35309     },
35310     
35311     /**
35312      * Returns the panel specified or null if it's not in this region.
35313      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35314      * @return {Roo.ContentPanel}
35315      */
35316     getPanel : function(id){
35317         if(typeof id == "object"){ // must be panel obj
35318             return id;
35319         }
35320         return this.panels.get(id);
35321     },
35322     
35323     /**
35324      * Returns this regions position (north/south/east/west/center).
35325      * @return {String} 
35326      */
35327     getPosition: function(){
35328         return this.position;    
35329     }
35330 });/*
35331  * Based on:
35332  * Ext JS Library 1.1.1
35333  * Copyright(c) 2006-2007, Ext JS, LLC.
35334  *
35335  * Originally Released Under LGPL - original licence link has changed is not relivant.
35336  *
35337  * Fork - LGPL
35338  * <script type="text/javascript">
35339  */
35340  
35341 /**
35342  * @class Roo.bootstrap.layout.Region
35343  * @extends Roo.bootstrap.layout.Basic
35344  * This class represents a region in a layout manager.
35345  
35346  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35347  * @cfg {Object}    cmargins        Margins for the element when collapsed (defaults to: north/south {top: 2, left: 0, right:0, bottom: 2} or east/west {top: 0, left: 2, right:2, bottom: 0})
35348  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35349  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35350  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35351  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35352  * @cfg {String}    title           The title for the region (overrides panel titles)
35353  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35354  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35355  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35356  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35357  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35358  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35359  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35360  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35361  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35362  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35363
35364  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35365  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35366  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35367  * @cfg {Number}    width           For East/West panels
35368  * @cfg {Number}    height          For North/South panels
35369  * @cfg {Boolean}   split           To show the splitter
35370  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35371  * 
35372  * @cfg {string}   cls             Extra CSS classes to add to region
35373  * 
35374  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35375  * @cfg {string}   region  the region that it inhabits..
35376  *
35377
35378  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35379  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35380
35381  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35382  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35383  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35384  */
35385 Roo.bootstrap.layout.Region = function(config)
35386 {
35387     this.applyConfig(config);
35388
35389     var mgr = config.mgr;
35390     var pos = config.region;
35391     config.skipConfig = true;
35392     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35393     
35394     if (mgr.el) {
35395         this.onRender(mgr.el);   
35396     }
35397      
35398     this.visible = true;
35399     this.collapsed = false;
35400     this.unrendered_panels = [];
35401 };
35402
35403 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35404
35405     position: '', // set by wrapper (eg. north/south etc..)
35406     unrendered_panels : null,  // unrendered panels.
35407     createBody : function(){
35408         /** This region's body element 
35409         * @type Roo.Element */
35410         this.bodyEl = this.el.createChild({
35411                 tag: "div",
35412                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35413         });
35414     },
35415
35416     onRender: function(ctr, pos)
35417     {
35418         var dh = Roo.DomHelper;
35419         /** This region's container element 
35420         * @type Roo.Element */
35421         this.el = dh.append(ctr.dom, {
35422                 tag: "div",
35423                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35424             }, true);
35425         /** This region's title element 
35426         * @type Roo.Element */
35427     
35428         this.titleEl = dh.append(this.el.dom,
35429             {
35430                     tag: "div",
35431                     unselectable: "on",
35432                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35433                     children:[
35434                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35435                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35436                     ]}, true);
35437         
35438         this.titleEl.enableDisplayMode();
35439         /** This region's title text element 
35440         * @type HTMLElement */
35441         this.titleTextEl = this.titleEl.dom.firstChild;
35442         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35443         /*
35444         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35445         this.closeBtn.enableDisplayMode();
35446         this.closeBtn.on("click", this.closeClicked, this);
35447         this.closeBtn.hide();
35448     */
35449         this.createBody(this.config);
35450         if(this.config.hideWhenEmpty){
35451             this.hide();
35452             this.on("paneladded", this.validateVisibility, this);
35453             this.on("panelremoved", this.validateVisibility, this);
35454         }
35455         if(this.autoScroll){
35456             this.bodyEl.setStyle("overflow", "auto");
35457         }else{
35458             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35459         }
35460         //if(c.titlebar !== false){
35461             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35462                 this.titleEl.hide();
35463             }else{
35464                 this.titleEl.show();
35465                 if(this.config.title){
35466                     this.titleTextEl.innerHTML = this.config.title;
35467                 }
35468             }
35469         //}
35470         if(this.config.collapsed){
35471             this.collapse(true);
35472         }
35473         if(this.config.hidden){
35474             this.hide();
35475         }
35476         
35477         if (this.unrendered_panels && this.unrendered_panels.length) {
35478             for (var i =0;i< this.unrendered_panels.length; i++) {
35479                 this.add(this.unrendered_panels[i]);
35480             }
35481             this.unrendered_panels = null;
35482             
35483         }
35484         
35485     },
35486     
35487     applyConfig : function(c)
35488     {
35489         /*
35490          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35491             var dh = Roo.DomHelper;
35492             if(c.titlebar !== false){
35493                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35494                 this.collapseBtn.on("click", this.collapse, this);
35495                 this.collapseBtn.enableDisplayMode();
35496                 /*
35497                 if(c.showPin === true || this.showPin){
35498                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35499                     this.stickBtn.enableDisplayMode();
35500                     this.stickBtn.on("click", this.expand, this);
35501                     this.stickBtn.hide();
35502                 }
35503                 
35504             }
35505             */
35506             /** This region's collapsed element
35507             * @type Roo.Element */
35508             /*
35509              *
35510             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35511                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35512             ]}, true);
35513             
35514             if(c.floatable !== false){
35515                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35516                this.collapsedEl.on("click", this.collapseClick, this);
35517             }
35518
35519             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35520                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35521                    id: "message", unselectable: "on", style:{"float":"left"}});
35522                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35523              }
35524             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35525             this.expandBtn.on("click", this.expand, this);
35526             
35527         }
35528         
35529         if(this.collapseBtn){
35530             this.collapseBtn.setVisible(c.collapsible == true);
35531         }
35532         
35533         this.cmargins = c.cmargins || this.cmargins ||
35534                          (this.position == "west" || this.position == "east" ?
35535                              {top: 0, left: 2, right:2, bottom: 0} :
35536                              {top: 2, left: 0, right:0, bottom: 2});
35537         */
35538         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35539         
35540         
35541         this.bottomTabs = c.tabPosition != "top";
35542         
35543         this.autoScroll = c.autoScroll || false;
35544         
35545         
35546        
35547         
35548         this.duration = c.duration || .30;
35549         this.slideDuration = c.slideDuration || .45;
35550         this.config = c;
35551        
35552     },
35553     /**
35554      * Returns true if this region is currently visible.
35555      * @return {Boolean}
35556      */
35557     isVisible : function(){
35558         return this.visible;
35559     },
35560
35561     /**
35562      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35563      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35564      */
35565     //setCollapsedTitle : function(title){
35566     //    title = title || "&#160;";
35567      //   if(this.collapsedTitleTextEl){
35568       //      this.collapsedTitleTextEl.innerHTML = title;
35569        // }
35570     //},
35571
35572     getBox : function(){
35573         var b;
35574       //  if(!this.collapsed){
35575             b = this.el.getBox(false, true);
35576        // }else{
35577           //  b = this.collapsedEl.getBox(false, true);
35578         //}
35579         return b;
35580     },
35581
35582     getMargins : function(){
35583         return this.margins;
35584         //return this.collapsed ? this.cmargins : this.margins;
35585     },
35586 /*
35587     highlight : function(){
35588         this.el.addClass("x-layout-panel-dragover");
35589     },
35590
35591     unhighlight : function(){
35592         this.el.removeClass("x-layout-panel-dragover");
35593     },
35594 */
35595     updateBox : function(box)
35596     {
35597         if (!this.bodyEl) {
35598             return; // not rendered yet..
35599         }
35600         
35601         this.box = box;
35602         if(!this.collapsed){
35603             this.el.dom.style.left = box.x + "px";
35604             this.el.dom.style.top = box.y + "px";
35605             this.updateBody(box.width, box.height);
35606         }else{
35607             this.collapsedEl.dom.style.left = box.x + "px";
35608             this.collapsedEl.dom.style.top = box.y + "px";
35609             this.collapsedEl.setSize(box.width, box.height);
35610         }
35611         if(this.tabs){
35612             this.tabs.autoSizeTabs();
35613         }
35614     },
35615
35616     updateBody : function(w, h)
35617     {
35618         if(w !== null){
35619             this.el.setWidth(w);
35620             w -= this.el.getBorderWidth("rl");
35621             if(this.config.adjustments){
35622                 w += this.config.adjustments[0];
35623             }
35624         }
35625         if(h !== null && h > 0){
35626             this.el.setHeight(h);
35627             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35628             h -= this.el.getBorderWidth("tb");
35629             if(this.config.adjustments){
35630                 h += this.config.adjustments[1];
35631             }
35632             this.bodyEl.setHeight(h);
35633             if(this.tabs){
35634                 h = this.tabs.syncHeight(h);
35635             }
35636         }
35637         if(this.panelSize){
35638             w = w !== null ? w : this.panelSize.width;
35639             h = h !== null ? h : this.panelSize.height;
35640         }
35641         if(this.activePanel){
35642             var el = this.activePanel.getEl();
35643             w = w !== null ? w : el.getWidth();
35644             h = h !== null ? h : el.getHeight();
35645             this.panelSize = {width: w, height: h};
35646             this.activePanel.setSize(w, h);
35647         }
35648         if(Roo.isIE && this.tabs){
35649             this.tabs.el.repaint();
35650         }
35651     },
35652
35653     /**
35654      * Returns the container element for this region.
35655      * @return {Roo.Element}
35656      */
35657     getEl : function(){
35658         return this.el;
35659     },
35660
35661     /**
35662      * Hides this region.
35663      */
35664     hide : function(){
35665         //if(!this.collapsed){
35666             this.el.dom.style.left = "-2000px";
35667             this.el.hide();
35668         //}else{
35669          //   this.collapsedEl.dom.style.left = "-2000px";
35670          //   this.collapsedEl.hide();
35671        // }
35672         this.visible = false;
35673         this.fireEvent("visibilitychange", this, false);
35674     },
35675
35676     /**
35677      * Shows this region if it was previously hidden.
35678      */
35679     show : function(){
35680         //if(!this.collapsed){
35681             this.el.show();
35682         //}else{
35683         //    this.collapsedEl.show();
35684        // }
35685         this.visible = true;
35686         this.fireEvent("visibilitychange", this, true);
35687     },
35688 /*
35689     closeClicked : function(){
35690         if(this.activePanel){
35691             this.remove(this.activePanel);
35692         }
35693     },
35694
35695     collapseClick : function(e){
35696         if(this.isSlid){
35697            e.stopPropagation();
35698            this.slideIn();
35699         }else{
35700            e.stopPropagation();
35701            this.slideOut();
35702         }
35703     },
35704 */
35705     /**
35706      * Collapses this region.
35707      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35708      */
35709     /*
35710     collapse : function(skipAnim, skipCheck = false){
35711         if(this.collapsed) {
35712             return;
35713         }
35714         
35715         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35716             
35717             this.collapsed = true;
35718             if(this.split){
35719                 this.split.el.hide();
35720             }
35721             if(this.config.animate && skipAnim !== true){
35722                 this.fireEvent("invalidated", this);
35723                 this.animateCollapse();
35724             }else{
35725                 this.el.setLocation(-20000,-20000);
35726                 this.el.hide();
35727                 this.collapsedEl.show();
35728                 this.fireEvent("collapsed", this);
35729                 this.fireEvent("invalidated", this);
35730             }
35731         }
35732         
35733     },
35734 */
35735     animateCollapse : function(){
35736         // overridden
35737     },
35738
35739     /**
35740      * Expands this region if it was previously collapsed.
35741      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35742      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35743      */
35744     /*
35745     expand : function(e, skipAnim){
35746         if(e) {
35747             e.stopPropagation();
35748         }
35749         if(!this.collapsed || this.el.hasActiveFx()) {
35750             return;
35751         }
35752         if(this.isSlid){
35753             this.afterSlideIn();
35754             skipAnim = true;
35755         }
35756         this.collapsed = false;
35757         if(this.config.animate && skipAnim !== true){
35758             this.animateExpand();
35759         }else{
35760             this.el.show();
35761             if(this.split){
35762                 this.split.el.show();
35763             }
35764             this.collapsedEl.setLocation(-2000,-2000);
35765             this.collapsedEl.hide();
35766             this.fireEvent("invalidated", this);
35767             this.fireEvent("expanded", this);
35768         }
35769     },
35770 */
35771     animateExpand : function(){
35772         // overridden
35773     },
35774
35775     initTabs : function()
35776     {
35777         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35778         
35779         var ts = new Roo.bootstrap.panel.Tabs({
35780                 el: this.bodyEl.dom,
35781                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35782                 disableTooltips: this.config.disableTabTips,
35783                 toolbar : this.config.toolbar
35784             });
35785         
35786         if(this.config.hideTabs){
35787             ts.stripWrap.setDisplayed(false);
35788         }
35789         this.tabs = ts;
35790         ts.resizeTabs = this.config.resizeTabs === true;
35791         ts.minTabWidth = this.config.minTabWidth || 40;
35792         ts.maxTabWidth = this.config.maxTabWidth || 250;
35793         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35794         ts.monitorResize = false;
35795         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35796         ts.bodyEl.addClass('roo-layout-tabs-body');
35797         this.panels.each(this.initPanelAsTab, this);
35798     },
35799
35800     initPanelAsTab : function(panel){
35801         var ti = this.tabs.addTab(
35802             panel.getEl().id,
35803             panel.getTitle(),
35804             null,
35805             this.config.closeOnTab && panel.isClosable(),
35806             panel.tpl
35807         );
35808         if(panel.tabTip !== undefined){
35809             ti.setTooltip(panel.tabTip);
35810         }
35811         ti.on("activate", function(){
35812               this.setActivePanel(panel);
35813         }, this);
35814         
35815         if(this.config.closeOnTab){
35816             ti.on("beforeclose", function(t, e){
35817                 e.cancel = true;
35818                 this.remove(panel);
35819             }, this);
35820         }
35821         
35822         panel.tabItem = ti;
35823         
35824         return ti;
35825     },
35826
35827     updatePanelTitle : function(panel, title)
35828     {
35829         if(this.activePanel == panel){
35830             this.updateTitle(title);
35831         }
35832         if(this.tabs){
35833             var ti = this.tabs.getTab(panel.getEl().id);
35834             ti.setText(title);
35835             if(panel.tabTip !== undefined){
35836                 ti.setTooltip(panel.tabTip);
35837             }
35838         }
35839     },
35840
35841     updateTitle : function(title){
35842         if(this.titleTextEl && !this.config.title){
35843             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35844         }
35845     },
35846
35847     setActivePanel : function(panel)
35848     {
35849         panel = this.getPanel(panel);
35850         if(this.activePanel && this.activePanel != panel){
35851             if(this.activePanel.setActiveState(false) === false){
35852                 return;
35853             }
35854         }
35855         this.activePanel = panel;
35856         panel.setActiveState(true);
35857         if(this.panelSize){
35858             panel.setSize(this.panelSize.width, this.panelSize.height);
35859         }
35860         if(this.closeBtn){
35861             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35862         }
35863         this.updateTitle(panel.getTitle());
35864         if(this.tabs){
35865             this.fireEvent("invalidated", this);
35866         }
35867         this.fireEvent("panelactivated", this, panel);
35868     },
35869
35870     /**
35871      * Shows the specified panel.
35872      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35873      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35874      */
35875     showPanel : function(panel)
35876     {
35877         panel = this.getPanel(panel);
35878         if(panel){
35879             if(this.tabs){
35880                 var tab = this.tabs.getTab(panel.getEl().id);
35881                 if(tab.isHidden()){
35882                     this.tabs.unhideTab(tab.id);
35883                 }
35884                 tab.activate();
35885             }else{
35886                 this.setActivePanel(panel);
35887             }
35888         }
35889         return panel;
35890     },
35891
35892     /**
35893      * Get the active panel for this region.
35894      * @return {Roo.ContentPanel} The active panel or null
35895      */
35896     getActivePanel : function(){
35897         return this.activePanel;
35898     },
35899
35900     validateVisibility : function(){
35901         if(this.panels.getCount() < 1){
35902             this.updateTitle("&#160;");
35903             this.closeBtn.hide();
35904             this.hide();
35905         }else{
35906             if(!this.isVisible()){
35907                 this.show();
35908             }
35909         }
35910     },
35911
35912     /**
35913      * Adds the passed ContentPanel(s) to this region.
35914      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35915      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35916      */
35917     add : function(panel)
35918     {
35919         if(arguments.length > 1){
35920             for(var i = 0, len = arguments.length; i < len; i++) {
35921                 this.add(arguments[i]);
35922             }
35923             return null;
35924         }
35925         
35926         // if we have not been rendered yet, then we can not really do much of this..
35927         if (!this.bodyEl) {
35928             this.unrendered_panels.push(panel);
35929             return panel;
35930         }
35931         
35932         
35933         
35934         
35935         if(this.hasPanel(panel)){
35936             this.showPanel(panel);
35937             return panel;
35938         }
35939         panel.setRegion(this);
35940         this.panels.add(panel);
35941        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35942             // sinle panel - no tab...?? would it not be better to render it with the tabs,
35943             // and hide them... ???
35944             this.bodyEl.dom.appendChild(panel.getEl().dom);
35945             if(panel.background !== true){
35946                 this.setActivePanel(panel);
35947             }
35948             this.fireEvent("paneladded", this, panel);
35949             return panel;
35950         }
35951         */
35952         if(!this.tabs){
35953             this.initTabs();
35954         }else{
35955             this.initPanelAsTab(panel);
35956         }
35957         
35958         
35959         if(panel.background !== true){
35960             this.tabs.activate(panel.getEl().id);
35961         }
35962         this.fireEvent("paneladded", this, panel);
35963         return panel;
35964     },
35965
35966     /**
35967      * Hides the tab for the specified panel.
35968      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35969      */
35970     hidePanel : function(panel){
35971         if(this.tabs && (panel = this.getPanel(panel))){
35972             this.tabs.hideTab(panel.getEl().id);
35973         }
35974     },
35975
35976     /**
35977      * Unhides the tab for a previously hidden panel.
35978      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35979      */
35980     unhidePanel : function(panel){
35981         if(this.tabs && (panel = this.getPanel(panel))){
35982             this.tabs.unhideTab(panel.getEl().id);
35983         }
35984     },
35985
35986     clearPanels : function(){
35987         while(this.panels.getCount() > 0){
35988              this.remove(this.panels.first());
35989         }
35990     },
35991
35992     /**
35993      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35994      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35995      * @param {Boolean} preservePanel Overrides the config preservePanel option
35996      * @return {Roo.ContentPanel} The panel that was removed
35997      */
35998     remove : function(panel, preservePanel)
35999     {
36000         panel = this.getPanel(panel);
36001         if(!panel){
36002             return null;
36003         }
36004         var e = {};
36005         this.fireEvent("beforeremove", this, panel, e);
36006         if(e.cancel === true){
36007             return null;
36008         }
36009         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36010         var panelId = panel.getId();
36011         this.panels.removeKey(panelId);
36012         if(preservePanel){
36013             document.body.appendChild(panel.getEl().dom);
36014         }
36015         if(this.tabs){
36016             this.tabs.removeTab(panel.getEl().id);
36017         }else if (!preservePanel){
36018             this.bodyEl.dom.removeChild(panel.getEl().dom);
36019         }
36020         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36021             var p = this.panels.first();
36022             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36023             tempEl.appendChild(p.getEl().dom);
36024             this.bodyEl.update("");
36025             this.bodyEl.dom.appendChild(p.getEl().dom);
36026             tempEl = null;
36027             this.updateTitle(p.getTitle());
36028             this.tabs = null;
36029             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36030             this.setActivePanel(p);
36031         }
36032         panel.setRegion(null);
36033         if(this.activePanel == panel){
36034             this.activePanel = null;
36035         }
36036         if(this.config.autoDestroy !== false && preservePanel !== true){
36037             try{panel.destroy();}catch(e){}
36038         }
36039         this.fireEvent("panelremoved", this, panel);
36040         return panel;
36041     },
36042
36043     /**
36044      * Returns the TabPanel component used by this region
36045      * @return {Roo.TabPanel}
36046      */
36047     getTabs : function(){
36048         return this.tabs;
36049     },
36050
36051     createTool : function(parentEl, className){
36052         var btn = Roo.DomHelper.append(parentEl, {
36053             tag: "div",
36054             cls: "x-layout-tools-button",
36055             children: [ {
36056                 tag: "div",
36057                 cls: "roo-layout-tools-button-inner " + className,
36058                 html: "&#160;"
36059             }]
36060         }, true);
36061         btn.addClassOnOver("roo-layout-tools-button-over");
36062         return btn;
36063     }
36064 });/*
36065  * Based on:
36066  * Ext JS Library 1.1.1
36067  * Copyright(c) 2006-2007, Ext JS, LLC.
36068  *
36069  * Originally Released Under LGPL - original licence link has changed is not relivant.
36070  *
36071  * Fork - LGPL
36072  * <script type="text/javascript">
36073  */
36074  
36075
36076
36077 /**
36078  * @class Roo.SplitLayoutRegion
36079  * @extends Roo.LayoutRegion
36080  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36081  */
36082 Roo.bootstrap.layout.Split = function(config){
36083     this.cursor = config.cursor;
36084     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36085 };
36086
36087 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36088 {
36089     splitTip : "Drag to resize.",
36090     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36091     useSplitTips : false,
36092
36093     applyConfig : function(config){
36094         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36095     },
36096     
36097     onRender : function(ctr,pos) {
36098         
36099         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36100         if(!this.config.split){
36101             return;
36102         }
36103         if(!this.split){
36104             
36105             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36106                             tag: "div",
36107                             id: this.el.id + "-split",
36108                             cls: "roo-layout-split roo-layout-split-"+this.position,
36109                             html: "&#160;"
36110             });
36111             /** The SplitBar for this region 
36112             * @type Roo.SplitBar */
36113             // does not exist yet...
36114             Roo.log([this.position, this.orientation]);
36115             
36116             this.split = new Roo.bootstrap.SplitBar({
36117                 dragElement : splitEl,
36118                 resizingElement: this.el,
36119                 orientation : this.orientation
36120             });
36121             
36122             this.split.on("moved", this.onSplitMove, this);
36123             this.split.useShim = this.config.useShim === true;
36124             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36125             if(this.useSplitTips){
36126                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36127             }
36128             //if(config.collapsible){
36129             //    this.split.el.on("dblclick", this.collapse,  this);
36130             //}
36131         }
36132         if(typeof this.config.minSize != "undefined"){
36133             this.split.minSize = this.config.minSize;
36134         }
36135         if(typeof this.config.maxSize != "undefined"){
36136             this.split.maxSize = this.config.maxSize;
36137         }
36138         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36139             this.hideSplitter();
36140         }
36141         
36142     },
36143
36144     getHMaxSize : function(){
36145          var cmax = this.config.maxSize || 10000;
36146          var center = this.mgr.getRegion("center");
36147          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36148     },
36149
36150     getVMaxSize : function(){
36151          var cmax = this.config.maxSize || 10000;
36152          var center = this.mgr.getRegion("center");
36153          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36154     },
36155
36156     onSplitMove : function(split, newSize){
36157         this.fireEvent("resized", this, newSize);
36158     },
36159     
36160     /** 
36161      * Returns the {@link Roo.SplitBar} for this region.
36162      * @return {Roo.SplitBar}
36163      */
36164     getSplitBar : function(){
36165         return this.split;
36166     },
36167     
36168     hide : function(){
36169         this.hideSplitter();
36170         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36171     },
36172
36173     hideSplitter : function(){
36174         if(this.split){
36175             this.split.el.setLocation(-2000,-2000);
36176             this.split.el.hide();
36177         }
36178     },
36179
36180     show : function(){
36181         if(this.split){
36182             this.split.el.show();
36183         }
36184         Roo.bootstrap.layout.Split.superclass.show.call(this);
36185     },
36186     
36187     beforeSlide: function(){
36188         if(Roo.isGecko){// firefox overflow auto bug workaround
36189             this.bodyEl.clip();
36190             if(this.tabs) {
36191                 this.tabs.bodyEl.clip();
36192             }
36193             if(this.activePanel){
36194                 this.activePanel.getEl().clip();
36195                 
36196                 if(this.activePanel.beforeSlide){
36197                     this.activePanel.beforeSlide();
36198                 }
36199             }
36200         }
36201     },
36202     
36203     afterSlide : function(){
36204         if(Roo.isGecko){// firefox overflow auto bug workaround
36205             this.bodyEl.unclip();
36206             if(this.tabs) {
36207                 this.tabs.bodyEl.unclip();
36208             }
36209             if(this.activePanel){
36210                 this.activePanel.getEl().unclip();
36211                 if(this.activePanel.afterSlide){
36212                     this.activePanel.afterSlide();
36213                 }
36214             }
36215         }
36216     },
36217
36218     initAutoHide : function(){
36219         if(this.autoHide !== false){
36220             if(!this.autoHideHd){
36221                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36222                 this.autoHideHd = {
36223                     "mouseout": function(e){
36224                         if(!e.within(this.el, true)){
36225                             st.delay(500);
36226                         }
36227                     },
36228                     "mouseover" : function(e){
36229                         st.cancel();
36230                     },
36231                     scope : this
36232                 };
36233             }
36234             this.el.on(this.autoHideHd);
36235         }
36236     },
36237
36238     clearAutoHide : function(){
36239         if(this.autoHide !== false){
36240             this.el.un("mouseout", this.autoHideHd.mouseout);
36241             this.el.un("mouseover", this.autoHideHd.mouseover);
36242         }
36243     },
36244
36245     clearMonitor : function(){
36246         Roo.get(document).un("click", this.slideInIf, this);
36247     },
36248
36249     // these names are backwards but not changed for compat
36250     slideOut : function(){
36251         if(this.isSlid || this.el.hasActiveFx()){
36252             return;
36253         }
36254         this.isSlid = true;
36255         if(this.collapseBtn){
36256             this.collapseBtn.hide();
36257         }
36258         this.closeBtnState = this.closeBtn.getStyle('display');
36259         this.closeBtn.hide();
36260         if(this.stickBtn){
36261             this.stickBtn.show();
36262         }
36263         this.el.show();
36264         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36265         this.beforeSlide();
36266         this.el.setStyle("z-index", 10001);
36267         this.el.slideIn(this.getSlideAnchor(), {
36268             callback: function(){
36269                 this.afterSlide();
36270                 this.initAutoHide();
36271                 Roo.get(document).on("click", this.slideInIf, this);
36272                 this.fireEvent("slideshow", this);
36273             },
36274             scope: this,
36275             block: true
36276         });
36277     },
36278
36279     afterSlideIn : function(){
36280         this.clearAutoHide();
36281         this.isSlid = false;
36282         this.clearMonitor();
36283         this.el.setStyle("z-index", "");
36284         if(this.collapseBtn){
36285             this.collapseBtn.show();
36286         }
36287         this.closeBtn.setStyle('display', this.closeBtnState);
36288         if(this.stickBtn){
36289             this.stickBtn.hide();
36290         }
36291         this.fireEvent("slidehide", this);
36292     },
36293
36294     slideIn : function(cb){
36295         if(!this.isSlid || this.el.hasActiveFx()){
36296             Roo.callback(cb);
36297             return;
36298         }
36299         this.isSlid = false;
36300         this.beforeSlide();
36301         this.el.slideOut(this.getSlideAnchor(), {
36302             callback: function(){
36303                 this.el.setLeftTop(-10000, -10000);
36304                 this.afterSlide();
36305                 this.afterSlideIn();
36306                 Roo.callback(cb);
36307             },
36308             scope: this,
36309             block: true
36310         });
36311     },
36312     
36313     slideInIf : function(e){
36314         if(!e.within(this.el)){
36315             this.slideIn();
36316         }
36317     },
36318
36319     animateCollapse : function(){
36320         this.beforeSlide();
36321         this.el.setStyle("z-index", 20000);
36322         var anchor = this.getSlideAnchor();
36323         this.el.slideOut(anchor, {
36324             callback : function(){
36325                 this.el.setStyle("z-index", "");
36326                 this.collapsedEl.slideIn(anchor, {duration:.3});
36327                 this.afterSlide();
36328                 this.el.setLocation(-10000,-10000);
36329                 this.el.hide();
36330                 this.fireEvent("collapsed", this);
36331             },
36332             scope: this,
36333             block: true
36334         });
36335     },
36336
36337     animateExpand : function(){
36338         this.beforeSlide();
36339         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36340         this.el.setStyle("z-index", 20000);
36341         this.collapsedEl.hide({
36342             duration:.1
36343         });
36344         this.el.slideIn(this.getSlideAnchor(), {
36345             callback : function(){
36346                 this.el.setStyle("z-index", "");
36347                 this.afterSlide();
36348                 if(this.split){
36349                     this.split.el.show();
36350                 }
36351                 this.fireEvent("invalidated", this);
36352                 this.fireEvent("expanded", this);
36353             },
36354             scope: this,
36355             block: true
36356         });
36357     },
36358
36359     anchors : {
36360         "west" : "left",
36361         "east" : "right",
36362         "north" : "top",
36363         "south" : "bottom"
36364     },
36365
36366     sanchors : {
36367         "west" : "l",
36368         "east" : "r",
36369         "north" : "t",
36370         "south" : "b"
36371     },
36372
36373     canchors : {
36374         "west" : "tl-tr",
36375         "east" : "tr-tl",
36376         "north" : "tl-bl",
36377         "south" : "bl-tl"
36378     },
36379
36380     getAnchor : function(){
36381         return this.anchors[this.position];
36382     },
36383
36384     getCollapseAnchor : function(){
36385         return this.canchors[this.position];
36386     },
36387
36388     getSlideAnchor : function(){
36389         return this.sanchors[this.position];
36390     },
36391
36392     getAlignAdj : function(){
36393         var cm = this.cmargins;
36394         switch(this.position){
36395             case "west":
36396                 return [0, 0];
36397             break;
36398             case "east":
36399                 return [0, 0];
36400             break;
36401             case "north":
36402                 return [0, 0];
36403             break;
36404             case "south":
36405                 return [0, 0];
36406             break;
36407         }
36408     },
36409
36410     getExpandAdj : function(){
36411         var c = this.collapsedEl, cm = this.cmargins;
36412         switch(this.position){
36413             case "west":
36414                 return [-(cm.right+c.getWidth()+cm.left), 0];
36415             break;
36416             case "east":
36417                 return [cm.right+c.getWidth()+cm.left, 0];
36418             break;
36419             case "north":
36420                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36421             break;
36422             case "south":
36423                 return [0, cm.top+cm.bottom+c.getHeight()];
36424             break;
36425         }
36426     }
36427 });/*
36428  * Based on:
36429  * Ext JS Library 1.1.1
36430  * Copyright(c) 2006-2007, Ext JS, LLC.
36431  *
36432  * Originally Released Under LGPL - original licence link has changed is not relivant.
36433  *
36434  * Fork - LGPL
36435  * <script type="text/javascript">
36436  */
36437 /*
36438  * These classes are private internal classes
36439  */
36440 Roo.bootstrap.layout.Center = function(config){
36441     config.region = "center";
36442     Roo.bootstrap.layout.Region.call(this, config);
36443     this.visible = true;
36444     this.minWidth = config.minWidth || 20;
36445     this.minHeight = config.minHeight || 20;
36446 };
36447
36448 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36449     hide : function(){
36450         // center panel can't be hidden
36451     },
36452     
36453     show : function(){
36454         // center panel can't be hidden
36455     },
36456     
36457     getMinWidth: function(){
36458         return this.minWidth;
36459     },
36460     
36461     getMinHeight: function(){
36462         return this.minHeight;
36463     }
36464 });
36465
36466
36467
36468
36469  
36470
36471
36472
36473
36474
36475 Roo.bootstrap.layout.North = function(config)
36476 {
36477     config.region = 'north';
36478     config.cursor = 'n-resize';
36479     
36480     Roo.bootstrap.layout.Split.call(this, config);
36481     
36482     
36483     if(this.split){
36484         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36485         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36486         this.split.el.addClass("roo-layout-split-v");
36487     }
36488     var size = config.initialSize || config.height;
36489     if(typeof size != "undefined"){
36490         this.el.setHeight(size);
36491     }
36492 };
36493 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36494 {
36495     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36496     
36497     
36498     
36499     getBox : function(){
36500         if(this.collapsed){
36501             return this.collapsedEl.getBox();
36502         }
36503         var box = this.el.getBox();
36504         if(this.split){
36505             box.height += this.split.el.getHeight();
36506         }
36507         return box;
36508     },
36509     
36510     updateBox : function(box){
36511         if(this.split && !this.collapsed){
36512             box.height -= this.split.el.getHeight();
36513             this.split.el.setLeft(box.x);
36514             this.split.el.setTop(box.y+box.height);
36515             this.split.el.setWidth(box.width);
36516         }
36517         if(this.collapsed){
36518             this.updateBody(box.width, null);
36519         }
36520         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36521     }
36522 });
36523
36524
36525
36526
36527
36528 Roo.bootstrap.layout.South = function(config){
36529     config.region = 'south';
36530     config.cursor = 's-resize';
36531     Roo.bootstrap.layout.Split.call(this, config);
36532     if(this.split){
36533         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36534         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36535         this.split.el.addClass("roo-layout-split-v");
36536     }
36537     var size = config.initialSize || config.height;
36538     if(typeof size != "undefined"){
36539         this.el.setHeight(size);
36540     }
36541 };
36542
36543 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36544     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36545     getBox : function(){
36546         if(this.collapsed){
36547             return this.collapsedEl.getBox();
36548         }
36549         var box = this.el.getBox();
36550         if(this.split){
36551             var sh = this.split.el.getHeight();
36552             box.height += sh;
36553             box.y -= sh;
36554         }
36555         return box;
36556     },
36557     
36558     updateBox : function(box){
36559         if(this.split && !this.collapsed){
36560             var sh = this.split.el.getHeight();
36561             box.height -= sh;
36562             box.y += sh;
36563             this.split.el.setLeft(box.x);
36564             this.split.el.setTop(box.y-sh);
36565             this.split.el.setWidth(box.width);
36566         }
36567         if(this.collapsed){
36568             this.updateBody(box.width, null);
36569         }
36570         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36571     }
36572 });
36573
36574 Roo.bootstrap.layout.East = function(config){
36575     config.region = "east";
36576     config.cursor = "e-resize";
36577     Roo.bootstrap.layout.Split.call(this, config);
36578     if(this.split){
36579         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36580         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36581         this.split.el.addClass("roo-layout-split-h");
36582     }
36583     var size = config.initialSize || config.width;
36584     if(typeof size != "undefined"){
36585         this.el.setWidth(size);
36586     }
36587 };
36588 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36589     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36590     getBox : function(){
36591         if(this.collapsed){
36592             return this.collapsedEl.getBox();
36593         }
36594         var box = this.el.getBox();
36595         if(this.split){
36596             var sw = this.split.el.getWidth();
36597             box.width += sw;
36598             box.x -= sw;
36599         }
36600         return box;
36601     },
36602
36603     updateBox : function(box){
36604         if(this.split && !this.collapsed){
36605             var sw = this.split.el.getWidth();
36606             box.width -= sw;
36607             this.split.el.setLeft(box.x);
36608             this.split.el.setTop(box.y);
36609             this.split.el.setHeight(box.height);
36610             box.x += sw;
36611         }
36612         if(this.collapsed){
36613             this.updateBody(null, box.height);
36614         }
36615         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36616     }
36617 });
36618
36619 Roo.bootstrap.layout.West = function(config){
36620     config.region = "west";
36621     config.cursor = "w-resize";
36622     
36623     Roo.bootstrap.layout.Split.call(this, config);
36624     if(this.split){
36625         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36626         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36627         this.split.el.addClass("roo-layout-split-h");
36628     }
36629     
36630 };
36631 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36632     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36633     
36634     onRender: function(ctr, pos)
36635     {
36636         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36637         var size = this.config.initialSize || this.config.width;
36638         if(typeof size != "undefined"){
36639             this.el.setWidth(size);
36640         }
36641     },
36642     
36643     getBox : function(){
36644         if(this.collapsed){
36645             return this.collapsedEl.getBox();
36646         }
36647         var box = this.el.getBox();
36648         if(this.split){
36649             box.width += this.split.el.getWidth();
36650         }
36651         return box;
36652     },
36653     
36654     updateBox : function(box){
36655         if(this.split && !this.collapsed){
36656             var sw = this.split.el.getWidth();
36657             box.width -= sw;
36658             this.split.el.setLeft(box.x+box.width);
36659             this.split.el.setTop(box.y);
36660             this.split.el.setHeight(box.height);
36661         }
36662         if(this.collapsed){
36663             this.updateBody(null, box.height);
36664         }
36665         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36666     }
36667 });
36668 Roo.namespace("Roo.bootstrap.panel");/*
36669  * Based on:
36670  * Ext JS Library 1.1.1
36671  * Copyright(c) 2006-2007, Ext JS, LLC.
36672  *
36673  * Originally Released Under LGPL - original licence link has changed is not relivant.
36674  *
36675  * Fork - LGPL
36676  * <script type="text/javascript">
36677  */
36678 /**
36679  * @class Roo.ContentPanel
36680  * @extends Roo.util.Observable
36681  * A basic ContentPanel element.
36682  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36683  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36684  * @cfg {Boolean/Object} autoCreate True to auto generate the DOM element for this panel, or a {@link Roo.DomHelper} config of the element to create
36685  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36686  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36687  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36688  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36689  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36690  * @cfg {String} title          The title for this panel
36691  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36692  * @cfg {String} url            Calls {@link #setUrl} with this value
36693  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36694  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36695  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36696  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36697  * @cfg {Boolean} badges render the badges
36698
36699  * @constructor
36700  * Create a new ContentPanel.
36701  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36702  * @param {String/Object} config A string to set only the title or a config object
36703  * @param {String} content (optional) Set the HTML content for this panel
36704  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36705  */
36706 Roo.bootstrap.panel.Content = function( config){
36707     
36708     this.tpl = config.tpl || false;
36709     
36710     var el = config.el;
36711     var content = config.content;
36712
36713     if(config.autoCreate){ // xtype is available if this is called from factory
36714         el = Roo.id();
36715     }
36716     this.el = Roo.get(el);
36717     if(!this.el && config && config.autoCreate){
36718         if(typeof config.autoCreate == "object"){
36719             if(!config.autoCreate.id){
36720                 config.autoCreate.id = config.id||el;
36721             }
36722             this.el = Roo.DomHelper.append(document.body,
36723                         config.autoCreate, true);
36724         }else{
36725             var elcfg =  {   tag: "div",
36726                             cls: "roo-layout-inactive-content",
36727                             id: config.id||el
36728                             };
36729             if (config.html) {
36730                 elcfg.html = config.html;
36731                 
36732             }
36733                         
36734             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36735         }
36736     } 
36737     this.closable = false;
36738     this.loaded = false;
36739     this.active = false;
36740    
36741       
36742     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36743         
36744         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36745         
36746         this.wrapEl = this.el; //this.el.wrap();
36747         var ti = [];
36748         if (config.toolbar.items) {
36749             ti = config.toolbar.items ;
36750             delete config.toolbar.items ;
36751         }
36752         
36753         var nitems = [];
36754         this.toolbar.render(this.wrapEl, 'before');
36755         for(var i =0;i < ti.length;i++) {
36756           //  Roo.log(['add child', items[i]]);
36757             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36758         }
36759         this.toolbar.items = nitems;
36760         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36761         delete config.toolbar;
36762         
36763     }
36764     /*
36765     // xtype created footer. - not sure if will work as we normally have to render first..
36766     if (this.footer && !this.footer.el && this.footer.xtype) {
36767         if (!this.wrapEl) {
36768             this.wrapEl = this.el.wrap();
36769         }
36770     
36771         this.footer.container = this.wrapEl.createChild();
36772          
36773         this.footer = Roo.factory(this.footer, Roo);
36774         
36775     }
36776     */
36777     
36778      if(typeof config == "string"){
36779         this.title = config;
36780     }else{
36781         Roo.apply(this, config);
36782     }
36783     
36784     if(this.resizeEl){
36785         this.resizeEl = Roo.get(this.resizeEl, true);
36786     }else{
36787         this.resizeEl = this.el;
36788     }
36789     // handle view.xtype
36790     
36791  
36792     
36793     
36794     this.addEvents({
36795         /**
36796          * @event activate
36797          * Fires when this panel is activated. 
36798          * @param {Roo.ContentPanel} this
36799          */
36800         "activate" : true,
36801         /**
36802          * @event deactivate
36803          * Fires when this panel is activated. 
36804          * @param {Roo.ContentPanel} this
36805          */
36806         "deactivate" : true,
36807
36808         /**
36809          * @event resize
36810          * Fires when this panel is resized if fitToFrame is true.
36811          * @param {Roo.ContentPanel} this
36812          * @param {Number} width The width after any component adjustments
36813          * @param {Number} height The height after any component adjustments
36814          */
36815         "resize" : true,
36816         
36817          /**
36818          * @event render
36819          * Fires when this tab is created
36820          * @param {Roo.ContentPanel} this
36821          */
36822         "render" : true
36823         
36824         
36825         
36826     });
36827     
36828
36829     
36830     
36831     if(this.autoScroll){
36832         this.resizeEl.setStyle("overflow", "auto");
36833     } else {
36834         // fix randome scrolling
36835         //this.el.on('scroll', function() {
36836         //    Roo.log('fix random scolling');
36837         //    this.scrollTo('top',0); 
36838         //});
36839     }
36840     content = content || this.content;
36841     if(content){
36842         this.setContent(content);
36843     }
36844     if(config && config.url){
36845         this.setUrl(this.url, this.params, this.loadOnce);
36846     }
36847     
36848     
36849     
36850     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36851     
36852     if (this.view && typeof(this.view.xtype) != 'undefined') {
36853         this.view.el = this.el.appendChild(document.createElement("div"));
36854         this.view = Roo.factory(this.view); 
36855         this.view.render  &&  this.view.render(false, '');  
36856     }
36857     
36858     
36859     this.fireEvent('render', this);
36860 };
36861
36862 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36863     
36864     tabTip : '',
36865     
36866     setRegion : function(region){
36867         this.region = region;
36868         this.setActiveClass(region && !this.background);
36869     },
36870     
36871     
36872     setActiveClass: function(state)
36873     {
36874         if(state){
36875            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36876            this.el.setStyle('position','relative');
36877         }else{
36878            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36879            this.el.setStyle('position', 'absolute');
36880         } 
36881     },
36882     
36883     /**
36884      * Returns the toolbar for this Panel if one was configured. 
36885      * @return {Roo.Toolbar} 
36886      */
36887     getToolbar : function(){
36888         return this.toolbar;
36889     },
36890     
36891     setActiveState : function(active)
36892     {
36893         this.active = active;
36894         this.setActiveClass(active);
36895         if(!active){
36896             if(this.fireEvent("deactivate", this) === false){
36897                 return false;
36898             }
36899             return true;
36900         }
36901         this.fireEvent("activate", this);
36902         return true;
36903     },
36904     /**
36905      * Updates this panel's element
36906      * @param {String} content The new content
36907      * @param {Boolean} loadScripts (optional) true to look for and process scripts
36908     */
36909     setContent : function(content, loadScripts){
36910         this.el.update(content, loadScripts);
36911     },
36912
36913     ignoreResize : function(w, h){
36914         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36915             return true;
36916         }else{
36917             this.lastSize = {width: w, height: h};
36918             return false;
36919         }
36920     },
36921     /**
36922      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36923      * @return {Roo.UpdateManager} The UpdateManager
36924      */
36925     getUpdateManager : function(){
36926         return this.el.getUpdateManager();
36927     },
36928      /**
36929      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36930      * @param {Object/String/Function} url The url for this request or a function to call to get the url or a config object containing any of the following options:
36931 <pre><code>
36932 panel.load({
36933     url: "your-url.php",
36934     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36935     callback: yourFunction,
36936     scope: yourObject, //(optional scope)
36937     discardUrl: false,
36938     nocache: false,
36939     text: "Loading...",
36940     timeout: 30,
36941     scripts: false
36942 });
36943 </code></pre>
36944      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
36945      * are shorthand for <i>disableCaching</i>, <i>indicatorText</i> and <i>loadScripts</i> and are used to set their associated property on this panel UpdateManager instance.
36946      * @param {String/Object} params (optional) The parameters to pass as either a URL encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
36947      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
36948      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
36949      * @return {Roo.ContentPanel} this
36950      */
36951     load : function(){
36952         var um = this.el.getUpdateManager();
36953         um.update.apply(um, arguments);
36954         return this;
36955     },
36956
36957
36958     /**
36959      * Set a URL to be used to load the content for this panel. When this panel is activated, the content will be loaded from that URL.
36960      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
36961      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
36962      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this panel is activated. (Defaults to false)
36963      * @return {Roo.UpdateManager} The UpdateManager
36964      */
36965     setUrl : function(url, params, loadOnce){
36966         if(this.refreshDelegate){
36967             this.removeListener("activate", this.refreshDelegate);
36968         }
36969         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36970         this.on("activate", this.refreshDelegate);
36971         return this.el.getUpdateManager();
36972     },
36973     
36974     _handleRefresh : function(url, params, loadOnce){
36975         if(!loadOnce || !this.loaded){
36976             var updater = this.el.getUpdateManager();
36977             updater.update(url, params, this._setLoaded.createDelegate(this));
36978         }
36979     },
36980     
36981     _setLoaded : function(){
36982         this.loaded = true;
36983     }, 
36984     
36985     /**
36986      * Returns this panel's id
36987      * @return {String} 
36988      */
36989     getId : function(){
36990         return this.el.id;
36991     },
36992     
36993     /** 
36994      * Returns this panel's element - used by regiosn to add.
36995      * @return {Roo.Element} 
36996      */
36997     getEl : function(){
36998         return this.wrapEl || this.el;
36999     },
37000     
37001    
37002     
37003     adjustForComponents : function(width, height)
37004     {
37005         //Roo.log('adjustForComponents ');
37006         if(this.resizeEl != this.el){
37007             width -= this.el.getFrameWidth('lr');
37008             height -= this.el.getFrameWidth('tb');
37009         }
37010         if(this.toolbar){
37011             var te = this.toolbar.getEl();
37012             te.setWidth(width);
37013             height -= te.getHeight();
37014         }
37015         if(this.footer){
37016             var te = this.footer.getEl();
37017             te.setWidth(width);
37018             height -= te.getHeight();
37019         }
37020         
37021         
37022         if(this.adjustments){
37023             width += this.adjustments[0];
37024             height += this.adjustments[1];
37025         }
37026         return {"width": width, "height": height};
37027     },
37028     
37029     setSize : function(width, height){
37030         if(this.fitToFrame && !this.ignoreResize(width, height)){
37031             if(this.fitContainer && this.resizeEl != this.el){
37032                 this.el.setSize(width, height);
37033             }
37034             var size = this.adjustForComponents(width, height);
37035             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37036             this.fireEvent('resize', this, size.width, size.height);
37037         }
37038     },
37039     
37040     /**
37041      * Returns this panel's title
37042      * @return {String} 
37043      */
37044     getTitle : function(){
37045         
37046         if (typeof(this.title) != 'object') {
37047             return this.title;
37048         }
37049         
37050         var t = '';
37051         for (var k in this.title) {
37052             if (!this.title.hasOwnProperty(k)) {
37053                 continue;
37054             }
37055             
37056             if (k.indexOf('-') >= 0) {
37057                 var s = k.split('-');
37058                 for (var i = 0; i<s.length; i++) {
37059                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37060                 }
37061             } else {
37062                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37063             }
37064         }
37065         return t;
37066     },
37067     
37068     /**
37069      * Set this panel's title
37070      * @param {String} title
37071      */
37072     setTitle : function(title){
37073         this.title = title;
37074         if(this.region){
37075             this.region.updatePanelTitle(this, title);
37076         }
37077     },
37078     
37079     /**
37080      * Returns true is this panel was configured to be closable
37081      * @return {Boolean} 
37082      */
37083     isClosable : function(){
37084         return this.closable;
37085     },
37086     
37087     beforeSlide : function(){
37088         this.el.clip();
37089         this.resizeEl.clip();
37090     },
37091     
37092     afterSlide : function(){
37093         this.el.unclip();
37094         this.resizeEl.unclip();
37095     },
37096     
37097     /**
37098      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37099      *   Will fail silently if the {@link #setUrl} method has not been called.
37100      *   This does not activate the panel, just updates its content.
37101      */
37102     refresh : function(){
37103         if(this.refreshDelegate){
37104            this.loaded = false;
37105            this.refreshDelegate();
37106         }
37107     },
37108     
37109     /**
37110      * Destroys this panel
37111      */
37112     destroy : function(){
37113         this.el.removeAllListeners();
37114         var tempEl = document.createElement("span");
37115         tempEl.appendChild(this.el.dom);
37116         tempEl.innerHTML = "";
37117         this.el.remove();
37118         this.el = null;
37119     },
37120     
37121     /**
37122      * form - if the content panel contains a form - this is a reference to it.
37123      * @type {Roo.form.Form}
37124      */
37125     form : false,
37126     /**
37127      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37128      *    This contains a reference to it.
37129      * @type {Roo.View}
37130      */
37131     view : false,
37132     
37133       /**
37134      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37135      * <pre><code>
37136
37137 layout.addxtype({
37138        xtype : 'Form',
37139        items: [ .... ]
37140    }
37141 );
37142
37143 </code></pre>
37144      * @param {Object} cfg Xtype definition of item to add.
37145      */
37146     
37147     
37148     getChildContainer: function () {
37149         return this.getEl();
37150     }
37151     
37152     
37153     /*
37154         var  ret = new Roo.factory(cfg);
37155         return ret;
37156         
37157         
37158         // add form..
37159         if (cfg.xtype.match(/^Form$/)) {
37160             
37161             var el;
37162             //if (this.footer) {
37163             //    el = this.footer.container.insertSibling(false, 'before');
37164             //} else {
37165                 el = this.el.createChild();
37166             //}
37167
37168             this.form = new  Roo.form.Form(cfg);
37169             
37170             
37171             if ( this.form.allItems.length) {
37172                 this.form.render(el.dom);
37173             }
37174             return this.form;
37175         }
37176         // should only have one of theses..
37177         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37178             // views.. should not be just added - used named prop 'view''
37179             
37180             cfg.el = this.el.appendChild(document.createElement("div"));
37181             // factory?
37182             
37183             var ret = new Roo.factory(cfg);
37184              
37185              ret.render && ret.render(false, ''); // render blank..
37186             this.view = ret;
37187             return ret;
37188         }
37189         return false;
37190     }
37191     \*/
37192 });
37193  
37194 /**
37195  * @class Roo.bootstrap.panel.Grid
37196  * @extends Roo.bootstrap.panel.Content
37197  * @constructor
37198  * Create a new GridPanel.
37199  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37200  * @param {Object} config A the config object
37201   
37202  */
37203
37204
37205
37206 Roo.bootstrap.panel.Grid = function(config)
37207 {
37208     
37209       
37210     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37211         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37212
37213     config.el = this.wrapper;
37214     //this.el = this.wrapper;
37215     
37216       if (config.container) {
37217         // ctor'ed from a Border/panel.grid
37218         
37219         
37220         this.wrapper.setStyle("overflow", "hidden");
37221         this.wrapper.addClass('roo-grid-container');
37222
37223     }
37224     
37225     
37226     if(config.toolbar){
37227         var tool_el = this.wrapper.createChild();    
37228         this.toolbar = Roo.factory(config.toolbar);
37229         var ti = [];
37230         if (config.toolbar.items) {
37231             ti = config.toolbar.items ;
37232             delete config.toolbar.items ;
37233         }
37234         
37235         var nitems = [];
37236         this.toolbar.render(tool_el);
37237         for(var i =0;i < ti.length;i++) {
37238           //  Roo.log(['add child', items[i]]);
37239             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37240         }
37241         this.toolbar.items = nitems;
37242         
37243         delete config.toolbar;
37244     }
37245     
37246     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37247     config.grid.scrollBody = true;;
37248     config.grid.monitorWindowResize = false; // turn off autosizing
37249     config.grid.autoHeight = false;
37250     config.grid.autoWidth = false;
37251     
37252     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37253     
37254     if (config.background) {
37255         // render grid on panel activation (if panel background)
37256         this.on('activate', function(gp) {
37257             if (!gp.grid.rendered) {
37258                 gp.grid.render(this.wrapper);
37259                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37260             }
37261         });
37262             
37263     } else {
37264         this.grid.render(this.wrapper);
37265         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37266
37267     }
37268     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37269     // ??? needed ??? config.el = this.wrapper;
37270     
37271     
37272     
37273   
37274     // xtype created footer. - not sure if will work as we normally have to render first..
37275     if (this.footer && !this.footer.el && this.footer.xtype) {
37276         
37277         var ctr = this.grid.getView().getFooterPanel(true);
37278         this.footer.dataSource = this.grid.dataSource;
37279         this.footer = Roo.factory(this.footer, Roo);
37280         this.footer.render(ctr);
37281         
37282     }
37283     
37284     
37285     
37286     
37287      
37288 };
37289
37290 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37291     getId : function(){
37292         return this.grid.id;
37293     },
37294     
37295     /**
37296      * Returns the grid for this panel
37297      * @return {Roo.bootstrap.Table} 
37298      */
37299     getGrid : function(){
37300         return this.grid;    
37301     },
37302     
37303     setSize : function(width, height){
37304         if(!this.ignoreResize(width, height)){
37305             var grid = this.grid;
37306             var size = this.adjustForComponents(width, height);
37307             var gridel = grid.getGridEl();
37308             gridel.setSize(size.width, size.height);
37309             /*
37310             var thd = grid.getGridEl().select('thead',true).first();
37311             var tbd = grid.getGridEl().select('tbody', true).first();
37312             if (tbd) {
37313                 tbd.setSize(width, height - thd.getHeight());
37314             }
37315             */
37316             grid.autoSize();
37317         }
37318     },
37319      
37320     
37321     
37322     beforeSlide : function(){
37323         this.grid.getView().scroller.clip();
37324     },
37325     
37326     afterSlide : function(){
37327         this.grid.getView().scroller.unclip();
37328     },
37329     
37330     destroy : function(){
37331         this.grid.destroy();
37332         delete this.grid;
37333         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37334     }
37335 });
37336
37337 /**
37338  * @class Roo.bootstrap.panel.Nest
37339  * @extends Roo.bootstrap.panel.Content
37340  * @constructor
37341  * Create a new Panel, that can contain a layout.Border.
37342  * 
37343  * 
37344  * @param {Roo.BorderLayout} layout The layout for this panel
37345  * @param {String/Object} config A string to set only the title or a config object
37346  */
37347 Roo.bootstrap.panel.Nest = function(config)
37348 {
37349     // construct with only one argument..
37350     /* FIXME - implement nicer consturctors
37351     if (layout.layout) {
37352         config = layout;
37353         layout = config.layout;
37354         delete config.layout;
37355     }
37356     if (layout.xtype && !layout.getEl) {
37357         // then layout needs constructing..
37358         layout = Roo.factory(layout, Roo);
37359     }
37360     */
37361     
37362     config.el =  config.layout.getEl();
37363     
37364     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37365     
37366     config.layout.monitorWindowResize = false; // turn off autosizing
37367     this.layout = config.layout;
37368     this.layout.getEl().addClass("roo-layout-nested-layout");
37369     
37370     
37371     
37372     
37373 };
37374
37375 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37376
37377     setSize : function(width, height){
37378         if(!this.ignoreResize(width, height)){
37379             var size = this.adjustForComponents(width, height);
37380             var el = this.layout.getEl();
37381             if (size.height < 1) {
37382                 el.setWidth(size.width);   
37383             } else {
37384                 el.setSize(size.width, size.height);
37385             }
37386             var touch = el.dom.offsetWidth;
37387             this.layout.layout();
37388             // ie requires a double layout on the first pass
37389             if(Roo.isIE && !this.initialized){
37390                 this.initialized = true;
37391                 this.layout.layout();
37392             }
37393         }
37394     },
37395     
37396     // activate all subpanels if not currently active..
37397     
37398     setActiveState : function(active){
37399         this.active = active;
37400         this.setActiveClass(active);
37401         
37402         if(!active){
37403             this.fireEvent("deactivate", this);
37404             return;
37405         }
37406         
37407         this.fireEvent("activate", this);
37408         // not sure if this should happen before or after..
37409         if (!this.layout) {
37410             return; // should not happen..
37411         }
37412         var reg = false;
37413         for (var r in this.layout.regions) {
37414             reg = this.layout.getRegion(r);
37415             if (reg.getActivePanel()) {
37416                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37417                 reg.setActivePanel(reg.getActivePanel());
37418                 continue;
37419             }
37420             if (!reg.panels.length) {
37421                 continue;
37422             }
37423             reg.showPanel(reg.getPanel(0));
37424         }
37425         
37426         
37427         
37428         
37429     },
37430     
37431     /**
37432      * Returns the nested BorderLayout for this panel
37433      * @return {Roo.BorderLayout} 
37434      */
37435     getLayout : function(){
37436         return this.layout;
37437     },
37438     
37439      /**
37440      * Adds a xtype elements to the layout of the nested panel
37441      * <pre><code>
37442
37443 panel.addxtype({
37444        xtype : 'ContentPanel',
37445        region: 'west',
37446        items: [ .... ]
37447    }
37448 );
37449
37450 panel.addxtype({
37451         xtype : 'NestedLayoutPanel',
37452         region: 'west',
37453         layout: {
37454            center: { },
37455            west: { }   
37456         },
37457         items : [ ... list of content panels or nested layout panels.. ]
37458    }
37459 );
37460 </code></pre>
37461      * @param {Object} cfg Xtype definition of item to add.
37462      */
37463     addxtype : function(cfg) {
37464         return this.layout.addxtype(cfg);
37465     
37466     }
37467 });        /*
37468  * Based on:
37469  * Ext JS Library 1.1.1
37470  * Copyright(c) 2006-2007, Ext JS, LLC.
37471  *
37472  * Originally Released Under LGPL - original licence link has changed is not relivant.
37473  *
37474  * Fork - LGPL
37475  * <script type="text/javascript">
37476  */
37477 /**
37478  * @class Roo.TabPanel
37479  * @extends Roo.util.Observable
37480  * A lightweight tab container.
37481  * <br><br>
37482  * Usage:
37483  * <pre><code>
37484 // basic tabs 1, built from existing content
37485 var tabs = new Roo.TabPanel("tabs1");
37486 tabs.addTab("script", "View Script");
37487 tabs.addTab("markup", "View Markup");
37488 tabs.activate("script");
37489
37490 // more advanced tabs, built from javascript
37491 var jtabs = new Roo.TabPanel("jtabs");
37492 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37493
37494 // set up the UpdateManager
37495 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37496 var updater = tab2.getUpdateManager();
37497 updater.setDefaultUrl("ajax1.htm");
37498 tab2.on('activate', updater.refresh, updater, true);
37499
37500 // Use setUrl for Ajax loading
37501 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37502 tab3.setUrl("ajax2.htm", null, true);
37503
37504 // Disabled tab
37505 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37506 tab4.disable();
37507
37508 jtabs.activate("jtabs-1");
37509  * </code></pre>
37510  * @constructor
37511  * Create a new TabPanel.
37512  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37513  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37514  */
37515 Roo.bootstrap.panel.Tabs = function(config){
37516     /**
37517     * The container element for this TabPanel.
37518     * @type Roo.Element
37519     */
37520     this.el = Roo.get(config.el);
37521     delete config.el;
37522     if(config){
37523         if(typeof config == "boolean"){
37524             this.tabPosition = config ? "bottom" : "top";
37525         }else{
37526             Roo.apply(this, config);
37527         }
37528     }
37529     
37530     if(this.tabPosition == "bottom"){
37531         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37532         this.el.addClass("roo-tabs-bottom");
37533     }
37534     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37535     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37536     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37537     if(Roo.isIE){
37538         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37539     }
37540     if(this.tabPosition != "bottom"){
37541         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37542          * @type Roo.Element
37543          */
37544         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37545         this.el.addClass("roo-tabs-top");
37546     }
37547     this.items = [];
37548
37549     this.bodyEl.setStyle("position", "relative");
37550
37551     this.active = null;
37552     this.activateDelegate = this.activate.createDelegate(this);
37553
37554     this.addEvents({
37555         /**
37556          * @event tabchange
37557          * Fires when the active tab changes
37558          * @param {Roo.TabPanel} this
37559          * @param {Roo.TabPanelItem} activePanel The new active tab
37560          */
37561         "tabchange": true,
37562         /**
37563          * @event beforetabchange
37564          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37565          * @param {Roo.TabPanel} this
37566          * @param {Object} e Set cancel to true on this object to cancel the tab change
37567          * @param {Roo.TabPanelItem} tab The tab being changed to
37568          */
37569         "beforetabchange" : true
37570     });
37571
37572     Roo.EventManager.onWindowResize(this.onResize, this);
37573     this.cpad = this.el.getPadding("lr");
37574     this.hiddenCount = 0;
37575
37576
37577     // toolbar on the tabbar support...
37578     if (this.toolbar) {
37579         alert("no toolbar support yet");
37580         this.toolbar  = false;
37581         /*
37582         var tcfg = this.toolbar;
37583         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37584         this.toolbar = new Roo.Toolbar(tcfg);
37585         if (Roo.isSafari) {
37586             var tbl = tcfg.container.child('table', true);
37587             tbl.setAttribute('width', '100%');
37588         }
37589         */
37590         
37591     }
37592    
37593
37594
37595     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37596 };
37597
37598 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37599     /*
37600      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37601      */
37602     tabPosition : "top",
37603     /*
37604      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37605      */
37606     currentTabWidth : 0,
37607     /*
37608      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37609      */
37610     minTabWidth : 40,
37611     /*
37612      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37613      */
37614     maxTabWidth : 250,
37615     /*
37616      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37617      */
37618     preferredTabWidth : 175,
37619     /*
37620      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37621      */
37622     resizeTabs : false,
37623     /*
37624      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37625      */
37626     monitorResize : true,
37627     /*
37628      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37629      */
37630     toolbar : false,
37631
37632     /**
37633      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37634      * @param {String} id The id of the div to use <b>or create</b>
37635      * @param {String} text The text for the tab
37636      * @param {String} content (optional) Content to put in the TabPanelItem body
37637      * @param {Boolean} closable (optional) True to create a close icon on the tab
37638      * @return {Roo.TabPanelItem} The created TabPanelItem
37639      */
37640     addTab : function(id, text, content, closable, tpl)
37641     {
37642         var item = new Roo.bootstrap.panel.TabItem({
37643             panel: this,
37644             id : id,
37645             text : text,
37646             closable : closable,
37647             tpl : tpl
37648         });
37649         this.addTabItem(item);
37650         if(content){
37651             item.setContent(content);
37652         }
37653         return item;
37654     },
37655
37656     /**
37657      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37658      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37659      * @return {Roo.TabPanelItem}
37660      */
37661     getTab : function(id){
37662         return this.items[id];
37663     },
37664
37665     /**
37666      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37667      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37668      */
37669     hideTab : function(id){
37670         var t = this.items[id];
37671         if(!t.isHidden()){
37672            t.setHidden(true);
37673            this.hiddenCount++;
37674            this.autoSizeTabs();
37675         }
37676     },
37677
37678     /**
37679      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37680      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37681      */
37682     unhideTab : function(id){
37683         var t = this.items[id];
37684         if(t.isHidden()){
37685            t.setHidden(false);
37686            this.hiddenCount--;
37687            this.autoSizeTabs();
37688         }
37689     },
37690
37691     /**
37692      * Adds an existing {@link Roo.TabPanelItem}.
37693      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37694      */
37695     addTabItem : function(item){
37696         this.items[item.id] = item;
37697         this.items.push(item);
37698       //  if(this.resizeTabs){
37699     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37700   //         this.autoSizeTabs();
37701 //        }else{
37702 //            item.autoSize();
37703        // }
37704     },
37705
37706     /**
37707      * Removes a {@link Roo.TabPanelItem}.
37708      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37709      */
37710     removeTab : function(id){
37711         var items = this.items;
37712         var tab = items[id];
37713         if(!tab) { return; }
37714         var index = items.indexOf(tab);
37715         if(this.active == tab && items.length > 1){
37716             var newTab = this.getNextAvailable(index);
37717             if(newTab) {
37718                 newTab.activate();
37719             }
37720         }
37721         this.stripEl.dom.removeChild(tab.pnode.dom);
37722         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37723             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37724         }
37725         items.splice(index, 1);
37726         delete this.items[tab.id];
37727         tab.fireEvent("close", tab);
37728         tab.purgeListeners();
37729         this.autoSizeTabs();
37730     },
37731
37732     getNextAvailable : function(start){
37733         var items = this.items;
37734         var index = start;
37735         // look for a next tab that will slide over to
37736         // replace the one being removed
37737         while(index < items.length){
37738             var item = items[++index];
37739             if(item && !item.isHidden()){
37740                 return item;
37741             }
37742         }
37743         // if one isn't found select the previous tab (on the left)
37744         index = start;
37745         while(index >= 0){
37746             var item = items[--index];
37747             if(item && !item.isHidden()){
37748                 return item;
37749             }
37750         }
37751         return null;
37752     },
37753
37754     /**
37755      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37756      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37757      */
37758     disableTab : function(id){
37759         var tab = this.items[id];
37760         if(tab && this.active != tab){
37761             tab.disable();
37762         }
37763     },
37764
37765     /**
37766      * Enables a {@link Roo.TabPanelItem} that is disabled.
37767      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37768      */
37769     enableTab : function(id){
37770         var tab = this.items[id];
37771         tab.enable();
37772     },
37773
37774     /**
37775      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37776      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37777      * @return {Roo.TabPanelItem} The TabPanelItem.
37778      */
37779     activate : function(id){
37780         var tab = this.items[id];
37781         if(!tab){
37782             return null;
37783         }
37784         if(tab == this.active || tab.disabled){
37785             return tab;
37786         }
37787         var e = {};
37788         this.fireEvent("beforetabchange", this, e, tab);
37789         if(e.cancel !== true && !tab.disabled){
37790             if(this.active){
37791                 this.active.hide();
37792             }
37793             this.active = this.items[id];
37794             this.active.show();
37795             this.fireEvent("tabchange", this, this.active);
37796         }
37797         return tab;
37798     },
37799
37800     /**
37801      * Gets the active {@link Roo.TabPanelItem}.
37802      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37803      */
37804     getActiveTab : function(){
37805         return this.active;
37806     },
37807
37808     /**
37809      * Updates the tab body element to fit the height of the container element
37810      * for overflow scrolling
37811      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37812      */
37813     syncHeight : function(targetHeight){
37814         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37815         var bm = this.bodyEl.getMargins();
37816         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37817         this.bodyEl.setHeight(newHeight);
37818         return newHeight;
37819     },
37820
37821     onResize : function(){
37822         if(this.monitorResize){
37823             this.autoSizeTabs();
37824         }
37825     },
37826
37827     /**
37828      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37829      */
37830     beginUpdate : function(){
37831         this.updating = true;
37832     },
37833
37834     /**
37835      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37836      */
37837     endUpdate : function(){
37838         this.updating = false;
37839         this.autoSizeTabs();
37840     },
37841
37842     /**
37843      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37844      */
37845     autoSizeTabs : function(){
37846         var count = this.items.length;
37847         var vcount = count - this.hiddenCount;
37848         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37849             return;
37850         }
37851         var w = Math.max(this.el.getWidth() - this.cpad, 10);
37852         var availWidth = Math.floor(w / vcount);
37853         var b = this.stripBody;
37854         if(b.getWidth() > w){
37855             var tabs = this.items;
37856             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37857             if(availWidth < this.minTabWidth){
37858                 /*if(!this.sleft){    // incomplete scrolling code
37859                     this.createScrollButtons();
37860                 }
37861                 this.showScroll();
37862                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37863             }
37864         }else{
37865             if(this.currentTabWidth < this.preferredTabWidth){
37866                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37867             }
37868         }
37869     },
37870
37871     /**
37872      * Returns the number of tabs in this TabPanel.
37873      * @return {Number}
37874      */
37875      getCount : function(){
37876          return this.items.length;
37877      },
37878
37879     /**
37880      * Resizes all the tabs to the passed width
37881      * @param {Number} The new width
37882      */
37883     setTabWidth : function(width){
37884         this.currentTabWidth = width;
37885         for(var i = 0, len = this.items.length; i < len; i++) {
37886                 if(!this.items[i].isHidden()) {
37887                 this.items[i].setWidth(width);
37888             }
37889         }
37890     },
37891
37892     /**
37893      * Destroys this TabPanel
37894      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37895      */
37896     destroy : function(removeEl){
37897         Roo.EventManager.removeResizeListener(this.onResize, this);
37898         for(var i = 0, len = this.items.length; i < len; i++){
37899             this.items[i].purgeListeners();
37900         }
37901         if(removeEl === true){
37902             this.el.update("");
37903             this.el.remove();
37904         }
37905     },
37906     
37907     createStrip : function(container)
37908     {
37909         var strip = document.createElement("nav");
37910         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37911         container.appendChild(strip);
37912         return strip;
37913     },
37914     
37915     createStripList : function(strip)
37916     {
37917         // div wrapper for retard IE
37918         // returns the "tr" element.
37919         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37920         //'<div class="x-tabs-strip-wrap">'+
37921           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37922           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37923         return strip.firstChild; //.firstChild.firstChild.firstChild;
37924     },
37925     createBody : function(container)
37926     {
37927         var body = document.createElement("div");
37928         Roo.id(body, "tab-body");
37929         //Roo.fly(body).addClass("x-tabs-body");
37930         Roo.fly(body).addClass("tab-content");
37931         container.appendChild(body);
37932         return body;
37933     },
37934     createItemBody :function(bodyEl, id){
37935         var body = Roo.getDom(id);
37936         if(!body){
37937             body = document.createElement("div");
37938             body.id = id;
37939         }
37940         //Roo.fly(body).addClass("x-tabs-item-body");
37941         Roo.fly(body).addClass("tab-pane");
37942          bodyEl.insertBefore(body, bodyEl.firstChild);
37943         return body;
37944     },
37945     /** @private */
37946     createStripElements :  function(stripEl, text, closable, tpl)
37947     {
37948         var td = document.createElement("li"); // was td..
37949         
37950         
37951         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
37952         
37953         
37954         stripEl.appendChild(td);
37955         /*if(closable){
37956             td.className = "x-tabs-closable";
37957             if(!this.closeTpl){
37958                 this.closeTpl = new Roo.Template(
37959                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37960                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
37961                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
37962                 );
37963             }
37964             var el = this.closeTpl.overwrite(td, {"text": text});
37965             var close = el.getElementsByTagName("div")[0];
37966             var inner = el.getElementsByTagName("em")[0];
37967             return {"el": el, "close": close, "inner": inner};
37968         } else {
37969         */
37970         // not sure what this is..
37971 //            if(!this.tabTpl){
37972                 //this.tabTpl = new Roo.Template(
37973                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37974                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
37975                 //);
37976 //                this.tabTpl = new Roo.Template(
37977 //                   '<a href="#">' +
37978 //                   '<span unselectable="on"' +
37979 //                            (this.disableTooltips ? '' : ' title="{text}"') +
37980 //                            ' >{text}</span></a>'
37981 //                );
37982 //                
37983 //            }
37984
37985
37986             var template = tpl || this.tabTpl || false;
37987             
37988             if(!template){
37989                 
37990                 template = new Roo.Template(
37991                    '<a href="#">' +
37992                    '<span unselectable="on"' +
37993                             (this.disableTooltips ? '' : ' title="{text}"') +
37994                             ' >{text}</span></a>'
37995                 );
37996             }
37997             
37998             switch (typeof(template)) {
37999                 case 'object' :
38000                     break;
38001                 case 'string' :
38002                     template = new Roo.Template(template);
38003                     break;
38004                 default :
38005                     break;
38006             }
38007             
38008             var el = template.overwrite(td, {"text": text});
38009             
38010             var inner = el.getElementsByTagName("span")[0];
38011             
38012             return {"el": el, "inner": inner};
38013             
38014     }
38015         
38016     
38017 });
38018
38019 /**
38020  * @class Roo.TabPanelItem
38021  * @extends Roo.util.Observable
38022  * Represents an individual item (tab plus body) in a TabPanel.
38023  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38024  * @param {String} id The id of this TabPanelItem
38025  * @param {String} text The text for the tab of this TabPanelItem
38026  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38027  */
38028 Roo.bootstrap.panel.TabItem = function(config){
38029     /**
38030      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38031      * @type Roo.TabPanel
38032      */
38033     this.tabPanel = config.panel;
38034     /**
38035      * The id for this TabPanelItem
38036      * @type String
38037      */
38038     this.id = config.id;
38039     /** @private */
38040     this.disabled = false;
38041     /** @private */
38042     this.text = config.text;
38043     /** @private */
38044     this.loaded = false;
38045     this.closable = config.closable;
38046
38047     /**
38048      * The body element for this TabPanelItem.
38049      * @type Roo.Element
38050      */
38051     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38052     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38053     this.bodyEl.setStyle("display", "block");
38054     this.bodyEl.setStyle("zoom", "1");
38055     //this.hideAction();
38056
38057     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38058     /** @private */
38059     this.el = Roo.get(els.el);
38060     this.inner = Roo.get(els.inner, true);
38061     this.textEl = Roo.get(this.el.dom.firstChild, true);
38062     this.pnode = Roo.get(els.el.parentNode, true);
38063 //    this.el.on("mousedown", this.onTabMouseDown, this);
38064     this.el.on("click", this.onTabClick, this);
38065     /** @private */
38066     if(config.closable){
38067         var c = Roo.get(els.close, true);
38068         c.dom.title = this.closeText;
38069         c.addClassOnOver("close-over");
38070         c.on("click", this.closeClick, this);
38071      }
38072
38073     this.addEvents({
38074          /**
38075          * @event activate
38076          * Fires when this tab becomes the active tab.
38077          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38078          * @param {Roo.TabPanelItem} this
38079          */
38080         "activate": true,
38081         /**
38082          * @event beforeclose
38083          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38084          * @param {Roo.TabPanelItem} this
38085          * @param {Object} e Set cancel to true on this object to cancel the close.
38086          */
38087         "beforeclose": true,
38088         /**
38089          * @event close
38090          * Fires when this tab is closed.
38091          * @param {Roo.TabPanelItem} this
38092          */
38093          "close": true,
38094         /**
38095          * @event deactivate
38096          * Fires when this tab is no longer the active tab.
38097          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38098          * @param {Roo.TabPanelItem} this
38099          */
38100          "deactivate" : true
38101     });
38102     this.hidden = false;
38103
38104     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38105 };
38106
38107 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38108            {
38109     purgeListeners : function(){
38110        Roo.util.Observable.prototype.purgeListeners.call(this);
38111        this.el.removeAllListeners();
38112     },
38113     /**
38114      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38115      */
38116     show : function(){
38117         this.pnode.addClass("active");
38118         this.showAction();
38119         if(Roo.isOpera){
38120             this.tabPanel.stripWrap.repaint();
38121         }
38122         this.fireEvent("activate", this.tabPanel, this);
38123     },
38124
38125     /**
38126      * Returns true if this tab is the active tab.
38127      * @return {Boolean}
38128      */
38129     isActive : function(){
38130         return this.tabPanel.getActiveTab() == this;
38131     },
38132
38133     /**
38134      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38135      */
38136     hide : function(){
38137         this.pnode.removeClass("active");
38138         this.hideAction();
38139         this.fireEvent("deactivate", this.tabPanel, this);
38140     },
38141
38142     hideAction : function(){
38143         this.bodyEl.hide();
38144         this.bodyEl.setStyle("position", "absolute");
38145         this.bodyEl.setLeft("-20000px");
38146         this.bodyEl.setTop("-20000px");
38147     },
38148
38149     showAction : function(){
38150         this.bodyEl.setStyle("position", "relative");
38151         this.bodyEl.setTop("");
38152         this.bodyEl.setLeft("");
38153         this.bodyEl.show();
38154     },
38155
38156     /**
38157      * Set the tooltip for the tab.
38158      * @param {String} tooltip The tab's tooltip
38159      */
38160     setTooltip : function(text){
38161         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38162             this.textEl.dom.qtip = text;
38163             this.textEl.dom.removeAttribute('title');
38164         }else{
38165             this.textEl.dom.title = text;
38166         }
38167     },
38168
38169     onTabClick : function(e){
38170         e.preventDefault();
38171         this.tabPanel.activate(this.id);
38172     },
38173
38174     onTabMouseDown : function(e){
38175         e.preventDefault();
38176         this.tabPanel.activate(this.id);
38177     },
38178 /*
38179     getWidth : function(){
38180         return this.inner.getWidth();
38181     },
38182
38183     setWidth : function(width){
38184         var iwidth = width - this.pnode.getPadding("lr");
38185         this.inner.setWidth(iwidth);
38186         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38187         this.pnode.setWidth(width);
38188     },
38189 */
38190     /**
38191      * Show or hide the tab
38192      * @param {Boolean} hidden True to hide or false to show.
38193      */
38194     setHidden : function(hidden){
38195         this.hidden = hidden;
38196         this.pnode.setStyle("display", hidden ? "none" : "");
38197     },
38198
38199     /**
38200      * Returns true if this tab is "hidden"
38201      * @return {Boolean}
38202      */
38203     isHidden : function(){
38204         return this.hidden;
38205     },
38206
38207     /**
38208      * Returns the text for this tab
38209      * @return {String}
38210      */
38211     getText : function(){
38212         return this.text;
38213     },
38214     /*
38215     autoSize : function(){
38216         //this.el.beginMeasure();
38217         this.textEl.setWidth(1);
38218         /*
38219          *  #2804 [new] Tabs in Roojs
38220          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38221          */
38222         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38223         //this.el.endMeasure();
38224     //},
38225
38226     /**
38227      * Sets the text for the tab (Note: this also sets the tooltip text)
38228      * @param {String} text The tab's text and tooltip
38229      */
38230     setText : function(text){
38231         this.text = text;
38232         this.textEl.update(text);
38233         this.setTooltip(text);
38234         //if(!this.tabPanel.resizeTabs){
38235         //    this.autoSize();
38236         //}
38237     },
38238     /**
38239      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38240      */
38241     activate : function(){
38242         this.tabPanel.activate(this.id);
38243     },
38244
38245     /**
38246      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38247      */
38248     disable : function(){
38249         if(this.tabPanel.active != this){
38250             this.disabled = true;
38251             this.pnode.addClass("disabled");
38252         }
38253     },
38254
38255     /**
38256      * Enables this TabPanelItem if it was previously disabled.
38257      */
38258     enable : function(){
38259         this.disabled = false;
38260         this.pnode.removeClass("disabled");
38261     },
38262
38263     /**
38264      * Sets the content for this TabPanelItem.
38265      * @param {String} content The content
38266      * @param {Boolean} loadScripts true to look for and load scripts
38267      */
38268     setContent : function(content, loadScripts){
38269         this.bodyEl.update(content, loadScripts);
38270     },
38271
38272     /**
38273      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38274      * @return {Roo.UpdateManager} The UpdateManager
38275      */
38276     getUpdateManager : function(){
38277         return this.bodyEl.getUpdateManager();
38278     },
38279
38280     /**
38281      * Set a URL to be used to load the content for this TabPanelItem.
38282      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38283      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
38284      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this TabPanelItem is activated. (Defaults to false)
38285      * @return {Roo.UpdateManager} The UpdateManager
38286      */
38287     setUrl : function(url, params, loadOnce){
38288         if(this.refreshDelegate){
38289             this.un('activate', this.refreshDelegate);
38290         }
38291         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38292         this.on("activate", this.refreshDelegate);
38293         return this.bodyEl.getUpdateManager();
38294     },
38295
38296     /** @private */
38297     _handleRefresh : function(url, params, loadOnce){
38298         if(!loadOnce || !this.loaded){
38299             var updater = this.bodyEl.getUpdateManager();
38300             updater.update(url, params, this._setLoaded.createDelegate(this));
38301         }
38302     },
38303
38304     /**
38305      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38306      *   Will fail silently if the setUrl method has not been called.
38307      *   This does not activate the panel, just updates its content.
38308      */
38309     refresh : function(){
38310         if(this.refreshDelegate){
38311            this.loaded = false;
38312            this.refreshDelegate();
38313         }
38314     },
38315
38316     /** @private */
38317     _setLoaded : function(){
38318         this.loaded = true;
38319     },
38320
38321     /** @private */
38322     closeClick : function(e){
38323         var o = {};
38324         e.stopEvent();
38325         this.fireEvent("beforeclose", this, o);
38326         if(o.cancel !== true){
38327             this.tabPanel.removeTab(this.id);
38328         }
38329     },
38330     /**
38331      * The text displayed in the tooltip for the close icon.
38332      * @type String
38333      */
38334     closeText : "Close this tab"
38335 });
38336 /**
38337 *    This script refer to:
38338 *    Title: International Telephone Input
38339 *    Author: Jack O'Connor
38340 *    Code version:  v12.1.12
38341 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38342 **/
38343
38344 Roo.bootstrap.PhoneInputData = function() {
38345     var d = [
38346       [
38347         "Afghanistan (‫افغانستان‬‎)",
38348         "af",
38349         "93"
38350       ],
38351       [
38352         "Albania (Shqipëri)",
38353         "al",
38354         "355"
38355       ],
38356       [
38357         "Algeria (‫الجزائر‬‎)",
38358         "dz",
38359         "213"
38360       ],
38361       [
38362         "American Samoa",
38363         "as",
38364         "1684"
38365       ],
38366       [
38367         "Andorra",
38368         "ad",
38369         "376"
38370       ],
38371       [
38372         "Angola",
38373         "ao",
38374         "244"
38375       ],
38376       [
38377         "Anguilla",
38378         "ai",
38379         "1264"
38380       ],
38381       [
38382         "Antigua and Barbuda",
38383         "ag",
38384         "1268"
38385       ],
38386       [
38387         "Argentina",
38388         "ar",
38389         "54"
38390       ],
38391       [
38392         "Armenia (Հայաստան)",
38393         "am",
38394         "374"
38395       ],
38396       [
38397         "Aruba",
38398         "aw",
38399         "297"
38400       ],
38401       [
38402         "Australia",
38403         "au",
38404         "61",
38405         0
38406       ],
38407       [
38408         "Austria (Österreich)",
38409         "at",
38410         "43"
38411       ],
38412       [
38413         "Azerbaijan (Azərbaycan)",
38414         "az",
38415         "994"
38416       ],
38417       [
38418         "Bahamas",
38419         "bs",
38420         "1242"
38421       ],
38422       [
38423         "Bahrain (‫البحرين‬‎)",
38424         "bh",
38425         "973"
38426       ],
38427       [
38428         "Bangladesh (বাংলাদেশ)",
38429         "bd",
38430         "880"
38431       ],
38432       [
38433         "Barbados",
38434         "bb",
38435         "1246"
38436       ],
38437       [
38438         "Belarus (Беларусь)",
38439         "by",
38440         "375"
38441       ],
38442       [
38443         "Belgium (België)",
38444         "be",
38445         "32"
38446       ],
38447       [
38448         "Belize",
38449         "bz",
38450         "501"
38451       ],
38452       [
38453         "Benin (Bénin)",
38454         "bj",
38455         "229"
38456       ],
38457       [
38458         "Bermuda",
38459         "bm",
38460         "1441"
38461       ],
38462       [
38463         "Bhutan (འབྲུག)",
38464         "bt",
38465         "975"
38466       ],
38467       [
38468         "Bolivia",
38469         "bo",
38470         "591"
38471       ],
38472       [
38473         "Bosnia and Herzegovina (Босна и Херцеговина)",
38474         "ba",
38475         "387"
38476       ],
38477       [
38478         "Botswana",
38479         "bw",
38480         "267"
38481       ],
38482       [
38483         "Brazil (Brasil)",
38484         "br",
38485         "55"
38486       ],
38487       [
38488         "British Indian Ocean Territory",
38489         "io",
38490         "246"
38491       ],
38492       [
38493         "British Virgin Islands",
38494         "vg",
38495         "1284"
38496       ],
38497       [
38498         "Brunei",
38499         "bn",
38500         "673"
38501       ],
38502       [
38503         "Bulgaria (България)",
38504         "bg",
38505         "359"
38506       ],
38507       [
38508         "Burkina Faso",
38509         "bf",
38510         "226"
38511       ],
38512       [
38513         "Burundi (Uburundi)",
38514         "bi",
38515         "257"
38516       ],
38517       [
38518         "Cambodia (កម្ពុជា)",
38519         "kh",
38520         "855"
38521       ],
38522       [
38523         "Cameroon (Cameroun)",
38524         "cm",
38525         "237"
38526       ],
38527       [
38528         "Canada",
38529         "ca",
38530         "1",
38531         1,
38532         ["204", "226", "236", "249", "250", "289", "306", "343", "365", "387", "403", "416", "418", "431", "437", "438", "450", "506", "514", "519", "548", "579", "581", "587", "604", "613", "639", "647", "672", "705", "709", "742", "778", "780", "782", "807", "819", "825", "867", "873", "902", "905"]
38533       ],
38534       [
38535         "Cape Verde (Kabu Verdi)",
38536         "cv",
38537         "238"
38538       ],
38539       [
38540         "Caribbean Netherlands",
38541         "bq",
38542         "599",
38543         1
38544       ],
38545       [
38546         "Cayman Islands",
38547         "ky",
38548         "1345"
38549       ],
38550       [
38551         "Central African Republic (République centrafricaine)",
38552         "cf",
38553         "236"
38554       ],
38555       [
38556         "Chad (Tchad)",
38557         "td",
38558         "235"
38559       ],
38560       [
38561         "Chile",
38562         "cl",
38563         "56"
38564       ],
38565       [
38566         "China (中国)",
38567         "cn",
38568         "86"
38569       ],
38570       [
38571         "Christmas Island",
38572         "cx",
38573         "61",
38574         2
38575       ],
38576       [
38577         "Cocos (Keeling) Islands",
38578         "cc",
38579         "61",
38580         1
38581       ],
38582       [
38583         "Colombia",
38584         "co",
38585         "57"
38586       ],
38587       [
38588         "Comoros (‫جزر القمر‬‎)",
38589         "km",
38590         "269"
38591       ],
38592       [
38593         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38594         "cd",
38595         "243"
38596       ],
38597       [
38598         "Congo (Republic) (Congo-Brazzaville)",
38599         "cg",
38600         "242"
38601       ],
38602       [
38603         "Cook Islands",
38604         "ck",
38605         "682"
38606       ],
38607       [
38608         "Costa Rica",
38609         "cr",
38610         "506"
38611       ],
38612       [
38613         "Côte d’Ivoire",
38614         "ci",
38615         "225"
38616       ],
38617       [
38618         "Croatia (Hrvatska)",
38619         "hr",
38620         "385"
38621       ],
38622       [
38623         "Cuba",
38624         "cu",
38625         "53"
38626       ],
38627       [
38628         "Curaçao",
38629         "cw",
38630         "599",
38631         0
38632       ],
38633       [
38634         "Cyprus (Κύπρος)",
38635         "cy",
38636         "357"
38637       ],
38638       [
38639         "Czech Republic (Česká republika)",
38640         "cz",
38641         "420"
38642       ],
38643       [
38644         "Denmark (Danmark)",
38645         "dk",
38646         "45"
38647       ],
38648       [
38649         "Djibouti",
38650         "dj",
38651         "253"
38652       ],
38653       [
38654         "Dominica",
38655         "dm",
38656         "1767"
38657       ],
38658       [
38659         "Dominican Republic (República Dominicana)",
38660         "do",
38661         "1",
38662         2,
38663         ["809", "829", "849"]
38664       ],
38665       [
38666         "Ecuador",
38667         "ec",
38668         "593"
38669       ],
38670       [
38671         "Egypt (‫مصر‬‎)",
38672         "eg",
38673         "20"
38674       ],
38675       [
38676         "El Salvador",
38677         "sv",
38678         "503"
38679       ],
38680       [
38681         "Equatorial Guinea (Guinea Ecuatorial)",
38682         "gq",
38683         "240"
38684       ],
38685       [
38686         "Eritrea",
38687         "er",
38688         "291"
38689       ],
38690       [
38691         "Estonia (Eesti)",
38692         "ee",
38693         "372"
38694       ],
38695       [
38696         "Ethiopia",
38697         "et",
38698         "251"
38699       ],
38700       [
38701         "Falkland Islands (Islas Malvinas)",
38702         "fk",
38703         "500"
38704       ],
38705       [
38706         "Faroe Islands (Føroyar)",
38707         "fo",
38708         "298"
38709       ],
38710       [
38711         "Fiji",
38712         "fj",
38713         "679"
38714       ],
38715       [
38716         "Finland (Suomi)",
38717         "fi",
38718         "358",
38719         0
38720       ],
38721       [
38722         "France",
38723         "fr",
38724         "33"
38725       ],
38726       [
38727         "French Guiana (Guyane française)",
38728         "gf",
38729         "594"
38730       ],
38731       [
38732         "French Polynesia (Polynésie française)",
38733         "pf",
38734         "689"
38735       ],
38736       [
38737         "Gabon",
38738         "ga",
38739         "241"
38740       ],
38741       [
38742         "Gambia",
38743         "gm",
38744         "220"
38745       ],
38746       [
38747         "Georgia (საქართველო)",
38748         "ge",
38749         "995"
38750       ],
38751       [
38752         "Germany (Deutschland)",
38753         "de",
38754         "49"
38755       ],
38756       [
38757         "Ghana (Gaana)",
38758         "gh",
38759         "233"
38760       ],
38761       [
38762         "Gibraltar",
38763         "gi",
38764         "350"
38765       ],
38766       [
38767         "Greece (Ελλάδα)",
38768         "gr",
38769         "30"
38770       ],
38771       [
38772         "Greenland (Kalaallit Nunaat)",
38773         "gl",
38774         "299"
38775       ],
38776       [
38777         "Grenada",
38778         "gd",
38779         "1473"
38780       ],
38781       [
38782         "Guadeloupe",
38783         "gp",
38784         "590",
38785         0
38786       ],
38787       [
38788         "Guam",
38789         "gu",
38790         "1671"
38791       ],
38792       [
38793         "Guatemala",
38794         "gt",
38795         "502"
38796       ],
38797       [
38798         "Guernsey",
38799         "gg",
38800         "44",
38801         1
38802       ],
38803       [
38804         "Guinea (Guinée)",
38805         "gn",
38806         "224"
38807       ],
38808       [
38809         "Guinea-Bissau (Guiné Bissau)",
38810         "gw",
38811         "245"
38812       ],
38813       [
38814         "Guyana",
38815         "gy",
38816         "592"
38817       ],
38818       [
38819         "Haiti",
38820         "ht",
38821         "509"
38822       ],
38823       [
38824         "Honduras",
38825         "hn",
38826         "504"
38827       ],
38828       [
38829         "Hong Kong (香港)",
38830         "hk",
38831         "852"
38832       ],
38833       [
38834         "Hungary (Magyarország)",
38835         "hu",
38836         "36"
38837       ],
38838       [
38839         "Iceland (Ísland)",
38840         "is",
38841         "354"
38842       ],
38843       [
38844         "India (भारत)",
38845         "in",
38846         "91"
38847       ],
38848       [
38849         "Indonesia",
38850         "id",
38851         "62"
38852       ],
38853       [
38854         "Iran (‫ایران‬‎)",
38855         "ir",
38856         "98"
38857       ],
38858       [
38859         "Iraq (‫العراق‬‎)",
38860         "iq",
38861         "964"
38862       ],
38863       [
38864         "Ireland",
38865         "ie",
38866         "353"
38867       ],
38868       [
38869         "Isle of Man",
38870         "im",
38871         "44",
38872         2
38873       ],
38874       [
38875         "Israel (‫ישראל‬‎)",
38876         "il",
38877         "972"
38878       ],
38879       [
38880         "Italy (Italia)",
38881         "it",
38882         "39",
38883         0
38884       ],
38885       [
38886         "Jamaica",
38887         "jm",
38888         "1876"
38889       ],
38890       [
38891         "Japan (日本)",
38892         "jp",
38893         "81"
38894       ],
38895       [
38896         "Jersey",
38897         "je",
38898         "44",
38899         3
38900       ],
38901       [
38902         "Jordan (‫الأردن‬‎)",
38903         "jo",
38904         "962"
38905       ],
38906       [
38907         "Kazakhstan (Казахстан)",
38908         "kz",
38909         "7",
38910         1
38911       ],
38912       [
38913         "Kenya",
38914         "ke",
38915         "254"
38916       ],
38917       [
38918         "Kiribati",
38919         "ki",
38920         "686"
38921       ],
38922       [
38923         "Kosovo",
38924         "xk",
38925         "383"
38926       ],
38927       [
38928         "Kuwait (‫الكويت‬‎)",
38929         "kw",
38930         "965"
38931       ],
38932       [
38933         "Kyrgyzstan (Кыргызстан)",
38934         "kg",
38935         "996"
38936       ],
38937       [
38938         "Laos (ລາວ)",
38939         "la",
38940         "856"
38941       ],
38942       [
38943         "Latvia (Latvija)",
38944         "lv",
38945         "371"
38946       ],
38947       [
38948         "Lebanon (‫لبنان‬‎)",
38949         "lb",
38950         "961"
38951       ],
38952       [
38953         "Lesotho",
38954         "ls",
38955         "266"
38956       ],
38957       [
38958         "Liberia",
38959         "lr",
38960         "231"
38961       ],
38962       [
38963         "Libya (‫ليبيا‬‎)",
38964         "ly",
38965         "218"
38966       ],
38967       [
38968         "Liechtenstein",
38969         "li",
38970         "423"
38971       ],
38972       [
38973         "Lithuania (Lietuva)",
38974         "lt",
38975         "370"
38976       ],
38977       [
38978         "Luxembourg",
38979         "lu",
38980         "352"
38981       ],
38982       [
38983         "Macau (澳門)",
38984         "mo",
38985         "853"
38986       ],
38987       [
38988         "Macedonia (FYROM) (Македонија)",
38989         "mk",
38990         "389"
38991       ],
38992       [
38993         "Madagascar (Madagasikara)",
38994         "mg",
38995         "261"
38996       ],
38997       [
38998         "Malawi",
38999         "mw",
39000         "265"
39001       ],
39002       [
39003         "Malaysia",
39004         "my",
39005         "60"
39006       ],
39007       [
39008         "Maldives",
39009         "mv",
39010         "960"
39011       ],
39012       [
39013         "Mali",
39014         "ml",
39015         "223"
39016       ],
39017       [
39018         "Malta",
39019         "mt",
39020         "356"
39021       ],
39022       [
39023         "Marshall Islands",
39024         "mh",
39025         "692"
39026       ],
39027       [
39028         "Martinique",
39029         "mq",
39030         "596"
39031       ],
39032       [
39033         "Mauritania (‫موريتانيا‬‎)",
39034         "mr",
39035         "222"
39036       ],
39037       [
39038         "Mauritius (Moris)",
39039         "mu",
39040         "230"
39041       ],
39042       [
39043         "Mayotte",
39044         "yt",
39045         "262",
39046         1
39047       ],
39048       [
39049         "Mexico (México)",
39050         "mx",
39051         "52"
39052       ],
39053       [
39054         "Micronesia",
39055         "fm",
39056         "691"
39057       ],
39058       [
39059         "Moldova (Republica Moldova)",
39060         "md",
39061         "373"
39062       ],
39063       [
39064         "Monaco",
39065         "mc",
39066         "377"
39067       ],
39068       [
39069         "Mongolia (Монгол)",
39070         "mn",
39071         "976"
39072       ],
39073       [
39074         "Montenegro (Crna Gora)",
39075         "me",
39076         "382"
39077       ],
39078       [
39079         "Montserrat",
39080         "ms",
39081         "1664"
39082       ],
39083       [
39084         "Morocco (‫المغرب‬‎)",
39085         "ma",
39086         "212",
39087         0
39088       ],
39089       [
39090         "Mozambique (Moçambique)",
39091         "mz",
39092         "258"
39093       ],
39094       [
39095         "Myanmar (Burma) (မြန်မာ)",
39096         "mm",
39097         "95"
39098       ],
39099       [
39100         "Namibia (Namibië)",
39101         "na",
39102         "264"
39103       ],
39104       [
39105         "Nauru",
39106         "nr",
39107         "674"
39108       ],
39109       [
39110         "Nepal (नेपाल)",
39111         "np",
39112         "977"
39113       ],
39114       [
39115         "Netherlands (Nederland)",
39116         "nl",
39117         "31"
39118       ],
39119       [
39120         "New Caledonia (Nouvelle-Calédonie)",
39121         "nc",
39122         "687"
39123       ],
39124       [
39125         "New Zealand",
39126         "nz",
39127         "64"
39128       ],
39129       [
39130         "Nicaragua",
39131         "ni",
39132         "505"
39133       ],
39134       [
39135         "Niger (Nijar)",
39136         "ne",
39137         "227"
39138       ],
39139       [
39140         "Nigeria",
39141         "ng",
39142         "234"
39143       ],
39144       [
39145         "Niue",
39146         "nu",
39147         "683"
39148       ],
39149       [
39150         "Norfolk Island",
39151         "nf",
39152         "672"
39153       ],
39154       [
39155         "North Korea (조선 민주주의 인민 공화국)",
39156         "kp",
39157         "850"
39158       ],
39159       [
39160         "Northern Mariana Islands",
39161         "mp",
39162         "1670"
39163       ],
39164       [
39165         "Norway (Norge)",
39166         "no",
39167         "47",
39168         0
39169       ],
39170       [
39171         "Oman (‫عُمان‬‎)",
39172         "om",
39173         "968"
39174       ],
39175       [
39176         "Pakistan (‫پاکستان‬‎)",
39177         "pk",
39178         "92"
39179       ],
39180       [
39181         "Palau",
39182         "pw",
39183         "680"
39184       ],
39185       [
39186         "Palestine (‫فلسطين‬‎)",
39187         "ps",
39188         "970"
39189       ],
39190       [
39191         "Panama (Panamá)",
39192         "pa",
39193         "507"
39194       ],
39195       [
39196         "Papua New Guinea",
39197         "pg",
39198         "675"
39199       ],
39200       [
39201         "Paraguay",
39202         "py",
39203         "595"
39204       ],
39205       [
39206         "Peru (Perú)",
39207         "pe",
39208         "51"
39209       ],
39210       [
39211         "Philippines",
39212         "ph",
39213         "63"
39214       ],
39215       [
39216         "Poland (Polska)",
39217         "pl",
39218         "48"
39219       ],
39220       [
39221         "Portugal",
39222         "pt",
39223         "351"
39224       ],
39225       [
39226         "Puerto Rico",
39227         "pr",
39228         "1",
39229         3,
39230         ["787", "939"]
39231       ],
39232       [
39233         "Qatar (‫قطر‬‎)",
39234         "qa",
39235         "974"
39236       ],
39237       [
39238         "Réunion (La Réunion)",
39239         "re",
39240         "262",
39241         0
39242       ],
39243       [
39244         "Romania (România)",
39245         "ro",
39246         "40"
39247       ],
39248       [
39249         "Russia (Россия)",
39250         "ru",
39251         "7",
39252         0
39253       ],
39254       [
39255         "Rwanda",
39256         "rw",
39257         "250"
39258       ],
39259       [
39260         "Saint Barthélemy",
39261         "bl",
39262         "590",
39263         1
39264       ],
39265       [
39266         "Saint Helena",
39267         "sh",
39268         "290"
39269       ],
39270       [
39271         "Saint Kitts and Nevis",
39272         "kn",
39273         "1869"
39274       ],
39275       [
39276         "Saint Lucia",
39277         "lc",
39278         "1758"
39279       ],
39280       [
39281         "Saint Martin (Saint-Martin (partie française))",
39282         "mf",
39283         "590",
39284         2
39285       ],
39286       [
39287         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39288         "pm",
39289         "508"
39290       ],
39291       [
39292         "Saint Vincent and the Grenadines",
39293         "vc",
39294         "1784"
39295       ],
39296       [
39297         "Samoa",
39298         "ws",
39299         "685"
39300       ],
39301       [
39302         "San Marino",
39303         "sm",
39304         "378"
39305       ],
39306       [
39307         "São Tomé and Príncipe (São Tomé e Príncipe)",
39308         "st",
39309         "239"
39310       ],
39311       [
39312         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39313         "sa",
39314         "966"
39315       ],
39316       [
39317         "Senegal (Sénégal)",
39318         "sn",
39319         "221"
39320       ],
39321       [
39322         "Serbia (Србија)",
39323         "rs",
39324         "381"
39325       ],
39326       [
39327         "Seychelles",
39328         "sc",
39329         "248"
39330       ],
39331       [
39332         "Sierra Leone",
39333         "sl",
39334         "232"
39335       ],
39336       [
39337         "Singapore",
39338         "sg",
39339         "65"
39340       ],
39341       [
39342         "Sint Maarten",
39343         "sx",
39344         "1721"
39345       ],
39346       [
39347         "Slovakia (Slovensko)",
39348         "sk",
39349         "421"
39350       ],
39351       [
39352         "Slovenia (Slovenija)",
39353         "si",
39354         "386"
39355       ],
39356       [
39357         "Solomon Islands",
39358         "sb",
39359         "677"
39360       ],
39361       [
39362         "Somalia (Soomaaliya)",
39363         "so",
39364         "252"
39365       ],
39366       [
39367         "South Africa",
39368         "za",
39369         "27"
39370       ],
39371       [
39372         "South Korea (대한민국)",
39373         "kr",
39374         "82"
39375       ],
39376       [
39377         "South Sudan (‫جنوب السودان‬‎)",
39378         "ss",
39379         "211"
39380       ],
39381       [
39382         "Spain (España)",
39383         "es",
39384         "34"
39385       ],
39386       [
39387         "Sri Lanka (ශ්‍රී ලංකාව)",
39388         "lk",
39389         "94"
39390       ],
39391       [
39392         "Sudan (‫السودان‬‎)",
39393         "sd",
39394         "249"
39395       ],
39396       [
39397         "Suriname",
39398         "sr",
39399         "597"
39400       ],
39401       [
39402         "Svalbard and Jan Mayen",
39403         "sj",
39404         "47",
39405         1
39406       ],
39407       [
39408         "Swaziland",
39409         "sz",
39410         "268"
39411       ],
39412       [
39413         "Sweden (Sverige)",
39414         "se",
39415         "46"
39416       ],
39417       [
39418         "Switzerland (Schweiz)",
39419         "ch",
39420         "41"
39421       ],
39422       [
39423         "Syria (‫سوريا‬‎)",
39424         "sy",
39425         "963"
39426       ],
39427       [
39428         "Taiwan (台灣)",
39429         "tw",
39430         "886"
39431       ],
39432       [
39433         "Tajikistan",
39434         "tj",
39435         "992"
39436       ],
39437       [
39438         "Tanzania",
39439         "tz",
39440         "255"
39441       ],
39442       [
39443         "Thailand (ไทย)",
39444         "th",
39445         "66"
39446       ],
39447       [
39448         "Timor-Leste",
39449         "tl",
39450         "670"
39451       ],
39452       [
39453         "Togo",
39454         "tg",
39455         "228"
39456       ],
39457       [
39458         "Tokelau",
39459         "tk",
39460         "690"
39461       ],
39462       [
39463         "Tonga",
39464         "to",
39465         "676"
39466       ],
39467       [
39468         "Trinidad and Tobago",
39469         "tt",
39470         "1868"
39471       ],
39472       [
39473         "Tunisia (‫تونس‬‎)",
39474         "tn",
39475         "216"
39476       ],
39477       [
39478         "Turkey (Türkiye)",
39479         "tr",
39480         "90"
39481       ],
39482       [
39483         "Turkmenistan",
39484         "tm",
39485         "993"
39486       ],
39487       [
39488         "Turks and Caicos Islands",
39489         "tc",
39490         "1649"
39491       ],
39492       [
39493         "Tuvalu",
39494         "tv",
39495         "688"
39496       ],
39497       [
39498         "U.S. Virgin Islands",
39499         "vi",
39500         "1340"
39501       ],
39502       [
39503         "Uganda",
39504         "ug",
39505         "256"
39506       ],
39507       [
39508         "Ukraine (Україна)",
39509         "ua",
39510         "380"
39511       ],
39512       [
39513         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39514         "ae",
39515         "971"
39516       ],
39517       [
39518         "United Kingdom",
39519         "gb",
39520         "44",
39521         0
39522       ],
39523       [
39524         "United States",
39525         "us",
39526         "1",
39527         0
39528       ],
39529       [
39530         "Uruguay",
39531         "uy",
39532         "598"
39533       ],
39534       [
39535         "Uzbekistan (Oʻzbekiston)",
39536         "uz",
39537         "998"
39538       ],
39539       [
39540         "Vanuatu",
39541         "vu",
39542         "678"
39543       ],
39544       [
39545         "Vatican City (Città del Vaticano)",
39546         "va",
39547         "39",
39548         1
39549       ],
39550       [
39551         "Venezuela",
39552         "ve",
39553         "58"
39554       ],
39555       [
39556         "Vietnam (Việt Nam)",
39557         "vn",
39558         "84"
39559       ],
39560       [
39561         "Wallis and Futuna (Wallis-et-Futuna)",
39562         "wf",
39563         "681"
39564       ],
39565       [
39566         "Western Sahara (‫الصحراء الغربية‬‎)",
39567         "eh",
39568         "212",
39569         1
39570       ],
39571       [
39572         "Yemen (‫اليمن‬‎)",
39573         "ye",
39574         "967"
39575       ],
39576       [
39577         "Zambia",
39578         "zm",
39579         "260"
39580       ],
39581       [
39582         "Zimbabwe",
39583         "zw",
39584         "263"
39585       ],
39586       [
39587         "Åland Islands",
39588         "ax",
39589         "358",
39590         1
39591       ]
39592   ];
39593   
39594   return d;
39595 }/**
39596 *    This script refer to:
39597 *    Title: International Telephone Input
39598 *    Author: Jack O'Connor
39599 *    Code version:  v12.1.12
39600 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39601 **/
39602
39603 /**
39604  * @class Roo.bootstrap.PhoneInput
39605  * @extends Roo.bootstrap.TriggerField
39606  * An input with International dial-code selection
39607  
39608  * @cfg {String} defaultDialCode default '+852'
39609  * @cfg {Array} preferedCountries default []
39610   
39611  * @constructor
39612  * Create a new PhoneInput.
39613  * @param {Object} config Configuration options
39614  */
39615
39616 Roo.bootstrap.PhoneInput = function(config) {
39617     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39618 };
39619
39620 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39621         
39622         listWidth: undefined,
39623         
39624         selectedClass: 'active',
39625         
39626         invalidClass : "has-warning",
39627         
39628         validClass: 'has-success',
39629         
39630         allowed: '0123456789',
39631         
39632         /**
39633          * @cfg {String} defaultDialCode The default dial code when initializing the input
39634          */
39635         defaultDialCode: '+852',
39636         
39637         /**
39638          * @cfg {Array} preferedCountries A list of iso2 in array (e.g. ['hk','us']). Those related countries will show at the top of the input's choices
39639          */
39640         preferedCountries: false,
39641         
39642         getAutoCreate : function()
39643         {
39644             var data = Roo.bootstrap.PhoneInputData();
39645             var align = this.labelAlign || this.parentLabelAlign();
39646             var id = Roo.id();
39647             
39648             this.allCountries = [];
39649             this.dialCodeMapping = [];
39650             
39651             for (var i = 0; i < data.length; i++) {
39652               var c = data[i];
39653               this.allCountries[i] = {
39654                 name: c[0],
39655                 iso2: c[1],
39656                 dialCode: c[2],
39657                 priority: c[3] || 0,
39658                 areaCodes: c[4] || null
39659               };
39660               this.dialCodeMapping[c[2]] = {
39661                   name: c[0],
39662                   iso2: c[1],
39663                   priority: c[3] || 0,
39664                   areaCodes: c[4] || null
39665               };
39666             }
39667             
39668             var cfg = {
39669                 cls: 'form-group',
39670                 cn: []
39671             };
39672             
39673             var input =  {
39674                 tag: 'input',
39675                 id : id,
39676                 cls : 'form-control tel-input',
39677                 autocomplete: 'new-password'
39678             };
39679             
39680             var hiddenInput = {
39681                 tag: 'input',
39682                 type: 'hidden',
39683                 cls: 'hidden-tel-input'
39684             };
39685             
39686             if (this.name) {
39687                 hiddenInput.name = this.name;
39688             }
39689             
39690             if (this.disabled) {
39691                 input.disabled = true;
39692             }
39693             
39694             var flag_container = {
39695                 tag: 'div',
39696                 cls: 'flag-box',
39697                 cn: [
39698                     {
39699                         tag: 'div',
39700                         cls: 'flag'
39701                     },
39702                     {
39703                         tag: 'div',
39704                         cls: 'caret'
39705                     }
39706                 ]
39707             };
39708             
39709             var box = {
39710                 tag: 'div',
39711                 cls: this.hasFeedback ? 'has-feedback' : '',
39712                 cn: [
39713                     hiddenInput,
39714                     input,
39715                     {
39716                         tag: 'input',
39717                         cls: 'dial-code-holder',
39718                         disabled: true
39719                     }
39720                 ]
39721             };
39722             
39723             var container = {
39724                 cls: 'roo-select2-container input-group',
39725                 cn: [
39726                     flag_container,
39727                     box
39728                 ]
39729             };
39730             
39731             if (this.fieldLabel.length) {
39732                 var indicator = {
39733                     tag: 'i',
39734                     tooltip: 'This field is required'
39735                 };
39736                 
39737                 var label = {
39738                     tag: 'label',
39739                     'for':  id,
39740                     cls: 'control-label',
39741                     cn: []
39742                 };
39743                 
39744                 var label_text = {
39745                     tag: 'span',
39746                     html: this.fieldLabel
39747                 };
39748                 
39749                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39750                 label.cn = [
39751                     indicator,
39752                     label_text
39753                 ];
39754                 
39755                 if(this.indicatorpos == 'right') {
39756                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39757                     label.cn = [
39758                         label_text,
39759                         indicator
39760                     ];
39761                 }
39762                 
39763                 if(align == 'left') {
39764                     container = {
39765                         tag: 'div',
39766                         cn: [
39767                             container
39768                         ]
39769                     };
39770                     
39771                     if(this.labelWidth > 12){
39772                         label.style = "width: " + this.labelWidth + 'px';
39773                     }
39774                     if(this.labelWidth < 13 && this.labelmd == 0){
39775                         this.labelmd = this.labelWidth;
39776                     }
39777                     if(this.labellg > 0){
39778                         label.cls += ' col-lg-' + this.labellg;
39779                         input.cls += ' col-lg-' + (12 - this.labellg);
39780                     }
39781                     if(this.labelmd > 0){
39782                         label.cls += ' col-md-' + this.labelmd;
39783                         container.cls += ' col-md-' + (12 - this.labelmd);
39784                     }
39785                     if(this.labelsm > 0){
39786                         label.cls += ' col-sm-' + this.labelsm;
39787                         container.cls += ' col-sm-' + (12 - this.labelsm);
39788                     }
39789                     if(this.labelxs > 0){
39790                         label.cls += ' col-xs-' + this.labelxs;
39791                         container.cls += ' col-xs-' + (12 - this.labelxs);
39792                     }
39793                 }
39794             }
39795             
39796             cfg.cn = [
39797                 label,
39798                 container
39799             ];
39800             
39801             var settings = this;
39802             
39803             ['xs','sm','md','lg'].map(function(size){
39804                 if (settings[size]) {
39805                     cfg.cls += ' col-' + size + '-' + settings[size];
39806                 }
39807             });
39808             
39809             this.store = new Roo.data.Store({
39810                 proxy : new Roo.data.MemoryProxy({}),
39811                 reader : new Roo.data.JsonReader({
39812                     fields : [
39813                         {
39814                             'name' : 'name',
39815                             'type' : 'string'
39816                         },
39817                         {
39818                             'name' : 'iso2',
39819                             'type' : 'string'
39820                         },
39821                         {
39822                             'name' : 'dialCode',
39823                             'type' : 'string'
39824                         },
39825                         {
39826                             'name' : 'priority',
39827                             'type' : 'string'
39828                         },
39829                         {
39830                             'name' : 'areaCodes',
39831                             'type' : 'string'
39832                         }
39833                     ]
39834                 })
39835             });
39836             
39837             if(!this.preferedCountries) {
39838                 this.preferedCountries = [
39839                     'hk',
39840                     'gb',
39841                     'us'
39842                 ];
39843             }
39844             
39845             var p = this.preferedCountries.reverse();
39846             
39847             if(p) {
39848                 for (var i = 0; i < p.length; i++) {
39849                     for (var j = 0; j < this.allCountries.length; j++) {
39850                         if(this.allCountries[j].iso2 == p[i]) {
39851                             var t = this.allCountries[j];
39852                             this.allCountries.splice(j,1);
39853                             this.allCountries.unshift(t);
39854                         }
39855                     } 
39856                 }
39857             }
39858             
39859             this.store.proxy.data = {
39860                 success: true,
39861                 data: this.allCountries
39862             };
39863             
39864             return cfg;
39865         },
39866         
39867         initEvents : function()
39868         {
39869             this.createList();
39870             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39871             
39872             this.indicator = this.indicatorEl();
39873             this.flag = this.flagEl();
39874             this.dialCodeHolder = this.dialCodeHolderEl();
39875             
39876             this.trigger = this.el.select('div.flag-box',true).first();
39877             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39878             
39879             var _this = this;
39880             
39881             (function(){
39882                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39883                 _this.list.setWidth(lw);
39884             }).defer(100);
39885             
39886             this.list.on('mouseover', this.onViewOver, this);
39887             this.list.on('mousemove', this.onViewMove, this);
39888             this.inputEl().on("keyup", this.onKeyUp, this);
39889             
39890             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
39891
39892             this.view = new Roo.View(this.list, this.tpl, {
39893                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
39894             });
39895             
39896             this.view.on('click', this.onViewClick, this);
39897             this.setValue(this.defaultDialCode);
39898         },
39899         
39900         onTriggerClick : function(e)
39901         {
39902             Roo.log('trigger click');
39903             if(this.disabled){
39904                 return;
39905             }
39906             
39907             if(this.isExpanded()){
39908                 this.collapse();
39909                 this.hasFocus = false;
39910             }else {
39911                 this.store.load({});
39912                 this.hasFocus = true;
39913                 this.expand();
39914             }
39915         },
39916         
39917         isExpanded : function()
39918         {
39919             return this.list.isVisible();
39920         },
39921         
39922         collapse : function()
39923         {
39924             if(!this.isExpanded()){
39925                 return;
39926             }
39927             this.list.hide();
39928             Roo.get(document).un('mousedown', this.collapseIf, this);
39929             Roo.get(document).un('mousewheel', this.collapseIf, this);
39930             this.fireEvent('collapse', this);
39931             this.validate();
39932         },
39933         
39934         expand : function()
39935         {
39936             Roo.log('expand');
39937
39938             if(this.isExpanded() || !this.hasFocus){
39939                 return;
39940             }
39941             
39942             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
39943             this.list.setWidth(lw);
39944             
39945             this.list.show();
39946             this.restrictHeight();
39947             
39948             Roo.get(document).on('mousedown', this.collapseIf, this);
39949             Roo.get(document).on('mousewheel', this.collapseIf, this);
39950             
39951             this.fireEvent('expand', this);
39952         },
39953         
39954         restrictHeight : function()
39955         {
39956             this.list.alignTo(this.inputEl(), this.listAlign);
39957             this.list.alignTo(this.inputEl(), this.listAlign);
39958         },
39959         
39960         onViewOver : function(e, t)
39961         {
39962             if(this.inKeyMode){
39963                 return;
39964             }
39965             var item = this.view.findItemFromChild(t);
39966             
39967             if(item){
39968                 var index = this.view.indexOf(item);
39969                 this.select(index, false);
39970             }
39971         },
39972
39973         // private
39974         onViewClick : function(view, doFocus, el, e)
39975         {
39976             var index = this.view.getSelectedIndexes()[0];
39977             
39978             var r = this.store.getAt(index);
39979             
39980             if(r){
39981                 this.onSelect(r, index);
39982             }
39983             if(doFocus !== false && !this.blockFocus){
39984                 this.inputEl().focus();
39985             }
39986         },
39987         
39988         onViewMove : function(e, t)
39989         {
39990             this.inKeyMode = false;
39991         },
39992         
39993         select : function(index, scrollIntoView)
39994         {
39995             this.selectedIndex = index;
39996             this.view.select(index);
39997             if(scrollIntoView !== false){
39998                 var el = this.view.getNode(index);
39999                 if(el){
40000                     this.list.scrollChildIntoView(el, false);
40001                 }
40002             }
40003         },
40004         
40005         createList : function()
40006         {
40007             this.list = Roo.get(document.body).createChild({
40008                 tag: 'ul',
40009                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40010                 style: 'display:none'
40011             });
40012             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
40013         },
40014         
40015         collapseIf : function(e)
40016         {
40017             var in_combo  = e.within(this.el);
40018             var in_list =  e.within(this.list);
40019             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40020             
40021             if (in_combo || in_list || is_list) {
40022                 return;
40023             }
40024             this.collapse();
40025         },
40026         
40027         onSelect : function(record, index)
40028         {
40029             if(this.fireEvent('beforeselect', this, record, index) !== false){
40030                 
40031                 this.setFlagClass(record.data.iso2);
40032                 this.setDialCode(record.data.dialCode);
40033                 this.hasFocus = false;
40034                 this.collapse();
40035                 this.fireEvent('select', this, record, index);
40036             }
40037         },
40038         
40039         flagEl : function()
40040         {
40041             var flag = this.el.select('div.flag',true).first();
40042             if(!flag){
40043                 return false;
40044             }
40045             return flag;
40046         },
40047         
40048         dialCodeHolderEl : function()
40049         {
40050             var d = this.el.select('input.dial-code-holder',true).first();
40051             if(!d){
40052                 return false;
40053             }
40054             return d;
40055         },
40056         
40057         setDialCode : function(v)
40058         {
40059             this.dialCodeHolder.dom.value = '+'+v;
40060         },
40061         
40062         setFlagClass : function(n)
40063         {
40064             this.flag.dom.className = 'flag '+n;
40065         },
40066         
40067         getValue : function()
40068         {
40069             var v = this.inputEl().getValue();
40070             if(this.dialCodeHolder) {
40071                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40072             }
40073             return v;
40074         },
40075         
40076         setValue : function(v)
40077         {
40078             var d = this.getDialCode(v);
40079             
40080             //invalid dial code
40081             if(v.length == 0 || !d || d.length == 0) {
40082                 if(this.rendered){
40083                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40084                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40085                 }
40086                 return;
40087             }
40088             
40089             //valid dial code
40090             this.setFlagClass(this.dialCodeMapping[d].iso2);
40091             this.setDialCode(d);
40092             this.inputEl().dom.value = v.replace('+'+d,'');
40093             this.hiddenEl().dom.value = this.getValue();
40094             
40095             this.validate();
40096         },
40097         
40098         getDialCode : function(v = '')
40099         {
40100             if (v.length == 0) {
40101                 return this.dialCodeHolder.dom.value;
40102             }
40103             
40104             var dialCode = "";
40105             if (v.charAt(0) != "+") {
40106                 return false;
40107             }
40108             var numericChars = "";
40109             for (var i = 1; i < v.length; i++) {
40110               var c = v.charAt(i);
40111               if (!isNaN(c)) {
40112                 numericChars += c;
40113                 if (this.dialCodeMapping[numericChars]) {
40114                   dialCode = v.substr(1, i);
40115                 }
40116                 if (numericChars.length == 4) {
40117                   break;
40118                 }
40119               }
40120             }
40121             return dialCode;
40122         },
40123         
40124         reset : function()
40125         {
40126             this.setValue(this.defaultDialCode);
40127             this.validate();
40128         },
40129         
40130         hiddenEl : function()
40131         {
40132             return this.el.select('input.hidden-tel-input',true).first();
40133         },
40134         
40135         onKeyUp : function(e){
40136             
40137             var k = e.getKey();
40138             var c = e.getCharCode();
40139             
40140             if(
40141                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40142                     this.allowed.indexOf(String.fromCharCode(c)) === -1
40143             ){
40144                 e.stopEvent();
40145             }
40146             
40147             // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40148             //     return;
40149             // }
40150             if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40151                 e.stopEvent();
40152             }
40153             
40154             this.setValue(this.getValue());
40155         }
40156         
40157 });
40158 /**
40159  * @class Roo.bootstrap.MoneyField
40160  * @extends Roo.bootstrap.ComboBox
40161  * Bootstrap MoneyField class
40162  * 
40163  * @constructor
40164  * Create a new MoneyField.
40165  * @param {Object} config Configuration options
40166  */
40167
40168 Roo.bootstrap.MoneyField = function(config) {
40169     
40170     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40171     
40172 };
40173
40174 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40175     
40176     /**
40177      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40178      */
40179     allowDecimals : true,
40180     /**
40181      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40182      */
40183     decimalSeparator : ".",
40184     /**
40185      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40186      */
40187     decimalPrecision : 0,
40188     /**
40189      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40190      */
40191     allowNegative : true,
40192     /**
40193      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40194      */
40195     minValue : Number.NEGATIVE_INFINITY,
40196     /**
40197      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40198      */
40199     maxValue : Number.MAX_VALUE,
40200     /**
40201      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40202      */
40203     minText : "The minimum value for this field is {0}",
40204     /**
40205      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40206      */
40207     maxText : "The maximum value for this field is {0}",
40208     /**
40209      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40210      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40211      */
40212     nanText : "{0} is not a valid number",
40213     /**
40214      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40215      */
40216     castInt : true,
40217     /**
40218      * @cfg {String} defaults currency of the MoneyField
40219      * value should be in lkey
40220      */
40221     defaultCurrency : false,
40222     /**
40223      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40224      */
40225     thousandsDelimiter : false,
40226     
40227     
40228     inputlg : 9,
40229     inputmd : 9,
40230     inputsm : 9,
40231     inputxs : 6,
40232     
40233     store : false,
40234     
40235     getAutoCreate : function()
40236     {
40237         var align = this.labelAlign || this.parentLabelAlign();
40238         
40239         var id = Roo.id();
40240
40241         var cfg = {
40242             cls: 'form-group',
40243             cn: []
40244         };
40245
40246         var input =  {
40247             tag: 'input',
40248             id : id,
40249             cls : 'form-control roo-money-amount-input',
40250             autocomplete: 'new-password'
40251         };
40252         
40253         var hiddenInput = {
40254             tag: 'input',
40255             type: 'hidden',
40256             id: Roo.id(),
40257             cls: 'hidden-number-input'
40258         };
40259         
40260         if (this.name) {
40261             hiddenInput.name = this.name;
40262         }
40263
40264         if (this.disabled) {
40265             input.disabled = true;
40266         }
40267
40268         var clg = 12 - this.inputlg;
40269         var cmd = 12 - this.inputmd;
40270         var csm = 12 - this.inputsm;
40271         var cxs = 12 - this.inputxs;
40272         
40273         var container = {
40274             tag : 'div',
40275             cls : 'row roo-money-field',
40276             cn : [
40277                 {
40278                     tag : 'div',
40279                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40280                     cn : [
40281                         {
40282                             tag : 'div',
40283                             cls: 'roo-select2-container input-group',
40284                             cn: [
40285                                 {
40286                                     tag : 'input',
40287                                     cls : 'form-control roo-money-currency-input',
40288                                     autocomplete: 'new-password',
40289                                     readOnly : 1,
40290                                     name : this.currencyName
40291                                 },
40292                                 {
40293                                     tag :'span',
40294                                     cls : 'input-group-addon',
40295                                     cn : [
40296                                         {
40297                                             tag: 'span',
40298                                             cls: 'caret'
40299                                         }
40300                                     ]
40301                                 }
40302                             ]
40303                         }
40304                     ]
40305                 },
40306                 {
40307                     tag : 'div',
40308                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40309                     cn : [
40310                         {
40311                             tag: 'div',
40312                             cls: this.hasFeedback ? 'has-feedback' : '',
40313                             cn: [
40314                                 input
40315                             ]
40316                         }
40317                     ]
40318                 }
40319             ]
40320             
40321         };
40322         
40323         if (this.fieldLabel.length) {
40324             var indicator = {
40325                 tag: 'i',
40326                 tooltip: 'This field is required'
40327             };
40328
40329             var label = {
40330                 tag: 'label',
40331                 'for':  id,
40332                 cls: 'control-label',
40333                 cn: []
40334             };
40335
40336             var label_text = {
40337                 tag: 'span',
40338                 html: this.fieldLabel
40339             };
40340
40341             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40342             label.cn = [
40343                 indicator,
40344                 label_text
40345             ];
40346
40347             if(this.indicatorpos == 'right') {
40348                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40349                 label.cn = [
40350                     label_text,
40351                     indicator
40352                 ];
40353             }
40354
40355             if(align == 'left') {
40356                 container = {
40357                     tag: 'div',
40358                     cn: [
40359                         container
40360                     ]
40361                 };
40362
40363                 if(this.labelWidth > 12){
40364                     label.style = "width: " + this.labelWidth + 'px';
40365                 }
40366                 if(this.labelWidth < 13 && this.labelmd == 0){
40367                     this.labelmd = this.labelWidth;
40368                 }
40369                 if(this.labellg > 0){
40370                     label.cls += ' col-lg-' + this.labellg;
40371                     input.cls += ' col-lg-' + (12 - this.labellg);
40372                 }
40373                 if(this.labelmd > 0){
40374                     label.cls += ' col-md-' + this.labelmd;
40375                     container.cls += ' col-md-' + (12 - this.labelmd);
40376                 }
40377                 if(this.labelsm > 0){
40378                     label.cls += ' col-sm-' + this.labelsm;
40379                     container.cls += ' col-sm-' + (12 - this.labelsm);
40380                 }
40381                 if(this.labelxs > 0){
40382                     label.cls += ' col-xs-' + this.labelxs;
40383                     container.cls += ' col-xs-' + (12 - this.labelxs);
40384                 }
40385             }
40386         }
40387
40388         cfg.cn = [
40389             label,
40390             container,
40391             hiddenInput
40392         ];
40393         
40394         var settings = this;
40395
40396         ['xs','sm','md','lg'].map(function(size){
40397             if (settings[size]) {
40398                 cfg.cls += ' col-' + size + '-' + settings[size];
40399             }
40400         });
40401         
40402         return cfg;
40403     },
40404     
40405     initEvents : function()
40406     {
40407         this.indicator = this.indicatorEl();
40408         
40409         this.initCurrencyEvent();
40410         
40411         this.initNumberEvent();
40412     },
40413     
40414     initCurrencyEvent : function()
40415     {
40416         if (!this.store) {
40417             throw "can not find store for combo";
40418         }
40419         
40420         this.store = Roo.factory(this.store, Roo.data);
40421         this.store.parent = this;
40422         
40423         this.createList();
40424         
40425         this.triggerEl = this.el.select('.input-group-addon', true).first();
40426         
40427         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40428         
40429         var _this = this;
40430         
40431         (function(){
40432             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40433             _this.list.setWidth(lw);
40434         }).defer(100);
40435         
40436         this.list.on('mouseover', this.onViewOver, this);
40437         this.list.on('mousemove', this.onViewMove, this);
40438         this.list.on('scroll', this.onViewScroll, this);
40439         
40440         if(!this.tpl){
40441             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40442         }
40443         
40444         this.view = new Roo.View(this.list, this.tpl, {
40445             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40446         });
40447         
40448         this.view.on('click', this.onViewClick, this);
40449         
40450         this.store.on('beforeload', this.onBeforeLoad, this);
40451         this.store.on('load', this.onLoad, this);
40452         this.store.on('loadexception', this.onLoadException, this);
40453         
40454         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40455             "up" : function(e){
40456                 this.inKeyMode = true;
40457                 this.selectPrev();
40458             },
40459
40460             "down" : function(e){
40461                 if(!this.isExpanded()){
40462                     this.onTriggerClick();
40463                 }else{
40464                     this.inKeyMode = true;
40465                     this.selectNext();
40466                 }
40467             },
40468
40469             "enter" : function(e){
40470                 this.collapse();
40471                 
40472                 if(this.fireEvent("specialkey", this, e)){
40473                     this.onViewClick(false);
40474                 }
40475                 
40476                 return true;
40477             },
40478
40479             "esc" : function(e){
40480                 this.collapse();
40481             },
40482
40483             "tab" : function(e){
40484                 this.collapse();
40485                 
40486                 if(this.fireEvent("specialkey", this, e)){
40487                     this.onViewClick(false);
40488                 }
40489                 
40490                 return true;
40491             },
40492
40493             scope : this,
40494
40495             doRelay : function(foo, bar, hname){
40496                 if(hname == 'down' || this.scope.isExpanded()){
40497                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40498                 }
40499                 return true;
40500             },
40501
40502             forceKeyDown: true
40503         });
40504         
40505         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40506         
40507     },
40508     
40509     initNumberEvent : function(e)
40510     {
40511         this.inputEl().on("keydown" , this.fireKey,  this);
40512         this.inputEl().on("focus", this.onFocus,  this);
40513         this.inputEl().on("blur", this.onBlur,  this);
40514         
40515         this.inputEl().relayEvent('keyup', this);
40516         
40517         if(this.indicator){
40518             this.indicator.addClass('invisible');
40519         }
40520  
40521         this.originalValue = this.getValue();
40522         
40523         if(this.validationEvent == 'keyup'){
40524             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40525             this.inputEl().on('keyup', this.filterValidation, this);
40526         }
40527         else if(this.validationEvent !== false){
40528             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40529         }
40530         
40531         if(this.selectOnFocus){
40532             this.on("focus", this.preFocus, this);
40533             
40534         }
40535         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40536             this.inputEl().on("keypress", this.filterKeys, this);
40537         } else {
40538             this.inputEl().relayEvent('keypress', this);
40539         }
40540         
40541         var allowed = "0123456789";
40542         
40543         if(this.allowDecimals){
40544             allowed += this.decimalSeparator;
40545         }
40546         
40547         if(this.allowNegative){
40548             allowed += "-";
40549         }
40550         
40551         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40552         
40553         var keyPress = function(e){
40554             
40555             var k = e.getKey();
40556             
40557             var c = e.getCharCode();
40558             
40559             if(
40560                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40561                     allowed.indexOf(String.fromCharCode(c)) === -1
40562             ){
40563                 e.stopEvent();
40564                 return;
40565             }
40566             
40567             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40568                 return;
40569             }
40570             
40571             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40572                 e.stopEvent();
40573             }
40574         };
40575         
40576         this.inputEl().on("keypress", keyPress, this);
40577         
40578     },
40579     
40580     onTriggerClick : function(e)
40581     {   
40582         if(this.disabled){
40583             return;
40584         }
40585         
40586         this.page = 0;
40587         this.loadNext = false;
40588         
40589         if(this.isExpanded()){
40590             this.collapse();
40591             return;
40592         }
40593         
40594         this.hasFocus = true;
40595         
40596         if(this.triggerAction == 'all') {
40597             this.doQuery(this.allQuery, true);
40598             return;
40599         }
40600         
40601         this.doQuery(this.getRawValue());
40602     },
40603     
40604     getCurrency : function()
40605     {   
40606         var v = this.currencyEl().getValue();
40607         
40608         return v;
40609     },
40610     
40611     restrictHeight : function()
40612     {
40613         this.list.alignTo(this.currencyEl(), this.listAlign);
40614         this.list.alignTo(this.currencyEl(), this.listAlign);
40615     },
40616     
40617     onViewClick : function(view, doFocus, el, e)
40618     {
40619         var index = this.view.getSelectedIndexes()[0];
40620         
40621         var r = this.store.getAt(index);
40622         
40623         if(r){
40624             this.onSelect(r, index);
40625         }
40626     },
40627     
40628     onSelect : function(record, index){
40629         
40630         if(this.fireEvent('beforeselect', this, record, index) !== false){
40631         
40632             this.setFromCurrencyData(index > -1 ? record.data : false);
40633             
40634             this.collapse();
40635             
40636             this.fireEvent('select', this, record, index);
40637         }
40638     },
40639     
40640     setFromCurrencyData : function(o)
40641     {
40642         var currency = '';
40643         
40644         this.lastCurrency = o;
40645         
40646         if (this.currencyField) {
40647             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40648         } else {
40649             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40650         }
40651         
40652         this.lastSelectionText = currency;
40653         
40654         //setting default currency
40655         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40656             this.setCurrency(this.defaultCurrency);
40657             return;
40658         }
40659         
40660         this.setCurrency(currency);
40661     },
40662     
40663     setFromData : function(o)
40664     {
40665         var c = {};
40666         
40667         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40668         
40669         this.setFromCurrencyData(c);
40670         
40671         var value = '';
40672         
40673         if (this.name) {
40674             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40675         } else {
40676             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40677         }
40678         
40679         this.setValue(value);
40680         
40681     },
40682     
40683     setCurrency : function(v)
40684     {   
40685         this.currencyValue = v;
40686         
40687         if(this.rendered){
40688             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40689             this.validate();
40690         }
40691     },
40692     
40693     setValue : function(v)
40694     {
40695         v = this.fixPrecision(v);
40696         
40697         v = String(v).replace(".", this.decimalSeparator);
40698         
40699         this.value = v;
40700         
40701         if(this.rendered){
40702             
40703             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40704             
40705             this.inputEl().dom.value = Roo.util.Format.number(v, this.decimalPrecision, 
40706                 this.thousandsDelimiter || ','
40707             );
40708             
40709             if(this.allowBlank && !v) {
40710                 this.inputEl().dom.value = '';
40711             }
40712             
40713             this.validate();
40714         }
40715     },
40716     
40717     getRawValue : function()
40718     {
40719         var v = this.inputEl().getValue();
40720         
40721         return v;
40722     },
40723     
40724     getValue : function()
40725     {
40726         return this.fixPrecision(this.parseValue(this.getRawValue()));
40727     },
40728     
40729     parseValue : function(value)
40730     {
40731         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40732         return isNaN(value) ? '' : value;
40733     },
40734     
40735     fixPrecision : function(value)
40736     {
40737         var nan = isNaN(value);
40738         
40739         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40740             return nan ? '' : value;
40741         }
40742         
40743         return parseFloat(value).toFixed(this.decimalPrecision);
40744     },
40745     
40746     decimalPrecisionFcn : function(v)
40747     {
40748         return Math.floor(v);
40749     },
40750     
40751     validateValue : function(value)
40752     {
40753         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40754             return false;
40755         }
40756         
40757         var num = this.parseValue(value);
40758         
40759         if(isNaN(num)){
40760             this.markInvalid(String.format(this.nanText, value));
40761             return false;
40762         }
40763         
40764         if(num < this.minValue){
40765             this.markInvalid(String.format(this.minText, this.minValue));
40766             return false;
40767         }
40768         
40769         if(num > this.maxValue){
40770             this.markInvalid(String.format(this.maxText, this.maxValue));
40771             return false;
40772         }
40773         
40774         return true;
40775     },
40776     
40777     validate : function()
40778     {
40779         if(this.disabled || this.allowBlank){
40780             this.markValid();
40781             return true;
40782         }
40783         
40784         var currency = this.getCurrency();
40785         
40786         if(this.validateValue(this.getRawValue()) && currency.length){
40787             this.markValid();
40788             return true;
40789         }
40790         
40791         this.markInvalid();
40792         return false;
40793     },
40794     
40795     getName: function()
40796     {
40797         return this.name;
40798     },
40799     
40800     beforeBlur : function()
40801     {
40802         if(!this.castInt){
40803             return;
40804         }
40805         
40806         var v = this.parseValue(this.getRawValue());
40807         
40808         if(v || v == 0){
40809             this.setValue(v);
40810         }
40811     },
40812     
40813     onBlur : function()
40814     {
40815         this.beforeBlur();
40816         
40817         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40818             //this.el.removeClass(this.focusClass);
40819         }
40820         
40821         this.hasFocus = false;
40822         
40823         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40824             this.validate();
40825         }
40826         
40827         var v = this.getValue();
40828         
40829         if(String(v) !== String(this.startValue)){
40830             this.fireEvent('change', this, v, this.startValue);
40831         }
40832         
40833         this.fireEvent("blur", this);
40834     },
40835     
40836     inputEl : function()
40837     {
40838         return this.el.select('.roo-money-amount-input', true).first();
40839     },
40840     
40841     currencyEl : function()
40842     {
40843         return this.el.select('.roo-money-currency-input', true).first();
40844     },
40845     
40846     hiddenEl : function()
40847     {
40848         return this.el.select('input.hidden-number-input',true).first();
40849     }
40850     
40851 });