sync
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
22  
23  * @constructor
24  * Do not use directly - it does not do anything..
25  * @param {Object} config The config object
26  */
27
28
29
30 Roo.bootstrap.Component = function(config){
31     Roo.bootstrap.Component.superclass.constructor.call(this, config);
32        
33     this.addEvents({
34         /**
35          * @event childrenrendered
36          * Fires when the children have been rendered..
37          * @param {Roo.bootstrap.Component} this
38          */
39         "childrenrendered" : true
40         
41         
42         
43     });
44     
45     
46 };
47
48 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
49     
50     
51     allowDomMove : false, // to stop relocations in parent onRender...
52     
53     cls : false,
54     
55     style : false,
56     
57     autoCreate : false,
58     
59     tooltip : null,
60     /**
61      * Initialize Events for the element
62      */
63     initEvents : function() { },
64     
65     xattr : false,
66     
67     parentId : false,
68     
69     can_build_overlaid : true,
70     
71     container_method : false,
72     
73     dataId : false,
74     
75     name : false,
76     
77     parent: function() {
78         // returns the parent component..
79         return Roo.ComponentMgr.get(this.parentId)
80         
81         
82     },
83     
84     // private
85     onRender : function(ct, position)
86     {
87        // Roo.log("Call onRender: " + this.xtype);
88         
89         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
90         
91         if(this.el){
92             if (this.el.attr('xtype')) {
93                 this.el.attr('xtypex', this.el.attr('xtype'));
94                 this.el.dom.removeAttribute('xtype');
95                 
96                 this.initEvents();
97             }
98             
99             return;
100         }
101         
102          
103         
104         var cfg = Roo.apply({},  this.getAutoCreate());
105         
106         cfg.id = this.id || Roo.id();
107         
108         // fill in the extra attributes 
109         if (this.xattr && typeof(this.xattr) =='object') {
110             for (var i in this.xattr) {
111                 cfg[i] = this.xattr[i];
112             }
113         }
114         
115         if(this.dataId){
116             cfg.dataId = this.dataId;
117         }
118         
119         if (this.cls) {
120             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
121         }
122         
123         if (this.style) { // fixme needs to support more complex style data.
124             cfg.style = this.style;
125         }
126         
127         if(this.name){
128             cfg.name = this.name;
129         }
130         
131         this.el = ct.createChild(cfg, position);
132         
133         if (this.tooltip) {
134             this.tooltipEl().attr('tooltip', this.tooltip);
135         }
136         
137         if(this.tabIndex !== undefined){
138             this.el.dom.setAttribute('tabIndex', this.tabIndex);
139         }
140         
141         this.initEvents();
142         
143     },
144     /**
145      * Fetch the element to add children to
146      * @return {Roo.Element} defaults to this.el
147      */
148     getChildContainer : function()
149     {
150         return this.el;
151     },
152     /**
153      * Fetch the element to display the tooltip on.
154      * @return {Roo.Element} defaults to this.el
155      */
156     tooltipEl : function()
157     {
158         return this.el;
159     },
160         
161     addxtype  : function(tree,cntr)
162     {
163         var cn = this;
164         
165         cn = Roo.factory(tree);
166         //Roo.log(['addxtype', cn]);
167            
168         cn.parentType = this.xtype; //??
169         cn.parentId = this.id;
170         
171         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
172         if (typeof(cn.container_method) == 'string') {
173             cntr = cn.container_method;
174         }
175         
176         
177         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
178         
179         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
180         
181         var build_from_html =  Roo.XComponent.build_from_html;
182           
183         var is_body  = (tree.xtype == 'Body') ;
184           
185         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
186           
187         var self_cntr_el = Roo.get(this[cntr](false));
188         
189         // do not try and build conditional elements 
190         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
191             return false;
192         }
193         
194         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
195             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
196                 return this.addxtypeChild(tree,cntr, is_body);
197             }
198             
199             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
200                 
201             if(echild){
202                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
203             }
204             
205             Roo.log('skipping render');
206             return cn;
207             
208         }
209         
210         var ret = false;
211         if (!build_from_html) {
212             return false;
213         }
214         
215         // this i think handles overlaying multiple children of the same type
216         // with the sam eelement.. - which might be buggy..
217         while (true) {
218             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
219             
220             if (!echild) {
221                 break;
222             }
223             
224             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
225                 break;
226             }
227             
228             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
229         }
230        
231         return ret;
232     },
233     
234     
235     addxtypeChild : function (tree, cntr, is_body)
236     {
237         Roo.debug && Roo.log('addxtypeChild:' + cntr);
238         var cn = this;
239         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
240         
241         
242         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
243                     (typeof(tree['flexy:foreach']) != 'undefined');
244           
245     
246         
247         skip_children = false;
248         // render the element if it's not BODY.
249         if (!is_body) {
250             
251             // if parent was disabled, then do not try and create the children..
252             if(!this[cntr](true)){
253                 tree.items = [];
254                 return tree;
255             }
256            
257             cn = Roo.factory(tree);
258            
259             cn.parentType = this.xtype; //??
260             cn.parentId = this.id;
261             
262             var build_from_html =  Roo.XComponent.build_from_html;
263             
264             
265             // does the container contain child eleemnts with 'xtype' attributes.
266             // that match this xtype..
267             // note - when we render we create these as well..
268             // so we should check to see if body has xtype set.
269             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
270                
271                 var self_cntr_el = Roo.get(this[cntr](false));
272                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
273                 if (echild) { 
274                     //Roo.log(Roo.XComponent.build_from_html);
275                     //Roo.log("got echild:");
276                     //Roo.log(echild);
277                 }
278                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
279                 // and are not displayed -this causes this to use up the wrong element when matching.
280                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
281                 
282                 
283                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
284                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
285                   
286                   
287                   
288                     cn.el = echild;
289                   //  Roo.log("GOT");
290                     //echild.dom.removeAttribute('xtype');
291                 } else {
292                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
293                     Roo.debug && Roo.log(self_cntr_el);
294                     Roo.debug && Roo.log(echild);
295                     Roo.debug && Roo.log(cn);
296                 }
297             }
298            
299             
300            
301             // if object has flexy:if - then it may or may not be rendered.
302             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
303                 // skip a flexy if element.
304                 Roo.debug && Roo.log('skipping render');
305                 Roo.debug && Roo.log(tree);
306                 if (!cn.el) {
307                     Roo.debug && Roo.log('skipping all children');
308                     skip_children = true;
309                 }
310                 
311              } else {
312                  
313                 // actually if flexy:foreach is found, we really want to create 
314                 // multiple copies here...
315                 //Roo.log('render');
316                 //Roo.log(this[cntr]());
317                 // some elements do not have render methods.. like the layouts...
318                 /*
319                 if(this[cntr](true) === false){
320                     cn.items = [];
321                     return cn;
322                 }
323                 */
324                 cn.render && cn.render(this[cntr](true));
325                 
326              }
327             // then add the element..
328         }
329          
330         // handle the kids..
331         
332         var nitems = [];
333         /*
334         if (typeof (tree.menu) != 'undefined') {
335             tree.menu.parentType = cn.xtype;
336             tree.menu.triggerEl = cn.el;
337             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
338             
339         }
340         */
341         if (!tree.items || !tree.items.length) {
342             cn.items = nitems;
343             //Roo.log(["no children", this]);
344             
345             return cn;
346         }
347          
348         var items = tree.items;
349         delete tree.items;
350         
351         //Roo.log(items.length);
352             // add the items..
353         if (!skip_children) {    
354             for(var i =0;i < items.length;i++) {
355               //  Roo.log(['add child', items[i]]);
356                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
357             }
358         }
359         
360         cn.items = nitems;
361         
362         //Roo.log("fire childrenrendered");
363         
364         cn.fireEvent('childrenrendered', this);
365         
366         return cn;
367     },
368      /**
369      * Get the element that will be used to show or hide
370      */
371     getVisibilityEl : function()
372     {
373         
374         if (typeof(this.visibilityEl) == 'object') {
375             return this.visibilityEl;
376         }
377         
378         if (typeof(this.visibilityEl) == 'string') {
379             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
380         }
381         
382         
383         return this.getEl();
384         
385     },
386     
387     /**
388      * Show a component - removes 'hidden' class
389      */
390     show : function()
391     {
392         if(!this.getVisibilityEl()){
393             return;
394         }
395          
396         this.getVisibilityEl().removeClass('hidden');
397         
398         
399     },
400     /**
401      * Hide a component - adds 'hidden' class
402      */
403     hide: function()
404     {
405         if(!this.getVisibilityEl()){
406             return;
407         }
408         
409         this.getVisibilityEl().addClass('hidden');
410         
411     }
412 });
413
414  /*
415  * - LGPL
416  *
417  * Body
418  *
419  */
420
421 /**
422  * @class Roo.bootstrap.Body
423  * @extends Roo.bootstrap.Component
424  * Bootstrap Body class
425  *
426  * @constructor
427  * Create a new body
428  * @param {Object} config The config object
429  */
430
431 Roo.bootstrap.Body = function(config){
432
433     config = config || {};
434
435     Roo.bootstrap.Body.superclass.constructor.call(this, config);
436     this.el = Roo.get(config.el ? config.el : document.body );
437     if (this.cls && this.cls.length) {
438         Roo.get(document.body).addClass(this.cls);
439     }
440 };
441
442 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
443
444     is_body : true,// just to make sure it's constructed?
445
446         autoCreate : {
447         cls: 'container'
448     },
449     onRender : function(ct, position)
450     {
451        /* Roo.log("Roo.bootstrap.Body - onRender");
452         if (this.cls && this.cls.length) {
453             Roo.get(document.body).addClass(this.cls);
454         }
455         // style??? xttr???
456         */
457     }
458
459
460
461
462 });
463 /*
464  * - LGPL
465  *
466  * button group
467  * 
468  */
469
470
471 /**
472  * @class Roo.bootstrap.ButtonGroup
473  * @extends Roo.bootstrap.Component
474  * Bootstrap ButtonGroup class
475  * @cfg {String} size lg | sm | xs (default empty normal)
476  * @cfg {String} align vertical | justified  (default none)
477  * @cfg {String} direction up | down (default down)
478  * @cfg {Boolean} toolbar false | true
479  * @cfg {Boolean} btn true | false
480  * 
481  * 
482  * @constructor
483  * Create a new Input
484  * @param {Object} config The config object
485  */
486
487 Roo.bootstrap.ButtonGroup = function(config){
488     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
489 };
490
491 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
492     
493     size: '',
494     align: '',
495     direction: '',
496     toolbar: false,
497     btn: true,
498
499     getAutoCreate : function(){
500         var cfg = {
501             cls: 'btn-group',
502             html : null
503         };
504         
505         cfg.html = this.html || cfg.html;
506         
507         if (this.toolbar) {
508             cfg = {
509                 cls: 'btn-toolbar',
510                 html: null
511             };
512             
513             return cfg;
514         }
515         
516         if (['vertical','justified'].indexOf(this.align)!==-1) {
517             cfg.cls = 'btn-group-' + this.align;
518             
519             if (this.align == 'justified') {
520                 console.log(this.items);
521             }
522         }
523         
524         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
525             cfg.cls += ' btn-group-' + this.size;
526         }
527         
528         if (this.direction == 'up') {
529             cfg.cls += ' dropup' ;
530         }
531         
532         return cfg;
533     }
534    
535 });
536
537  /*
538  * - LGPL
539  *
540  * button
541  * 
542  */
543
544 /**
545  * @class Roo.bootstrap.Button
546  * @extends Roo.bootstrap.Component
547  * Bootstrap Button class
548  * @cfg {String} html The button content
549  * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default 
550  * @cfg {String} size ( lg | sm | xs)
551  * @cfg {String} tag ( a | input | submit)
552  * @cfg {String} href empty or href
553  * @cfg {Boolean} disabled default false;
554  * @cfg {Boolean} isClose default false;
555  * @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)
556  * @cfg {String} badge text for badge
557  * @cfg {String} theme default 
558  * @cfg {Boolean} inverse 
559  * @cfg {Boolean} toggle 
560  * @cfg {String} ontext text for on toggle state
561  * @cfg {String} offtext text for off toggle state
562  * @cfg {Boolean} defaulton 
563  * @cfg {Boolean} preventDefault  default true
564  * @cfg {Boolean} removeClass remove the standard class..
565  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
566  * 
567  * @constructor
568  * Create a new button
569  * @param {Object} config The config object
570  */
571
572
573 Roo.bootstrap.Button = function(config){
574     Roo.bootstrap.Button.superclass.constructor.call(this, config);
575     this.weightClass = ["btn-default", 
576                        "btn-primary", 
577                        "btn-success", 
578                        "btn-info", 
579                        "btn-warning",
580                        "btn-danger",
581                        "btn-link"
582                       ],  
583     this.addEvents({
584         // raw events
585         /**
586          * @event click
587          * When a butotn is pressed
588          * @param {Roo.bootstrap.Button} this
589          * @param {Roo.EventObject} e
590          */
591         "click" : true,
592          /**
593          * @event toggle
594          * After the button has been toggles
595          * @param {Roo.EventObject} e
596          * @param {boolean} pressed (also available as button.pressed)
597          */
598         "toggle" : true
599     });
600 };
601
602 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
603     html: false,
604     active: false,
605     weight: '',
606     size: '',
607     tag: 'button',
608     href: '',
609     disabled: false,
610     isClose: false,
611     glyphicon: '',
612     badge: '',
613     theme: 'default',
614     inverse: false,
615     
616     toggle: false,
617     ontext: 'ON',
618     offtext: 'OFF',
619     defaulton: true,
620     preventDefault: true,
621     removeClass: false,
622     name: false,
623     target: false,
624     
625     
626     pressed : null,
627      
628     
629     getAutoCreate : function(){
630         
631         var cfg = {
632             tag : 'button',
633             cls : 'roo-button',
634             html: ''
635         };
636         
637         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
638             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
639             this.tag = 'button';
640         } else {
641             cfg.tag = this.tag;
642         }
643         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
644         
645         if (this.toggle == true) {
646             cfg={
647                 tag: 'div',
648                 cls: 'slider-frame roo-button',
649                 cn: [
650                     {
651                         tag: 'span',
652                         'data-on-text':'ON',
653                         'data-off-text':'OFF',
654                         cls: 'slider-button',
655                         html: this.offtext
656                     }
657                 ]
658             };
659             
660             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
661                 cfg.cls += ' '+this.weight;
662             }
663             
664             return cfg;
665         }
666         
667         if (this.isClose) {
668             cfg.cls += ' close';
669             
670             cfg["aria-hidden"] = true;
671             
672             cfg.html = "&times;";
673             
674             return cfg;
675         }
676         
677          
678         if (this.theme==='default') {
679             cfg.cls = 'btn roo-button';
680             
681             //if (this.parentType != 'Navbar') {
682             this.weight = this.weight.length ?  this.weight : 'default';
683             //}
684             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
685                 
686                 cfg.cls += ' btn-' + this.weight;
687             }
688         } else if (this.theme==='glow') {
689             
690             cfg.tag = 'a';
691             cfg.cls = 'btn-glow roo-button';
692             
693             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
694                 
695                 cfg.cls += ' ' + this.weight;
696             }
697         }
698    
699         
700         if (this.inverse) {
701             this.cls += ' inverse';
702         }
703         
704         
705         if (this.active) {
706             cfg.cls += ' active';
707         }
708         
709         if (this.disabled) {
710             cfg.disabled = 'disabled';
711         }
712         
713         if (this.items) {
714             Roo.log('changing to ul' );
715             cfg.tag = 'ul';
716             this.glyphicon = 'caret';
717         }
718         
719         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
720          
721         //gsRoo.log(this.parentType);
722         if (this.parentType === 'Navbar' && !this.parent().bar) {
723             Roo.log('changing to li?');
724             
725             cfg.tag = 'li';
726             
727             cfg.cls = '';
728             cfg.cn =  [{
729                 tag : 'a',
730                 cls : 'roo-button',
731                 html : this.html,
732                 href : this.href || '#'
733             }];
734             if (this.menu) {
735                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
736                 cfg.cls += ' dropdown';
737             }   
738             
739             delete cfg.html;
740             
741         }
742         
743        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
744         
745         if (this.glyphicon) {
746             cfg.html = ' ' + cfg.html;
747             
748             cfg.cn = [
749                 {
750                     tag: 'span',
751                     cls: 'glyphicon glyphicon-' + this.glyphicon
752                 }
753             ];
754         }
755         
756         if (this.badge) {
757             cfg.html += ' ';
758             
759             cfg.tag = 'a';
760             
761 //            cfg.cls='btn roo-button';
762             
763             cfg.href=this.href;
764             
765             var value = cfg.html;
766             
767             if(this.glyphicon){
768                 value = {
769                             tag: 'span',
770                             cls: 'glyphicon glyphicon-' + this.glyphicon,
771                             html: this.html
772                         };
773                 
774             }
775             
776             cfg.cn = [
777                 value,
778                 {
779                     tag: 'span',
780                     cls: 'badge',
781                     html: this.badge
782                 }
783             ];
784             
785             cfg.html='';
786         }
787         
788         if (this.menu) {
789             cfg.cls += ' dropdown';
790             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
791         }
792         
793         if (cfg.tag !== 'a' && this.href !== '') {
794             throw "Tag must be a to set href.";
795         } else if (this.href.length > 0) {
796             cfg.href = this.href;
797         }
798         
799         if(this.removeClass){
800             cfg.cls = '';
801         }
802         
803         if(this.target){
804             cfg.target = this.target;
805         }
806         
807         return cfg;
808     },
809     initEvents: function() {
810        // Roo.log('init events?');
811 //        Roo.log(this.el.dom);
812         // add the menu...
813         
814         if (typeof (this.menu) != 'undefined') {
815             this.menu.parentType = this.xtype;
816             this.menu.triggerEl = this.el;
817             this.addxtype(Roo.apply({}, this.menu));
818         }
819
820
821        if (this.el.hasClass('roo-button')) {
822             this.el.on('click', this.onClick, this);
823        } else {
824             this.el.select('.roo-button').on('click', this.onClick, this);
825        }
826        
827        if(this.removeClass){
828            this.el.on('click', this.onClick, this);
829        }
830        
831        this.el.enableDisplayMode();
832         
833     },
834     onClick : function(e)
835     {
836         if (this.disabled) {
837             return;
838         }
839         
840         
841         Roo.log('button on click ');
842         if(this.preventDefault){
843             e.preventDefault();
844         }
845         if (this.pressed === true || this.pressed === false) {
846             this.pressed = !this.pressed;
847             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
848             this.fireEvent('toggle', this, e, this.pressed);
849         }
850         
851         
852         this.fireEvent('click', this, e);
853     },
854     
855     /**
856      * Enables this button
857      */
858     enable : function()
859     {
860         this.disabled = false;
861         this.el.removeClass('disabled');
862     },
863     
864     /**
865      * Disable this button
866      */
867     disable : function()
868     {
869         this.disabled = true;
870         this.el.addClass('disabled');
871     },
872      /**
873      * sets the active state on/off, 
874      * @param {Boolean} state (optional) Force a particular state
875      */
876     setActive : function(v) {
877         
878         this.el[v ? 'addClass' : 'removeClass']('active');
879     },
880      /**
881      * toggles the current active state 
882      */
883     toggleActive : function()
884     {
885        var active = this.el.hasClass('active');
886        this.setActive(!active);
887        
888         
889     },
890     setText : function(str)
891     {
892         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
893     },
894     getText : function()
895     {
896         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
897     },
898     hide: function() {
899        
900      
901         this.el.hide();   
902     },
903     show: function() {
904        
905         this.el.show();   
906     },
907     setWeight : function(str)
908     {
909           this.el.removeClass(this.weightClass);
910         this.el.addClass('btn-' + str);        
911     }
912     
913     
914 });
915
916  /*
917  * - LGPL
918  *
919  * column
920  * 
921  */
922
923 /**
924  * @class Roo.bootstrap.Column
925  * @extends Roo.bootstrap.Component
926  * Bootstrap Column class
927  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
928  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
929  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
930  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
931  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
932  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
933  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
934  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
935  *
936  * 
937  * @cfg {Boolean} hidden (true|false) hide the element
938  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
939  * @cfg {String} fa (ban|check|...) font awesome icon
940  * @cfg {Number} fasize (1|2|....) font awsome size
941
942  * @cfg {String} icon (info-sign|check|...) glyphicon name
943
944  * @cfg {String} html content of column.
945  * 
946  * @constructor
947  * Create a new Column
948  * @param {Object} config The config object
949  */
950
951 Roo.bootstrap.Column = function(config){
952     Roo.bootstrap.Column.superclass.constructor.call(this, config);
953 };
954
955 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
956     
957     xs: false,
958     sm: false,
959     md: false,
960     lg: false,
961     xsoff: false,
962     smoff: false,
963     mdoff: false,
964     lgoff: false,
965     html: '',
966     offset: 0,
967     alert: false,
968     fa: false,
969     icon : false,
970     hidden : false,
971     fasize : 1,
972     
973     getAutoCreate : function(){
974         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
975         
976         cfg = {
977             tag: 'div',
978             cls: 'column'
979         };
980         
981         var settings=this;
982         ['xs','sm','md','lg'].map(function(size){
983             //Roo.log( size + ':' + settings[size]);
984             
985             if (settings[size+'off'] !== false) {
986                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
987             }
988             
989             if (settings[size] === false) {
990                 return;
991             }
992             
993             if (!settings[size]) { // 0 = hidden
994                 cfg.cls += ' hidden-' + size;
995                 return;
996             }
997             cfg.cls += ' col-' + size + '-' + settings[size];
998             
999         });
1000         
1001         if (this.hidden) {
1002             cfg.cls += ' hidden';
1003         }
1004         
1005         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1006             cfg.cls +=' alert alert-' + this.alert;
1007         }
1008         
1009         
1010         if (this.html.length) {
1011             cfg.html = this.html;
1012         }
1013         if (this.fa) {
1014             var fasize = '';
1015             if (this.fasize > 1) {
1016                 fasize = ' fa-' + this.fasize + 'x';
1017             }
1018             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1019             
1020             
1021         }
1022         if (this.icon) {
1023             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1024         }
1025         
1026         return cfg;
1027     }
1028    
1029 });
1030
1031  
1032
1033  /*
1034  * - LGPL
1035  *
1036  * page container.
1037  * 
1038  */
1039
1040
1041 /**
1042  * @class Roo.bootstrap.Container
1043  * @extends Roo.bootstrap.Component
1044  * Bootstrap Container class
1045  * @cfg {Boolean} jumbotron is it a jumbotron element
1046  * @cfg {String} html content of element
1047  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1048  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1049  * @cfg {String} header content of header (for panel)
1050  * @cfg {String} footer content of footer (for panel)
1051  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1052  * @cfg {String} tag (header|aside|section) type of HTML tag.
1053  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1054  * @cfg {String} fa font awesome icon
1055  * @cfg {String} icon (info-sign|check|...) glyphicon name
1056  * @cfg {Boolean} hidden (true|false) hide the element
1057  * @cfg {Boolean} expandable (true|false) default false
1058  * @cfg {Boolean} expanded (true|false) default true
1059  * @cfg {String} rheader contet on the right of header
1060  * @cfg {Boolean} clickable (true|false) default false
1061
1062  *     
1063  * @constructor
1064  * Create a new Container
1065  * @param {Object} config The config object
1066  */
1067
1068 Roo.bootstrap.Container = function(config){
1069     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1070     
1071     this.addEvents({
1072         // raw events
1073          /**
1074          * @event expand
1075          * After the panel has been expand
1076          * 
1077          * @param {Roo.bootstrap.Container} this
1078          */
1079         "expand" : true,
1080         /**
1081          * @event collapse
1082          * After the panel has been collapsed
1083          * 
1084          * @param {Roo.bootstrap.Container} this
1085          */
1086         "collapse" : true,
1087         /**
1088          * @event click
1089          * When a element is chick
1090          * @param {Roo.bootstrap.Container} this
1091          * @param {Roo.EventObject} e
1092          */
1093         "click" : true
1094     });
1095 };
1096
1097 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1098     
1099     jumbotron : false,
1100     well: '',
1101     panel : '',
1102     header: '',
1103     footer : '',
1104     sticky: '',
1105     tag : false,
1106     alert : false,
1107     fa: false,
1108     icon : false,
1109     expandable : false,
1110     rheader : '',
1111     expanded : true,
1112     clickable: false,
1113   
1114      
1115     getChildContainer : function() {
1116         
1117         if(!this.el){
1118             return false;
1119         }
1120         
1121         if (this.panel.length) {
1122             return this.el.select('.panel-body',true).first();
1123         }
1124         
1125         return this.el;
1126     },
1127     
1128     
1129     getAutoCreate : function(){
1130         
1131         var cfg = {
1132             tag : this.tag || 'div',
1133             html : '',
1134             cls : ''
1135         };
1136         if (this.jumbotron) {
1137             cfg.cls = 'jumbotron';
1138         }
1139         
1140         
1141         
1142         // - this is applied by the parent..
1143         //if (this.cls) {
1144         //    cfg.cls = this.cls + '';
1145         //}
1146         
1147         if (this.sticky.length) {
1148             
1149             var bd = Roo.get(document.body);
1150             if (!bd.hasClass('bootstrap-sticky')) {
1151                 bd.addClass('bootstrap-sticky');
1152                 Roo.select('html',true).setStyle('height', '100%');
1153             }
1154              
1155             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1156         }
1157         
1158         
1159         if (this.well.length) {
1160             switch (this.well) {
1161                 case 'lg':
1162                 case 'sm':
1163                     cfg.cls +=' well well-' +this.well;
1164                     break;
1165                 default:
1166                     cfg.cls +=' well';
1167                     break;
1168             }
1169         }
1170         
1171         if (this.hidden) {
1172             cfg.cls += ' hidden';
1173         }
1174         
1175         
1176         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1177             cfg.cls +=' alert alert-' + this.alert;
1178         }
1179         
1180         var body = cfg;
1181         
1182         if (this.panel.length) {
1183             cfg.cls += ' panel panel-' + this.panel;
1184             cfg.cn = [];
1185             if (this.header.length) {
1186                 
1187                 var h = [];
1188                 
1189                 if(this.expandable){
1190                     
1191                     cfg.cls = cfg.cls + ' expandable';
1192                     
1193                     h.push({
1194                         tag: 'i',
1195                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1196                     });
1197                     
1198                 }
1199                 
1200                 h.push(
1201                     {
1202                         tag: 'span',
1203                         cls : 'panel-title',
1204                         html : (this.expandable ? '&nbsp;' : '') + this.header
1205                     },
1206                     {
1207                         tag: 'span',
1208                         cls: 'panel-header-right',
1209                         html: this.rheader
1210                     }
1211                 );
1212                 
1213                 cfg.cn.push({
1214                     cls : 'panel-heading',
1215                     style : this.expandable ? 'cursor: pointer' : '',
1216                     cn : h
1217                 });
1218                 
1219             }
1220             
1221             body = false;
1222             cfg.cn.push({
1223                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1224                 html : this.html
1225             });
1226             
1227             
1228             if (this.footer.length) {
1229                 cfg.cn.push({
1230                     cls : 'panel-footer',
1231                     html : this.footer
1232                     
1233                 });
1234             }
1235             
1236         }
1237         
1238         if (body) {
1239             body.html = this.html || cfg.html;
1240             // prefix with the icons..
1241             if (this.fa) {
1242                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1243             }
1244             if (this.icon) {
1245                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1246             }
1247             
1248             
1249         }
1250         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1251             cfg.cls =  'container';
1252         }
1253         
1254         return cfg;
1255     },
1256     
1257     initEvents: function() 
1258     {
1259         if(this.expandable){
1260             var headerEl = this.headerEl();
1261         
1262             if(headerEl){
1263                 headerEl.on('click', this.onToggleClick, this);
1264             }
1265         }
1266         
1267         if(this.clickable){
1268             this.el.on('click', this.onClick, this);
1269         }
1270         
1271     },
1272     
1273     onToggleClick : function()
1274     {
1275         var headerEl = this.headerEl();
1276         
1277         if(!headerEl){
1278             return;
1279         }
1280         
1281         if(this.expanded){
1282             this.collapse();
1283             return;
1284         }
1285         
1286         this.expand();
1287     },
1288     
1289     expand : function()
1290     {
1291         if(this.fireEvent('expand', this)) {
1292             
1293             this.expanded = true;
1294             
1295             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1296             
1297             this.el.select('.panel-body',true).first().removeClass('hide');
1298             
1299             var toggleEl = this.toggleEl();
1300
1301             if(!toggleEl){
1302                 return;
1303             }
1304
1305             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1306         }
1307         
1308     },
1309     
1310     collapse : function()
1311     {
1312         if(this.fireEvent('collapse', this)) {
1313             
1314             this.expanded = false;
1315             
1316             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1317             this.el.select('.panel-body',true).first().addClass('hide');
1318         
1319             var toggleEl = this.toggleEl();
1320
1321             if(!toggleEl){
1322                 return;
1323             }
1324
1325             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1326         }
1327     },
1328     
1329     toggleEl : function()
1330     {
1331         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1332             return;
1333         }
1334         
1335         return this.el.select('.panel-heading .fa',true).first();
1336     },
1337     
1338     headerEl : function()
1339     {
1340         if(!this.el || !this.panel.length || !this.header.length){
1341             return;
1342         }
1343         
1344         return this.el.select('.panel-heading',true).first()
1345     },
1346     
1347     bodyEl : function()
1348     {
1349         if(!this.el || !this.panel.length){
1350             return;
1351         }
1352         
1353         return this.el.select('.panel-body',true).first()
1354     },
1355     
1356     titleEl : function()
1357     {
1358         if(!this.el || !this.panel.length || !this.header.length){
1359             return;
1360         }
1361         
1362         return this.el.select('.panel-title',true).first();
1363     },
1364     
1365     setTitle : function(v)
1366     {
1367         var titleEl = this.titleEl();
1368         
1369         if(!titleEl){
1370             return;
1371         }
1372         
1373         titleEl.dom.innerHTML = v;
1374     },
1375     
1376     getTitle : function()
1377     {
1378         
1379         var titleEl = this.titleEl();
1380         
1381         if(!titleEl){
1382             return '';
1383         }
1384         
1385         return titleEl.dom.innerHTML;
1386     },
1387     
1388     setRightTitle : function(v)
1389     {
1390         var t = this.el.select('.panel-header-right',true).first();
1391         
1392         if(!t){
1393             return;
1394         }
1395         
1396         t.dom.innerHTML = v;
1397     },
1398     
1399     onClick : function(e)
1400     {
1401         e.preventDefault();
1402         
1403         this.fireEvent('click', this, e);
1404     },
1405     
1406     allChildren : function()
1407     {
1408         var r=new Roo.util.MixedCollection(false, function(o){
1409             return o.id || (o.id = Roo.id());
1410         });
1411         var iter = function(el) {
1412             if (el.inputEl) {
1413                 r.add(el);
1414             }
1415             if (!el.items) {
1416                 return;
1417             }
1418             Roo.each(el.items,function(e) {
1419                 iter(e);
1420             });
1421         };
1422
1423         iter(this);
1424         return r;
1425     },
1426     
1427     checkEmpty : function()
1428     {
1429         var items = this.allChildren();
1430         var isEmpty = true;
1431         
1432         items.each(function(f){
1433             if(f.el.isVisible()) {
1434                 isEmpty = false;
1435             }
1436         });
1437         
1438         return isEmpty;
1439     }
1440    
1441 });
1442
1443  /*
1444  * - LGPL
1445  *
1446  * image
1447  * 
1448  */
1449
1450
1451 /**
1452  * @class Roo.bootstrap.Img
1453  * @extends Roo.bootstrap.Component
1454  * Bootstrap Img class
1455  * @cfg {Boolean} imgResponsive false | true
1456  * @cfg {String} border rounded | circle | thumbnail
1457  * @cfg {String} src image source
1458  * @cfg {String} alt image alternative text
1459  * @cfg {String} href a tag href
1460  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1461  * @cfg {String} xsUrl xs image source
1462  * @cfg {String} smUrl sm image source
1463  * @cfg {String} mdUrl md image source
1464  * @cfg {String} lgUrl lg image source
1465  * 
1466  * @constructor
1467  * Create a new Input
1468  * @param {Object} config The config object
1469  */
1470
1471 Roo.bootstrap.Img = function(config){
1472     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1473     
1474     this.addEvents({
1475         // img events
1476         /**
1477          * @event click
1478          * The img click event for the img.
1479          * @param {Roo.EventObject} e
1480          */
1481         "click" : true
1482     });
1483 };
1484
1485 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1486     
1487     imgResponsive: true,
1488     border: '',
1489     src: 'about:blank',
1490     href: false,
1491     target: false,
1492     xsUrl: '',
1493     smUrl: '',
1494     mdUrl: '',
1495     lgUrl: '',
1496
1497     getAutoCreate : function()
1498     {   
1499         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1500             return this.createSingleImg();
1501         }
1502         
1503         var cfg = {
1504             tag: 'div',
1505             cls: 'roo-image-responsive-group',
1506             cn: []
1507         };
1508         var _this = this;
1509         
1510         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1511             
1512             if(!_this[size + 'Url']){
1513                 return;
1514             }
1515             
1516             var img = {
1517                 tag: 'img',
1518                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1519                 html: _this.html || cfg.html,
1520                 src: _this[size + 'Url']
1521             };
1522             
1523             img.cls += ' roo-image-responsive-' + size;
1524             
1525             var s = ['xs', 'sm', 'md', 'lg'];
1526             
1527             s.splice(s.indexOf(size), 1);
1528             
1529             Roo.each(s, function(ss){
1530                 img.cls += ' hidden-' + ss;
1531             });
1532             
1533             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1534                 cfg.cls += ' img-' + _this.border;
1535             }
1536             
1537             if(_this.alt){
1538                 cfg.alt = _this.alt;
1539             }
1540             
1541             if(_this.href){
1542                 var a = {
1543                     tag: 'a',
1544                     href: _this.href,
1545                     cn: [
1546                         img
1547                     ]
1548                 };
1549
1550                 if(this.target){
1551                     a.target = _this.target;
1552                 }
1553             }
1554             
1555             cfg.cn.push((_this.href) ? a : img);
1556             
1557         });
1558         
1559         return cfg;
1560     },
1561     
1562     createSingleImg : function()
1563     {
1564         var cfg = {
1565             tag: 'img',
1566             cls: (this.imgResponsive) ? 'img-responsive' : '',
1567             html : null,
1568             src : 'about:blank'  // just incase src get's set to undefined?!?
1569         };
1570         
1571         cfg.html = this.html || cfg.html;
1572         
1573         cfg.src = this.src || cfg.src;
1574         
1575         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1576             cfg.cls += ' img-' + this.border;
1577         }
1578         
1579         if(this.alt){
1580             cfg.alt = this.alt;
1581         }
1582         
1583         if(this.href){
1584             var a = {
1585                 tag: 'a',
1586                 href: this.href,
1587                 cn: [
1588                     cfg
1589                 ]
1590             };
1591             
1592             if(this.target){
1593                 a.target = this.target;
1594             }
1595             
1596         }
1597         
1598         return (this.href) ? a : cfg;
1599     },
1600     
1601     initEvents: function() 
1602     {
1603         if(!this.href){
1604             this.el.on('click', this.onClick, this);
1605         }
1606         
1607     },
1608     
1609     onClick : function(e)
1610     {
1611         Roo.log('img onclick');
1612         this.fireEvent('click', this, e);
1613     },
1614     /**
1615      * Sets the url of the image - used to update it
1616      * @param {String} url the url of the image
1617      */
1618     
1619     setSrc : function(url)
1620     {
1621         this.src =  url;
1622         
1623         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1624             this.el.dom.src =  url;
1625             return;
1626         }
1627         
1628         this.el.select('img', true).first().dom.src =  url;
1629     }
1630     
1631     
1632    
1633 });
1634
1635  /*
1636  * - LGPL
1637  *
1638  * image
1639  * 
1640  */
1641
1642
1643 /**
1644  * @class Roo.bootstrap.Link
1645  * @extends Roo.bootstrap.Component
1646  * Bootstrap Link Class
1647  * @cfg {String} alt image alternative text
1648  * @cfg {String} href a tag href
1649  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1650  * @cfg {String} html the content of the link.
1651  * @cfg {String} anchor name for the anchor link
1652  * @cfg {String} fa - favicon
1653
1654  * @cfg {Boolean} preventDefault (true | false) default false
1655
1656  * 
1657  * @constructor
1658  * Create a new Input
1659  * @param {Object} config The config object
1660  */
1661
1662 Roo.bootstrap.Link = function(config){
1663     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1664     
1665     this.addEvents({
1666         // img events
1667         /**
1668          * @event click
1669          * The img click event for the img.
1670          * @param {Roo.EventObject} e
1671          */
1672         "click" : true
1673     });
1674 };
1675
1676 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1677     
1678     href: false,
1679     target: false,
1680     preventDefault: false,
1681     anchor : false,
1682     alt : false,
1683     fa: false,
1684
1685
1686     getAutoCreate : function()
1687     {
1688         var html = this.html || '';
1689         
1690         if (this.fa !== false) {
1691             html = '<i class="fa fa-' + this.fa + '"></i>';
1692         }
1693         var cfg = {
1694             tag: 'a'
1695         };
1696         // anchor's do not require html/href...
1697         if (this.anchor === false) {
1698             cfg.html = html;
1699             cfg.href = this.href || '#';
1700         } else {
1701             cfg.name = this.anchor;
1702             if (this.html !== false || this.fa !== false) {
1703                 cfg.html = html;
1704             }
1705             if (this.href !== false) {
1706                 cfg.href = this.href;
1707             }
1708         }
1709         
1710         if(this.alt !== false){
1711             cfg.alt = this.alt;
1712         }
1713         
1714         
1715         if(this.target !== false) {
1716             cfg.target = this.target;
1717         }
1718         
1719         return cfg;
1720     },
1721     
1722     initEvents: function() {
1723         
1724         if(!this.href || this.preventDefault){
1725             this.el.on('click', this.onClick, this);
1726         }
1727     },
1728     
1729     onClick : function(e)
1730     {
1731         if(this.preventDefault){
1732             e.preventDefault();
1733         }
1734         //Roo.log('img onclick');
1735         this.fireEvent('click', this, e);
1736     }
1737    
1738 });
1739
1740  /*
1741  * - LGPL
1742  *
1743  * header
1744  * 
1745  */
1746
1747 /**
1748  * @class Roo.bootstrap.Header
1749  * @extends Roo.bootstrap.Component
1750  * Bootstrap Header class
1751  * @cfg {String} html content of header
1752  * @cfg {Number} level (1|2|3|4|5|6) default 1
1753  * 
1754  * @constructor
1755  * Create a new Header
1756  * @param {Object} config The config object
1757  */
1758
1759
1760 Roo.bootstrap.Header  = function(config){
1761     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1762 };
1763
1764 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1765     
1766     //href : false,
1767     html : false,
1768     level : 1,
1769     
1770     
1771     
1772     getAutoCreate : function(){
1773         
1774         
1775         
1776         var cfg = {
1777             tag: 'h' + (1 *this.level),
1778             html: this.html || ''
1779         } ;
1780         
1781         return cfg;
1782     }
1783    
1784 });
1785
1786  
1787
1788  /*
1789  * Based on:
1790  * Ext JS Library 1.1.1
1791  * Copyright(c) 2006-2007, Ext JS, LLC.
1792  *
1793  * Originally Released Under LGPL - original licence link has changed is not relivant.
1794  *
1795  * Fork - LGPL
1796  * <script type="text/javascript">
1797  */
1798  
1799 /**
1800  * @class Roo.bootstrap.MenuMgr
1801  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1802  * @singleton
1803  */
1804 Roo.bootstrap.MenuMgr = function(){
1805    var menus, active, groups = {}, attached = false, lastShow = new Date();
1806
1807    // private - called when first menu is created
1808    function init(){
1809        menus = {};
1810        active = new Roo.util.MixedCollection();
1811        Roo.get(document).addKeyListener(27, function(){
1812            if(active.length > 0){
1813                hideAll();
1814            }
1815        });
1816    }
1817
1818    // private
1819    function hideAll(){
1820        if(active && active.length > 0){
1821            var c = active.clone();
1822            c.each(function(m){
1823                m.hide();
1824            });
1825        }
1826    }
1827
1828    // private
1829    function onHide(m){
1830        active.remove(m);
1831        if(active.length < 1){
1832            Roo.get(document).un("mouseup", onMouseDown);
1833             
1834            attached = false;
1835        }
1836    }
1837
1838    // private
1839    function onShow(m){
1840        var last = active.last();
1841        lastShow = new Date();
1842        active.add(m);
1843        if(!attached){
1844           Roo.get(document).on("mouseup", onMouseDown);
1845            
1846            attached = true;
1847        }
1848        if(m.parentMenu){
1849           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1850           m.parentMenu.activeChild = m;
1851        }else if(last && last.isVisible()){
1852           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1853        }
1854    }
1855
1856    // private
1857    function onBeforeHide(m){
1858        if(m.activeChild){
1859            m.activeChild.hide();
1860        }
1861        if(m.autoHideTimer){
1862            clearTimeout(m.autoHideTimer);
1863            delete m.autoHideTimer;
1864        }
1865    }
1866
1867    // private
1868    function onBeforeShow(m){
1869        var pm = m.parentMenu;
1870        if(!pm && !m.allowOtherMenus){
1871            hideAll();
1872        }else if(pm && pm.activeChild && active != m){
1873            pm.activeChild.hide();
1874        }
1875    }
1876
1877    // private this should really trigger on mouseup..
1878    function onMouseDown(e){
1879         Roo.log("on Mouse Up");
1880         
1881         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1882             Roo.log("MenuManager hideAll");
1883             hideAll();
1884             e.stopEvent();
1885         }
1886         
1887         
1888    }
1889
1890    // private
1891    function onBeforeCheck(mi, state){
1892        if(state){
1893            var g = groups[mi.group];
1894            for(var i = 0, l = g.length; i < l; i++){
1895                if(g[i] != mi){
1896                    g[i].setChecked(false);
1897                }
1898            }
1899        }
1900    }
1901
1902    return {
1903
1904        /**
1905         * Hides all menus that are currently visible
1906         */
1907        hideAll : function(){
1908             hideAll();  
1909        },
1910
1911        // private
1912        register : function(menu){
1913            if(!menus){
1914                init();
1915            }
1916            menus[menu.id] = menu;
1917            menu.on("beforehide", onBeforeHide);
1918            menu.on("hide", onHide);
1919            menu.on("beforeshow", onBeforeShow);
1920            menu.on("show", onShow);
1921            var g = menu.group;
1922            if(g && menu.events["checkchange"]){
1923                if(!groups[g]){
1924                    groups[g] = [];
1925                }
1926                groups[g].push(menu);
1927                menu.on("checkchange", onCheck);
1928            }
1929        },
1930
1931         /**
1932          * Returns a {@link Roo.menu.Menu} object
1933          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1934          * be used to generate and return a new Menu instance.
1935          */
1936        get : function(menu){
1937            if(typeof menu == "string"){ // menu id
1938                return menus[menu];
1939            }else if(menu.events){  // menu instance
1940                return menu;
1941            }
1942            /*else if(typeof menu.length == 'number'){ // array of menu items?
1943                return new Roo.bootstrap.Menu({items:menu});
1944            }else{ // otherwise, must be a config
1945                return new Roo.bootstrap.Menu(menu);
1946            }
1947            */
1948            return false;
1949        },
1950
1951        // private
1952        unregister : function(menu){
1953            delete menus[menu.id];
1954            menu.un("beforehide", onBeforeHide);
1955            menu.un("hide", onHide);
1956            menu.un("beforeshow", onBeforeShow);
1957            menu.un("show", onShow);
1958            var g = menu.group;
1959            if(g && menu.events["checkchange"]){
1960                groups[g].remove(menu);
1961                menu.un("checkchange", onCheck);
1962            }
1963        },
1964
1965        // private
1966        registerCheckable : function(menuItem){
1967            var g = menuItem.group;
1968            if(g){
1969                if(!groups[g]){
1970                    groups[g] = [];
1971                }
1972                groups[g].push(menuItem);
1973                menuItem.on("beforecheckchange", onBeforeCheck);
1974            }
1975        },
1976
1977        // private
1978        unregisterCheckable : function(menuItem){
1979            var g = menuItem.group;
1980            if(g){
1981                groups[g].remove(menuItem);
1982                menuItem.un("beforecheckchange", onBeforeCheck);
1983            }
1984        }
1985    };
1986 }();/*
1987  * - LGPL
1988  *
1989  * menu
1990  * 
1991  */
1992
1993 /**
1994  * @class Roo.bootstrap.Menu
1995  * @extends Roo.bootstrap.Component
1996  * Bootstrap Menu class - container for MenuItems
1997  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1998  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1999  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
2000  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
2001  * 
2002  * @constructor
2003  * Create a new Menu
2004  * @param {Object} config The config object
2005  */
2006
2007
2008 Roo.bootstrap.Menu = function(config){
2009     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2010     if (this.registerMenu && this.type != 'treeview')  {
2011         Roo.bootstrap.MenuMgr.register(this);
2012     }
2013     this.addEvents({
2014         /**
2015          * @event beforeshow
2016          * Fires before this menu is displayed
2017          * @param {Roo.menu.Menu} this
2018          */
2019         beforeshow : true,
2020         /**
2021          * @event beforehide
2022          * Fires before this menu is hidden
2023          * @param {Roo.menu.Menu} this
2024          */
2025         beforehide : true,
2026         /**
2027          * @event show
2028          * Fires after this menu is displayed
2029          * @param {Roo.menu.Menu} this
2030          */
2031         show : true,
2032         /**
2033          * @event hide
2034          * Fires after this menu is hidden
2035          * @param {Roo.menu.Menu} this
2036          */
2037         hide : true,
2038         /**
2039          * @event click
2040          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2041          * @param {Roo.menu.Menu} this
2042          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2043          * @param {Roo.EventObject} e
2044          */
2045         click : true,
2046         /**
2047          * @event mouseover
2048          * Fires when the mouse is hovering over this menu
2049          * @param {Roo.menu.Menu} this
2050          * @param {Roo.EventObject} e
2051          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2052          */
2053         mouseover : true,
2054         /**
2055          * @event mouseout
2056          * Fires when the mouse exits this menu
2057          * @param {Roo.menu.Menu} this
2058          * @param {Roo.EventObject} e
2059          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2060          */
2061         mouseout : true,
2062         /**
2063          * @event itemclick
2064          * Fires when a menu item contained in this menu is clicked
2065          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2066          * @param {Roo.EventObject} e
2067          */
2068         itemclick: true
2069     });
2070     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2071 };
2072
2073 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2074     
2075    /// html : false,
2076     //align : '',
2077     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2078     type: false,
2079     /**
2080      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2081      */
2082     registerMenu : true,
2083     
2084     menuItems :false, // stores the menu items..
2085     
2086     hidden:true,
2087         
2088     parentMenu : false,
2089     
2090     stopEvent : true,
2091     
2092     isLink : false,
2093     
2094     getChildContainer : function() {
2095         return this.el;  
2096     },
2097     
2098     getAutoCreate : function(){
2099          
2100         //if (['right'].indexOf(this.align)!==-1) {
2101         //    cfg.cn[1].cls += ' pull-right'
2102         //}
2103         
2104         
2105         var cfg = {
2106             tag : 'ul',
2107             cls : 'dropdown-menu' ,
2108             style : 'z-index:1000'
2109             
2110         };
2111         
2112         if (this.type === 'submenu') {
2113             cfg.cls = 'submenu active';
2114         }
2115         if (this.type === 'treeview') {
2116             cfg.cls = 'treeview-menu';
2117         }
2118         
2119         return cfg;
2120     },
2121     initEvents : function() {
2122         
2123        // Roo.log("ADD event");
2124        // Roo.log(this.triggerEl.dom);
2125         
2126         this.triggerEl.on('click', this.onTriggerClick, this);
2127         
2128         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2129         
2130         this.triggerEl.addClass('dropdown-toggle');
2131         
2132         if (Roo.isTouch) {
2133             this.el.on('touchstart'  , this.onTouch, this);
2134         }
2135         this.el.on('click' , this.onClick, this);
2136
2137         this.el.on("mouseover", this.onMouseOver, this);
2138         this.el.on("mouseout", this.onMouseOut, this);
2139         
2140     },
2141     
2142     findTargetItem : function(e)
2143     {
2144         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2145         if(!t){
2146             return false;
2147         }
2148         //Roo.log(t);         Roo.log(t.id);
2149         if(t && t.id){
2150             //Roo.log(this.menuitems);
2151             return this.menuitems.get(t.id);
2152             
2153             //return this.items.get(t.menuItemId);
2154         }
2155         
2156         return false;
2157     },
2158     
2159     onTouch : function(e) 
2160     {
2161         Roo.log("menu.onTouch");
2162         //e.stopEvent(); this make the user popdown broken
2163         this.onClick(e);
2164     },
2165     
2166     onClick : function(e)
2167     {
2168         Roo.log("menu.onClick");
2169         
2170         var t = this.findTargetItem(e);
2171         if(!t || t.isContainer){
2172             return;
2173         }
2174         Roo.log(e);
2175         /*
2176         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2177             if(t == this.activeItem && t.shouldDeactivate(e)){
2178                 this.activeItem.deactivate();
2179                 delete this.activeItem;
2180                 return;
2181             }
2182             if(t.canActivate){
2183                 this.setActiveItem(t, true);
2184             }
2185             return;
2186             
2187             
2188         }
2189         */
2190        
2191         Roo.log('pass click event');
2192         
2193         t.onClick(e);
2194         
2195         this.fireEvent("click", this, t, e);
2196         
2197         var _this = this;
2198         
2199         if(!t.href.length || t.href == '#'){
2200             (function() { _this.hide(); }).defer(100);
2201         }
2202         
2203     },
2204     
2205     onMouseOver : function(e){
2206         var t  = this.findTargetItem(e);
2207         //Roo.log(t);
2208         //if(t){
2209         //    if(t.canActivate && !t.disabled){
2210         //        this.setActiveItem(t, true);
2211         //    }
2212         //}
2213         
2214         this.fireEvent("mouseover", this, e, t);
2215     },
2216     isVisible : function(){
2217         return !this.hidden;
2218     },
2219      onMouseOut : function(e){
2220         var t  = this.findTargetItem(e);
2221         
2222         //if(t ){
2223         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2224         //        this.activeItem.deactivate();
2225         //        delete this.activeItem;
2226         //    }
2227         //}
2228         this.fireEvent("mouseout", this, e, t);
2229     },
2230     
2231     
2232     /**
2233      * Displays this menu relative to another element
2234      * @param {String/HTMLElement/Roo.Element} element The element to align to
2235      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2236      * the element (defaults to this.defaultAlign)
2237      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2238      */
2239     show : function(el, pos, parentMenu){
2240         this.parentMenu = parentMenu;
2241         if(!this.el){
2242             this.render();
2243         }
2244         this.fireEvent("beforeshow", this);
2245         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2246     },
2247      /**
2248      * Displays this menu at a specific xy position
2249      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2250      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2251      */
2252     showAt : function(xy, parentMenu, /* private: */_e){
2253         this.parentMenu = parentMenu;
2254         if(!this.el){
2255             this.render();
2256         }
2257         if(_e !== false){
2258             this.fireEvent("beforeshow", this);
2259             //xy = this.el.adjustForConstraints(xy);
2260         }
2261         
2262         //this.el.show();
2263         this.hideMenuItems();
2264         this.hidden = false;
2265         this.triggerEl.addClass('open');
2266         
2267         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2268             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2269         }
2270         
2271         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2272             this.el.setXY(xy);
2273         }
2274         
2275         this.focus();
2276         this.fireEvent("show", this);
2277     },
2278     
2279     focus : function(){
2280         return;
2281         if(!this.hidden){
2282             this.doFocus.defer(50, this);
2283         }
2284     },
2285
2286     doFocus : function(){
2287         if(!this.hidden){
2288             this.focusEl.focus();
2289         }
2290     },
2291
2292     /**
2293      * Hides this menu and optionally all parent menus
2294      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2295      */
2296     hide : function(deep)
2297     {
2298         
2299         this.hideMenuItems();
2300         if(this.el && this.isVisible()){
2301             this.fireEvent("beforehide", this);
2302             if(this.activeItem){
2303                 this.activeItem.deactivate();
2304                 this.activeItem = null;
2305             }
2306             this.triggerEl.removeClass('open');;
2307             this.hidden = true;
2308             this.fireEvent("hide", this);
2309         }
2310         if(deep === true && this.parentMenu){
2311             this.parentMenu.hide(true);
2312         }
2313     },
2314     
2315     onTriggerClick : function(e)
2316     {
2317         Roo.log('trigger click');
2318         
2319         var target = e.getTarget();
2320         
2321         Roo.log(target.nodeName.toLowerCase());
2322         
2323         if(target.nodeName.toLowerCase() === 'i'){
2324             e.preventDefault();
2325         }
2326         
2327     },
2328     
2329     onTriggerPress  : function(e)
2330     {
2331         Roo.log('trigger press');
2332         //Roo.log(e.getTarget());
2333        // Roo.log(this.triggerEl.dom);
2334        
2335         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2336         var pel = Roo.get(e.getTarget());
2337         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2338             Roo.log('is treeview or dropdown?');
2339             return;
2340         }
2341         
2342         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2343             return;
2344         }
2345         
2346         if (this.isVisible()) {
2347             Roo.log('hide');
2348             this.hide();
2349         } else {
2350             Roo.log('show');
2351             this.show(this.triggerEl, false, false);
2352         }
2353         
2354         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2355             e.stopEvent();
2356         }
2357         
2358     },
2359        
2360     
2361     hideMenuItems : function()
2362     {
2363         Roo.log("hide Menu Items");
2364         if (!this.el) { 
2365             return;
2366         }
2367         //$(backdrop).remove()
2368         this.el.select('.open',true).each(function(aa) {
2369             
2370             aa.removeClass('open');
2371           //var parent = getParent($(this))
2372           //var relatedTarget = { relatedTarget: this }
2373           
2374            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2375           //if (e.isDefaultPrevented()) return
2376            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2377         });
2378     },
2379     addxtypeChild : function (tree, cntr) {
2380         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2381           
2382         this.menuitems.add(comp);
2383         return comp;
2384
2385     },
2386     getEl : function()
2387     {
2388         Roo.log(this.el);
2389         return this.el;
2390     },
2391     
2392     clear : function()
2393     {
2394         this.getEl().dom.innerHTML = '';
2395         this.menuitems.clear();
2396     }
2397 });
2398
2399  
2400  /*
2401  * - LGPL
2402  *
2403  * menu item
2404  * 
2405  */
2406
2407
2408 /**
2409  * @class Roo.bootstrap.MenuItem
2410  * @extends Roo.bootstrap.Component
2411  * Bootstrap MenuItem class
2412  * @cfg {String} html the menu label
2413  * @cfg {String} href the link
2414  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2415  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2416  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2417  * @cfg {String} fa favicon to show on left of menu item.
2418  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2419  * 
2420  * 
2421  * @constructor
2422  * Create a new MenuItem
2423  * @param {Object} config The config object
2424  */
2425
2426
2427 Roo.bootstrap.MenuItem = function(config){
2428     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2429     this.addEvents({
2430         // raw events
2431         /**
2432          * @event click
2433          * The raw click event for the entire grid.
2434          * @param {Roo.bootstrap.MenuItem} this
2435          * @param {Roo.EventObject} e
2436          */
2437         "click" : true
2438     });
2439 };
2440
2441 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2442     
2443     href : false,
2444     html : false,
2445     preventDefault: false,
2446     isContainer : false,
2447     active : false,
2448     fa: false,
2449     
2450     getAutoCreate : function(){
2451         
2452         if(this.isContainer){
2453             return {
2454                 tag: 'li',
2455                 cls: 'dropdown-menu-item'
2456             };
2457         }
2458         var ctag = {
2459             tag: 'span',
2460             html: 'Link'
2461         };
2462         
2463         var anc = {
2464             tag : 'a',
2465             href : '#',
2466             cn : [  ]
2467         };
2468         
2469         if (this.fa !== false) {
2470             anc.cn.push({
2471                 tag : 'i',
2472                 cls : 'fa fa-' + this.fa
2473             });
2474         }
2475         
2476         anc.cn.push(ctag);
2477         
2478         
2479         var cfg= {
2480             tag: 'li',
2481             cls: 'dropdown-menu-item',
2482             cn: [ anc ]
2483         };
2484         if (this.parent().type == 'treeview') {
2485             cfg.cls = 'treeview-menu';
2486         }
2487         if (this.active) {
2488             cfg.cls += ' active';
2489         }
2490         
2491         
2492         
2493         anc.href = this.href || cfg.cn[0].href ;
2494         ctag.html = this.html || cfg.cn[0].html ;
2495         return cfg;
2496     },
2497     
2498     initEvents: function()
2499     {
2500         if (this.parent().type == 'treeview') {
2501             this.el.select('a').on('click', this.onClick, this);
2502         }
2503         
2504         if (this.menu) {
2505             this.menu.parentType = this.xtype;
2506             this.menu.triggerEl = this.el;
2507             this.menu = this.addxtype(Roo.apply({}, this.menu));
2508         }
2509         
2510     },
2511     onClick : function(e)
2512     {
2513         Roo.log('item on click ');
2514         
2515         if(this.preventDefault){
2516             e.preventDefault();
2517         }
2518         //this.parent().hideMenuItems();
2519         
2520         this.fireEvent('click', this, e);
2521     },
2522     getEl : function()
2523     {
2524         return this.el;
2525     } 
2526 });
2527
2528  
2529
2530  /*
2531  * - LGPL
2532  *
2533  * menu separator
2534  * 
2535  */
2536
2537
2538 /**
2539  * @class Roo.bootstrap.MenuSeparator
2540  * @extends Roo.bootstrap.Component
2541  * Bootstrap MenuSeparator class
2542  * 
2543  * @constructor
2544  * Create a new MenuItem
2545  * @param {Object} config The config object
2546  */
2547
2548
2549 Roo.bootstrap.MenuSeparator = function(config){
2550     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2551 };
2552
2553 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2554     
2555     getAutoCreate : function(){
2556         var cfg = {
2557             cls: 'divider',
2558             tag : 'li'
2559         };
2560         
2561         return cfg;
2562     }
2563    
2564 });
2565
2566  
2567
2568  
2569 /*
2570 * Licence: LGPL
2571 */
2572
2573 /**
2574  * @class Roo.bootstrap.Modal
2575  * @extends Roo.bootstrap.Component
2576  * Bootstrap Modal class
2577  * @cfg {String} title Title of dialog
2578  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2579  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2580  * @cfg {Boolean} specificTitle default false
2581  * @cfg {Array} buttons Array of buttons or standard button set..
2582  * @cfg {String} buttonPosition (left|right|center) default right
2583  * @cfg {Boolean} animate default true
2584  * @cfg {Boolean} allow_close default true
2585  * @cfg {Boolean} fitwindow default false
2586  * @cfg {String} size (sm|lg) default empty
2587  *
2588  *
2589  * @constructor
2590  * Create a new Modal Dialog
2591  * @param {Object} config The config object
2592  */
2593
2594 Roo.bootstrap.Modal = function(config){
2595     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2596     this.addEvents({
2597         // raw events
2598         /**
2599          * @event btnclick
2600          * The raw btnclick event for the button
2601          * @param {Roo.EventObject} e
2602          */
2603         "btnclick" : true,
2604         /**
2605          * @event resize
2606          * Fire when dialog resize
2607          * @param {Roo.bootstrap.Modal} this
2608          * @param {Roo.EventObject} e
2609          */
2610         "resize" : true
2611     });
2612     this.buttons = this.buttons || [];
2613
2614     if (this.tmpl) {
2615         this.tmpl = Roo.factory(this.tmpl);
2616     }
2617
2618 };
2619
2620 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2621
2622     title : 'test dialog',
2623
2624     buttons : false,
2625
2626     // set on load...
2627
2628     html: false,
2629
2630     tmp: false,
2631
2632     specificTitle: false,
2633
2634     buttonPosition: 'right',
2635
2636     allow_close : true,
2637
2638     animate : true,
2639
2640     fitwindow: false,
2641
2642
2643      // private
2644     dialogEl: false,
2645     bodyEl:  false,
2646     footerEl:  false,
2647     titleEl:  false,
2648     closeEl:  false,
2649
2650     size: '',
2651
2652
2653     onRender : function(ct, position)
2654     {
2655         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2656
2657         if(!this.el){
2658             var cfg = Roo.apply({},  this.getAutoCreate());
2659             cfg.id = Roo.id();
2660             //if(!cfg.name){
2661             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2662             //}
2663             //if (!cfg.name.length) {
2664             //    delete cfg.name;
2665            // }
2666             if (this.cls) {
2667                 cfg.cls += ' ' + this.cls;
2668             }
2669             if (this.style) {
2670                 cfg.style = this.style;
2671             }
2672             this.el = Roo.get(document.body).createChild(cfg, position);
2673         }
2674         //var type = this.el.dom.type;
2675
2676
2677         if(this.tabIndex !== undefined){
2678             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2679         }
2680
2681         this.dialogEl = this.el.select('.modal-dialog',true).first();
2682         this.bodyEl = this.el.select('.modal-body',true).first();
2683         this.closeEl = this.el.select('.modal-header .close', true).first();
2684         this.headerEl = this.el.select('.modal-header',true).first();
2685         this.titleEl = this.el.select('.modal-title',true).first();
2686         this.footerEl = this.el.select('.modal-footer',true).first();
2687
2688         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2689         this.maskEl.enableDisplayMode("block");
2690         this.maskEl.hide();
2691         //this.el.addClass("x-dlg-modal");
2692
2693         if (this.buttons.length) {
2694             Roo.each(this.buttons, function(bb) {
2695                 var b = Roo.apply({}, bb);
2696                 b.xns = b.xns || Roo.bootstrap;
2697                 b.xtype = b.xtype || 'Button';
2698                 if (typeof(b.listeners) == 'undefined') {
2699                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2700                 }
2701
2702                 var btn = Roo.factory(b);
2703
2704                 btn.render(this.el.select('.modal-footer div').first());
2705
2706             },this);
2707         }
2708         // render the children.
2709         var nitems = [];
2710
2711         if(typeof(this.items) != 'undefined'){
2712             var items = this.items;
2713             delete this.items;
2714
2715             for(var i =0;i < items.length;i++) {
2716                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2717             }
2718         }
2719
2720         this.items = nitems;
2721
2722         // where are these used - they used to be body/close/footer
2723
2724
2725         this.initEvents();
2726         //this.el.addClass([this.fieldClass, this.cls]);
2727
2728     },
2729
2730     getAutoCreate : function(){
2731
2732
2733         var bdy = {
2734                 cls : 'modal-body',
2735                 html : this.html || ''
2736         };
2737
2738         var title = {
2739             tag: 'h4',
2740             cls : 'modal-title',
2741             html : this.title
2742         };
2743
2744         if(this.specificTitle){
2745             title = this.title;
2746
2747         };
2748
2749         var header = [];
2750         if (this.allow_close) {
2751             header.push({
2752                 tag: 'button',
2753                 cls : 'close',
2754                 html : '&times'
2755             });
2756         }
2757
2758         header.push(title);
2759
2760         var size = '';
2761
2762         if(this.size.length){
2763             size = 'modal-' + this.size;
2764         }
2765
2766         var modal = {
2767             cls: "modal",
2768             style : 'display: none',
2769             cn : [
2770                 {
2771                     cls: "modal-dialog " + size,
2772                     cn : [
2773                         {
2774                             cls : "modal-content",
2775                             cn : [
2776                                 {
2777                                     cls : 'modal-header',
2778                                     cn : header
2779                                 },
2780                                 bdy,
2781                                 {
2782                                     cls : 'modal-footer',
2783                                     cn : [
2784                                         {
2785                                             tag: 'div',
2786                                             cls: 'btn-' + this.buttonPosition
2787                                         }
2788                                     ]
2789
2790                                 }
2791
2792
2793                             ]
2794
2795                         }
2796                     ]
2797
2798                 }
2799             ]
2800         };
2801
2802         if(this.animate){
2803             modal.cls += ' fade';
2804         }
2805
2806         return modal;
2807
2808     },
2809     getChildContainer : function() {
2810
2811          return this.bodyEl;
2812
2813     },
2814     getButtonContainer : function() {
2815          return this.el.select('.modal-footer div',true).first();
2816
2817     },
2818     initEvents : function()
2819     {
2820         if (this.allow_close) {
2821             this.closeEl.on('click', this.hide, this);
2822         }
2823         Roo.EventManager.onWindowResize(this.resize, this, true);
2824
2825
2826     },
2827
2828     resize : function()
2829     {
2830         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),  Roo.lib.Dom.getViewHeight(true));
2831         if (this.fitwindow) {
2832             var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2833             var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2834             this.setSize(w,h);
2835         }
2836     },
2837
2838     setSize : function(w,h)
2839     {
2840         if (!w && !h) {
2841             return;
2842         }
2843         this.resizeTo(w,h);
2844     },
2845
2846     show : function() {
2847
2848         if (!this.rendered) {
2849             this.render();
2850         }
2851
2852         this.el.setStyle('display', 'block');
2853
2854         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2855             var _this = this;
2856             (function(){
2857                 this.el.addClass('in');
2858             }).defer(50, this);
2859         }else{
2860             this.el.addClass('in');
2861
2862         }
2863
2864         // not sure how we can show data in here..
2865         //if (this.tmpl) {
2866         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2867         //}
2868
2869         Roo.get(document.body).addClass("x-body-masked");
2870         
2871         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2872         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2873         this.maskEl.show();
2874         
2875         this.resize();
2876         
2877         this.fireEvent('show', this);
2878
2879         // set zindex here - otherwise it appears to be ignored...
2880         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2881
2882         (function () {
2883             this.items.forEach( function(e) {
2884                 e.layout ? e.layout() : false;
2885
2886             });
2887         }).defer(100,this);
2888
2889     },
2890     hide : function()
2891     {
2892         if(this.fireEvent("beforehide", this) !== false){
2893             this.maskEl.hide();
2894             Roo.get(document.body).removeClass("x-body-masked");
2895             this.el.removeClass('in');
2896             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2897
2898             if(this.animate){ // why
2899                 var _this = this;
2900                 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2901             }else{
2902                 this.el.setStyle('display', 'none');
2903             }
2904             this.fireEvent('hide', this);
2905         }
2906     },
2907
2908     addButton : function(str, cb)
2909     {
2910
2911
2912         var b = Roo.apply({}, { html : str } );
2913         b.xns = b.xns || Roo.bootstrap;
2914         b.xtype = b.xtype || 'Button';
2915         if (typeof(b.listeners) == 'undefined') {
2916             b.listeners = { click : cb.createDelegate(this)  };
2917         }
2918
2919         var btn = Roo.factory(b);
2920
2921         btn.render(this.el.select('.modal-footer div').first());
2922
2923         return btn;
2924
2925     },
2926
2927     setDefaultButton : function(btn)
2928     {
2929         //this.el.select('.modal-footer').()
2930     },
2931     diff : false,
2932
2933     resizeTo: function(w,h)
2934     {
2935         // skip.. ?? why??
2936
2937         this.dialogEl.setWidth(w);
2938         if (this.diff === false) {
2939             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2940         }
2941
2942         this.bodyEl.setHeight(h-this.diff);
2943
2944         this.fireEvent('resize', this);
2945
2946     },
2947     setContentSize  : function(w, h)
2948     {
2949
2950     },
2951     onButtonClick: function(btn,e)
2952     {
2953         //Roo.log([a,b,c]);
2954         this.fireEvent('btnclick', btn.name, e);
2955     },
2956      /**
2957      * Set the title of the Dialog
2958      * @param {String} str new Title
2959      */
2960     setTitle: function(str) {
2961         this.titleEl.dom.innerHTML = str;
2962     },
2963     /**
2964      * Set the body of the Dialog
2965      * @param {String} str new Title
2966      */
2967     setBody: function(str) {
2968         this.bodyEl.dom.innerHTML = str;
2969     },
2970     /**
2971      * Set the body of the Dialog using the template
2972      * @param {Obj} data - apply this data to the template and replace the body contents.
2973      */
2974     applyBody: function(obj)
2975     {
2976         if (!this.tmpl) {
2977             Roo.log("Error - using apply Body without a template");
2978             //code
2979         }
2980         this.tmpl.overwrite(this.bodyEl, obj);
2981     }
2982
2983 });
2984
2985
2986 Roo.apply(Roo.bootstrap.Modal,  {
2987     /**
2988          * Button config that displays a single OK button
2989          * @type Object
2990          */
2991         OK :  [{
2992             name : 'ok',
2993             weight : 'primary',
2994             html : 'OK'
2995         }],
2996         /**
2997          * Button config that displays Yes and No buttons
2998          * @type Object
2999          */
3000         YESNO : [
3001             {
3002                 name  : 'no',
3003                 html : 'No'
3004             },
3005             {
3006                 name  :'yes',
3007                 weight : 'primary',
3008                 html : 'Yes'
3009             }
3010         ],
3011
3012         /**
3013          * Button config that displays OK and Cancel buttons
3014          * @type Object
3015          */
3016         OKCANCEL : [
3017             {
3018                name : 'cancel',
3019                 html : 'Cancel'
3020             },
3021             {
3022                 name : 'ok',
3023                 weight : 'primary',
3024                 html : 'OK'
3025             }
3026         ],
3027         /**
3028          * Button config that displays Yes, No and Cancel buttons
3029          * @type Object
3030          */
3031         YESNOCANCEL : [
3032             {
3033                 name : 'yes',
3034                 weight : 'primary',
3035                 html : 'Yes'
3036             },
3037             {
3038                 name : 'no',
3039                 html : 'No'
3040             },
3041             {
3042                 name : 'cancel',
3043                 html : 'Cancel'
3044             }
3045         ],
3046         
3047         zIndex : 10001
3048 });
3049 /*
3050  * - LGPL
3051  *
3052  * messagebox - can be used as a replace
3053  * 
3054  */
3055 /**
3056  * @class Roo.MessageBox
3057  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3058  * Example usage:
3059  *<pre><code>
3060 // Basic alert:
3061 Roo.Msg.alert('Status', 'Changes saved successfully.');
3062
3063 // Prompt for user data:
3064 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3065     if (btn == 'ok'){
3066         // process text value...
3067     }
3068 });
3069
3070 // Show a dialog using config options:
3071 Roo.Msg.show({
3072    title:'Save Changes?',
3073    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3074    buttons: Roo.Msg.YESNOCANCEL,
3075    fn: processResult,
3076    animEl: 'elId'
3077 });
3078 </code></pre>
3079  * @singleton
3080  */
3081 Roo.bootstrap.MessageBox = function(){
3082     var dlg, opt, mask, waitTimer;
3083     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3084     var buttons, activeTextEl, bwidth;
3085
3086     
3087     // private
3088     var handleButton = function(button){
3089         dlg.hide();
3090         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3091     };
3092
3093     // private
3094     var handleHide = function(){
3095         if(opt && opt.cls){
3096             dlg.el.removeClass(opt.cls);
3097         }
3098         //if(waitTimer){
3099         //    Roo.TaskMgr.stop(waitTimer);
3100         //    waitTimer = null;
3101         //}
3102     };
3103
3104     // private
3105     var updateButtons = function(b){
3106         var width = 0;
3107         if(!b){
3108             buttons["ok"].hide();
3109             buttons["cancel"].hide();
3110             buttons["yes"].hide();
3111             buttons["no"].hide();
3112             //dlg.footer.dom.style.display = 'none';
3113             return width;
3114         }
3115         dlg.footerEl.dom.style.display = '';
3116         for(var k in buttons){
3117             if(typeof buttons[k] != "function"){
3118                 if(b[k]){
3119                     buttons[k].show();
3120                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3121                     width += buttons[k].el.getWidth()+15;
3122                 }else{
3123                     buttons[k].hide();
3124                 }
3125             }
3126         }
3127         return width;
3128     };
3129
3130     // private
3131     var handleEsc = function(d, k, e){
3132         if(opt && opt.closable !== false){
3133             dlg.hide();
3134         }
3135         if(e){
3136             e.stopEvent();
3137         }
3138     };
3139
3140     return {
3141         /**
3142          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3143          * @return {Roo.BasicDialog} The BasicDialog element
3144          */
3145         getDialog : function(){
3146            if(!dlg){
3147                 dlg = new Roo.bootstrap.Modal( {
3148                     //draggable: true,
3149                     //resizable:false,
3150                     //constraintoviewport:false,
3151                     //fixedcenter:true,
3152                     //collapsible : false,
3153                     //shim:true,
3154                     //modal: true,
3155                 //    width: 'auto',
3156                   //  height:100,
3157                     //buttonAlign:"center",
3158                     closeClick : function(){
3159                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3160                             handleButton("no");
3161                         }else{
3162                             handleButton("cancel");
3163                         }
3164                     }
3165                 });
3166                 dlg.render();
3167                 dlg.on("hide", handleHide);
3168                 mask = dlg.mask;
3169                 //dlg.addKeyListener(27, handleEsc);
3170                 buttons = {};
3171                 this.buttons = buttons;
3172                 var bt = this.buttonText;
3173                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3174                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3175                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3176                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3177                 //Roo.log(buttons);
3178                 bodyEl = dlg.bodyEl.createChild({
3179
3180                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3181                         '<textarea class="roo-mb-textarea"></textarea>' +
3182                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3183                 });
3184                 msgEl = bodyEl.dom.firstChild;
3185                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3186                 textboxEl.enableDisplayMode();
3187                 textboxEl.addKeyListener([10,13], function(){
3188                     if(dlg.isVisible() && opt && opt.buttons){
3189                         if(opt.buttons.ok){
3190                             handleButton("ok");
3191                         }else if(opt.buttons.yes){
3192                             handleButton("yes");
3193                         }
3194                     }
3195                 });
3196                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3197                 textareaEl.enableDisplayMode();
3198                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3199                 progressEl.enableDisplayMode();
3200                 
3201                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3202                 var pf = progressEl.dom.firstChild;
3203                 if (pf) {
3204                     pp = Roo.get(pf.firstChild);
3205                     pp.setHeight(pf.offsetHeight);
3206                 }
3207                 
3208             }
3209             return dlg;
3210         },
3211
3212         /**
3213          * Updates the message box body text
3214          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3215          * the XHTML-compliant non-breaking space character '&amp;#160;')
3216          * @return {Roo.MessageBox} This message box
3217          */
3218         updateText : function(text)
3219         {
3220             if(!dlg.isVisible() && !opt.width){
3221                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3222                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3223             }
3224             msgEl.innerHTML = text || '&#160;';
3225       
3226             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3227             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3228             var w = Math.max(
3229                     Math.min(opt.width || cw , this.maxWidth), 
3230                     Math.max(opt.minWidth || this.minWidth, bwidth)
3231             );
3232             if(opt.prompt){
3233                 activeTextEl.setWidth(w);
3234             }
3235             if(dlg.isVisible()){
3236                 dlg.fixedcenter = false;
3237             }
3238             // to big, make it scroll. = But as usual stupid IE does not support
3239             // !important..
3240             
3241             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3242                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3243                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3244             } else {
3245                 bodyEl.dom.style.height = '';
3246                 bodyEl.dom.style.overflowY = '';
3247             }
3248             if (cw > w) {
3249                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3250             } else {
3251                 bodyEl.dom.style.overflowX = '';
3252             }
3253             
3254             dlg.setContentSize(w, bodyEl.getHeight());
3255             if(dlg.isVisible()){
3256                 dlg.fixedcenter = true;
3257             }
3258             return this;
3259         },
3260
3261         /**
3262          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3263          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3264          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3265          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3266          * @return {Roo.MessageBox} This message box
3267          */
3268         updateProgress : function(value, text){
3269             if(text){
3270                 this.updateText(text);
3271             }
3272             
3273             if (pp) { // weird bug on my firefox - for some reason this is not defined
3274                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3275                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3276             }
3277             return this;
3278         },        
3279
3280         /**
3281          * Returns true if the message box is currently displayed
3282          * @return {Boolean} True if the message box is visible, else false
3283          */
3284         isVisible : function(){
3285             return dlg && dlg.isVisible();  
3286         },
3287
3288         /**
3289          * Hides the message box if it is displayed
3290          */
3291         hide : function(){
3292             if(this.isVisible()){
3293                 dlg.hide();
3294             }  
3295         },
3296
3297         /**
3298          * Displays a new message box, or reinitializes an existing message box, based on the config options
3299          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3300          * The following config object properties are supported:
3301          * <pre>
3302 Property    Type             Description
3303 ----------  ---------------  ------------------------------------------------------------------------------------
3304 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3305                                    closes (defaults to undefined)
3306 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3307                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3308 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3309                                    progress and wait dialogs will ignore this property and always hide the
3310                                    close button as they can only be closed programmatically.
3311 cls               String           A custom CSS class to apply to the message box element
3312 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3313                                    displayed (defaults to 75)
3314 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3315                                    function will be btn (the name of the button that was clicked, if applicable,
3316                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3317                                    Progress and wait dialogs will ignore this option since they do not respond to
3318                                    user actions and can only be closed programmatically, so any required function
3319                                    should be called by the same code after it closes the dialog.
3320 icon              String           A CSS class that provides a background image to be used as an icon for
3321                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3322 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3323 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3324 modal             Boolean          False to allow user interaction with the page while the message box is
3325                                    displayed (defaults to true)
3326 msg               String           A string that will replace the existing message box body text (defaults
3327                                    to the XHTML-compliant non-breaking space character '&#160;')
3328 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3329 progress          Boolean          True to display a progress bar (defaults to false)
3330 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3331 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3332 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3333 title             String           The title text
3334 value             String           The string value to set into the active textbox element if displayed
3335 wait              Boolean          True to display a progress bar (defaults to false)
3336 width             Number           The width of the dialog in pixels
3337 </pre>
3338          *
3339          * Example usage:
3340          * <pre><code>
3341 Roo.Msg.show({
3342    title: 'Address',
3343    msg: 'Please enter your address:',
3344    width: 300,
3345    buttons: Roo.MessageBox.OKCANCEL,
3346    multiline: true,
3347    fn: saveAddress,
3348    animEl: 'addAddressBtn'
3349 });
3350 </code></pre>
3351          * @param {Object} config Configuration options
3352          * @return {Roo.MessageBox} This message box
3353          */
3354         show : function(options)
3355         {
3356             
3357             // this causes nightmares if you show one dialog after another
3358             // especially on callbacks..
3359              
3360             if(this.isVisible()){
3361                 
3362                 this.hide();
3363                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3364                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3365                 Roo.log("New Dialog Message:" +  options.msg )
3366                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3367                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3368                 
3369             }
3370             var d = this.getDialog();
3371             opt = options;
3372             d.setTitle(opt.title || "&#160;");
3373             d.closeEl.setDisplayed(opt.closable !== false);
3374             activeTextEl = textboxEl;
3375             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3376             if(opt.prompt){
3377                 if(opt.multiline){
3378                     textboxEl.hide();
3379                     textareaEl.show();
3380                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3381                         opt.multiline : this.defaultTextHeight);
3382                     activeTextEl = textareaEl;
3383                 }else{
3384                     textboxEl.show();
3385                     textareaEl.hide();
3386                 }
3387             }else{
3388                 textboxEl.hide();
3389                 textareaEl.hide();
3390             }
3391             progressEl.setDisplayed(opt.progress === true);
3392             this.updateProgress(0);
3393             activeTextEl.dom.value = opt.value || "";
3394             if(opt.prompt){
3395                 dlg.setDefaultButton(activeTextEl);
3396             }else{
3397                 var bs = opt.buttons;
3398                 var db = null;
3399                 if(bs && bs.ok){
3400                     db = buttons["ok"];
3401                 }else if(bs && bs.yes){
3402                     db = buttons["yes"];
3403                 }
3404                 dlg.setDefaultButton(db);
3405             }
3406             bwidth = updateButtons(opt.buttons);
3407             this.updateText(opt.msg);
3408             if(opt.cls){
3409                 d.el.addClass(opt.cls);
3410             }
3411             d.proxyDrag = opt.proxyDrag === true;
3412             d.modal = opt.modal !== false;
3413             d.mask = opt.modal !== false ? mask : false;
3414             if(!d.isVisible()){
3415                 // force it to the end of the z-index stack so it gets a cursor in FF
3416                 document.body.appendChild(dlg.el.dom);
3417                 d.animateTarget = null;
3418                 d.show(options.animEl);
3419             }
3420             return this;
3421         },
3422
3423         /**
3424          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3425          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3426          * and closing the message box when the process is complete.
3427          * @param {String} title The title bar text
3428          * @param {String} msg The message box body text
3429          * @return {Roo.MessageBox} This message box
3430          */
3431         progress : function(title, msg){
3432             this.show({
3433                 title : title,
3434                 msg : msg,
3435                 buttons: false,
3436                 progress:true,
3437                 closable:false,
3438                 minWidth: this.minProgressWidth,
3439                 modal : true
3440             });
3441             return this;
3442         },
3443
3444         /**
3445          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3446          * If a callback function is passed it will be called after the user clicks the button, and the
3447          * id of the button that was clicked will be passed as the only parameter to the callback
3448          * (could also be the top-right close button).
3449          * @param {String} title The title bar text
3450          * @param {String} msg The message box body text
3451          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3452          * @param {Object} scope (optional) The scope of the callback function
3453          * @return {Roo.MessageBox} This message box
3454          */
3455         alert : function(title, msg, fn, scope)
3456         {
3457             this.show({
3458                 title : title,
3459                 msg : msg,
3460                 buttons: this.OK,
3461                 fn: fn,
3462                 closable : false,
3463                 scope : scope,
3464                 modal : true
3465             });
3466             return this;
3467         },
3468
3469         /**
3470          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3471          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3472          * You are responsible for closing the message box when the process is complete.
3473          * @param {String} msg The message box body text
3474          * @param {String} title (optional) The title bar text
3475          * @return {Roo.MessageBox} This message box
3476          */
3477         wait : function(msg, title){
3478             this.show({
3479                 title : title,
3480                 msg : msg,
3481                 buttons: false,
3482                 closable:false,
3483                 progress:true,
3484                 modal:true,
3485                 width:300,
3486                 wait:true
3487             });
3488             waitTimer = Roo.TaskMgr.start({
3489                 run: function(i){
3490                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3491                 },
3492                 interval: 1000
3493             });
3494             return this;
3495         },
3496
3497         /**
3498          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3499          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3500          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3501          * @param {String} title The title bar text
3502          * @param {String} msg The message box body text
3503          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3504          * @param {Object} scope (optional) The scope of the callback function
3505          * @return {Roo.MessageBox} This message box
3506          */
3507         confirm : function(title, msg, fn, scope){
3508             this.show({
3509                 title : title,
3510                 msg : msg,
3511                 buttons: this.YESNO,
3512                 fn: fn,
3513                 scope : scope,
3514                 modal : true
3515             });
3516             return this;
3517         },
3518
3519         /**
3520          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3521          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3522          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3523          * (could also be the top-right close button) and the text that was entered will be passed as the two
3524          * parameters to the callback.
3525          * @param {String} title The title bar text
3526          * @param {String} msg The message box body text
3527          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3528          * @param {Object} scope (optional) The scope of the callback function
3529          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3530          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3531          * @return {Roo.MessageBox} This message box
3532          */
3533         prompt : function(title, msg, fn, scope, multiline){
3534             this.show({
3535                 title : title,
3536                 msg : msg,
3537                 buttons: this.OKCANCEL,
3538                 fn: fn,
3539                 minWidth:250,
3540                 scope : scope,
3541                 prompt:true,
3542                 multiline: multiline,
3543                 modal : true
3544             });
3545             return this;
3546         },
3547
3548         /**
3549          * Button config that displays a single OK button
3550          * @type Object
3551          */
3552         OK : {ok:true},
3553         /**
3554          * Button config that displays Yes and No buttons
3555          * @type Object
3556          */
3557         YESNO : {yes:true, no:true},
3558         /**
3559          * Button config that displays OK and Cancel buttons
3560          * @type Object
3561          */
3562         OKCANCEL : {ok:true, cancel:true},
3563         /**
3564          * Button config that displays Yes, No and Cancel buttons
3565          * @type Object
3566          */
3567         YESNOCANCEL : {yes:true, no:true, cancel:true},
3568
3569         /**
3570          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3571          * @type Number
3572          */
3573         defaultTextHeight : 75,
3574         /**
3575          * The maximum width in pixels of the message box (defaults to 600)
3576          * @type Number
3577          */
3578         maxWidth : 600,
3579         /**
3580          * The minimum width in pixels of the message box (defaults to 100)
3581          * @type Number
3582          */
3583         minWidth : 100,
3584         /**
3585          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3586          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3587          * @type Number
3588          */
3589         minProgressWidth : 250,
3590         /**
3591          * An object containing the default button text strings that can be overriden for localized language support.
3592          * Supported properties are: ok, cancel, yes and no.
3593          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3594          * @type Object
3595          */
3596         buttonText : {
3597             ok : "OK",
3598             cancel : "Cancel",
3599             yes : "Yes",
3600             no : "No"
3601         }
3602     };
3603 }();
3604
3605 /**
3606  * Shorthand for {@link Roo.MessageBox}
3607  */
3608 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3609 Roo.Msg = Roo.Msg || Roo.MessageBox;
3610 /*
3611  * - LGPL
3612  *
3613  * navbar
3614  * 
3615  */
3616
3617 /**
3618  * @class Roo.bootstrap.Navbar
3619  * @extends Roo.bootstrap.Component
3620  * Bootstrap Navbar class
3621
3622  * @constructor
3623  * Create a new Navbar
3624  * @param {Object} config The config object
3625  */
3626
3627
3628 Roo.bootstrap.Navbar = function(config){
3629     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3630     this.addEvents({
3631         // raw events
3632         /**
3633          * @event beforetoggle
3634          * Fire before toggle the menu
3635          * @param {Roo.EventObject} e
3636          */
3637         "beforetoggle" : true
3638     });
3639 };
3640
3641 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3642     
3643     
3644    
3645     // private
3646     navItems : false,
3647     loadMask : false,
3648     
3649     
3650     getAutoCreate : function(){
3651         
3652         
3653         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3654         
3655     },
3656     
3657     initEvents :function ()
3658     {
3659         //Roo.log(this.el.select('.navbar-toggle',true));
3660         this.el.select('.navbar-toggle',true).on('click', function() {
3661             if(this.fireEvent('beforetoggle', this) !== false){
3662                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3663             }
3664             
3665         }, this);
3666         
3667         var mark = {
3668             tag: "div",
3669             cls:"x-dlg-mask"
3670         };
3671         
3672         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3673         
3674         var size = this.el.getSize();
3675         this.maskEl.setSize(size.width, size.height);
3676         this.maskEl.enableDisplayMode("block");
3677         this.maskEl.hide();
3678         
3679         if(this.loadMask){
3680             this.maskEl.show();
3681         }
3682     },
3683     
3684     
3685     getChildContainer : function()
3686     {
3687         if (this.el.select('.collapse').getCount()) {
3688             return this.el.select('.collapse',true).first();
3689         }
3690         
3691         return this.el;
3692     },
3693     
3694     mask : function()
3695     {
3696         this.maskEl.show();
3697     },
3698     
3699     unmask : function()
3700     {
3701         this.maskEl.hide();
3702     } 
3703     
3704     
3705     
3706     
3707 });
3708
3709
3710
3711  
3712
3713  /*
3714  * - LGPL
3715  *
3716  * navbar
3717  * 
3718  */
3719
3720 /**
3721  * @class Roo.bootstrap.NavSimplebar
3722  * @extends Roo.bootstrap.Navbar
3723  * Bootstrap Sidebar class
3724  *
3725  * @cfg {Boolean} inverse is inverted color
3726  * 
3727  * @cfg {String} type (nav | pills | tabs)
3728  * @cfg {Boolean} arrangement stacked | justified
3729  * @cfg {String} align (left | right) alignment
3730  * 
3731  * @cfg {Boolean} main (true|false) main nav bar? default false
3732  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3733  * 
3734  * @cfg {String} tag (header|footer|nav|div) default is nav 
3735
3736  * 
3737  * 
3738  * 
3739  * @constructor
3740  * Create a new Sidebar
3741  * @param {Object} config The config object
3742  */
3743
3744
3745 Roo.bootstrap.NavSimplebar = function(config){
3746     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3747 };
3748
3749 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3750     
3751     inverse: false,
3752     
3753     type: false,
3754     arrangement: '',
3755     align : false,
3756     
3757     
3758     
3759     main : false,
3760     
3761     
3762     tag : false,
3763     
3764     
3765     getAutoCreate : function(){
3766         
3767         
3768         var cfg = {
3769             tag : this.tag || 'div',
3770             cls : 'navbar'
3771         };
3772           
3773         
3774         cfg.cn = [
3775             {
3776                 cls: 'nav',
3777                 tag : 'ul'
3778             }
3779         ];
3780         
3781          
3782         this.type = this.type || 'nav';
3783         if (['tabs','pills'].indexOf(this.type)!==-1) {
3784             cfg.cn[0].cls += ' nav-' + this.type
3785         
3786         
3787         } else {
3788             if (this.type!=='nav') {
3789                 Roo.log('nav type must be nav/tabs/pills')
3790             }
3791             cfg.cn[0].cls += ' navbar-nav'
3792         }
3793         
3794         
3795         
3796         
3797         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3798             cfg.cn[0].cls += ' nav-' + this.arrangement;
3799         }
3800         
3801         
3802         if (this.align === 'right') {
3803             cfg.cn[0].cls += ' navbar-right';
3804         }
3805         
3806         if (this.inverse) {
3807             cfg.cls += ' navbar-inverse';
3808             
3809         }
3810         
3811         
3812         return cfg;
3813     
3814         
3815     }
3816     
3817     
3818     
3819 });
3820
3821
3822
3823  
3824
3825  
3826        /*
3827  * - LGPL
3828  *
3829  * navbar
3830  * 
3831  */
3832
3833 /**
3834  * @class Roo.bootstrap.NavHeaderbar
3835  * @extends Roo.bootstrap.NavSimplebar
3836  * Bootstrap Sidebar class
3837  *
3838  * @cfg {String} brand what is brand
3839  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3840  * @cfg {String} brand_href href of the brand
3841  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3842  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3843  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3844  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3845  * 
3846  * @constructor
3847  * Create a new Sidebar
3848  * @param {Object} config The config object
3849  */
3850
3851
3852 Roo.bootstrap.NavHeaderbar = function(config){
3853     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3854       
3855 };
3856
3857 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3858     
3859     position: '',
3860     brand: '',
3861     brand_href: false,
3862     srButton : true,
3863     autohide : false,
3864     desktopCenter : false,
3865    
3866     
3867     getAutoCreate : function(){
3868         
3869         var   cfg = {
3870             tag: this.nav || 'nav',
3871             cls: 'navbar',
3872             role: 'navigation',
3873             cn: []
3874         };
3875         
3876         var cn = cfg.cn;
3877         if (this.desktopCenter) {
3878             cn.push({cls : 'container', cn : []});
3879             cn = cn[0].cn;
3880         }
3881         
3882         if(this.srButton){
3883             cn.push({
3884                 tag: 'div',
3885                 cls: 'navbar-header',
3886                 cn: [
3887                     {
3888                         tag: 'button',
3889                         type: 'button',
3890                         cls: 'navbar-toggle',
3891                         'data-toggle': 'collapse',
3892                         cn: [
3893                             {
3894                                 tag: 'span',
3895                                 cls: 'sr-only',
3896                                 html: 'Toggle navigation'
3897                             },
3898                             {
3899                                 tag: 'span',
3900                                 cls: 'icon-bar'
3901                             },
3902                             {
3903                                 tag: 'span',
3904                                 cls: 'icon-bar'
3905                             },
3906                             {
3907                                 tag: 'span',
3908                                 cls: 'icon-bar'
3909                             }
3910                         ]
3911                     }
3912                 ]
3913             });
3914         }
3915         
3916         cn.push({
3917             tag: 'div',
3918             cls: 'collapse navbar-collapse',
3919             cn : []
3920         });
3921         
3922         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3923         
3924         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3925             cfg.cls += ' navbar-' + this.position;
3926             
3927             // tag can override this..
3928             
3929             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3930         }
3931         
3932         if (this.brand !== '') {
3933             cn[0].cn.push({
3934                 tag: 'a',
3935                 href: this.brand_href ? this.brand_href : '#',
3936                 cls: 'navbar-brand',
3937                 cn: [
3938                 this.brand
3939                 ]
3940             });
3941         }
3942         
3943         if(this.main){
3944             cfg.cls += ' main-nav';
3945         }
3946         
3947         
3948         return cfg;
3949
3950         
3951     },
3952     getHeaderChildContainer : function()
3953     {
3954         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3955             return this.el.select('.navbar-header',true).first();
3956         }
3957         
3958         return this.getChildContainer();
3959     },
3960     
3961     
3962     initEvents : function()
3963     {
3964         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3965         
3966         if (this.autohide) {
3967             
3968             var prevScroll = 0;
3969             var ft = this.el;
3970             
3971             Roo.get(document).on('scroll',function(e) {
3972                 var ns = Roo.get(document).getScroll().top;
3973                 var os = prevScroll;
3974                 prevScroll = ns;
3975                 
3976                 if(ns > os){
3977                     ft.removeClass('slideDown');
3978                     ft.addClass('slideUp');
3979                     return;
3980                 }
3981                 ft.removeClass('slideUp');
3982                 ft.addClass('slideDown');
3983                  
3984               
3985           },this);
3986         }
3987     }    
3988     
3989 });
3990
3991
3992
3993  
3994
3995  /*
3996  * - LGPL
3997  *
3998  * navbar
3999  * 
4000  */
4001
4002 /**
4003  * @class Roo.bootstrap.NavSidebar
4004  * @extends Roo.bootstrap.Navbar
4005  * Bootstrap Sidebar class
4006  * 
4007  * @constructor
4008  * Create a new Sidebar
4009  * @param {Object} config The config object
4010  */
4011
4012
4013 Roo.bootstrap.NavSidebar = function(config){
4014     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4015 };
4016
4017 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4018     
4019     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4020     
4021     getAutoCreate : function(){
4022         
4023         
4024         return  {
4025             tag: 'div',
4026             cls: 'sidebar sidebar-nav'
4027         };
4028     
4029         
4030     }
4031     
4032     
4033     
4034 });
4035
4036
4037
4038  
4039
4040  /*
4041  * - LGPL
4042  *
4043  * nav group
4044  * 
4045  */
4046
4047 /**
4048  * @class Roo.bootstrap.NavGroup
4049  * @extends Roo.bootstrap.Component
4050  * Bootstrap NavGroup class
4051  * @cfg {String} align (left|right)
4052  * @cfg {Boolean} inverse
4053  * @cfg {String} type (nav|pills|tab) default nav
4054  * @cfg {String} navId - reference Id for navbar.
4055
4056  * 
4057  * @constructor
4058  * Create a new nav group
4059  * @param {Object} config The config object
4060  */
4061
4062 Roo.bootstrap.NavGroup = function(config){
4063     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4064     this.navItems = [];
4065    
4066     Roo.bootstrap.NavGroup.register(this);
4067      this.addEvents({
4068         /**
4069              * @event changed
4070              * Fires when the active item changes
4071              * @param {Roo.bootstrap.NavGroup} this
4072              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4073              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4074          */
4075         'changed': true
4076      });
4077     
4078 };
4079
4080 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4081     
4082     align: '',
4083     inverse: false,
4084     form: false,
4085     type: 'nav',
4086     navId : '',
4087     // private
4088     
4089     navItems : false, 
4090     
4091     getAutoCreate : function()
4092     {
4093         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4094         
4095         cfg = {
4096             tag : 'ul',
4097             cls: 'nav' 
4098         };
4099         
4100         if (['tabs','pills'].indexOf(this.type)!==-1) {
4101             cfg.cls += ' nav-' + this.type
4102         } else {
4103             if (this.type!=='nav') {
4104                 Roo.log('nav type must be nav/tabs/pills')
4105             }
4106             cfg.cls += ' navbar-nav'
4107         }
4108         
4109         if (this.parent() && this.parent().sidebar) {
4110             cfg = {
4111                 tag: 'ul',
4112                 cls: 'dashboard-menu sidebar-menu'
4113             };
4114             
4115             return cfg;
4116         }
4117         
4118         if (this.form === true) {
4119             cfg = {
4120                 tag: 'form',
4121                 cls: 'navbar-form'
4122             };
4123             
4124             if (this.align === 'right') {
4125                 cfg.cls += ' navbar-right';
4126             } else {
4127                 cfg.cls += ' navbar-left';
4128             }
4129         }
4130         
4131         if (this.align === 'right') {
4132             cfg.cls += ' navbar-right';
4133         }
4134         
4135         if (this.inverse) {
4136             cfg.cls += ' navbar-inverse';
4137             
4138         }
4139         
4140         
4141         return cfg;
4142     },
4143     /**
4144     * sets the active Navigation item
4145     * @param {Roo.bootstrap.NavItem} the new current navitem
4146     */
4147     setActiveItem : function(item)
4148     {
4149         var prev = false;
4150         Roo.each(this.navItems, function(v){
4151             if (v == item) {
4152                 return ;
4153             }
4154             if (v.isActive()) {
4155                 v.setActive(false, true);
4156                 prev = v;
4157                 
4158             }
4159             
4160         });
4161
4162         item.setActive(true, true);
4163         this.fireEvent('changed', this, item, prev);
4164         
4165         
4166     },
4167     /**
4168     * gets the active Navigation item
4169     * @return {Roo.bootstrap.NavItem} the current navitem
4170     */
4171     getActive : function()
4172     {
4173         
4174         var prev = false;
4175         Roo.each(this.navItems, function(v){
4176             
4177             if (v.isActive()) {
4178                 prev = v;
4179                 
4180             }
4181             
4182         });
4183         return prev;
4184     },
4185     
4186     indexOfNav : function()
4187     {
4188         
4189         var prev = false;
4190         Roo.each(this.navItems, function(v,i){
4191             
4192             if (v.isActive()) {
4193                 prev = i;
4194                 
4195             }
4196             
4197         });
4198         return prev;
4199     },
4200     /**
4201     * adds a Navigation item
4202     * @param {Roo.bootstrap.NavItem} the navitem to add
4203     */
4204     addItem : function(cfg)
4205     {
4206         var cn = new Roo.bootstrap.NavItem(cfg);
4207         this.register(cn);
4208         cn.parentId = this.id;
4209         cn.onRender(this.el, null);
4210         return cn;
4211     },
4212     /**
4213     * register a Navigation item
4214     * @param {Roo.bootstrap.NavItem} the navitem to add
4215     */
4216     register : function(item)
4217     {
4218         this.navItems.push( item);
4219         item.navId = this.navId;
4220     
4221     },
4222     
4223     /**
4224     * clear all the Navigation item
4225     */
4226    
4227     clearAll : function()
4228     {
4229         this.navItems = [];
4230         this.el.dom.innerHTML = '';
4231     },
4232     
4233     getNavItem: function(tabId)
4234     {
4235         var ret = false;
4236         Roo.each(this.navItems, function(e) {
4237             if (e.tabId == tabId) {
4238                ret =  e;
4239                return false;
4240             }
4241             return true;
4242             
4243         });
4244         return ret;
4245     },
4246     
4247     setActiveNext : function()
4248     {
4249         var i = this.indexOfNav(this.getActive());
4250         if (i > this.navItems.length) {
4251             return;
4252         }
4253         this.setActiveItem(this.navItems[i+1]);
4254     },
4255     setActivePrev : function()
4256     {
4257         var i = this.indexOfNav(this.getActive());
4258         if (i  < 1) {
4259             return;
4260         }
4261         this.setActiveItem(this.navItems[i-1]);
4262     },
4263     clearWasActive : function(except) {
4264         Roo.each(this.navItems, function(e) {
4265             if (e.tabId != except.tabId && e.was_active) {
4266                e.was_active = false;
4267                return false;
4268             }
4269             return true;
4270             
4271         });
4272     },
4273     getWasActive : function ()
4274     {
4275         var r = false;
4276         Roo.each(this.navItems, function(e) {
4277             if (e.was_active) {
4278                r = e;
4279                return false;
4280             }
4281             return true;
4282             
4283         });
4284         return r;
4285     }
4286     
4287     
4288 });
4289
4290  
4291 Roo.apply(Roo.bootstrap.NavGroup, {
4292     
4293     groups: {},
4294      /**
4295     * register a Navigation Group
4296     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4297     */
4298     register : function(navgrp)
4299     {
4300         this.groups[navgrp.navId] = navgrp;
4301         
4302     },
4303     /**
4304     * fetch a Navigation Group based on the navigation ID
4305     * @param {string} the navgroup to add
4306     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4307     */
4308     get: function(navId) {
4309         if (typeof(this.groups[navId]) == 'undefined') {
4310             return false;
4311             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4312         }
4313         return this.groups[navId] ;
4314     }
4315     
4316     
4317     
4318 });
4319
4320  /*
4321  * - LGPL
4322  *
4323  * row
4324  * 
4325  */
4326
4327 /**
4328  * @class Roo.bootstrap.NavItem
4329  * @extends Roo.bootstrap.Component
4330  * Bootstrap Navbar.NavItem class
4331  * @cfg {String} href  link to
4332  * @cfg {String} html content of button
4333  * @cfg {String} badge text inside badge
4334  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4335  * @cfg {String} glyphicon name of glyphicon
4336  * @cfg {String} icon name of font awesome icon
4337  * @cfg {Boolean} active Is item active
4338  * @cfg {Boolean} disabled Is item disabled
4339  
4340  * @cfg {Boolean} preventDefault (true | false) default false
4341  * @cfg {String} tabId the tab that this item activates.
4342  * @cfg {String} tagtype (a|span) render as a href or span?
4343  * @cfg {Boolean} animateRef (true|false) link to element default false  
4344   
4345  * @constructor
4346  * Create a new Navbar Item
4347  * @param {Object} config The config object
4348  */
4349 Roo.bootstrap.NavItem = function(config){
4350     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4351     this.addEvents({
4352         // raw events
4353         /**
4354          * @event click
4355          * The raw click event for the entire grid.
4356          * @param {Roo.EventObject} e
4357          */
4358         "click" : true,
4359          /**
4360             * @event changed
4361             * Fires when the active item active state changes
4362             * @param {Roo.bootstrap.NavItem} this
4363             * @param {boolean} state the new state
4364              
4365          */
4366         'changed': true,
4367         /**
4368             * @event scrollto
4369             * Fires when scroll to element
4370             * @param {Roo.bootstrap.NavItem} this
4371             * @param {Object} options
4372             * @param {Roo.EventObject} e
4373              
4374          */
4375         'scrollto': true
4376     });
4377    
4378 };
4379
4380 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4381     
4382     href: false,
4383     html: '',
4384     badge: '',
4385     icon: false,
4386     glyphicon: false,
4387     active: false,
4388     preventDefault : false,
4389     tabId : false,
4390     tagtype : 'a',
4391     disabled : false,
4392     animateRef : false,
4393     was_active : false,
4394     
4395     getAutoCreate : function(){
4396          
4397         var cfg = {
4398             tag: 'li',
4399             cls: 'nav-item'
4400             
4401         };
4402         
4403         if (this.active) {
4404             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4405         }
4406         if (this.disabled) {
4407             cfg.cls += ' disabled';
4408         }
4409         
4410         if (this.href || this.html || this.glyphicon || this.icon) {
4411             cfg.cn = [
4412                 {
4413                     tag: this.tagtype,
4414                     href : this.href || "#",
4415                     html: this.html || ''
4416                 }
4417             ];
4418             
4419             if (this.icon) {
4420                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4421             }
4422
4423             if(this.glyphicon) {
4424                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4425             }
4426             
4427             if (this.menu) {
4428                 
4429                 cfg.cn[0].html += " <span class='caret'></span>";
4430              
4431             }
4432             
4433             if (this.badge !== '') {
4434                  
4435                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4436             }
4437         }
4438         
4439         
4440         
4441         return cfg;
4442     },
4443     initEvents: function() 
4444     {
4445         if (typeof (this.menu) != 'undefined') {
4446             this.menu.parentType = this.xtype;
4447             this.menu.triggerEl = this.el;
4448             this.menu = this.addxtype(Roo.apply({}, this.menu));
4449         }
4450         
4451         this.el.select('a',true).on('click', this.onClick, this);
4452         
4453         if(this.tagtype == 'span'){
4454             this.el.select('span',true).on('click', this.onClick, this);
4455         }
4456        
4457         // at this point parent should be available..
4458         this.parent().register(this);
4459     },
4460     
4461     onClick : function(e)
4462     {
4463         if (e.getTarget('.dropdown-menu-item')) {
4464             // did you click on a menu itemm.... - then don't trigger onclick..
4465             return;
4466         }
4467         
4468         if(
4469                 this.preventDefault || 
4470                 this.href == '#' 
4471         ){
4472             Roo.log("NavItem - prevent Default?");
4473             e.preventDefault();
4474         }
4475         
4476         if (this.disabled) {
4477             return;
4478         }
4479         
4480         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4481         if (tg && tg.transition) {
4482             Roo.log("waiting for the transitionend");
4483             return;
4484         }
4485         
4486         
4487         
4488         //Roo.log("fire event clicked");
4489         if(this.fireEvent('click', this, e) === false){
4490             return;
4491         };
4492         
4493         if(this.tagtype == 'span'){
4494             return;
4495         }
4496         
4497         //Roo.log(this.href);
4498         var ael = this.el.select('a',true).first();
4499         //Roo.log(ael);
4500         
4501         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4502             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4503             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4504                 return; // ignore... - it's a 'hash' to another page.
4505             }
4506             Roo.log("NavItem - prevent Default?");
4507             e.preventDefault();
4508             this.scrollToElement(e);
4509         }
4510         
4511         
4512         var p =  this.parent();
4513    
4514         if (['tabs','pills'].indexOf(p.type)!==-1) {
4515             if (typeof(p.setActiveItem) !== 'undefined') {
4516                 p.setActiveItem(this);
4517             }
4518         }
4519         
4520         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4521         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4522             // remove the collapsed menu expand...
4523             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4524         }
4525     },
4526     
4527     isActive: function () {
4528         return this.active
4529     },
4530     setActive : function(state, fire, is_was_active)
4531     {
4532         if (this.active && !state && this.navId) {
4533             this.was_active = true;
4534             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4535             if (nv) {
4536                 nv.clearWasActive(this);
4537             }
4538             
4539         }
4540         this.active = state;
4541         
4542         if (!state ) {
4543             this.el.removeClass('active');
4544         } else if (!this.el.hasClass('active')) {
4545             this.el.addClass('active');
4546         }
4547         if (fire) {
4548             this.fireEvent('changed', this, state);
4549         }
4550         
4551         // show a panel if it's registered and related..
4552         
4553         if (!this.navId || !this.tabId || !state || is_was_active) {
4554             return;
4555         }
4556         
4557         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4558         if (!tg) {
4559             return;
4560         }
4561         var pan = tg.getPanelByName(this.tabId);
4562         if (!pan) {
4563             return;
4564         }
4565         // if we can not flip to new panel - go back to old nav highlight..
4566         if (false == tg.showPanel(pan)) {
4567             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4568             if (nv) {
4569                 var onav = nv.getWasActive();
4570                 if (onav) {
4571                     onav.setActive(true, false, true);
4572                 }
4573             }
4574             
4575         }
4576         
4577         
4578         
4579     },
4580      // this should not be here...
4581     setDisabled : function(state)
4582     {
4583         this.disabled = state;
4584         if (!state ) {
4585             this.el.removeClass('disabled');
4586         } else if (!this.el.hasClass('disabled')) {
4587             this.el.addClass('disabled');
4588         }
4589         
4590     },
4591     
4592     /**
4593      * Fetch the element to display the tooltip on.
4594      * @return {Roo.Element} defaults to this.el
4595      */
4596     tooltipEl : function()
4597     {
4598         return this.el.select('' + this.tagtype + '', true).first();
4599     },
4600     
4601     scrollToElement : function(e)
4602     {
4603         var c = document.body;
4604         
4605         /*
4606          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4607          */
4608         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4609             c = document.documentElement;
4610         }
4611         
4612         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4613         
4614         if(!target){
4615             return;
4616         }
4617
4618         var o = target.calcOffsetsTo(c);
4619         
4620         var options = {
4621             target : target,
4622             value : o[1]
4623         };
4624         
4625         this.fireEvent('scrollto', this, options, e);
4626         
4627         Roo.get(c).scrollTo('top', options.value, true);
4628         
4629         return;
4630     }
4631 });
4632  
4633
4634  /*
4635  * - LGPL
4636  *
4637  * sidebar item
4638  *
4639  *  li
4640  *    <span> icon </span>
4641  *    <span> text </span>
4642  *    <span>badge </span>
4643  */
4644
4645 /**
4646  * @class Roo.bootstrap.NavSidebarItem
4647  * @extends Roo.bootstrap.NavItem
4648  * Bootstrap Navbar.NavSidebarItem class
4649  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4650  * {Boolean} open is the menu open
4651  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4652  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4653  * {String} buttonSize (sm|md|lg)the extra classes for the button
4654  * {Boolean} showArrow show arrow next to the text (default true)
4655  * @constructor
4656  * Create a new Navbar Button
4657  * @param {Object} config The config object
4658  */
4659 Roo.bootstrap.NavSidebarItem = function(config){
4660     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4661     this.addEvents({
4662         // raw events
4663         /**
4664          * @event click
4665          * The raw click event for the entire grid.
4666          * @param {Roo.EventObject} e
4667          */
4668         "click" : true,
4669          /**
4670             * @event changed
4671             * Fires when the active item active state changes
4672             * @param {Roo.bootstrap.NavSidebarItem} this
4673             * @param {boolean} state the new state
4674              
4675          */
4676         'changed': true
4677     });
4678    
4679 };
4680
4681 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4682     
4683     badgeWeight : 'default',
4684     
4685     open: false,
4686     
4687     buttonView : false,
4688     
4689     buttonWeight : 'default',
4690     
4691     buttonSize : 'md',
4692     
4693     showArrow : true,
4694     
4695     getAutoCreate : function(){
4696         
4697         
4698         var a = {
4699                 tag: 'a',
4700                 href : this.href || '#',
4701                 cls: '',
4702                 html : '',
4703                 cn : []
4704         };
4705         
4706         if(this.buttonView){
4707             a = {
4708                 tag: 'button',
4709                 href : this.href || '#',
4710                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4711                 html : this.html,
4712                 cn : []
4713             };
4714         }
4715         
4716         var cfg = {
4717             tag: 'li',
4718             cls: '',
4719             cn: [ a ]
4720         };
4721         
4722         if (this.active) {
4723             cfg.cls += ' active';
4724         }
4725         
4726         if (this.disabled) {
4727             cfg.cls += ' disabled';
4728         }
4729         if (this.open) {
4730             cfg.cls += ' open x-open';
4731         }
4732         // left icon..
4733         if (this.glyphicon || this.icon) {
4734             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4735             a.cn.push({ tag : 'i', cls : c }) ;
4736         }
4737         
4738         if(!this.buttonView){
4739             var span = {
4740                 tag: 'span',
4741                 html : this.html || ''
4742             };
4743
4744             a.cn.push(span);
4745             
4746         }
4747         
4748         if (this.badge !== '') {
4749             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4750         }
4751         
4752         if (this.menu) {
4753             
4754             if(this.showArrow){
4755                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4756             }
4757             
4758             a.cls += ' dropdown-toggle treeview' ;
4759         }
4760         
4761         return cfg;
4762     },
4763     
4764     initEvents : function()
4765     { 
4766         if (typeof (this.menu) != 'undefined') {
4767             this.menu.parentType = this.xtype;
4768             this.menu.triggerEl = this.el;
4769             this.menu = this.addxtype(Roo.apply({}, this.menu));
4770         }
4771         
4772         this.el.on('click', this.onClick, this);
4773         
4774         if(this.badge !== ''){
4775             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4776         }
4777         
4778     },
4779     
4780     onClick : function(e)
4781     {
4782         if(this.disabled){
4783             e.preventDefault();
4784             return;
4785         }
4786         
4787         if(this.preventDefault){
4788             e.preventDefault();
4789         }
4790         
4791         this.fireEvent('click', this);
4792     },
4793     
4794     disable : function()
4795     {
4796         this.setDisabled(true);
4797     },
4798     
4799     enable : function()
4800     {
4801         this.setDisabled(false);
4802     },
4803     
4804     setDisabled : function(state)
4805     {
4806         if(this.disabled == state){
4807             return;
4808         }
4809         
4810         this.disabled = state;
4811         
4812         if (state) {
4813             this.el.addClass('disabled');
4814             return;
4815         }
4816         
4817         this.el.removeClass('disabled');
4818         
4819         return;
4820     },
4821     
4822     setActive : function(state)
4823     {
4824         if(this.active == state){
4825             return;
4826         }
4827         
4828         this.active = state;
4829         
4830         if (state) {
4831             this.el.addClass('active');
4832             return;
4833         }
4834         
4835         this.el.removeClass('active');
4836         
4837         return;
4838     },
4839     
4840     isActive: function () 
4841     {
4842         return this.active;
4843     },
4844     
4845     setBadge : function(str)
4846     {
4847         if(!this.badgeEl){
4848             return;
4849         }
4850         
4851         this.badgeEl.dom.innerHTML = str;
4852     }
4853     
4854    
4855      
4856  
4857 });
4858  
4859
4860  /*
4861  * - LGPL
4862  *
4863  * row
4864  * 
4865  */
4866
4867 /**
4868  * @class Roo.bootstrap.Row
4869  * @extends Roo.bootstrap.Component
4870  * Bootstrap Row class (contains columns...)
4871  * 
4872  * @constructor
4873  * Create a new Row
4874  * @param {Object} config The config object
4875  */
4876
4877 Roo.bootstrap.Row = function(config){
4878     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4879 };
4880
4881 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4882     
4883     getAutoCreate : function(){
4884        return {
4885             cls: 'row clearfix'
4886        };
4887     }
4888     
4889     
4890 });
4891
4892  
4893
4894  /*
4895  * - LGPL
4896  *
4897  * element
4898  * 
4899  */
4900
4901 /**
4902  * @class Roo.bootstrap.Element
4903  * @extends Roo.bootstrap.Component
4904  * Bootstrap Element class
4905  * @cfg {String} html contents of the element
4906  * @cfg {String} tag tag of the element
4907  * @cfg {String} cls class of the element
4908  * @cfg {Boolean} preventDefault (true|false) default false
4909  * @cfg {Boolean} clickable (true|false) default false
4910  * 
4911  * @constructor
4912  * Create a new Element
4913  * @param {Object} config The config object
4914  */
4915
4916 Roo.bootstrap.Element = function(config){
4917     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4918     
4919     this.addEvents({
4920         // raw events
4921         /**
4922          * @event click
4923          * When a element is chick
4924          * @param {Roo.bootstrap.Element} this
4925          * @param {Roo.EventObject} e
4926          */
4927         "click" : true
4928     });
4929 };
4930
4931 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4932     
4933     tag: 'div',
4934     cls: '',
4935     html: '',
4936     preventDefault: false, 
4937     clickable: false,
4938     
4939     getAutoCreate : function(){
4940         
4941         var cfg = {
4942             tag: this.tag,
4943             // cls: this.cls, double assign in parent class Component.js :: onRender
4944             html: this.html
4945         };
4946         
4947         return cfg;
4948     },
4949     
4950     initEvents: function() 
4951     {
4952         Roo.bootstrap.Element.superclass.initEvents.call(this);
4953         
4954         if(this.clickable){
4955             this.el.on('click', this.onClick, this);
4956         }
4957         
4958     },
4959     
4960     onClick : function(e)
4961     {
4962         if(this.preventDefault){
4963             e.preventDefault();
4964         }
4965         
4966         this.fireEvent('click', this, e);
4967     },
4968     
4969     getValue : function()
4970     {
4971         return this.el.dom.innerHTML;
4972     },
4973     
4974     setValue : function(value)
4975     {
4976         this.el.dom.innerHTML = value;
4977     }
4978    
4979 });
4980
4981  
4982
4983  /*
4984  * - LGPL
4985  *
4986  * pagination
4987  * 
4988  */
4989
4990 /**
4991  * @class Roo.bootstrap.Pagination
4992  * @extends Roo.bootstrap.Component
4993  * Bootstrap Pagination class
4994  * @cfg {String} size xs | sm | md | lg
4995  * @cfg {Boolean} inverse false | true
4996  * 
4997  * @constructor
4998  * Create a new Pagination
4999  * @param {Object} config The config object
5000  */
5001
5002 Roo.bootstrap.Pagination = function(config){
5003     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5004 };
5005
5006 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5007     
5008     cls: false,
5009     size: false,
5010     inverse: false,
5011     
5012     getAutoCreate : function(){
5013         var cfg = {
5014             tag: 'ul',
5015                 cls: 'pagination'
5016         };
5017         if (this.inverse) {
5018             cfg.cls += ' inverse';
5019         }
5020         if (this.html) {
5021             cfg.html=this.html;
5022         }
5023         if (this.cls) {
5024             cfg.cls += " " + this.cls;
5025         }
5026         return cfg;
5027     }
5028    
5029 });
5030
5031  
5032
5033  /*
5034  * - LGPL
5035  *
5036  * Pagination item
5037  * 
5038  */
5039
5040
5041 /**
5042  * @class Roo.bootstrap.PaginationItem
5043  * @extends Roo.bootstrap.Component
5044  * Bootstrap PaginationItem class
5045  * @cfg {String} html text
5046  * @cfg {String} href the link
5047  * @cfg {Boolean} preventDefault (true | false) default true
5048  * @cfg {Boolean} active (true | false) default false
5049  * @cfg {Boolean} disabled default false
5050  * 
5051  * 
5052  * @constructor
5053  * Create a new PaginationItem
5054  * @param {Object} config The config object
5055  */
5056
5057
5058 Roo.bootstrap.PaginationItem = function(config){
5059     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5060     this.addEvents({
5061         // raw events
5062         /**
5063          * @event click
5064          * The raw click event for the entire grid.
5065          * @param {Roo.EventObject} e
5066          */
5067         "click" : true
5068     });
5069 };
5070
5071 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5072     
5073     href : false,
5074     html : false,
5075     preventDefault: true,
5076     active : false,
5077     cls : false,
5078     disabled: false,
5079     
5080     getAutoCreate : function(){
5081         var cfg= {
5082             tag: 'li',
5083             cn: [
5084                 {
5085                     tag : 'a',
5086                     href : this.href ? this.href : '#',
5087                     html : this.html ? this.html : ''
5088                 }
5089             ]
5090         };
5091         
5092         if(this.cls){
5093             cfg.cls = this.cls;
5094         }
5095         
5096         if(this.disabled){
5097             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5098         }
5099         
5100         if(this.active){
5101             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5102         }
5103         
5104         return cfg;
5105     },
5106     
5107     initEvents: function() {
5108         
5109         this.el.on('click', this.onClick, this);
5110         
5111     },
5112     onClick : function(e)
5113     {
5114         Roo.log('PaginationItem on click ');
5115         if(this.preventDefault){
5116             e.preventDefault();
5117         }
5118         
5119         if(this.disabled){
5120             return;
5121         }
5122         
5123         this.fireEvent('click', this, e);
5124     }
5125    
5126 });
5127
5128  
5129
5130  /*
5131  * - LGPL
5132  *
5133  * slider
5134  * 
5135  */
5136
5137
5138 /**
5139  * @class Roo.bootstrap.Slider
5140  * @extends Roo.bootstrap.Component
5141  * Bootstrap Slider class
5142  *    
5143  * @constructor
5144  * Create a new Slider
5145  * @param {Object} config The config object
5146  */
5147
5148 Roo.bootstrap.Slider = function(config){
5149     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5150 };
5151
5152 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5153     
5154     getAutoCreate : function(){
5155         
5156         var cfg = {
5157             tag: 'div',
5158             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5159             cn: [
5160                 {
5161                     tag: 'a',
5162                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5163                 }
5164             ]
5165         };
5166         
5167         return cfg;
5168     }
5169    
5170 });
5171
5172  /*
5173  * Based on:
5174  * Ext JS Library 1.1.1
5175  * Copyright(c) 2006-2007, Ext JS, LLC.
5176  *
5177  * Originally Released Under LGPL - original licence link has changed is not relivant.
5178  *
5179  * Fork - LGPL
5180  * <script type="text/javascript">
5181  */
5182  
5183
5184 /**
5185  * @class Roo.grid.ColumnModel
5186  * @extends Roo.util.Observable
5187  * This is the default implementation of a ColumnModel used by the Grid. It defines
5188  * the columns in the grid.
5189  * <br>Usage:<br>
5190  <pre><code>
5191  var colModel = new Roo.grid.ColumnModel([
5192         {header: "Ticker", width: 60, sortable: true, locked: true},
5193         {header: "Company Name", width: 150, sortable: true},
5194         {header: "Market Cap.", width: 100, sortable: true},
5195         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5196         {header: "Employees", width: 100, sortable: true, resizable: false}
5197  ]);
5198  </code></pre>
5199  * <p>
5200  
5201  * The config options listed for this class are options which may appear in each
5202  * individual column definition.
5203  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5204  * @constructor
5205  * @param {Object} config An Array of column config objects. See this class's
5206  * config objects for details.
5207 */
5208 Roo.grid.ColumnModel = function(config){
5209         /**
5210      * The config passed into the constructor
5211      */
5212     this.config = config;
5213     this.lookup = {};
5214
5215     // if no id, create one
5216     // if the column does not have a dataIndex mapping,
5217     // map it to the order it is in the config
5218     for(var i = 0, len = config.length; i < len; i++){
5219         var c = config[i];
5220         if(typeof c.dataIndex == "undefined"){
5221             c.dataIndex = i;
5222         }
5223         if(typeof c.renderer == "string"){
5224             c.renderer = Roo.util.Format[c.renderer];
5225         }
5226         if(typeof c.id == "undefined"){
5227             c.id = Roo.id();
5228         }
5229         if(c.editor && c.editor.xtype){
5230             c.editor  = Roo.factory(c.editor, Roo.grid);
5231         }
5232         if(c.editor && c.editor.isFormField){
5233             c.editor = new Roo.grid.GridEditor(c.editor);
5234         }
5235         this.lookup[c.id] = c;
5236     }
5237
5238     /**
5239      * The width of columns which have no width specified (defaults to 100)
5240      * @type Number
5241      */
5242     this.defaultWidth = 100;
5243
5244     /**
5245      * Default sortable of columns which have no sortable specified (defaults to false)
5246      * @type Boolean
5247      */
5248     this.defaultSortable = false;
5249
5250     this.addEvents({
5251         /**
5252              * @event widthchange
5253              * Fires when the width of a column changes.
5254              * @param {ColumnModel} this
5255              * @param {Number} columnIndex The column index
5256              * @param {Number} newWidth The new width
5257              */
5258             "widthchange": true,
5259         /**
5260              * @event headerchange
5261              * Fires when the text of a header changes.
5262              * @param {ColumnModel} this
5263              * @param {Number} columnIndex The column index
5264              * @param {Number} newText The new header text
5265              */
5266             "headerchange": true,
5267         /**
5268              * @event hiddenchange
5269              * Fires when a column is hidden or "unhidden".
5270              * @param {ColumnModel} this
5271              * @param {Number} columnIndex The column index
5272              * @param {Boolean} hidden true if hidden, false otherwise
5273              */
5274             "hiddenchange": true,
5275             /**
5276          * @event columnmoved
5277          * Fires when a column is moved.
5278          * @param {ColumnModel} this
5279          * @param {Number} oldIndex
5280          * @param {Number} newIndex
5281          */
5282         "columnmoved" : true,
5283         /**
5284          * @event columlockchange
5285          * Fires when a column's locked state is changed
5286          * @param {ColumnModel} this
5287          * @param {Number} colIndex
5288          * @param {Boolean} locked true if locked
5289          */
5290         "columnlockchange" : true
5291     });
5292     Roo.grid.ColumnModel.superclass.constructor.call(this);
5293 };
5294 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5295     /**
5296      * @cfg {String} header The header text to display in the Grid view.
5297      */
5298     /**
5299      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5300      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5301      * specified, the column's index is used as an index into the Record's data Array.
5302      */
5303     /**
5304      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5305      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5306      */
5307     /**
5308      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5309      * Defaults to the value of the {@link #defaultSortable} property.
5310      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5311      */
5312     /**
5313      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5314      */
5315     /**
5316      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5317      */
5318     /**
5319      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5320      */
5321     /**
5322      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5323      */
5324     /**
5325      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5326      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5327      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5328      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5329      */
5330        /**
5331      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5332      */
5333     /**
5334      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5335      */
5336     /**
5337      * @cfg {String} cursor (Optional)
5338      */
5339     /**
5340      * @cfg {String} tooltip (Optional)
5341      */
5342     /**
5343      * @cfg {Number} xs (Optional)
5344      */
5345     /**
5346      * @cfg {Number} sm (Optional)
5347      */
5348     /**
5349      * @cfg {Number} md (Optional)
5350      */
5351     /**
5352      * @cfg {Number} lg (Optional)
5353      */
5354     /**
5355      * Returns the id of the column at the specified index.
5356      * @param {Number} index The column index
5357      * @return {String} the id
5358      */
5359     getColumnId : function(index){
5360         return this.config[index].id;
5361     },
5362
5363     /**
5364      * Returns the column for a specified id.
5365      * @param {String} id The column id
5366      * @return {Object} the column
5367      */
5368     getColumnById : function(id){
5369         return this.lookup[id];
5370     },
5371
5372     
5373     /**
5374      * Returns the column for a specified dataIndex.
5375      * @param {String} dataIndex The column dataIndex
5376      * @return {Object|Boolean} the column or false if not found
5377      */
5378     getColumnByDataIndex: function(dataIndex){
5379         var index = this.findColumnIndex(dataIndex);
5380         return index > -1 ? this.config[index] : false;
5381     },
5382     
5383     /**
5384      * Returns the index for a specified column id.
5385      * @param {String} id The column id
5386      * @return {Number} the index, or -1 if not found
5387      */
5388     getIndexById : function(id){
5389         for(var i = 0, len = this.config.length; i < len; i++){
5390             if(this.config[i].id == id){
5391                 return i;
5392             }
5393         }
5394         return -1;
5395     },
5396     
5397     /**
5398      * Returns the index for a specified column dataIndex.
5399      * @param {String} dataIndex The column dataIndex
5400      * @return {Number} the index, or -1 if not found
5401      */
5402     
5403     findColumnIndex : function(dataIndex){
5404         for(var i = 0, len = this.config.length; i < len; i++){
5405             if(this.config[i].dataIndex == dataIndex){
5406                 return i;
5407             }
5408         }
5409         return -1;
5410     },
5411     
5412     
5413     moveColumn : function(oldIndex, newIndex){
5414         var c = this.config[oldIndex];
5415         this.config.splice(oldIndex, 1);
5416         this.config.splice(newIndex, 0, c);
5417         this.dataMap = null;
5418         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5419     },
5420
5421     isLocked : function(colIndex){
5422         return this.config[colIndex].locked === true;
5423     },
5424
5425     setLocked : function(colIndex, value, suppressEvent){
5426         if(this.isLocked(colIndex) == value){
5427             return;
5428         }
5429         this.config[colIndex].locked = value;
5430         if(!suppressEvent){
5431             this.fireEvent("columnlockchange", this, colIndex, value);
5432         }
5433     },
5434
5435     getTotalLockedWidth : function(){
5436         var totalWidth = 0;
5437         for(var i = 0; i < this.config.length; i++){
5438             if(this.isLocked(i) && !this.isHidden(i)){
5439                 this.totalWidth += this.getColumnWidth(i);
5440             }
5441         }
5442         return totalWidth;
5443     },
5444
5445     getLockedCount : function(){
5446         for(var i = 0, len = this.config.length; i < len; i++){
5447             if(!this.isLocked(i)){
5448                 return i;
5449             }
5450         }
5451         
5452         return this.config.length;
5453     },
5454
5455     /**
5456      * Returns the number of columns.
5457      * @return {Number}
5458      */
5459     getColumnCount : function(visibleOnly){
5460         if(visibleOnly === true){
5461             var c = 0;
5462             for(var i = 0, len = this.config.length; i < len; i++){
5463                 if(!this.isHidden(i)){
5464                     c++;
5465                 }
5466             }
5467             return c;
5468         }
5469         return this.config.length;
5470     },
5471
5472     /**
5473      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5474      * @param {Function} fn
5475      * @param {Object} scope (optional)
5476      * @return {Array} result
5477      */
5478     getColumnsBy : function(fn, scope){
5479         var r = [];
5480         for(var i = 0, len = this.config.length; i < len; i++){
5481             var c = this.config[i];
5482             if(fn.call(scope||this, c, i) === true){
5483                 r[r.length] = c;
5484             }
5485         }
5486         return r;
5487     },
5488
5489     /**
5490      * Returns true if the specified column is sortable.
5491      * @param {Number} col The column index
5492      * @return {Boolean}
5493      */
5494     isSortable : function(col){
5495         if(typeof this.config[col].sortable == "undefined"){
5496             return this.defaultSortable;
5497         }
5498         return this.config[col].sortable;
5499     },
5500
5501     /**
5502      * Returns the rendering (formatting) function defined for the column.
5503      * @param {Number} col The column index.
5504      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5505      */
5506     getRenderer : function(col){
5507         if(!this.config[col].renderer){
5508             return Roo.grid.ColumnModel.defaultRenderer;
5509         }
5510         return this.config[col].renderer;
5511     },
5512
5513     /**
5514      * Sets the rendering (formatting) function for a column.
5515      * @param {Number} col The column index
5516      * @param {Function} fn The function to use to process the cell's raw data
5517      * to return HTML markup for the grid view. The render function is called with
5518      * the following parameters:<ul>
5519      * <li>Data value.</li>
5520      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5521      * <li>css A CSS style string to apply to the table cell.</li>
5522      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5523      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5524      * <li>Row index</li>
5525      * <li>Column index</li>
5526      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5527      */
5528     setRenderer : function(col, fn){
5529         this.config[col].renderer = fn;
5530     },
5531
5532     /**
5533      * Returns the width for the specified column.
5534      * @param {Number} col The column index
5535      * @return {Number}
5536      */
5537     getColumnWidth : function(col){
5538         return this.config[col].width * 1 || this.defaultWidth;
5539     },
5540
5541     /**
5542      * Sets the width for a column.
5543      * @param {Number} col The column index
5544      * @param {Number} width The new width
5545      */
5546     setColumnWidth : function(col, width, suppressEvent){
5547         this.config[col].width = width;
5548         this.totalWidth = null;
5549         if(!suppressEvent){
5550              this.fireEvent("widthchange", this, col, width);
5551         }
5552     },
5553
5554     /**
5555      * Returns the total width of all columns.
5556      * @param {Boolean} includeHidden True to include hidden column widths
5557      * @return {Number}
5558      */
5559     getTotalWidth : function(includeHidden){
5560         if(!this.totalWidth){
5561             this.totalWidth = 0;
5562             for(var i = 0, len = this.config.length; i < len; i++){
5563                 if(includeHidden || !this.isHidden(i)){
5564                     this.totalWidth += this.getColumnWidth(i);
5565                 }
5566             }
5567         }
5568         return this.totalWidth;
5569     },
5570
5571     /**
5572      * Returns the header for the specified column.
5573      * @param {Number} col The column index
5574      * @return {String}
5575      */
5576     getColumnHeader : function(col){
5577         return this.config[col].header;
5578     },
5579
5580     /**
5581      * Sets the header for a column.
5582      * @param {Number} col The column index
5583      * @param {String} header The new header
5584      */
5585     setColumnHeader : function(col, header){
5586         this.config[col].header = header;
5587         this.fireEvent("headerchange", this, col, header);
5588     },
5589
5590     /**
5591      * Returns the tooltip for the specified column.
5592      * @param {Number} col The column index
5593      * @return {String}
5594      */
5595     getColumnTooltip : function(col){
5596             return this.config[col].tooltip;
5597     },
5598     /**
5599      * Sets the tooltip for a column.
5600      * @param {Number} col The column index
5601      * @param {String} tooltip The new tooltip
5602      */
5603     setColumnTooltip : function(col, tooltip){
5604             this.config[col].tooltip = tooltip;
5605     },
5606
5607     /**
5608      * Returns the dataIndex for the specified column.
5609      * @param {Number} col The column index
5610      * @return {Number}
5611      */
5612     getDataIndex : function(col){
5613         return this.config[col].dataIndex;
5614     },
5615
5616     /**
5617      * Sets the dataIndex for a column.
5618      * @param {Number} col The column index
5619      * @param {Number} dataIndex The new dataIndex
5620      */
5621     setDataIndex : function(col, dataIndex){
5622         this.config[col].dataIndex = dataIndex;
5623     },
5624
5625     
5626     
5627     /**
5628      * Returns true if the cell is editable.
5629      * @param {Number} colIndex The column index
5630      * @param {Number} rowIndex The row index - this is nto actually used..?
5631      * @return {Boolean}
5632      */
5633     isCellEditable : function(colIndex, rowIndex){
5634         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5635     },
5636
5637     /**
5638      * Returns the editor defined for the cell/column.
5639      * return false or null to disable editing.
5640      * @param {Number} colIndex The column index
5641      * @param {Number} rowIndex The row index
5642      * @return {Object}
5643      */
5644     getCellEditor : function(colIndex, rowIndex){
5645         return this.config[colIndex].editor;
5646     },
5647
5648     /**
5649      * Sets if a column is editable.
5650      * @param {Number} col The column index
5651      * @param {Boolean} editable True if the column is editable
5652      */
5653     setEditable : function(col, editable){
5654         this.config[col].editable = editable;
5655     },
5656
5657
5658     /**
5659      * Returns true if the column is hidden.
5660      * @param {Number} colIndex The column index
5661      * @return {Boolean}
5662      */
5663     isHidden : function(colIndex){
5664         return this.config[colIndex].hidden;
5665     },
5666
5667
5668     /**
5669      * Returns true if the column width cannot be changed
5670      */
5671     isFixed : function(colIndex){
5672         return this.config[colIndex].fixed;
5673     },
5674
5675     /**
5676      * Returns true if the column can be resized
5677      * @return {Boolean}
5678      */
5679     isResizable : function(colIndex){
5680         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5681     },
5682     /**
5683      * Sets if a column is hidden.
5684      * @param {Number} colIndex The column index
5685      * @param {Boolean} hidden True if the column is hidden
5686      */
5687     setHidden : function(colIndex, hidden){
5688         this.config[colIndex].hidden = hidden;
5689         this.totalWidth = null;
5690         this.fireEvent("hiddenchange", this, colIndex, hidden);
5691     },
5692
5693     /**
5694      * Sets the editor for a column.
5695      * @param {Number} col The column index
5696      * @param {Object} editor The editor object
5697      */
5698     setEditor : function(col, editor){
5699         this.config[col].editor = editor;
5700     }
5701 });
5702
5703 Roo.grid.ColumnModel.defaultRenderer = function(value)
5704 {
5705     if(typeof value == "object") {
5706         return value;
5707     }
5708         if(typeof value == "string" && value.length < 1){
5709             return "&#160;";
5710         }
5711     
5712         return String.format("{0}", value);
5713 };
5714
5715 // Alias for backwards compatibility
5716 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5717 /*
5718  * Based on:
5719  * Ext JS Library 1.1.1
5720  * Copyright(c) 2006-2007, Ext JS, LLC.
5721  *
5722  * Originally Released Under LGPL - original licence link has changed is not relivant.
5723  *
5724  * Fork - LGPL
5725  * <script type="text/javascript">
5726  */
5727  
5728 /**
5729  * @class Roo.LoadMask
5730  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5731  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5732  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5733  * element's UpdateManager load indicator and will be destroyed after the initial load.
5734  * @constructor
5735  * Create a new LoadMask
5736  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5737  * @param {Object} config The config object
5738  */
5739 Roo.LoadMask = function(el, config){
5740     this.el = Roo.get(el);
5741     Roo.apply(this, config);
5742     if(this.store){
5743         this.store.on('beforeload', this.onBeforeLoad, this);
5744         this.store.on('load', this.onLoad, this);
5745         this.store.on('loadexception', this.onLoadException, this);
5746         this.removeMask = false;
5747     }else{
5748         var um = this.el.getUpdateManager();
5749         um.showLoadIndicator = false; // disable the default indicator
5750         um.on('beforeupdate', this.onBeforeLoad, this);
5751         um.on('update', this.onLoad, this);
5752         um.on('failure', this.onLoad, this);
5753         this.removeMask = true;
5754     }
5755 };
5756
5757 Roo.LoadMask.prototype = {
5758     /**
5759      * @cfg {Boolean} removeMask
5760      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5761      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5762      */
5763     /**
5764      * @cfg {String} msg
5765      * The text to display in a centered loading message box (defaults to 'Loading...')
5766      */
5767     msg : 'Loading...',
5768     /**
5769      * @cfg {String} msgCls
5770      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5771      */
5772     msgCls : 'x-mask-loading',
5773
5774     /**
5775      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5776      * @type Boolean
5777      */
5778     disabled: false,
5779
5780     /**
5781      * Disables the mask to prevent it from being displayed
5782      */
5783     disable : function(){
5784        this.disabled = true;
5785     },
5786
5787     /**
5788      * Enables the mask so that it can be displayed
5789      */
5790     enable : function(){
5791         this.disabled = false;
5792     },
5793     
5794     onLoadException : function()
5795     {
5796         Roo.log(arguments);
5797         
5798         if (typeof(arguments[3]) != 'undefined') {
5799             Roo.MessageBox.alert("Error loading",arguments[3]);
5800         } 
5801         /*
5802         try {
5803             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5804                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5805             }   
5806         } catch(e) {
5807             
5808         }
5809         */
5810     
5811         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5812     },
5813     // private
5814     onLoad : function()
5815     {
5816         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5817     },
5818
5819     // private
5820     onBeforeLoad : function(){
5821         if(!this.disabled){
5822             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5823         }
5824     },
5825
5826     // private
5827     destroy : function(){
5828         if(this.store){
5829             this.store.un('beforeload', this.onBeforeLoad, this);
5830             this.store.un('load', this.onLoad, this);
5831             this.store.un('loadexception', this.onLoadException, this);
5832         }else{
5833             var um = this.el.getUpdateManager();
5834             um.un('beforeupdate', this.onBeforeLoad, this);
5835             um.un('update', this.onLoad, this);
5836             um.un('failure', this.onLoad, this);
5837         }
5838     }
5839 };/*
5840  * - LGPL
5841  *
5842  * table
5843  * 
5844  */
5845
5846 /**
5847  * @class Roo.bootstrap.Table
5848  * @extends Roo.bootstrap.Component
5849  * Bootstrap Table class
5850  * @cfg {String} cls table class
5851  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5852  * @cfg {String} bgcolor Specifies the background color for a table
5853  * @cfg {Number} border Specifies whether the table cells should have borders or not
5854  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5855  * @cfg {Number} cellspacing Specifies the space between cells
5856  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5857  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5858  * @cfg {String} sortable Specifies that the table should be sortable
5859  * @cfg {String} summary Specifies a summary of the content of a table
5860  * @cfg {Number} width Specifies the width of a table
5861  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5862  * 
5863  * @cfg {boolean} striped Should the rows be alternative striped
5864  * @cfg {boolean} bordered Add borders to the table
5865  * @cfg {boolean} hover Add hover highlighting
5866  * @cfg {boolean} condensed Format condensed
5867  * @cfg {boolean} responsive Format condensed
5868  * @cfg {Boolean} loadMask (true|false) default false
5869  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5870  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5871  * @cfg {Boolean} rowSelection (true|false) default false
5872  * @cfg {Boolean} cellSelection (true|false) default false
5873  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5874  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5875  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
5876  
5877  * 
5878  * @constructor
5879  * Create a new Table
5880  * @param {Object} config The config object
5881  */
5882
5883 Roo.bootstrap.Table = function(config){
5884     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5885     
5886   
5887     
5888     // BC...
5889     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5890     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5891     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5892     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5893     
5894     this.sm = this.sm || {xtype: 'RowSelectionModel'};
5895     if (this.sm) {
5896         this.sm.grid = this;
5897         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5898         this.sm = this.selModel;
5899         this.sm.xmodule = this.xmodule || false;
5900     }
5901     
5902     if (this.cm && typeof(this.cm.config) == 'undefined') {
5903         this.colModel = new Roo.grid.ColumnModel(this.cm);
5904         this.cm = this.colModel;
5905         this.cm.xmodule = this.xmodule || false;
5906     }
5907     if (this.store) {
5908         this.store= Roo.factory(this.store, Roo.data);
5909         this.ds = this.store;
5910         this.ds.xmodule = this.xmodule || false;
5911          
5912     }
5913     if (this.footer && this.store) {
5914         this.footer.dataSource = this.ds;
5915         this.footer = Roo.factory(this.footer);
5916     }
5917     
5918     /** @private */
5919     this.addEvents({
5920         /**
5921          * @event cellclick
5922          * Fires when a cell is clicked
5923          * @param {Roo.bootstrap.Table} this
5924          * @param {Roo.Element} el
5925          * @param {Number} rowIndex
5926          * @param {Number} columnIndex
5927          * @param {Roo.EventObject} e
5928          */
5929         "cellclick" : true,
5930         /**
5931          * @event celldblclick
5932          * Fires when a cell is double clicked
5933          * @param {Roo.bootstrap.Table} this
5934          * @param {Roo.Element} el
5935          * @param {Number} rowIndex
5936          * @param {Number} columnIndex
5937          * @param {Roo.EventObject} e
5938          */
5939         "celldblclick" : true,
5940         /**
5941          * @event rowclick
5942          * Fires when a row is clicked
5943          * @param {Roo.bootstrap.Table} this
5944          * @param {Roo.Element} el
5945          * @param {Number} rowIndex
5946          * @param {Roo.EventObject} e
5947          */
5948         "rowclick" : true,
5949         /**
5950          * @event rowdblclick
5951          * Fires when a row is double clicked
5952          * @param {Roo.bootstrap.Table} this
5953          * @param {Roo.Element} el
5954          * @param {Number} rowIndex
5955          * @param {Roo.EventObject} e
5956          */
5957         "rowdblclick" : true,
5958         /**
5959          * @event mouseover
5960          * Fires when a mouseover occur
5961          * @param {Roo.bootstrap.Table} this
5962          * @param {Roo.Element} el
5963          * @param {Number} rowIndex
5964          * @param {Number} columnIndex
5965          * @param {Roo.EventObject} e
5966          */
5967         "mouseover" : true,
5968         /**
5969          * @event mouseout
5970          * Fires when a mouseout occur
5971          * @param {Roo.bootstrap.Table} this
5972          * @param {Roo.Element} el
5973          * @param {Number} rowIndex
5974          * @param {Number} columnIndex
5975          * @param {Roo.EventObject} e
5976          */
5977         "mouseout" : true,
5978         /**
5979          * @event rowclass
5980          * Fires when a row is rendered, so you can change add a style to it.
5981          * @param {Roo.bootstrap.Table} this
5982          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5983          */
5984         'rowclass' : true,
5985           /**
5986          * @event rowsrendered
5987          * Fires when all the  rows have been rendered
5988          * @param {Roo.bootstrap.Table} this
5989          */
5990         'rowsrendered' : true,
5991         /**
5992          * @event contextmenu
5993          * The raw contextmenu event for the entire grid.
5994          * @param {Roo.EventObject} e
5995          */
5996         "contextmenu" : true,
5997         /**
5998          * @event rowcontextmenu
5999          * Fires when a row is right clicked
6000          * @param {Roo.bootstrap.Table} this
6001          * @param {Number} rowIndex
6002          * @param {Roo.EventObject} e
6003          */
6004         "rowcontextmenu" : true,
6005         /**
6006          * @event cellcontextmenu
6007          * Fires when a cell is right clicked
6008          * @param {Roo.bootstrap.Table} this
6009          * @param {Number} rowIndex
6010          * @param {Number} cellIndex
6011          * @param {Roo.EventObject} e
6012          */
6013          "cellcontextmenu" : true,
6014          /**
6015          * @event headercontextmenu
6016          * Fires when a header is right clicked
6017          * @param {Roo.bootstrap.Table} this
6018          * @param {Number} columnIndex
6019          * @param {Roo.EventObject} e
6020          */
6021         "headercontextmenu" : true
6022     });
6023 };
6024
6025 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6026     
6027     cls: false,
6028     align: false,
6029     bgcolor: false,
6030     border: false,
6031     cellpadding: false,
6032     cellspacing: false,
6033     frame: false,
6034     rules: false,
6035     sortable: false,
6036     summary: false,
6037     width: false,
6038     striped : false,
6039     scrollBody : false,
6040     bordered: false,
6041     hover:  false,
6042     condensed : false,
6043     responsive : false,
6044     sm : false,
6045     cm : false,
6046     store : false,
6047     loadMask : false,
6048     footerShow : true,
6049     headerShow : true,
6050   
6051     rowSelection : false,
6052     cellSelection : false,
6053     layout : false,
6054     
6055     // Roo.Element - the tbody
6056     mainBody: false,
6057     // Roo.Element - thead element
6058     mainHead: false,
6059     
6060     container: false, // used by gridpanel...
6061     
6062     lazyLoad : false,
6063     
6064     getAutoCreate : function()
6065     {
6066         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6067         
6068         cfg = {
6069             tag: 'table',
6070             cls : 'table',
6071             cn : []
6072         };
6073         if (this.scrollBody) {
6074             cfg.cls += ' table-body-fixed';
6075         }    
6076         if (this.striped) {
6077             cfg.cls += ' table-striped';
6078         }
6079         
6080         if (this.hover) {
6081             cfg.cls += ' table-hover';
6082         }
6083         if (this.bordered) {
6084             cfg.cls += ' table-bordered';
6085         }
6086         if (this.condensed) {
6087             cfg.cls += ' table-condensed';
6088         }
6089         if (this.responsive) {
6090             cfg.cls += ' table-responsive';
6091         }
6092         
6093         if (this.cls) {
6094             cfg.cls+=  ' ' +this.cls;
6095         }
6096         
6097         // this lot should be simplifed...
6098         
6099         if (this.align) {
6100             cfg.align=this.align;
6101         }
6102         if (this.bgcolor) {
6103             cfg.bgcolor=this.bgcolor;
6104         }
6105         if (this.border) {
6106             cfg.border=this.border;
6107         }
6108         if (this.cellpadding) {
6109             cfg.cellpadding=this.cellpadding;
6110         }
6111         if (this.cellspacing) {
6112             cfg.cellspacing=this.cellspacing;
6113         }
6114         if (this.frame) {
6115             cfg.frame=this.frame;
6116         }
6117         if (this.rules) {
6118             cfg.rules=this.rules;
6119         }
6120         if (this.sortable) {
6121             cfg.sortable=this.sortable;
6122         }
6123         if (this.summary) {
6124             cfg.summary=this.summary;
6125         }
6126         if (this.width) {
6127             cfg.width=this.width;
6128         }
6129         if (this.layout) {
6130             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6131         }
6132         
6133         if(this.store || this.cm){
6134             if(this.headerShow){
6135                 cfg.cn.push(this.renderHeader());
6136             }
6137             
6138             cfg.cn.push(this.renderBody());
6139             
6140             if(this.footerShow){
6141                 cfg.cn.push(this.renderFooter());
6142             }
6143             // where does this come from?
6144             //cfg.cls+=  ' TableGrid';
6145         }
6146         
6147         return { cn : [ cfg ] };
6148     },
6149     
6150     initEvents : function()
6151     {   
6152         if(!this.store || !this.cm){
6153             return;
6154         }
6155         if (this.selModel) {
6156             this.selModel.initEvents();
6157         }
6158         
6159         
6160         //Roo.log('initEvents with ds!!!!');
6161         
6162         this.mainBody = this.el.select('tbody', true).first();
6163         this.mainHead = this.el.select('thead', true).first();
6164         
6165         
6166         
6167         
6168         var _this = this;
6169         
6170         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6171             e.on('click', _this.sort, _this);
6172         });
6173         
6174         this.mainBody.on("click", this.onClick, this);
6175         this.mainBody.on("dblclick", this.onDblClick, this);
6176         
6177         // why is this done????? = it breaks dialogs??
6178         //this.parent().el.setStyle('position', 'relative');
6179         
6180         
6181         if (this.footer) {
6182             this.footer.parentId = this.id;
6183             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6184             
6185             if(this.lazyLoad){
6186                 this.el.select('tfoot tr td').first().addClass('hide');
6187             }
6188         } 
6189         
6190         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6191         
6192         this.store.on('load', this.onLoad, this);
6193         this.store.on('beforeload', this.onBeforeLoad, this);
6194         this.store.on('update', this.onUpdate, this);
6195         this.store.on('add', this.onAdd, this);
6196         this.store.on("clear", this.clear, this);
6197         
6198         this.el.on("contextmenu", this.onContextMenu, this);
6199         
6200         this.mainBody.on('scroll', this.onBodyScroll, this);
6201         
6202         this.cm.on("headerchange", this.onHeaderChange, this);
6203         
6204     },
6205     
6206     onContextMenu : function(e, t)
6207     {
6208         this.processEvent("contextmenu", e);
6209     },
6210     
6211     processEvent : function(name, e)
6212     {
6213         if (name != 'touchstart' ) {
6214             this.fireEvent(name, e);    
6215         }
6216         
6217         var t = e.getTarget();
6218         
6219         var cell = Roo.get(t);
6220         
6221         if(!cell){
6222             return;
6223         }
6224         
6225         if(cell.findParent('tfoot', false, true)){
6226             return;
6227         }
6228         
6229         if(cell.findParent('thead', false, true)){
6230             
6231             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6232                 cell = Roo.get(t).findParent('th', false, true);
6233                 if (!cell) {
6234                     Roo.log("failed to find th in thead?");
6235                     Roo.log(e.getTarget());
6236                     return;
6237                 }
6238             }
6239             
6240             var cellIndex = cell.dom.cellIndex;
6241             
6242             var ename = name == 'touchstart' ? 'click' : name;
6243             this.fireEvent("header" + ename, this, cellIndex, e);
6244             
6245             return;
6246         }
6247         
6248         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6249             cell = Roo.get(t).findParent('td', false, true);
6250             if (!cell) {
6251                 Roo.log("failed to find th in tbody?");
6252                 Roo.log(e.getTarget());
6253                 return;
6254             }
6255         }
6256         
6257         var row = cell.findParent('tr', false, true);
6258         var cellIndex = cell.dom.cellIndex;
6259         var rowIndex = row.dom.rowIndex - 1;
6260         
6261         if(row !== false){
6262             
6263             this.fireEvent("row" + name, this, rowIndex, e);
6264             
6265             if(cell !== false){
6266             
6267                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6268             }
6269         }
6270         
6271     },
6272     
6273     onMouseover : function(e, el)
6274     {
6275         var cell = Roo.get(el);
6276         
6277         if(!cell){
6278             return;
6279         }
6280         
6281         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6282             cell = cell.findParent('td', false, true);
6283         }
6284         
6285         var row = cell.findParent('tr', false, true);
6286         var cellIndex = cell.dom.cellIndex;
6287         var rowIndex = row.dom.rowIndex - 1; // start from 0
6288         
6289         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6290         
6291     },
6292     
6293     onMouseout : function(e, el)
6294     {
6295         var cell = Roo.get(el);
6296         
6297         if(!cell){
6298             return;
6299         }
6300         
6301         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6302             cell = cell.findParent('td', false, true);
6303         }
6304         
6305         var row = cell.findParent('tr', false, true);
6306         var cellIndex = cell.dom.cellIndex;
6307         var rowIndex = row.dom.rowIndex - 1; // start from 0
6308         
6309         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6310         
6311     },
6312     
6313     onClick : function(e, el)
6314     {
6315         var cell = Roo.get(el);
6316         
6317         if(!cell || (!this.cellSelection && !this.rowSelection)){
6318             return;
6319         }
6320         
6321         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6322             cell = cell.findParent('td', false, true);
6323         }
6324         
6325         if(!cell || typeof(cell) == 'undefined'){
6326             return;
6327         }
6328         
6329         var row = cell.findParent('tr', false, true);
6330         
6331         if(!row || typeof(row) == 'undefined'){
6332             return;
6333         }
6334         
6335         var cellIndex = cell.dom.cellIndex;
6336         var rowIndex = this.getRowIndex(row);
6337         
6338         // why??? - should these not be based on SelectionModel?
6339         if(this.cellSelection){
6340             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6341         }
6342         
6343         if(this.rowSelection){
6344             this.fireEvent('rowclick', this, row, rowIndex, e);
6345         }
6346         
6347         
6348     },
6349         
6350     onDblClick : function(e,el)
6351     {
6352         var cell = Roo.get(el);
6353         
6354         if(!cell || (!this.cellSelection && !this.rowSelection)){
6355             return;
6356         }
6357         
6358         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6359             cell = cell.findParent('td', false, true);
6360         }
6361         
6362         if(!cell || typeof(cell) == 'undefined'){
6363             return;
6364         }
6365         
6366         var row = cell.findParent('tr', false, true);
6367         
6368         if(!row || typeof(row) == 'undefined'){
6369             return;
6370         }
6371         
6372         var cellIndex = cell.dom.cellIndex;
6373         var rowIndex = this.getRowIndex(row);
6374         
6375         if(this.cellSelection){
6376             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6377         }
6378         
6379         if(this.rowSelection){
6380             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6381         }
6382     },
6383     
6384     sort : function(e,el)
6385     {
6386         var col = Roo.get(el);
6387         
6388         if(!col.hasClass('sortable')){
6389             return;
6390         }
6391         
6392         var sort = col.attr('sort');
6393         var dir = 'ASC';
6394         
6395         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6396             dir = 'DESC';
6397         }
6398         
6399         this.store.sortInfo = {field : sort, direction : dir};
6400         
6401         if (this.footer) {
6402             Roo.log("calling footer first");
6403             this.footer.onClick('first');
6404         } else {
6405         
6406             this.store.load({ params : { start : 0 } });
6407         }
6408     },
6409     
6410     renderHeader : function()
6411     {
6412         var header = {
6413             tag: 'thead',
6414             cn : []
6415         };
6416         
6417         var cm = this.cm;
6418         this.totalWidth = 0;
6419         
6420         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6421             
6422             var config = cm.config[i];
6423             
6424             var c = {
6425                 tag: 'th',
6426                 style : '',
6427                 html: cm.getColumnHeader(i)
6428             };
6429             
6430             var hh = '';
6431             
6432             if(typeof(config.sortable) != 'undefined' && config.sortable){
6433                 c.cls = 'sortable';
6434                 c.html = '<i class="glyphicon"></i>' + c.html;
6435             }
6436             
6437             if(typeof(config.lgHeader) != 'undefined'){
6438                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6439             }
6440             
6441             if(typeof(config.mdHeader) != 'undefined'){
6442                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6443             }
6444             
6445             if(typeof(config.smHeader) != 'undefined'){
6446                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6447             }
6448             
6449             if(typeof(config.xsHeader) != 'undefined'){
6450                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6451             }
6452             
6453             if(hh.length){
6454                 c.html = hh;
6455             }
6456             
6457             if(typeof(config.tooltip) != 'undefined'){
6458                 c.tooltip = config.tooltip;
6459             }
6460             
6461             if(typeof(config.colspan) != 'undefined'){
6462                 c.colspan = config.colspan;
6463             }
6464             
6465             if(typeof(config.hidden) != 'undefined' && config.hidden){
6466                 c.style += ' display:none;';
6467             }
6468             
6469             if(typeof(config.dataIndex) != 'undefined'){
6470                 c.sort = config.dataIndex;
6471             }
6472             
6473            
6474             
6475             if(typeof(config.align) != 'undefined' && config.align.length){
6476                 c.style += ' text-align:' + config.align + ';';
6477             }
6478             
6479             if(typeof(config.width) != 'undefined'){
6480                 c.style += ' width:' + config.width + 'px;';
6481                 this.totalWidth += config.width;
6482             } else {
6483                 this.totalWidth += 100; // assume minimum of 100 per column?
6484             }
6485             
6486             if(typeof(config.cls) != 'undefined'){
6487                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6488             }
6489             
6490             ['xs','sm','md','lg'].map(function(size){
6491                 
6492                 if(typeof(config[size]) == 'undefined'){
6493                     return;
6494                 }
6495                 
6496                 if (!config[size]) { // 0 = hidden
6497                     c.cls += ' hidden-' + size;
6498                     return;
6499                 }
6500                 
6501                 c.cls += ' col-' + size + '-' + config[size];
6502
6503             });
6504             
6505             header.cn.push(c)
6506         }
6507         
6508         return header;
6509     },
6510     
6511     renderBody : function()
6512     {
6513         var body = {
6514             tag: 'tbody',
6515             cn : [
6516                 {
6517                     tag: 'tr',
6518                     cn : [
6519                         {
6520                             tag : 'td',
6521                             colspan :  this.cm.getColumnCount()
6522                         }
6523                     ]
6524                 }
6525             ]
6526         };
6527         
6528         return body;
6529     },
6530     
6531     renderFooter : function()
6532     {
6533         var footer = {
6534             tag: 'tfoot',
6535             cn : [
6536                 {
6537                     tag: 'tr',
6538                     cn : [
6539                         {
6540                             tag : 'td',
6541                             colspan :  this.cm.getColumnCount()
6542                         }
6543                     ]
6544                 }
6545             ]
6546         };
6547         
6548         return footer;
6549     },
6550     
6551     
6552     
6553     onLoad : function()
6554     {
6555 //        Roo.log('ds onload');
6556         this.clear();
6557         
6558         var _this = this;
6559         var cm = this.cm;
6560         var ds = this.store;
6561         
6562         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6563             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6564             if (_this.store.sortInfo) {
6565                     
6566                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6567                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6568                 }
6569                 
6570                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6571                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6572                 }
6573             }
6574         });
6575         
6576         var tbody =  this.mainBody;
6577               
6578         if(ds.getCount() > 0){
6579             ds.data.each(function(d,rowIndex){
6580                 var row =  this.renderRow(cm, ds, rowIndex);
6581                 
6582                 tbody.createChild(row);
6583                 
6584                 var _this = this;
6585                 
6586                 if(row.cellObjects.length){
6587                     Roo.each(row.cellObjects, function(r){
6588                         _this.renderCellObject(r);
6589                     })
6590                 }
6591                 
6592             }, this);
6593         }
6594         
6595         Roo.each(this.el.select('tbody td', true).elements, function(e){
6596             e.on('mouseover', _this.onMouseover, _this);
6597         });
6598         
6599         Roo.each(this.el.select('tbody td', true).elements, function(e){
6600             e.on('mouseout', _this.onMouseout, _this);
6601         });
6602         this.fireEvent('rowsrendered', this);
6603         //if(this.loadMask){
6604         //    this.maskEl.hide();
6605         //}
6606         
6607         this.autoSize();
6608     },
6609     
6610     
6611     onUpdate : function(ds,record)
6612     {
6613         this.refreshRow(record);
6614         this.autoSize();
6615     },
6616     
6617     onRemove : function(ds, record, index, isUpdate){
6618         if(isUpdate !== true){
6619             this.fireEvent("beforerowremoved", this, index, record);
6620         }
6621         var bt = this.mainBody.dom;
6622         
6623         var rows = this.el.select('tbody > tr', true).elements;
6624         
6625         if(typeof(rows[index]) != 'undefined'){
6626             bt.removeChild(rows[index].dom);
6627         }
6628         
6629 //        if(bt.rows[index]){
6630 //            bt.removeChild(bt.rows[index]);
6631 //        }
6632         
6633         if(isUpdate !== true){
6634             //this.stripeRows(index);
6635             //this.syncRowHeights(index, index);
6636             //this.layout();
6637             this.fireEvent("rowremoved", this, index, record);
6638         }
6639     },
6640     
6641     onAdd : function(ds, records, rowIndex)
6642     {
6643         //Roo.log('on Add called');
6644         // - note this does not handle multiple adding very well..
6645         var bt = this.mainBody.dom;
6646         for (var i =0 ; i < records.length;i++) {
6647             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6648             //Roo.log(records[i]);
6649             //Roo.log(this.store.getAt(rowIndex+i));
6650             this.insertRow(this.store, rowIndex + i, false);
6651             return;
6652         }
6653         
6654     },
6655     
6656     
6657     refreshRow : function(record){
6658         var ds = this.store, index;
6659         if(typeof record == 'number'){
6660             index = record;
6661             record = ds.getAt(index);
6662         }else{
6663             index = ds.indexOf(record);
6664         }
6665         this.insertRow(ds, index, true);
6666         this.autoSize();
6667         this.onRemove(ds, record, index+1, true);
6668         this.autoSize();
6669         //this.syncRowHeights(index, index);
6670         //this.layout();
6671         this.fireEvent("rowupdated", this, index, record);
6672     },
6673     
6674     insertRow : function(dm, rowIndex, isUpdate){
6675         
6676         if(!isUpdate){
6677             this.fireEvent("beforerowsinserted", this, rowIndex);
6678         }
6679             //var s = this.getScrollState();
6680         var row = this.renderRow(this.cm, this.store, rowIndex);
6681         // insert before rowIndex..
6682         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6683         
6684         var _this = this;
6685                 
6686         if(row.cellObjects.length){
6687             Roo.each(row.cellObjects, function(r){
6688                 _this.renderCellObject(r);
6689             })
6690         }
6691             
6692         if(!isUpdate){
6693             this.fireEvent("rowsinserted", this, rowIndex);
6694             //this.syncRowHeights(firstRow, lastRow);
6695             //this.stripeRows(firstRow);
6696             //this.layout();
6697         }
6698         
6699     },
6700     
6701     
6702     getRowDom : function(rowIndex)
6703     {
6704         var rows = this.el.select('tbody > tr', true).elements;
6705         
6706         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6707         
6708     },
6709     // returns the object tree for a tr..
6710   
6711     
6712     renderRow : function(cm, ds, rowIndex) 
6713     {
6714         
6715         var d = ds.getAt(rowIndex);
6716         
6717         var row = {
6718             tag : 'tr',
6719             cn : []
6720         };
6721             
6722         var cellObjects = [];
6723         
6724         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6725             var config = cm.config[i];
6726             
6727             var renderer = cm.getRenderer(i);
6728             var value = '';
6729             var id = false;
6730             
6731             if(typeof(renderer) !== 'undefined'){
6732                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6733             }
6734             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6735             // and are rendered into the cells after the row is rendered - using the id for the element.
6736             
6737             if(typeof(value) === 'object'){
6738                 id = Roo.id();
6739                 cellObjects.push({
6740                     container : id,
6741                     cfg : value 
6742                 })
6743             }
6744             
6745             var rowcfg = {
6746                 record: d,
6747                 rowIndex : rowIndex,
6748                 colIndex : i,
6749                 rowClass : ''
6750             };
6751
6752             this.fireEvent('rowclass', this, rowcfg);
6753             
6754             var td = {
6755                 tag: 'td',
6756                 cls : rowcfg.rowClass,
6757                 style: '',
6758                 html: (typeof(value) === 'object') ? '' : value
6759             };
6760             
6761             if (id) {
6762                 td.id = id;
6763             }
6764             
6765             if(typeof(config.colspan) != 'undefined'){
6766                 td.colspan = config.colspan;
6767             }
6768             
6769             if(typeof(config.hidden) != 'undefined' && config.hidden){
6770                 td.style += ' display:none;';
6771             }
6772             
6773             if(typeof(config.align) != 'undefined' && config.align.length){
6774                 td.style += ' text-align:' + config.align + ';';
6775             }
6776             
6777             if(typeof(config.width) != 'undefined'){
6778                 td.style += ' width:' +  config.width + 'px;';
6779             }
6780             
6781             if(typeof(config.cursor) != 'undefined'){
6782                 td.style += ' cursor:' +  config.cursor + ';';
6783             }
6784             
6785             if(typeof(config.cls) != 'undefined'){
6786                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6787             }
6788             
6789             ['xs','sm','md','lg'].map(function(size){
6790                 
6791                 if(typeof(config[size]) == 'undefined'){
6792                     return;
6793                 }
6794                 
6795                 if (!config[size]) { // 0 = hidden
6796                     td.cls += ' hidden-' + size;
6797                     return;
6798                 }
6799                 
6800                 td.cls += ' col-' + size + '-' + config[size];
6801
6802             });
6803              
6804             row.cn.push(td);
6805            
6806         }
6807         
6808         row.cellObjects = cellObjects;
6809         
6810         return row;
6811           
6812     },
6813     
6814     
6815     
6816     onBeforeLoad : function()
6817     {
6818         //Roo.log('ds onBeforeLoad');
6819         
6820         //this.clear();
6821         
6822         //if(this.loadMask){
6823         //    this.maskEl.show();
6824         //}
6825     },
6826      /**
6827      * Remove all rows
6828      */
6829     clear : function()
6830     {
6831         this.el.select('tbody', true).first().dom.innerHTML = '';
6832     },
6833     /**
6834      * Show or hide a row.
6835      * @param {Number} rowIndex to show or hide
6836      * @param {Boolean} state hide
6837      */
6838     setRowVisibility : function(rowIndex, state)
6839     {
6840         var bt = this.mainBody.dom;
6841         
6842         var rows = this.el.select('tbody > tr', true).elements;
6843         
6844         if(typeof(rows[rowIndex]) == 'undefined'){
6845             return;
6846         }
6847         rows[rowIndex].dom.style.display = state ? '' : 'none';
6848     },
6849     
6850     
6851     getSelectionModel : function(){
6852         if(!this.selModel){
6853             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6854         }
6855         return this.selModel;
6856     },
6857     /*
6858      * Render the Roo.bootstrap object from renderder
6859      */
6860     renderCellObject : function(r)
6861     {
6862         var _this = this;
6863         
6864         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6865         
6866         var t = r.cfg.render(r.container);
6867         
6868         if(r.cfg.cn){
6869             Roo.each(r.cfg.cn, function(c){
6870                 var child = {
6871                     container: t.getChildContainer(),
6872                     cfg: c
6873                 };
6874                 _this.renderCellObject(child);
6875             })
6876         }
6877     },
6878     
6879     getRowIndex : function(row)
6880     {
6881         var rowIndex = -1;
6882         
6883         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6884             if(el != row){
6885                 return;
6886             }
6887             
6888             rowIndex = index;
6889         });
6890         
6891         return rowIndex;
6892     },
6893      /**
6894      * Returns the grid's underlying element = used by panel.Grid
6895      * @return {Element} The element
6896      */
6897     getGridEl : function(){
6898         return this.el;
6899     },
6900      /**
6901      * Forces a resize - used by panel.Grid
6902      * @return {Element} The element
6903      */
6904     autoSize : function()
6905     {
6906         //var ctr = Roo.get(this.container.dom.parentElement);
6907         var ctr = Roo.get(this.el.dom);
6908         
6909         var thd = this.getGridEl().select('thead',true).first();
6910         var tbd = this.getGridEl().select('tbody', true).first();
6911         var tfd = this.getGridEl().select('tfoot', true).first();
6912         
6913         var cw = ctr.getWidth();
6914         
6915         if (tbd) {
6916             
6917             tbd.setSize(ctr.getWidth(),
6918                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
6919             );
6920             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6921             cw -= barsize;
6922         }
6923         cw = Math.max(cw, this.totalWidth);
6924         this.getGridEl().select('tr',true).setWidth(cw);
6925         // resize 'expandable coloumn?
6926         
6927         return; // we doe not have a view in this design..
6928         
6929     },
6930     onBodyScroll: function()
6931     {
6932         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6933         if(this.mainHead){
6934             this.mainHead.setStyle({
6935                 'position' : 'relative',
6936                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6937             });
6938         }
6939         
6940         if(this.lazyLoad){
6941             
6942             var scrollHeight = this.mainBody.dom.scrollHeight;
6943             
6944             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
6945             
6946             var height = this.mainBody.getHeight();
6947             
6948             if(scrollHeight - height == scrollTop) {
6949                 
6950                 var total = this.ds.getTotalCount();
6951                 
6952                 if(this.footer.cursor + this.footer.pageSize < total){
6953                     
6954                     this.footer.ds.load({
6955                         params : {
6956                             start : this.footer.cursor + this.footer.pageSize,
6957                             limit : this.footer.pageSize
6958                         },
6959                         add : true
6960                     });
6961                 }
6962             }
6963             
6964         }
6965     },
6966     
6967     onHeaderChange : function()
6968     {
6969         
6970         var header = this.renderHeader();
6971         var table = this.el.select('table', true).first();
6972         
6973         this.mainHead.remove();
6974         this.mainHead = table.createChild(header, this.mainBody, false);
6975     }
6976     
6977 });
6978
6979  
6980
6981  /*
6982  * - LGPL
6983  *
6984  * table cell
6985  * 
6986  */
6987
6988 /**
6989  * @class Roo.bootstrap.TableCell
6990  * @extends Roo.bootstrap.Component
6991  * Bootstrap TableCell class
6992  * @cfg {String} html cell contain text
6993  * @cfg {String} cls cell class
6994  * @cfg {String} tag cell tag (td|th) default td
6995  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6996  * @cfg {String} align Aligns the content in a cell
6997  * @cfg {String} axis Categorizes cells
6998  * @cfg {String} bgcolor Specifies the background color of a cell
6999  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7000  * @cfg {Number} colspan Specifies the number of columns a cell should span
7001  * @cfg {String} headers Specifies one or more header cells a cell is related to
7002  * @cfg {Number} height Sets the height of a cell
7003  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7004  * @cfg {Number} rowspan Sets the number of rows a cell should span
7005  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7006  * @cfg {String} valign Vertical aligns the content in a cell
7007  * @cfg {Number} width Specifies the width of a cell
7008  * 
7009  * @constructor
7010  * Create a new TableCell
7011  * @param {Object} config The config object
7012  */
7013
7014 Roo.bootstrap.TableCell = function(config){
7015     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7016 };
7017
7018 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7019     
7020     html: false,
7021     cls: false,
7022     tag: false,
7023     abbr: false,
7024     align: false,
7025     axis: false,
7026     bgcolor: false,
7027     charoff: false,
7028     colspan: false,
7029     headers: false,
7030     height: false,
7031     nowrap: false,
7032     rowspan: false,
7033     scope: false,
7034     valign: false,
7035     width: false,
7036     
7037     
7038     getAutoCreate : function(){
7039         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7040         
7041         cfg = {
7042             tag: 'td'
7043         };
7044         
7045         if(this.tag){
7046             cfg.tag = this.tag;
7047         }
7048         
7049         if (this.html) {
7050             cfg.html=this.html
7051         }
7052         if (this.cls) {
7053             cfg.cls=this.cls
7054         }
7055         if (this.abbr) {
7056             cfg.abbr=this.abbr
7057         }
7058         if (this.align) {
7059             cfg.align=this.align
7060         }
7061         if (this.axis) {
7062             cfg.axis=this.axis
7063         }
7064         if (this.bgcolor) {
7065             cfg.bgcolor=this.bgcolor
7066         }
7067         if (this.charoff) {
7068             cfg.charoff=this.charoff
7069         }
7070         if (this.colspan) {
7071             cfg.colspan=this.colspan
7072         }
7073         if (this.headers) {
7074             cfg.headers=this.headers
7075         }
7076         if (this.height) {
7077             cfg.height=this.height
7078         }
7079         if (this.nowrap) {
7080             cfg.nowrap=this.nowrap
7081         }
7082         if (this.rowspan) {
7083             cfg.rowspan=this.rowspan
7084         }
7085         if (this.scope) {
7086             cfg.scope=this.scope
7087         }
7088         if (this.valign) {
7089             cfg.valign=this.valign
7090         }
7091         if (this.width) {
7092             cfg.width=this.width
7093         }
7094         
7095         
7096         return cfg;
7097     }
7098    
7099 });
7100
7101  
7102
7103  /*
7104  * - LGPL
7105  *
7106  * table row
7107  * 
7108  */
7109
7110 /**
7111  * @class Roo.bootstrap.TableRow
7112  * @extends Roo.bootstrap.Component
7113  * Bootstrap TableRow class
7114  * @cfg {String} cls row class
7115  * @cfg {String} align Aligns the content in a table row
7116  * @cfg {String} bgcolor Specifies a background color for a table row
7117  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7118  * @cfg {String} valign Vertical aligns the content in a table row
7119  * 
7120  * @constructor
7121  * Create a new TableRow
7122  * @param {Object} config The config object
7123  */
7124
7125 Roo.bootstrap.TableRow = function(config){
7126     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7127 };
7128
7129 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7130     
7131     cls: false,
7132     align: false,
7133     bgcolor: false,
7134     charoff: false,
7135     valign: false,
7136     
7137     getAutoCreate : function(){
7138         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7139         
7140         cfg = {
7141             tag: 'tr'
7142         };
7143             
7144         if(this.cls){
7145             cfg.cls = this.cls;
7146         }
7147         if(this.align){
7148             cfg.align = this.align;
7149         }
7150         if(this.bgcolor){
7151             cfg.bgcolor = this.bgcolor;
7152         }
7153         if(this.charoff){
7154             cfg.charoff = this.charoff;
7155         }
7156         if(this.valign){
7157             cfg.valign = this.valign;
7158         }
7159         
7160         return cfg;
7161     }
7162    
7163 });
7164
7165  
7166
7167  /*
7168  * - LGPL
7169  *
7170  * table body
7171  * 
7172  */
7173
7174 /**
7175  * @class Roo.bootstrap.TableBody
7176  * @extends Roo.bootstrap.Component
7177  * Bootstrap TableBody class
7178  * @cfg {String} cls element class
7179  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7180  * @cfg {String} align Aligns the content inside the element
7181  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7182  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7183  * 
7184  * @constructor
7185  * Create a new TableBody
7186  * @param {Object} config The config object
7187  */
7188
7189 Roo.bootstrap.TableBody = function(config){
7190     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7191 };
7192
7193 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7194     
7195     cls: false,
7196     tag: false,
7197     align: false,
7198     charoff: false,
7199     valign: false,
7200     
7201     getAutoCreate : function(){
7202         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7203         
7204         cfg = {
7205             tag: 'tbody'
7206         };
7207             
7208         if (this.cls) {
7209             cfg.cls=this.cls
7210         }
7211         if(this.tag){
7212             cfg.tag = this.tag;
7213         }
7214         
7215         if(this.align){
7216             cfg.align = this.align;
7217         }
7218         if(this.charoff){
7219             cfg.charoff = this.charoff;
7220         }
7221         if(this.valign){
7222             cfg.valign = this.valign;
7223         }
7224         
7225         return cfg;
7226     }
7227     
7228     
7229 //    initEvents : function()
7230 //    {
7231 //        
7232 //        if(!this.store){
7233 //            return;
7234 //        }
7235 //        
7236 //        this.store = Roo.factory(this.store, Roo.data);
7237 //        this.store.on('load', this.onLoad, this);
7238 //        
7239 //        this.store.load();
7240 //        
7241 //    },
7242 //    
7243 //    onLoad: function () 
7244 //    {   
7245 //        this.fireEvent('load', this);
7246 //    }
7247 //    
7248 //   
7249 });
7250
7251  
7252
7253  /*
7254  * Based on:
7255  * Ext JS Library 1.1.1
7256  * Copyright(c) 2006-2007, Ext JS, LLC.
7257  *
7258  * Originally Released Under LGPL - original licence link has changed is not relivant.
7259  *
7260  * Fork - LGPL
7261  * <script type="text/javascript">
7262  */
7263
7264 // as we use this in bootstrap.
7265 Roo.namespace('Roo.form');
7266  /**
7267  * @class Roo.form.Action
7268  * Internal Class used to handle form actions
7269  * @constructor
7270  * @param {Roo.form.BasicForm} el The form element or its id
7271  * @param {Object} config Configuration options
7272  */
7273
7274  
7275  
7276 // define the action interface
7277 Roo.form.Action = function(form, options){
7278     this.form = form;
7279     this.options = options || {};
7280 };
7281 /**
7282  * Client Validation Failed
7283  * @const 
7284  */
7285 Roo.form.Action.CLIENT_INVALID = 'client';
7286 /**
7287  * Server Validation Failed
7288  * @const 
7289  */
7290 Roo.form.Action.SERVER_INVALID = 'server';
7291  /**
7292  * Connect to Server Failed
7293  * @const 
7294  */
7295 Roo.form.Action.CONNECT_FAILURE = 'connect';
7296 /**
7297  * Reading Data from Server Failed
7298  * @const 
7299  */
7300 Roo.form.Action.LOAD_FAILURE = 'load';
7301
7302 Roo.form.Action.prototype = {
7303     type : 'default',
7304     failureType : undefined,
7305     response : undefined,
7306     result : undefined,
7307
7308     // interface method
7309     run : function(options){
7310
7311     },
7312
7313     // interface method
7314     success : function(response){
7315
7316     },
7317
7318     // interface method
7319     handleResponse : function(response){
7320
7321     },
7322
7323     // default connection failure
7324     failure : function(response){
7325         
7326         this.response = response;
7327         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7328         this.form.afterAction(this, false);
7329     },
7330
7331     processResponse : function(response){
7332         this.response = response;
7333         if(!response.responseText){
7334             return true;
7335         }
7336         this.result = this.handleResponse(response);
7337         return this.result;
7338     },
7339
7340     // utility functions used internally
7341     getUrl : function(appendParams){
7342         var url = this.options.url || this.form.url || this.form.el.dom.action;
7343         if(appendParams){
7344             var p = this.getParams();
7345             if(p){
7346                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7347             }
7348         }
7349         return url;
7350     },
7351
7352     getMethod : function(){
7353         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7354     },
7355
7356     getParams : function(){
7357         var bp = this.form.baseParams;
7358         var p = this.options.params;
7359         if(p){
7360             if(typeof p == "object"){
7361                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7362             }else if(typeof p == 'string' && bp){
7363                 p += '&' + Roo.urlEncode(bp);
7364             }
7365         }else if(bp){
7366             p = Roo.urlEncode(bp);
7367         }
7368         return p;
7369     },
7370
7371     createCallback : function(){
7372         return {
7373             success: this.success,
7374             failure: this.failure,
7375             scope: this,
7376             timeout: (this.form.timeout*1000),
7377             upload: this.form.fileUpload ? this.success : undefined
7378         };
7379     }
7380 };
7381
7382 Roo.form.Action.Submit = function(form, options){
7383     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7384 };
7385
7386 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7387     type : 'submit',
7388
7389     haveProgress : false,
7390     uploadComplete : false,
7391     
7392     // uploadProgress indicator.
7393     uploadProgress : function()
7394     {
7395         if (!this.form.progressUrl) {
7396             return;
7397         }
7398         
7399         if (!this.haveProgress) {
7400             Roo.MessageBox.progress("Uploading", "Uploading");
7401         }
7402         if (this.uploadComplete) {
7403            Roo.MessageBox.hide();
7404            return;
7405         }
7406         
7407         this.haveProgress = true;
7408    
7409         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7410         
7411         var c = new Roo.data.Connection();
7412         c.request({
7413             url : this.form.progressUrl,
7414             params: {
7415                 id : uid
7416             },
7417             method: 'GET',
7418             success : function(req){
7419                //console.log(data);
7420                 var rdata = false;
7421                 var edata;
7422                 try  {
7423                    rdata = Roo.decode(req.responseText)
7424                 } catch (e) {
7425                     Roo.log("Invalid data from server..");
7426                     Roo.log(edata);
7427                     return;
7428                 }
7429                 if (!rdata || !rdata.success) {
7430                     Roo.log(rdata);
7431                     Roo.MessageBox.alert(Roo.encode(rdata));
7432                     return;
7433                 }
7434                 var data = rdata.data;
7435                 
7436                 if (this.uploadComplete) {
7437                    Roo.MessageBox.hide();
7438                    return;
7439                 }
7440                    
7441                 if (data){
7442                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7443                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7444                     );
7445                 }
7446                 this.uploadProgress.defer(2000,this);
7447             },
7448        
7449             failure: function(data) {
7450                 Roo.log('progress url failed ');
7451                 Roo.log(data);
7452             },
7453             scope : this
7454         });
7455            
7456     },
7457     
7458     
7459     run : function()
7460     {
7461         // run get Values on the form, so it syncs any secondary forms.
7462         this.form.getValues();
7463         
7464         var o = this.options;
7465         var method = this.getMethod();
7466         var isPost = method == 'POST';
7467         if(o.clientValidation === false || this.form.isValid()){
7468             
7469             if (this.form.progressUrl) {
7470                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7471                     (new Date() * 1) + '' + Math.random());
7472                     
7473             } 
7474             
7475             
7476             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7477                 form:this.form.el.dom,
7478                 url:this.getUrl(!isPost),
7479                 method: method,
7480                 params:isPost ? this.getParams() : null,
7481                 isUpload: this.form.fileUpload
7482             }));
7483             
7484             this.uploadProgress();
7485
7486         }else if (o.clientValidation !== false){ // client validation failed
7487             this.failureType = Roo.form.Action.CLIENT_INVALID;
7488             this.form.afterAction(this, false);
7489         }
7490     },
7491
7492     success : function(response)
7493     {
7494         this.uploadComplete= true;
7495         if (this.haveProgress) {
7496             Roo.MessageBox.hide();
7497         }
7498         
7499         
7500         var result = this.processResponse(response);
7501         if(result === true || result.success){
7502             this.form.afterAction(this, true);
7503             return;
7504         }
7505         if(result.errors){
7506             this.form.markInvalid(result.errors);
7507             this.failureType = Roo.form.Action.SERVER_INVALID;
7508         }
7509         this.form.afterAction(this, false);
7510     },
7511     failure : function(response)
7512     {
7513         this.uploadComplete= true;
7514         if (this.haveProgress) {
7515             Roo.MessageBox.hide();
7516         }
7517         
7518         this.response = response;
7519         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7520         this.form.afterAction(this, false);
7521     },
7522     
7523     handleResponse : function(response){
7524         if(this.form.errorReader){
7525             var rs = this.form.errorReader.read(response);
7526             var errors = [];
7527             if(rs.records){
7528                 for(var i = 0, len = rs.records.length; i < len; i++) {
7529                     var r = rs.records[i];
7530                     errors[i] = r.data;
7531                 }
7532             }
7533             if(errors.length < 1){
7534                 errors = null;
7535             }
7536             return {
7537                 success : rs.success,
7538                 errors : errors
7539             };
7540         }
7541         var ret = false;
7542         try {
7543             ret = Roo.decode(response.responseText);
7544         } catch (e) {
7545             ret = {
7546                 success: false,
7547                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7548                 errors : []
7549             };
7550         }
7551         return ret;
7552         
7553     }
7554 });
7555
7556
7557 Roo.form.Action.Load = function(form, options){
7558     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7559     this.reader = this.form.reader;
7560 };
7561
7562 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7563     type : 'load',
7564
7565     run : function(){
7566         
7567         Roo.Ajax.request(Roo.apply(
7568                 this.createCallback(), {
7569                     method:this.getMethod(),
7570                     url:this.getUrl(false),
7571                     params:this.getParams()
7572         }));
7573     },
7574
7575     success : function(response){
7576         
7577         var result = this.processResponse(response);
7578         if(result === true || !result.success || !result.data){
7579             this.failureType = Roo.form.Action.LOAD_FAILURE;
7580             this.form.afterAction(this, false);
7581             return;
7582         }
7583         this.form.clearInvalid();
7584         this.form.setValues(result.data);
7585         this.form.afterAction(this, true);
7586     },
7587
7588     handleResponse : function(response){
7589         if(this.form.reader){
7590             var rs = this.form.reader.read(response);
7591             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7592             return {
7593                 success : rs.success,
7594                 data : data
7595             };
7596         }
7597         return Roo.decode(response.responseText);
7598     }
7599 });
7600
7601 Roo.form.Action.ACTION_TYPES = {
7602     'load' : Roo.form.Action.Load,
7603     'submit' : Roo.form.Action.Submit
7604 };/*
7605  * - LGPL
7606  *
7607  * form
7608  *
7609  */
7610
7611 /**
7612  * @class Roo.bootstrap.Form
7613  * @extends Roo.bootstrap.Component
7614  * Bootstrap Form class
7615  * @cfg {String} method  GET | POST (default POST)
7616  * @cfg {String} labelAlign top | left (default top)
7617  * @cfg {String} align left  | right - for navbars
7618  * @cfg {Boolean} loadMask load mask when submit (default true)
7619
7620  *
7621  * @constructor
7622  * Create a new Form
7623  * @param {Object} config The config object
7624  */
7625
7626
7627 Roo.bootstrap.Form = function(config){
7628     
7629     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7630     
7631     Roo.bootstrap.Form.popover.apply();
7632     
7633     this.addEvents({
7634         /**
7635          * @event clientvalidation
7636          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7637          * @param {Form} this
7638          * @param {Boolean} valid true if the form has passed client-side validation
7639          */
7640         clientvalidation: true,
7641         /**
7642          * @event beforeaction
7643          * Fires before any action is performed. Return false to cancel the action.
7644          * @param {Form} this
7645          * @param {Action} action The action to be performed
7646          */
7647         beforeaction: true,
7648         /**
7649          * @event actionfailed
7650          * Fires when an action fails.
7651          * @param {Form} this
7652          * @param {Action} action The action that failed
7653          */
7654         actionfailed : true,
7655         /**
7656          * @event actioncomplete
7657          * Fires when an action is completed.
7658          * @param {Form} this
7659          * @param {Action} action The action that completed
7660          */
7661         actioncomplete : true
7662     });
7663 };
7664
7665 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7666
7667      /**
7668      * @cfg {String} method
7669      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7670      */
7671     method : 'POST',
7672     /**
7673      * @cfg {String} url
7674      * The URL to use for form actions if one isn't supplied in the action options.
7675      */
7676     /**
7677      * @cfg {Boolean} fileUpload
7678      * Set to true if this form is a file upload.
7679      */
7680
7681     /**
7682      * @cfg {Object} baseParams
7683      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7684      */
7685
7686     /**
7687      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7688      */
7689     timeout: 30,
7690     /**
7691      * @cfg {Sting} align (left|right) for navbar forms
7692      */
7693     align : 'left',
7694
7695     // private
7696     activeAction : null,
7697
7698     /**
7699      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7700      * element by passing it or its id or mask the form itself by passing in true.
7701      * @type Mixed
7702      */
7703     waitMsgTarget : false,
7704
7705     loadMask : true,
7706     
7707     /**
7708      * @cfg {Boolean} errorMask (true|false) default false
7709      */
7710     errorMask : false,
7711     
7712     /**
7713      * @cfg {Number} maskOffset Default 100
7714      */
7715     maskOffset : 100,
7716     
7717     /**
7718      * @cfg {Boolean} maskBody
7719      */
7720     maskBody : false,
7721
7722     getAutoCreate : function(){
7723
7724         var cfg = {
7725             tag: 'form',
7726             method : this.method || 'POST',
7727             id : this.id || Roo.id(),
7728             cls : ''
7729         };
7730         if (this.parent().xtype.match(/^Nav/)) {
7731             cfg.cls = 'navbar-form navbar-' + this.align;
7732
7733         }
7734
7735         if (this.labelAlign == 'left' ) {
7736             cfg.cls += ' form-horizontal';
7737         }
7738
7739
7740         return cfg;
7741     },
7742     initEvents : function()
7743     {
7744         this.el.on('submit', this.onSubmit, this);
7745         // this was added as random key presses on the form where triggering form submit.
7746         this.el.on('keypress', function(e) {
7747             if (e.getCharCode() != 13) {
7748                 return true;
7749             }
7750             // we might need to allow it for textareas.. and some other items.
7751             // check e.getTarget().
7752
7753             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7754                 return true;
7755             }
7756
7757             Roo.log("keypress blocked");
7758
7759             e.preventDefault();
7760             return false;
7761         });
7762         
7763     },
7764     // private
7765     onSubmit : function(e){
7766         e.stopEvent();
7767     },
7768
7769      /**
7770      * Returns true if client-side validation on the form is successful.
7771      * @return Boolean
7772      */
7773     isValid : function(){
7774         var items = this.getItems();
7775         var valid = true;
7776         var target = false;
7777         
7778         items.each(function(f){
7779             if(f.validate()){
7780                 return;
7781             }
7782             valid = false;
7783
7784             if(!target && f.el.isVisible(true)){
7785                 target = f;
7786             }
7787            
7788         });
7789         
7790         if(this.errorMask && !valid){
7791             Roo.bootstrap.Form.popover.mask(this, target);
7792         }
7793         
7794         return valid;
7795     },
7796     
7797     /**
7798      * Returns true if any fields in this form have changed since their original load.
7799      * @return Boolean
7800      */
7801     isDirty : function(){
7802         var dirty = false;
7803         var items = this.getItems();
7804         items.each(function(f){
7805            if(f.isDirty()){
7806                dirty = true;
7807                return false;
7808            }
7809            return true;
7810         });
7811         return dirty;
7812     },
7813      /**
7814      * Performs a predefined action (submit or load) or custom actions you define on this form.
7815      * @param {String} actionName The name of the action type
7816      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7817      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7818      * accept other config options):
7819      * <pre>
7820 Property          Type             Description
7821 ----------------  ---------------  ----------------------------------------------------------------------------------
7822 url               String           The url for the action (defaults to the form's url)
7823 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7824 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7825 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7826                                    validate the form on the client (defaults to false)
7827      * </pre>
7828      * @return {BasicForm} this
7829      */
7830     doAction : function(action, options){
7831         if(typeof action == 'string'){
7832             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7833         }
7834         if(this.fireEvent('beforeaction', this, action) !== false){
7835             this.beforeAction(action);
7836             action.run.defer(100, action);
7837         }
7838         return this;
7839     },
7840
7841     // private
7842     beforeAction : function(action){
7843         var o = action.options;
7844         
7845         if(this.loadMask){
7846             
7847             if(this.maskBody){
7848                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7849             } else {
7850                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7851             }
7852         }
7853         // not really supported yet.. ??
7854
7855         //if(this.waitMsgTarget === true){
7856         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7857         //}else if(this.waitMsgTarget){
7858         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7859         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7860         //}else {
7861         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7862        // }
7863
7864     },
7865
7866     // private
7867     afterAction : function(action, success){
7868         this.activeAction = null;
7869         var o = action.options;
7870
7871         if(this.loadMask){
7872             
7873             if(this.maskBody){
7874                 Roo.get(document.body).unmask();
7875             } else {
7876                 this.el.unmask();
7877             }
7878         }
7879         
7880         //if(this.waitMsgTarget === true){
7881 //            this.el.unmask();
7882         //}else if(this.waitMsgTarget){
7883         //    this.waitMsgTarget.unmask();
7884         //}else{
7885         //    Roo.MessageBox.updateProgress(1);
7886         //    Roo.MessageBox.hide();
7887        // }
7888         //
7889         if(success){
7890             if(o.reset){
7891                 this.reset();
7892             }
7893             Roo.callback(o.success, o.scope, [this, action]);
7894             this.fireEvent('actioncomplete', this, action);
7895
7896         }else{
7897
7898             // failure condition..
7899             // we have a scenario where updates need confirming.
7900             // eg. if a locking scenario exists..
7901             // we look for { errors : { needs_confirm : true }} in the response.
7902             if (
7903                 (typeof(action.result) != 'undefined')  &&
7904                 (typeof(action.result.errors) != 'undefined')  &&
7905                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7906            ){
7907                 var _t = this;
7908                 Roo.log("not supported yet");
7909                  /*
7910
7911                 Roo.MessageBox.confirm(
7912                     "Change requires confirmation",
7913                     action.result.errorMsg,
7914                     function(r) {
7915                         if (r != 'yes') {
7916                             return;
7917                         }
7918                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7919                     }
7920
7921                 );
7922                 */
7923
7924
7925                 return;
7926             }
7927
7928             Roo.callback(o.failure, o.scope, [this, action]);
7929             // show an error message if no failed handler is set..
7930             if (!this.hasListener('actionfailed')) {
7931                 Roo.log("need to add dialog support");
7932                 /*
7933                 Roo.MessageBox.alert("Error",
7934                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7935                         action.result.errorMsg :
7936                         "Saving Failed, please check your entries or try again"
7937                 );
7938                 */
7939             }
7940
7941             this.fireEvent('actionfailed', this, action);
7942         }
7943
7944     },
7945     /**
7946      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7947      * @param {String} id The value to search for
7948      * @return Field
7949      */
7950     findField : function(id){
7951         var items = this.getItems();
7952         var field = items.get(id);
7953         if(!field){
7954              items.each(function(f){
7955                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7956                     field = f;
7957                     return false;
7958                 }
7959                 return true;
7960             });
7961         }
7962         return field || null;
7963     },
7964      /**
7965      * Mark fields in this form invalid in bulk.
7966      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7967      * @return {BasicForm} this
7968      */
7969     markInvalid : function(errors){
7970         if(errors instanceof Array){
7971             for(var i = 0, len = errors.length; i < len; i++){
7972                 var fieldError = errors[i];
7973                 var f = this.findField(fieldError.id);
7974                 if(f){
7975                     f.markInvalid(fieldError.msg);
7976                 }
7977             }
7978         }else{
7979             var field, id;
7980             for(id in errors){
7981                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7982                     field.markInvalid(errors[id]);
7983                 }
7984             }
7985         }
7986         //Roo.each(this.childForms || [], function (f) {
7987         //    f.markInvalid(errors);
7988         //});
7989
7990         return this;
7991     },
7992
7993     /**
7994      * Set values for fields in this form in bulk.
7995      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7996      * @return {BasicForm} this
7997      */
7998     setValues : function(values){
7999         if(values instanceof Array){ // array of objects
8000             for(var i = 0, len = values.length; i < len; i++){
8001                 var v = values[i];
8002                 var f = this.findField(v.id);
8003                 if(f){
8004                     f.setValue(v.value);
8005                     if(this.trackResetOnLoad){
8006                         f.originalValue = f.getValue();
8007                     }
8008                 }
8009             }
8010         }else{ // object hash
8011             var field, id;
8012             for(id in values){
8013                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8014
8015                     if (field.setFromData &&
8016                         field.valueField &&
8017                         field.displayField &&
8018                         // combos' with local stores can
8019                         // be queried via setValue()
8020                         // to set their value..
8021                         (field.store && !field.store.isLocal)
8022                         ) {
8023                         // it's a combo
8024                         var sd = { };
8025                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8026                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8027                         field.setFromData(sd);
8028
8029                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8030                         
8031                         field.setFromData(values);
8032                         
8033                     } else {
8034                         field.setValue(values[id]);
8035                     }
8036
8037
8038                     if(this.trackResetOnLoad){
8039                         field.originalValue = field.getValue();
8040                     }
8041                 }
8042             }
8043         }
8044
8045         //Roo.each(this.childForms || [], function (f) {
8046         //    f.setValues(values);
8047         //});
8048
8049         return this;
8050     },
8051
8052     /**
8053      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8054      * they are returned as an array.
8055      * @param {Boolean} asString
8056      * @return {Object}
8057      */
8058     getValues : function(asString){
8059         //if (this.childForms) {
8060             // copy values from the child forms
8061         //    Roo.each(this.childForms, function (f) {
8062         //        this.setValues(f.getValues());
8063         //    }, this);
8064         //}
8065
8066
8067
8068         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8069         if(asString === true){
8070             return fs;
8071         }
8072         return Roo.urlDecode(fs);
8073     },
8074
8075     /**
8076      * Returns the fields in this form as an object with key/value pairs.
8077      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8078      * @return {Object}
8079      */
8080     getFieldValues : function(with_hidden)
8081     {
8082         var items = this.getItems();
8083         var ret = {};
8084         items.each(function(f){
8085             
8086             if (!f.getName()) {
8087                 return;
8088             }
8089             
8090             var v = f.getValue();
8091             
8092             if (f.inputType =='radio') {
8093                 if (typeof(ret[f.getName()]) == 'undefined') {
8094                     ret[f.getName()] = ''; // empty..
8095                 }
8096
8097                 if (!f.el.dom.checked) {
8098                     return;
8099
8100                 }
8101                 v = f.el.dom.value;
8102
8103             }
8104             
8105             if(f.xtype == 'MoneyField'){
8106                 ret[f.currencyName] = f.getCurrency();
8107             }
8108
8109             // not sure if this supported any more..
8110             if ((typeof(v) == 'object') && f.getRawValue) {
8111                 v = f.getRawValue() ; // dates..
8112             }
8113             // combo boxes where name != hiddenName...
8114             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8115                 ret[f.name] = f.getRawValue();
8116             }
8117             ret[f.getName()] = v;
8118         });
8119
8120         return ret;
8121     },
8122
8123     /**
8124      * Clears all invalid messages in this form.
8125      * @return {BasicForm} this
8126      */
8127     clearInvalid : function(){
8128         var items = this.getItems();
8129
8130         items.each(function(f){
8131            f.clearInvalid();
8132         });
8133
8134         return this;
8135     },
8136
8137     /**
8138      * Resets this form.
8139      * @return {BasicForm} this
8140      */
8141     reset : function(){
8142         var items = this.getItems();
8143         items.each(function(f){
8144             f.reset();
8145         });
8146
8147         Roo.each(this.childForms || [], function (f) {
8148             f.reset();
8149         });
8150
8151
8152         return this;
8153     },
8154     
8155     getItems : function()
8156     {
8157         var r=new Roo.util.MixedCollection(false, function(o){
8158             return o.id || (o.id = Roo.id());
8159         });
8160         var iter = function(el) {
8161             if (el.inputEl) {
8162                 r.add(el);
8163             }
8164             if (!el.items) {
8165                 return;
8166             }
8167             Roo.each(el.items,function(e) {
8168                 iter(e);
8169             });
8170         };
8171
8172         iter(this);
8173         return r;
8174     },
8175     
8176     hideFields : function(items)
8177     {
8178         Roo.each(items, function(i){
8179             
8180             var f = this.findField(i);
8181             
8182             if(!f){
8183                 return;
8184             }
8185             
8186             if(f.xtype == 'DateField'){
8187                 f.setVisible(false);
8188                 return;
8189             }
8190             
8191             f.hide();
8192             
8193         }, this);
8194     },
8195     
8196     showFields : function(items)
8197     {
8198         Roo.each(items, function(i){
8199             
8200             var f = this.findField(i);
8201             
8202             if(!f){
8203                 return;
8204             }
8205             
8206             if(f.xtype == 'DateField'){
8207                 f.setVisible(true);
8208                 return;
8209             }
8210             
8211             f.show();
8212             
8213         }, this);
8214     }
8215
8216 });
8217
8218 Roo.apply(Roo.bootstrap.Form, {
8219     
8220     popover : {
8221         
8222         padding : 5,
8223         
8224         isApplied : false,
8225         
8226         isMasked : false,
8227         
8228         form : false,
8229         
8230         target : false,
8231         
8232         toolTip : false,
8233         
8234         intervalID : false,
8235         
8236         maskEl : false,
8237         
8238         apply : function()
8239         {
8240             if(this.isApplied){
8241                 return;
8242             }
8243             
8244             this.maskEl = {
8245                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8246                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8247                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8248                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8249             };
8250             
8251             this.maskEl.top.enableDisplayMode("block");
8252             this.maskEl.left.enableDisplayMode("block");
8253             this.maskEl.bottom.enableDisplayMode("block");
8254             this.maskEl.right.enableDisplayMode("block");
8255             
8256             this.toolTip = new Roo.bootstrap.Tooltip({
8257                 cls : 'roo-form-error-popover',
8258                 alignment : {
8259                     'left' : ['r-l', [-2,0], 'right'],
8260                     'right' : ['l-r', [2,0], 'left'],
8261                     'bottom' : ['tl-bl', [0,2], 'top'],
8262                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8263                 }
8264             });
8265             
8266             this.toolTip.render(Roo.get(document.body));
8267
8268             this.toolTip.el.enableDisplayMode("block");
8269             
8270             Roo.get(document.body).on('click', function(){
8271                 this.unmask();
8272             }, this);
8273             
8274             Roo.get(document.body).on('touchstart', function(){
8275                 this.unmask();
8276             }, this);
8277             
8278             this.isApplied = true
8279         },
8280         
8281         mask : function(form, target)
8282         {
8283             this.form = form;
8284             
8285             this.target = target;
8286             
8287             if(!this.form.errorMask || !target.el){
8288                 return;
8289             }
8290             
8291             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8292             
8293             Roo.log(scrollable);
8294             
8295             var ot = this.target.el.calcOffsetsTo(scrollable);
8296             
8297             var scrollTo = ot[1] - this.form.maskOffset;
8298             
8299             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8300             
8301             scrollable.scrollTo('top', scrollTo);
8302             
8303             var box = this.target.el.getBox();
8304             Roo.log(box);
8305             var zIndex = Roo.bootstrap.Modal.zIndex++;
8306
8307             
8308             this.maskEl.top.setStyle('position', 'absolute');
8309             this.maskEl.top.setStyle('z-index', zIndex);
8310             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8311             this.maskEl.top.setLeft(0);
8312             this.maskEl.top.setTop(0);
8313             this.maskEl.top.show();
8314             
8315             this.maskEl.left.setStyle('position', 'absolute');
8316             this.maskEl.left.setStyle('z-index', zIndex);
8317             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8318             this.maskEl.left.setLeft(0);
8319             this.maskEl.left.setTop(box.y - this.padding);
8320             this.maskEl.left.show();
8321
8322             this.maskEl.bottom.setStyle('position', 'absolute');
8323             this.maskEl.bottom.setStyle('z-index', zIndex);
8324             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8325             this.maskEl.bottom.setLeft(0);
8326             this.maskEl.bottom.setTop(box.bottom + this.padding);
8327             this.maskEl.bottom.show();
8328
8329             this.maskEl.right.setStyle('position', 'absolute');
8330             this.maskEl.right.setStyle('z-index', zIndex);
8331             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8332             this.maskEl.right.setLeft(box.right + this.padding);
8333             this.maskEl.right.setTop(box.y - this.padding);
8334             this.maskEl.right.show();
8335
8336             this.toolTip.bindEl = this.target.el;
8337
8338             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8339
8340             var tip = this.target.blankText;
8341
8342             if(this.target.getValue() !== '' ) {
8343                 
8344                 if (this.target.invalidText.length) {
8345                     tip = this.target.invalidText;
8346                 } else if (this.target.regexText.length){
8347                     tip = this.target.regexText;
8348                 }
8349             }
8350
8351             this.toolTip.show(tip);
8352
8353             this.intervalID = window.setInterval(function() {
8354                 Roo.bootstrap.Form.popover.unmask();
8355             }, 10000);
8356
8357             window.onwheel = function(){ return false;};
8358             
8359             (function(){ this.isMasked = true; }).defer(500, this);
8360             
8361         },
8362         
8363         unmask : function()
8364         {
8365             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8366                 return;
8367             }
8368             
8369             this.maskEl.top.setStyle('position', 'absolute');
8370             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8371             this.maskEl.top.hide();
8372
8373             this.maskEl.left.setStyle('position', 'absolute');
8374             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8375             this.maskEl.left.hide();
8376
8377             this.maskEl.bottom.setStyle('position', 'absolute');
8378             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8379             this.maskEl.bottom.hide();
8380
8381             this.maskEl.right.setStyle('position', 'absolute');
8382             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8383             this.maskEl.right.hide();
8384             
8385             this.toolTip.hide();
8386             
8387             this.toolTip.el.hide();
8388             
8389             window.onwheel = function(){ return true;};
8390             
8391             if(this.intervalID){
8392                 window.clearInterval(this.intervalID);
8393                 this.intervalID = false;
8394             }
8395             
8396             this.isMasked = false;
8397             
8398         }
8399         
8400     }
8401     
8402 });
8403
8404 /*
8405  * Based on:
8406  * Ext JS Library 1.1.1
8407  * Copyright(c) 2006-2007, Ext JS, LLC.
8408  *
8409  * Originally Released Under LGPL - original licence link has changed is not relivant.
8410  *
8411  * Fork - LGPL
8412  * <script type="text/javascript">
8413  */
8414 /**
8415  * @class Roo.form.VTypes
8416  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8417  * @singleton
8418  */
8419 Roo.form.VTypes = function(){
8420     // closure these in so they are only created once.
8421     var alpha = /^[a-zA-Z_]+$/;
8422     var alphanum = /^[a-zA-Z0-9_]+$/;
8423     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8424     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8425
8426     // All these messages and functions are configurable
8427     return {
8428         /**
8429          * The function used to validate email addresses
8430          * @param {String} value The email address
8431          */
8432         'email' : function(v){
8433             return email.test(v);
8434         },
8435         /**
8436          * The error text to display when the email validation function returns false
8437          * @type String
8438          */
8439         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8440         /**
8441          * The keystroke filter mask to be applied on email input
8442          * @type RegExp
8443          */
8444         'emailMask' : /[a-z0-9_\.\-@]/i,
8445
8446         /**
8447          * The function used to validate URLs
8448          * @param {String} value The URL
8449          */
8450         'url' : function(v){
8451             return url.test(v);
8452         },
8453         /**
8454          * The error text to display when the url validation function returns false
8455          * @type String
8456          */
8457         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8458         
8459         /**
8460          * The function used to validate alpha values
8461          * @param {String} value The value
8462          */
8463         'alpha' : function(v){
8464             return alpha.test(v);
8465         },
8466         /**
8467          * The error text to display when the alpha validation function returns false
8468          * @type String
8469          */
8470         'alphaText' : 'This field should only contain letters and _',
8471         /**
8472          * The keystroke filter mask to be applied on alpha input
8473          * @type RegExp
8474          */
8475         'alphaMask' : /[a-z_]/i,
8476
8477         /**
8478          * The function used to validate alphanumeric values
8479          * @param {String} value The value
8480          */
8481         'alphanum' : function(v){
8482             return alphanum.test(v);
8483         },
8484         /**
8485          * The error text to display when the alphanumeric validation function returns false
8486          * @type String
8487          */
8488         'alphanumText' : 'This field should only contain letters, numbers and _',
8489         /**
8490          * The keystroke filter mask to be applied on alphanumeric input
8491          * @type RegExp
8492          */
8493         'alphanumMask' : /[a-z0-9_]/i
8494     };
8495 }();/*
8496  * - LGPL
8497  *
8498  * Input
8499  * 
8500  */
8501
8502 /**
8503  * @class Roo.bootstrap.Input
8504  * @extends Roo.bootstrap.Component
8505  * Bootstrap Input class
8506  * @cfg {Boolean} disabled is it disabled
8507  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8508  * @cfg {String} name name of the input
8509  * @cfg {string} fieldLabel - the label associated
8510  * @cfg {string} placeholder - placeholder to put in text.
8511  * @cfg {string}  before - input group add on before
8512  * @cfg {string} after - input group add on after
8513  * @cfg {string} size - (lg|sm) or leave empty..
8514  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8515  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8516  * @cfg {Number} md colspan out of 12 for computer-sized screens
8517  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8518  * @cfg {string} value default value of the input
8519  * @cfg {Number} labelWidth set the width of label 
8520  * @cfg {Number} labellg set the width of label (1-12)
8521  * @cfg {Number} labelmd set the width of label (1-12)
8522  * @cfg {Number} labelsm set the width of label (1-12)
8523  * @cfg {Number} labelxs set the width of label (1-12)
8524  * @cfg {String} labelAlign (top|left)
8525  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8526  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8527  * @cfg {String} indicatorpos (left|right) default left
8528
8529  * @cfg {String} align (left|center|right) Default left
8530  * @cfg {Boolean} forceFeedback (true|false) Default false
8531  * 
8532  * @constructor
8533  * Create a new Input
8534  * @param {Object} config The config object
8535  */
8536
8537 Roo.bootstrap.Input = function(config){
8538     
8539     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8540     
8541     this.addEvents({
8542         /**
8543          * @event focus
8544          * Fires when this field receives input focus.
8545          * @param {Roo.form.Field} this
8546          */
8547         focus : true,
8548         /**
8549          * @event blur
8550          * Fires when this field loses input focus.
8551          * @param {Roo.form.Field} this
8552          */
8553         blur : true,
8554         /**
8555          * @event specialkey
8556          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8557          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8558          * @param {Roo.form.Field} this
8559          * @param {Roo.EventObject} e The event object
8560          */
8561         specialkey : true,
8562         /**
8563          * @event change
8564          * Fires just before the field blurs if the field value has changed.
8565          * @param {Roo.form.Field} this
8566          * @param {Mixed} newValue The new value
8567          * @param {Mixed} oldValue The original value
8568          */
8569         change : true,
8570         /**
8571          * @event invalid
8572          * Fires after the field has been marked as invalid.
8573          * @param {Roo.form.Field} this
8574          * @param {String} msg The validation message
8575          */
8576         invalid : true,
8577         /**
8578          * @event valid
8579          * Fires after the field has been validated with no errors.
8580          * @param {Roo.form.Field} this
8581          */
8582         valid : true,
8583          /**
8584          * @event keyup
8585          * Fires after the key up
8586          * @param {Roo.form.Field} this
8587          * @param {Roo.EventObject}  e The event Object
8588          */
8589         keyup : true
8590     });
8591 };
8592
8593 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8594      /**
8595      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8596       automatic validation (defaults to "keyup").
8597      */
8598     validationEvent : "keyup",
8599      /**
8600      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8601      */
8602     validateOnBlur : true,
8603     /**
8604      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8605      */
8606     validationDelay : 250,
8607      /**
8608      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8609      */
8610     focusClass : "x-form-focus",  // not needed???
8611     
8612        
8613     /**
8614      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8615      */
8616     invalidClass : "has-warning",
8617     
8618     /**
8619      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8620      */
8621     validClass : "has-success",
8622     
8623     /**
8624      * @cfg {Boolean} hasFeedback (true|false) default true
8625      */
8626     hasFeedback : true,
8627     
8628     /**
8629      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8630      */
8631     invalidFeedbackClass : "glyphicon-warning-sign",
8632     
8633     /**
8634      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8635      */
8636     validFeedbackClass : "glyphicon-ok",
8637     
8638     /**
8639      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8640      */
8641     selectOnFocus : false,
8642     
8643      /**
8644      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8645      */
8646     maskRe : null,
8647        /**
8648      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8649      */
8650     vtype : null,
8651     
8652       /**
8653      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8654      */
8655     disableKeyFilter : false,
8656     
8657        /**
8658      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8659      */
8660     disabled : false,
8661      /**
8662      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8663      */
8664     allowBlank : true,
8665     /**
8666      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8667      */
8668     blankText : "Please complete this mandatory field",
8669     
8670      /**
8671      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8672      */
8673     minLength : 0,
8674     /**
8675      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8676      */
8677     maxLength : Number.MAX_VALUE,
8678     /**
8679      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8680      */
8681     minLengthText : "The minimum length for this field is {0}",
8682     /**
8683      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8684      */
8685     maxLengthText : "The maximum length for this field is {0}",
8686   
8687     
8688     /**
8689      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8690      * If available, this function will be called only after the basic validators all return true, and will be passed the
8691      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8692      */
8693     validator : null,
8694     /**
8695      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8696      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8697      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8698      */
8699     regex : null,
8700     /**
8701      * @cfg {String} regexText -- Depricated - use Invalid Text
8702      */
8703     regexText : "",
8704     
8705     /**
8706      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8707      */
8708     invalidText : "",
8709     
8710     
8711     
8712     autocomplete: false,
8713     
8714     
8715     fieldLabel : '',
8716     inputType : 'text',
8717     
8718     name : false,
8719     placeholder: false,
8720     before : false,
8721     after : false,
8722     size : false,
8723     hasFocus : false,
8724     preventMark: false,
8725     isFormField : true,
8726     value : '',
8727     labelWidth : 2,
8728     labelAlign : false,
8729     readOnly : false,
8730     align : false,
8731     formatedValue : false,
8732     forceFeedback : false,
8733     
8734     indicatorpos : 'left',
8735     
8736     labellg : 0,
8737     labelmd : 0,
8738     labelsm : 0,
8739     labelxs : 0,
8740     
8741     parentLabelAlign : function()
8742     {
8743         var parent = this;
8744         while (parent.parent()) {
8745             parent = parent.parent();
8746             if (typeof(parent.labelAlign) !='undefined') {
8747                 return parent.labelAlign;
8748             }
8749         }
8750         return 'left';
8751         
8752     },
8753     
8754     getAutoCreate : function()
8755     {
8756         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8757         
8758         var id = Roo.id();
8759         
8760         var cfg = {};
8761         
8762         if(this.inputType != 'hidden'){
8763             cfg.cls = 'form-group' //input-group
8764         }
8765         
8766         var input =  {
8767             tag: 'input',
8768             id : id,
8769             type : this.inputType,
8770             value : this.value,
8771             cls : 'form-control',
8772             placeholder : this.placeholder || '',
8773             autocomplete : this.autocomplete || 'new-password'
8774         };
8775         
8776         if(this.align){
8777             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8778         }
8779         
8780         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8781             input.maxLength = this.maxLength;
8782         }
8783         
8784         if (this.disabled) {
8785             input.disabled=true;
8786         }
8787         
8788         if (this.readOnly) {
8789             input.readonly=true;
8790         }
8791         
8792         if (this.name) {
8793             input.name = this.name;
8794         }
8795         
8796         if (this.size) {
8797             input.cls += ' input-' + this.size;
8798         }
8799         
8800         var settings=this;
8801         ['xs','sm','md','lg'].map(function(size){
8802             if (settings[size]) {
8803                 cfg.cls += ' col-' + size + '-' + settings[size];
8804             }
8805         });
8806         
8807         var inputblock = input;
8808         
8809         var feedback = {
8810             tag: 'span',
8811             cls: 'glyphicon form-control-feedback'
8812         };
8813             
8814         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8815             
8816             inputblock = {
8817                 cls : 'has-feedback',
8818                 cn :  [
8819                     input,
8820                     feedback
8821                 ] 
8822             };  
8823         }
8824         
8825         if (this.before || this.after) {
8826             
8827             inputblock = {
8828                 cls : 'input-group',
8829                 cn :  [] 
8830             };
8831             
8832             if (this.before && typeof(this.before) == 'string') {
8833                 
8834                 inputblock.cn.push({
8835                     tag :'span',
8836                     cls : 'roo-input-before input-group-addon',
8837                     html : this.before
8838                 });
8839             }
8840             if (this.before && typeof(this.before) == 'object') {
8841                 this.before = Roo.factory(this.before);
8842                 
8843                 inputblock.cn.push({
8844                     tag :'span',
8845                     cls : 'roo-input-before input-group-' +
8846                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8847                 });
8848             }
8849             
8850             inputblock.cn.push(input);
8851             
8852             if (this.after && typeof(this.after) == 'string') {
8853                 inputblock.cn.push({
8854                     tag :'span',
8855                     cls : 'roo-input-after input-group-addon',
8856                     html : this.after
8857                 });
8858             }
8859             if (this.after && typeof(this.after) == 'object') {
8860                 this.after = Roo.factory(this.after);
8861                 
8862                 inputblock.cn.push({
8863                     tag :'span',
8864                     cls : 'roo-input-after input-group-' +
8865                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8866                 });
8867             }
8868             
8869             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8870                 inputblock.cls += ' has-feedback';
8871                 inputblock.cn.push(feedback);
8872             }
8873         };
8874         
8875         if (align ==='left' && this.fieldLabel.length) {
8876             
8877             cfg.cls += ' roo-form-group-label-left';
8878             
8879             cfg.cn = [
8880                 {
8881                     tag : 'i',
8882                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8883                     tooltip : 'This field is required'
8884                 },
8885                 {
8886                     tag: 'label',
8887                     'for' :  id,
8888                     cls : 'control-label',
8889                     html : this.fieldLabel
8890
8891                 },
8892                 {
8893                     cls : "", 
8894                     cn: [
8895                         inputblock
8896                     ]
8897                 }
8898             ];
8899             
8900             var labelCfg = cfg.cn[1];
8901             var contentCfg = cfg.cn[2];
8902             
8903             if(this.indicatorpos == 'right'){
8904                 cfg.cn = [
8905                     {
8906                         tag: 'label',
8907                         'for' :  id,
8908                         cls : 'control-label',
8909                         cn : [
8910                             {
8911                                 tag : 'span',
8912                                 html : this.fieldLabel
8913                             },
8914                             {
8915                                 tag : 'i',
8916                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8917                                 tooltip : 'This field is required'
8918                             }
8919                         ]
8920                     },
8921                     {
8922                         cls : "",
8923                         cn: [
8924                             inputblock
8925                         ]
8926                     }
8927
8928                 ];
8929                 
8930                 labelCfg = cfg.cn[0];
8931                 contentCfg = cfg.cn[1];
8932             
8933             }
8934             
8935             if(this.labelWidth > 12){
8936                 labelCfg.style = "width: " + this.labelWidth + 'px';
8937             }
8938             
8939             if(this.labelWidth < 13 && this.labelmd == 0){
8940                 this.labelmd = this.labelWidth;
8941             }
8942             
8943             if(this.labellg > 0){
8944                 labelCfg.cls += ' col-lg-' + this.labellg;
8945                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
8946             }
8947             
8948             if(this.labelmd > 0){
8949                 labelCfg.cls += ' col-md-' + this.labelmd;
8950                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
8951             }
8952             
8953             if(this.labelsm > 0){
8954                 labelCfg.cls += ' col-sm-' + this.labelsm;
8955                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
8956             }
8957             
8958             if(this.labelxs > 0){
8959                 labelCfg.cls += ' col-xs-' + this.labelxs;
8960                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
8961             }
8962             
8963             
8964         } else if ( this.fieldLabel.length) {
8965                 
8966             cfg.cn = [
8967                 {
8968                     tag : 'i',
8969                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8970                     tooltip : 'This field is required'
8971                 },
8972                 {
8973                     tag: 'label',
8974                    //cls : 'input-group-addon',
8975                     html : this.fieldLabel
8976
8977                 },
8978
8979                inputblock
8980
8981            ];
8982            
8983            if(this.indicatorpos == 'right'){
8984                 
8985                 cfg.cn = [
8986                     {
8987                         tag: 'label',
8988                        //cls : 'input-group-addon',
8989                         html : this.fieldLabel
8990
8991                     },
8992                     {
8993                         tag : 'i',
8994                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8995                         tooltip : 'This field is required'
8996                     },
8997
8998                    inputblock
8999
9000                ];
9001
9002             }
9003
9004         } else {
9005             
9006             cfg.cn = [
9007
9008                     inputblock
9009
9010             ];
9011                 
9012                 
9013         };
9014         
9015         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9016            cfg.cls += ' navbar-form';
9017         }
9018         
9019         if (this.parentType === 'NavGroup') {
9020            cfg.cls += ' navbar-form';
9021            cfg.tag = 'li';
9022         }
9023         
9024         return cfg;
9025         
9026     },
9027     /**
9028      * return the real input element.
9029      */
9030     inputEl: function ()
9031     {
9032         return this.el.select('input.form-control',true).first();
9033     },
9034     
9035     tooltipEl : function()
9036     {
9037         return this.inputEl();
9038     },
9039     
9040     indicatorEl : function()
9041     {
9042         var indicator = this.el.select('i.roo-required-indicator',true).first();
9043         
9044         if(!indicator){
9045             return false;
9046         }
9047         
9048         return indicator;
9049         
9050     },
9051     
9052     setDisabled : function(v)
9053     {
9054         var i  = this.inputEl().dom;
9055         if (!v) {
9056             i.removeAttribute('disabled');
9057             return;
9058             
9059         }
9060         i.setAttribute('disabled','true');
9061     },
9062     initEvents : function()
9063     {
9064           
9065         this.inputEl().on("keydown" , this.fireKey,  this);
9066         this.inputEl().on("focus", this.onFocus,  this);
9067         this.inputEl().on("blur", this.onBlur,  this);
9068         
9069         this.inputEl().relayEvent('keyup', this);
9070         
9071         this.indicator = this.indicatorEl();
9072         
9073         if(this.indicator){
9074             this.indicator.addClass('invisible');
9075             
9076         }
9077  
9078         // reference to original value for reset
9079         this.originalValue = this.getValue();
9080         //Roo.form.TextField.superclass.initEvents.call(this);
9081         if(this.validationEvent == 'keyup'){
9082             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9083             this.inputEl().on('keyup', this.filterValidation, this);
9084         }
9085         else if(this.validationEvent !== false){
9086             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9087         }
9088         
9089         if(this.selectOnFocus){
9090             this.on("focus", this.preFocus, this);
9091             
9092         }
9093         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9094             this.inputEl().on("keypress", this.filterKeys, this);
9095         } else {
9096             this.inputEl().relayEvent('keypress', this);
9097         }
9098        /* if(this.grow){
9099             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9100             this.el.on("click", this.autoSize,  this);
9101         }
9102         */
9103         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9104             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9105         }
9106         
9107         if (typeof(this.before) == 'object') {
9108             this.before.render(this.el.select('.roo-input-before',true).first());
9109         }
9110         if (typeof(this.after) == 'object') {
9111             this.after.render(this.el.select('.roo-input-after',true).first());
9112         }
9113         
9114         
9115     },
9116     filterValidation : function(e){
9117         if(!e.isNavKeyPress()){
9118             this.validationTask.delay(this.validationDelay);
9119         }
9120     },
9121      /**
9122      * Validates the field value
9123      * @return {Boolean} True if the value is valid, else false
9124      */
9125     validate : function(){
9126         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9127         if(this.disabled || this.validateValue(this.getRawValue())){
9128             this.markValid();
9129             return true;
9130         }
9131         
9132         this.markInvalid();
9133         return false;
9134     },
9135     
9136     
9137     /**
9138      * Validates a value according to the field's validation rules and marks the field as invalid
9139      * if the validation fails
9140      * @param {Mixed} value The value to validate
9141      * @return {Boolean} True if the value is valid, else false
9142      */
9143     validateValue : function(value)
9144     {
9145         if(this.getEl().hasClass('hidden') || !this.inputEl().isVisible(true)){
9146             return true;
9147         }
9148         
9149         if(value.length < 1)  { // if it's blank
9150             if(this.allowBlank){
9151                 return true;
9152             }
9153             return false;
9154         }
9155         
9156         if(value.length < this.minLength){
9157             return false;
9158         }
9159         if(value.length > this.maxLength){
9160             return false;
9161         }
9162         if(this.vtype){
9163             var vt = Roo.form.VTypes;
9164             if(!vt[this.vtype](value, this)){
9165                 return false;
9166             }
9167         }
9168         if(typeof this.validator == "function"){
9169             var msg = this.validator(value);
9170             if(msg !== true){
9171                 return false;
9172             }
9173             if (typeof(msg) == 'string') {
9174                 this.invalidText = msg;
9175             }
9176         }
9177         
9178         if(this.regex && !this.regex.test(value)){
9179             return false;
9180         }
9181         
9182         return true;
9183     },
9184     
9185      // private
9186     fireKey : function(e){
9187         //Roo.log('field ' + e.getKey());
9188         if(e.isNavKeyPress()){
9189             this.fireEvent("specialkey", this, e);
9190         }
9191     },
9192     focus : function (selectText){
9193         if(this.rendered){
9194             this.inputEl().focus();
9195             if(selectText === true){
9196                 this.inputEl().dom.select();
9197             }
9198         }
9199         return this;
9200     } ,
9201     
9202     onFocus : function(){
9203         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9204            // this.el.addClass(this.focusClass);
9205         }
9206         if(!this.hasFocus){
9207             this.hasFocus = true;
9208             this.startValue = this.getValue();
9209             this.fireEvent("focus", this);
9210         }
9211     },
9212     
9213     beforeBlur : Roo.emptyFn,
9214
9215     
9216     // private
9217     onBlur : function(){
9218         this.beforeBlur();
9219         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9220             //this.el.removeClass(this.focusClass);
9221         }
9222         this.hasFocus = false;
9223         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9224             this.validate();
9225         }
9226         var v = this.getValue();
9227         if(String(v) !== String(this.startValue)){
9228             this.fireEvent('change', this, v, this.startValue);
9229         }
9230         this.fireEvent("blur", this);
9231     },
9232     
9233     /**
9234      * Resets the current field value to the originally loaded value and clears any validation messages
9235      */
9236     reset : function(){
9237         this.setValue(this.originalValue);
9238         this.validate();
9239     },
9240      /**
9241      * Returns the name of the field
9242      * @return {Mixed} name The name field
9243      */
9244     getName: function(){
9245         return this.name;
9246     },
9247      /**
9248      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9249      * @return {Mixed} value The field value
9250      */
9251     getValue : function(){
9252         
9253         var v = this.inputEl().getValue();
9254         
9255         return v;
9256     },
9257     /**
9258      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9259      * @return {Mixed} value The field value
9260      */
9261     getRawValue : function(){
9262         var v = this.inputEl().getValue();
9263         
9264         return v;
9265     },
9266     
9267     /**
9268      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9269      * @param {Mixed} value The value to set
9270      */
9271     setRawValue : function(v){
9272         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9273     },
9274     
9275     selectText : function(start, end){
9276         var v = this.getRawValue();
9277         if(v.length > 0){
9278             start = start === undefined ? 0 : start;
9279             end = end === undefined ? v.length : end;
9280             var d = this.inputEl().dom;
9281             if(d.setSelectionRange){
9282                 d.setSelectionRange(start, end);
9283             }else if(d.createTextRange){
9284                 var range = d.createTextRange();
9285                 range.moveStart("character", start);
9286                 range.moveEnd("character", v.length-end);
9287                 range.select();
9288             }
9289         }
9290     },
9291     
9292     /**
9293      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9294      * @param {Mixed} value The value to set
9295      */
9296     setValue : function(v){
9297         this.value = v;
9298         if(this.rendered){
9299             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9300             this.validate();
9301         }
9302     },
9303     
9304     /*
9305     processValue : function(value){
9306         if(this.stripCharsRe){
9307             var newValue = value.replace(this.stripCharsRe, '');
9308             if(newValue !== value){
9309                 this.setRawValue(newValue);
9310                 return newValue;
9311             }
9312         }
9313         return value;
9314     },
9315   */
9316     preFocus : function(){
9317         
9318         if(this.selectOnFocus){
9319             this.inputEl().dom.select();
9320         }
9321     },
9322     filterKeys : function(e){
9323         var k = e.getKey();
9324         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9325             return;
9326         }
9327         var c = e.getCharCode(), cc = String.fromCharCode(c);
9328         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9329             return;
9330         }
9331         if(!this.maskRe.test(cc)){
9332             e.stopEvent();
9333         }
9334     },
9335      /**
9336      * Clear any invalid styles/messages for this field
9337      */
9338     clearInvalid : function(){
9339         
9340         if(!this.el || this.preventMark){ // not rendered
9341             return;
9342         }
9343         
9344      
9345         this.el.removeClass(this.invalidClass);
9346         
9347         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9348             
9349             var feedback = this.el.select('.form-control-feedback', true).first();
9350             
9351             if(feedback){
9352                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9353             }
9354             
9355         }
9356         
9357         this.fireEvent('valid', this);
9358     },
9359     
9360      /**
9361      * Mark this field as valid
9362      */
9363     markValid : function()
9364     {
9365         if(!this.el  || this.preventMark){ // not rendered...
9366             return;
9367         }
9368         
9369         this.el.removeClass([this.invalidClass, this.validClass]);
9370         
9371         var feedback = this.el.select('.form-control-feedback', true).first();
9372             
9373         if(feedback){
9374             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9375         }
9376         
9377         if(this.indicator){
9378             this.indicator.removeClass('visible');
9379             this.indicator.addClass('invisible');
9380         }
9381         
9382         if(this.disabled){
9383             return;
9384         }
9385         
9386         if(this.allowBlank && !this.getRawValue().length){
9387             return;
9388         }
9389         
9390         this.el.addClass(this.validClass);
9391         
9392         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9393             
9394             var feedback = this.el.select('.form-control-feedback', true).first();
9395             
9396             if(feedback){
9397                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9398                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9399             }
9400             
9401         }
9402         
9403         this.fireEvent('valid', this);
9404     },
9405     
9406      /**
9407      * Mark this field as invalid
9408      * @param {String} msg The validation message
9409      */
9410     markInvalid : function(msg)
9411     {
9412         if(!this.el  || this.preventMark){ // not rendered
9413             return;
9414         }
9415         
9416         this.el.removeClass([this.invalidClass, this.validClass]);
9417         
9418         var feedback = this.el.select('.form-control-feedback', true).first();
9419             
9420         if(feedback){
9421             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9422         }
9423
9424         if(this.disabled){
9425             return;
9426         }
9427         
9428         if(this.allowBlank && !this.getRawValue().length){
9429             return;
9430         }
9431         
9432         if(this.indicator){
9433             this.indicator.removeClass('invisible');
9434             this.indicator.addClass('visible');
9435         }
9436         
9437         this.el.addClass(this.invalidClass);
9438         
9439         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9440             
9441             var feedback = this.el.select('.form-control-feedback', true).first();
9442             
9443             if(feedback){
9444                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9445                 
9446                 if(this.getValue().length || this.forceFeedback){
9447                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9448                 }
9449                 
9450             }
9451             
9452         }
9453         
9454         this.fireEvent('invalid', this, msg);
9455     },
9456     // private
9457     SafariOnKeyDown : function(event)
9458     {
9459         // this is a workaround for a password hang bug on chrome/ webkit.
9460         if (this.inputEl().dom.type != 'password') {
9461             return;
9462         }
9463         
9464         var isSelectAll = false;
9465         
9466         if(this.inputEl().dom.selectionEnd > 0){
9467             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9468         }
9469         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9470             event.preventDefault();
9471             this.setValue('');
9472             return;
9473         }
9474         
9475         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9476             
9477             event.preventDefault();
9478             // this is very hacky as keydown always get's upper case.
9479             //
9480             var cc = String.fromCharCode(event.getCharCode());
9481             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9482             
9483         }
9484     },
9485     adjustWidth : function(tag, w){
9486         tag = tag.toLowerCase();
9487         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9488             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9489                 if(tag == 'input'){
9490                     return w + 2;
9491                 }
9492                 if(tag == 'textarea'){
9493                     return w-2;
9494                 }
9495             }else if(Roo.isOpera){
9496                 if(tag == 'input'){
9497                     return w + 2;
9498                 }
9499                 if(tag == 'textarea'){
9500                     return w-2;
9501                 }
9502             }
9503         }
9504         return w;
9505     },
9506     
9507     setFieldLabel : function(v)
9508     {
9509         if(!this.rendered){
9510             return;
9511         }
9512         
9513         if(this.indicator){
9514             var ar = this.el.select('label > span',true);
9515             
9516             if (ar.elements.length) {
9517                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9518                 this.fieldLabel = v;
9519                 return;
9520             }
9521             
9522             var br = this.el.select('label',true);
9523             
9524             if(br.elements.length) {
9525                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9526                 this.fieldLabel = v;
9527                 return;
9528             }
9529             
9530             Roo.log('Cannot Found any of label > span || label in input');
9531             return;
9532         }
9533         
9534         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9535         this.fieldLabel = v;
9536         
9537         
9538     }
9539 });
9540
9541  
9542 /*
9543  * - LGPL
9544  *
9545  * Input
9546  * 
9547  */
9548
9549 /**
9550  * @class Roo.bootstrap.TextArea
9551  * @extends Roo.bootstrap.Input
9552  * Bootstrap TextArea class
9553  * @cfg {Number} cols Specifies the visible width of a text area
9554  * @cfg {Number} rows Specifies the visible number of lines in a text area
9555  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9556  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9557  * @cfg {string} html text
9558  * 
9559  * @constructor
9560  * Create a new TextArea
9561  * @param {Object} config The config object
9562  */
9563
9564 Roo.bootstrap.TextArea = function(config){
9565     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9566    
9567 };
9568
9569 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9570      
9571     cols : false,
9572     rows : 5,
9573     readOnly : false,
9574     warp : 'soft',
9575     resize : false,
9576     value: false,
9577     html: false,
9578     
9579     getAutoCreate : function(){
9580         
9581         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9582         
9583         var id = Roo.id();
9584         
9585         var cfg = {};
9586         
9587         if(this.inputType != 'hidden'){
9588             cfg.cls = 'form-group' //input-group
9589         }
9590         
9591         var input =  {
9592             tag: 'textarea',
9593             id : id,
9594             warp : this.warp,
9595             rows : this.rows,
9596             value : this.value || '',
9597             html: this.html || '',
9598             cls : 'form-control',
9599             placeholder : this.placeholder || '' 
9600             
9601         };
9602         
9603         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9604             input.maxLength = this.maxLength;
9605         }
9606         
9607         if(this.resize){
9608             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9609         }
9610         
9611         if(this.cols){
9612             input.cols = this.cols;
9613         }
9614         
9615         if (this.readOnly) {
9616             input.readonly = true;
9617         }
9618         
9619         if (this.name) {
9620             input.name = this.name;
9621         }
9622         
9623         if (this.size) {
9624             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9625         }
9626         
9627         var settings=this;
9628         ['xs','sm','md','lg'].map(function(size){
9629             if (settings[size]) {
9630                 cfg.cls += ' col-' + size + '-' + settings[size];
9631             }
9632         });
9633         
9634         var inputblock = input;
9635         
9636         if(this.hasFeedback && !this.allowBlank){
9637             
9638             var feedback = {
9639                 tag: 'span',
9640                 cls: 'glyphicon form-control-feedback'
9641             };
9642
9643             inputblock = {
9644                 cls : 'has-feedback',
9645                 cn :  [
9646                     input,
9647                     feedback
9648                 ] 
9649             };  
9650         }
9651         
9652         
9653         if (this.before || this.after) {
9654             
9655             inputblock = {
9656                 cls : 'input-group',
9657                 cn :  [] 
9658             };
9659             if (this.before) {
9660                 inputblock.cn.push({
9661                     tag :'span',
9662                     cls : 'input-group-addon',
9663                     html : this.before
9664                 });
9665             }
9666             
9667             inputblock.cn.push(input);
9668             
9669             if(this.hasFeedback && !this.allowBlank){
9670                 inputblock.cls += ' has-feedback';
9671                 inputblock.cn.push(feedback);
9672             }
9673             
9674             if (this.after) {
9675                 inputblock.cn.push({
9676                     tag :'span',
9677                     cls : 'input-group-addon',
9678                     html : this.after
9679                 });
9680             }
9681             
9682         }
9683         
9684         if (align ==='left' && this.fieldLabel.length) {
9685             cfg.cn = [
9686                 {
9687                     tag: 'label',
9688                     'for' :  id,
9689                     cls : 'control-label',
9690                     html : this.fieldLabel
9691                 },
9692                 {
9693                     cls : "",
9694                     cn: [
9695                         inputblock
9696                     ]
9697                 }
9698
9699             ];
9700             
9701             if(this.labelWidth > 12){
9702                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9703             }
9704
9705             if(this.labelWidth < 13 && this.labelmd == 0){
9706                 this.labelmd = this.labelWidth;
9707             }
9708
9709             if(this.labellg > 0){
9710                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9711                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9712             }
9713
9714             if(this.labelmd > 0){
9715                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9716                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9717             }
9718
9719             if(this.labelsm > 0){
9720                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9721                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9722             }
9723
9724             if(this.labelxs > 0){
9725                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9726                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9727             }
9728             
9729         } else if ( this.fieldLabel.length) {
9730             cfg.cn = [
9731
9732                {
9733                    tag: 'label',
9734                    //cls : 'input-group-addon',
9735                    html : this.fieldLabel
9736
9737                },
9738
9739                inputblock
9740
9741            ];
9742
9743         } else {
9744
9745             cfg.cn = [
9746
9747                 inputblock
9748
9749             ];
9750                 
9751         }
9752         
9753         if (this.disabled) {
9754             input.disabled=true;
9755         }
9756         
9757         return cfg;
9758         
9759     },
9760     /**
9761      * return the real textarea element.
9762      */
9763     inputEl: function ()
9764     {
9765         return this.el.select('textarea.form-control',true).first();
9766     },
9767     
9768     /**
9769      * Clear any invalid styles/messages for this field
9770      */
9771     clearInvalid : function()
9772     {
9773         
9774         if(!this.el || this.preventMark){ // not rendered
9775             return;
9776         }
9777         
9778         var label = this.el.select('label', true).first();
9779         var icon = this.el.select('i.fa-star', true).first();
9780         
9781         if(label && icon){
9782             icon.remove();
9783         }
9784         
9785         this.el.removeClass(this.invalidClass);
9786         
9787         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9788             
9789             var feedback = this.el.select('.form-control-feedback', true).first();
9790             
9791             if(feedback){
9792                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9793             }
9794             
9795         }
9796         
9797         this.fireEvent('valid', this);
9798     },
9799     
9800      /**
9801      * Mark this field as valid
9802      */
9803     markValid : function()
9804     {
9805         if(!this.el  || this.preventMark){ // not rendered
9806             return;
9807         }
9808         
9809         this.el.removeClass([this.invalidClass, this.validClass]);
9810         
9811         var feedback = this.el.select('.form-control-feedback', true).first();
9812             
9813         if(feedback){
9814             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9815         }
9816
9817         if(this.disabled || this.allowBlank){
9818             return;
9819         }
9820         
9821         var label = this.el.select('label', true).first();
9822         var icon = this.el.select('i.fa-star', true).first();
9823         
9824         if(label && icon){
9825             icon.remove();
9826         }
9827         
9828         this.el.addClass(this.validClass);
9829         
9830         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9831             
9832             var feedback = this.el.select('.form-control-feedback', true).first();
9833             
9834             if(feedback){
9835                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9836                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9837             }
9838             
9839         }
9840         
9841         this.fireEvent('valid', this);
9842     },
9843     
9844      /**
9845      * Mark this field as invalid
9846      * @param {String} msg The validation message
9847      */
9848     markInvalid : function(msg)
9849     {
9850         if(!this.el  || this.preventMark){ // not rendered
9851             return;
9852         }
9853         
9854         this.el.removeClass([this.invalidClass, this.validClass]);
9855         
9856         var feedback = this.el.select('.form-control-feedback', true).first();
9857             
9858         if(feedback){
9859             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9860         }
9861
9862         if(this.disabled || this.allowBlank){
9863             return;
9864         }
9865         
9866         var label = this.el.select('label', true).first();
9867         var icon = this.el.select('i.fa-star', true).first();
9868         
9869         if(!this.getValue().length && label && !icon){
9870             this.el.createChild({
9871                 tag : 'i',
9872                 cls : 'text-danger fa fa-lg fa-star',
9873                 tooltip : 'This field is required',
9874                 style : 'margin-right:5px;'
9875             }, label, true);
9876         }
9877
9878         this.el.addClass(this.invalidClass);
9879         
9880         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9881             
9882             var feedback = this.el.select('.form-control-feedback', true).first();
9883             
9884             if(feedback){
9885                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9886                 
9887                 if(this.getValue().length || this.forceFeedback){
9888                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9889                 }
9890                 
9891             }
9892             
9893         }
9894         
9895         this.fireEvent('invalid', this, msg);
9896     }
9897 });
9898
9899  
9900 /*
9901  * - LGPL
9902  *
9903  * trigger field - base class for combo..
9904  * 
9905  */
9906  
9907 /**
9908  * @class Roo.bootstrap.TriggerField
9909  * @extends Roo.bootstrap.Input
9910  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9911  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9912  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9913  * for which you can provide a custom implementation.  For example:
9914  * <pre><code>
9915 var trigger = new Roo.bootstrap.TriggerField();
9916 trigger.onTriggerClick = myTriggerFn;
9917 trigger.applyTo('my-field');
9918 </code></pre>
9919  *
9920  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9921  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9922  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9923  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9924  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9925
9926  * @constructor
9927  * Create a new TriggerField.
9928  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9929  * to the base TextField)
9930  */
9931 Roo.bootstrap.TriggerField = function(config){
9932     this.mimicing = false;
9933     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9934 };
9935
9936 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9937     /**
9938      * @cfg {String} triggerClass A CSS class to apply to the trigger
9939      */
9940      /**
9941      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9942      */
9943     hideTrigger:false,
9944
9945     /**
9946      * @cfg {Boolean} removable (true|false) special filter default false
9947      */
9948     removable : false,
9949     
9950     /** @cfg {Boolean} grow @hide */
9951     /** @cfg {Number} growMin @hide */
9952     /** @cfg {Number} growMax @hide */
9953
9954     /**
9955      * @hide 
9956      * @method
9957      */
9958     autoSize: Roo.emptyFn,
9959     // private
9960     monitorTab : true,
9961     // private
9962     deferHeight : true,
9963
9964     
9965     actionMode : 'wrap',
9966     
9967     caret : false,
9968     
9969     
9970     getAutoCreate : function(){
9971        
9972         var align = this.labelAlign || this.parentLabelAlign();
9973         
9974         var id = Roo.id();
9975         
9976         var cfg = {
9977             cls: 'form-group' //input-group
9978         };
9979         
9980         
9981         var input =  {
9982             tag: 'input',
9983             id : id,
9984             type : this.inputType,
9985             cls : 'form-control',
9986             autocomplete: 'new-password',
9987             placeholder : this.placeholder || '' 
9988             
9989         };
9990         if (this.name) {
9991             input.name = this.name;
9992         }
9993         if (this.size) {
9994             input.cls += ' input-' + this.size;
9995         }
9996         
9997         if (this.disabled) {
9998             input.disabled=true;
9999         }
10000         
10001         var inputblock = input;
10002         
10003         if(this.hasFeedback && !this.allowBlank){
10004             
10005             var feedback = {
10006                 tag: 'span',
10007                 cls: 'glyphicon form-control-feedback'
10008             };
10009             
10010             if(this.removable && !this.editable && !this.tickable){
10011                 inputblock = {
10012                     cls : 'has-feedback',
10013                     cn :  [
10014                         inputblock,
10015                         {
10016                             tag: 'button',
10017                             html : 'x',
10018                             cls : 'roo-combo-removable-btn close'
10019                         },
10020                         feedback
10021                     ] 
10022                 };
10023             } else {
10024                 inputblock = {
10025                     cls : 'has-feedback',
10026                     cn :  [
10027                         inputblock,
10028                         feedback
10029                     ] 
10030                 };
10031             }
10032
10033         } else {
10034             if(this.removable && !this.editable && !this.tickable){
10035                 inputblock = {
10036                     cls : 'roo-removable',
10037                     cn :  [
10038                         inputblock,
10039                         {
10040                             tag: 'button',
10041                             html : 'x',
10042                             cls : 'roo-combo-removable-btn close'
10043                         }
10044                     ] 
10045                 };
10046             }
10047         }
10048         
10049         if (this.before || this.after) {
10050             
10051             inputblock = {
10052                 cls : 'input-group',
10053                 cn :  [] 
10054             };
10055             if (this.before) {
10056                 inputblock.cn.push({
10057                     tag :'span',
10058                     cls : 'input-group-addon',
10059                     html : this.before
10060                 });
10061             }
10062             
10063             inputblock.cn.push(input);
10064             
10065             if(this.hasFeedback && !this.allowBlank){
10066                 inputblock.cls += ' has-feedback';
10067                 inputblock.cn.push(feedback);
10068             }
10069             
10070             if (this.after) {
10071                 inputblock.cn.push({
10072                     tag :'span',
10073                     cls : 'input-group-addon',
10074                     html : this.after
10075                 });
10076             }
10077             
10078         };
10079         
10080         var box = {
10081             tag: 'div',
10082             cn: [
10083                 {
10084                     tag: 'input',
10085                     type : 'hidden',
10086                     cls: 'form-hidden-field'
10087                 },
10088                 inputblock
10089             ]
10090             
10091         };
10092         
10093         if(this.multiple){
10094             box = {
10095                 tag: 'div',
10096                 cn: [
10097                     {
10098                         tag: 'input',
10099                         type : 'hidden',
10100                         cls: 'form-hidden-field'
10101                     },
10102                     {
10103                         tag: 'ul',
10104                         cls: 'roo-select2-choices',
10105                         cn:[
10106                             {
10107                                 tag: 'li',
10108                                 cls: 'roo-select2-search-field',
10109                                 cn: [
10110
10111                                     inputblock
10112                                 ]
10113                             }
10114                         ]
10115                     }
10116                 ]
10117             }
10118         };
10119         
10120         var combobox = {
10121             cls: 'roo-select2-container input-group',
10122             cn: [
10123                 box
10124 //                {
10125 //                    tag: 'ul',
10126 //                    cls: 'typeahead typeahead-long dropdown-menu',
10127 //                    style: 'display:none'
10128 //                }
10129             ]
10130         };
10131         
10132         if(!this.multiple && this.showToggleBtn){
10133             
10134             var caret = {
10135                         tag: 'span',
10136                         cls: 'caret'
10137              };
10138             if (this.caret != false) {
10139                 caret = {
10140                      tag: 'i',
10141                      cls: 'fa fa-' + this.caret
10142                 };
10143                 
10144             }
10145             
10146             combobox.cn.push({
10147                 tag :'span',
10148                 cls : 'input-group-addon btn dropdown-toggle',
10149                 cn : [
10150                     caret,
10151                     {
10152                         tag: 'span',
10153                         cls: 'combobox-clear',
10154                         cn  : [
10155                             {
10156                                 tag : 'i',
10157                                 cls: 'icon-remove'
10158                             }
10159                         ]
10160                     }
10161                 ]
10162
10163             })
10164         }
10165         
10166         if(this.multiple){
10167             combobox.cls += ' roo-select2-container-multi';
10168         }
10169         
10170         if (align ==='left' && this.fieldLabel.length) {
10171             
10172             cfg.cls += ' roo-form-group-label-left';
10173
10174             cfg.cn = [
10175                 {
10176                     tag : 'i',
10177                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10178                     tooltip : 'This field is required'
10179                 },
10180                 {
10181                     tag: 'label',
10182                     'for' :  id,
10183                     cls : 'control-label',
10184                     html : this.fieldLabel
10185
10186                 },
10187                 {
10188                     cls : "", 
10189                     cn: [
10190                         combobox
10191                     ]
10192                 }
10193
10194             ];
10195             
10196             var labelCfg = cfg.cn[1];
10197             var contentCfg = cfg.cn[2];
10198             
10199             if(this.indicatorpos == 'right'){
10200                 cfg.cn = [
10201                     {
10202                         tag: 'label',
10203                         'for' :  id,
10204                         cls : 'control-label',
10205                         cn : [
10206                             {
10207                                 tag : 'span',
10208                                 html : this.fieldLabel
10209                             },
10210                             {
10211                                 tag : 'i',
10212                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10213                                 tooltip : 'This field is required'
10214                             }
10215                         ]
10216                     },
10217                     {
10218                         cls : "", 
10219                         cn: [
10220                             combobox
10221                         ]
10222                     }
10223
10224                 ];
10225                 
10226                 labelCfg = cfg.cn[0];
10227                 contentCfg = cfg.cn[1];
10228             }
10229             
10230             if(this.labelWidth > 12){
10231                 labelCfg.style = "width: " + this.labelWidth + 'px';
10232             }
10233             
10234             if(this.labelWidth < 13 && this.labelmd == 0){
10235                 this.labelmd = this.labelWidth;
10236             }
10237             
10238             if(this.labellg > 0){
10239                 labelCfg.cls += ' col-lg-' + this.labellg;
10240                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10241             }
10242             
10243             if(this.labelmd > 0){
10244                 labelCfg.cls += ' col-md-' + this.labelmd;
10245                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10246             }
10247             
10248             if(this.labelsm > 0){
10249                 labelCfg.cls += ' col-sm-' + this.labelsm;
10250                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10251             }
10252             
10253             if(this.labelxs > 0){
10254                 labelCfg.cls += ' col-xs-' + this.labelxs;
10255                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10256             }
10257             
10258         } else if ( this.fieldLabel.length) {
10259 //                Roo.log(" label");
10260             cfg.cn = [
10261                 {
10262                    tag : 'i',
10263                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10264                    tooltip : 'This field is required'
10265                },
10266                {
10267                    tag: 'label',
10268                    //cls : 'input-group-addon',
10269                    html : this.fieldLabel
10270
10271                },
10272
10273                combobox
10274
10275             ];
10276             
10277             if(this.indicatorpos == 'right'){
10278                 
10279                 cfg.cn = [
10280                     {
10281                        tag: 'label',
10282                        cn : [
10283                            {
10284                                tag : 'span',
10285                                html : this.fieldLabel
10286                            },
10287                            {
10288                               tag : 'i',
10289                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10290                               tooltip : 'This field is required'
10291                            }
10292                        ]
10293
10294                     },
10295                     combobox
10296
10297                 ];
10298
10299             }
10300
10301         } else {
10302             
10303 //                Roo.log(" no label && no align");
10304                 cfg = combobox
10305                      
10306                 
10307         }
10308         
10309         var settings=this;
10310         ['xs','sm','md','lg'].map(function(size){
10311             if (settings[size]) {
10312                 cfg.cls += ' col-' + size + '-' + settings[size];
10313             }
10314         });
10315         
10316         return cfg;
10317         
10318     },
10319     
10320     
10321     
10322     // private
10323     onResize : function(w, h){
10324 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10325 //        if(typeof w == 'number'){
10326 //            var x = w - this.trigger.getWidth();
10327 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10328 //            this.trigger.setStyle('left', x+'px');
10329 //        }
10330     },
10331
10332     // private
10333     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10334
10335     // private
10336     getResizeEl : function(){
10337         return this.inputEl();
10338     },
10339
10340     // private
10341     getPositionEl : function(){
10342         return this.inputEl();
10343     },
10344
10345     // private
10346     alignErrorIcon : function(){
10347         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10348     },
10349
10350     // private
10351     initEvents : function(){
10352         
10353         this.createList();
10354         
10355         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10356         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10357         if(!this.multiple && this.showToggleBtn){
10358             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10359             if(this.hideTrigger){
10360                 this.trigger.setDisplayed(false);
10361             }
10362             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10363         }
10364         
10365         if(this.multiple){
10366             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10367         }
10368         
10369         if(this.removable && !this.editable && !this.tickable){
10370             var close = this.closeTriggerEl();
10371             
10372             if(close){
10373                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10374                 close.on('click', this.removeBtnClick, this, close);
10375             }
10376         }
10377         
10378         //this.trigger.addClassOnOver('x-form-trigger-over');
10379         //this.trigger.addClassOnClick('x-form-trigger-click');
10380         
10381         //if(!this.width){
10382         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10383         //}
10384     },
10385     
10386     closeTriggerEl : function()
10387     {
10388         var close = this.el.select('.roo-combo-removable-btn', true).first();
10389         return close ? close : false;
10390     },
10391     
10392     removeBtnClick : function(e, h, el)
10393     {
10394         e.preventDefault();
10395         
10396         if(this.fireEvent("remove", this) !== false){
10397             this.reset();
10398             this.fireEvent("afterremove", this)
10399         }
10400     },
10401     
10402     createList : function()
10403     {
10404         this.list = Roo.get(document.body).createChild({
10405             tag: 'ul',
10406             cls: 'typeahead typeahead-long dropdown-menu',
10407             style: 'display:none'
10408         });
10409         
10410         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10411         
10412     },
10413
10414     // private
10415     initTrigger : function(){
10416        
10417     },
10418
10419     // private
10420     onDestroy : function(){
10421         if(this.trigger){
10422             this.trigger.removeAllListeners();
10423           //  this.trigger.remove();
10424         }
10425         //if(this.wrap){
10426         //    this.wrap.remove();
10427         //}
10428         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10429     },
10430
10431     // private
10432     onFocus : function(){
10433         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10434         /*
10435         if(!this.mimicing){
10436             this.wrap.addClass('x-trigger-wrap-focus');
10437             this.mimicing = true;
10438             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10439             if(this.monitorTab){
10440                 this.el.on("keydown", this.checkTab, this);
10441             }
10442         }
10443         */
10444     },
10445
10446     // private
10447     checkTab : function(e){
10448         if(e.getKey() == e.TAB){
10449             this.triggerBlur();
10450         }
10451     },
10452
10453     // private
10454     onBlur : function(){
10455         // do nothing
10456     },
10457
10458     // private
10459     mimicBlur : function(e, t){
10460         /*
10461         if(!this.wrap.contains(t) && this.validateBlur()){
10462             this.triggerBlur();
10463         }
10464         */
10465     },
10466
10467     // private
10468     triggerBlur : function(){
10469         this.mimicing = false;
10470         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10471         if(this.monitorTab){
10472             this.el.un("keydown", this.checkTab, this);
10473         }
10474         //this.wrap.removeClass('x-trigger-wrap-focus');
10475         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10476     },
10477
10478     // private
10479     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10480     validateBlur : function(e, t){
10481         return true;
10482     },
10483
10484     // private
10485     onDisable : function(){
10486         this.inputEl().dom.disabled = true;
10487         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10488         //if(this.wrap){
10489         //    this.wrap.addClass('x-item-disabled');
10490         //}
10491     },
10492
10493     // private
10494     onEnable : function(){
10495         this.inputEl().dom.disabled = false;
10496         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10497         //if(this.wrap){
10498         //    this.el.removeClass('x-item-disabled');
10499         //}
10500     },
10501
10502     // private
10503     onShow : function(){
10504         var ae = this.getActionEl();
10505         
10506         if(ae){
10507             ae.dom.style.display = '';
10508             ae.dom.style.visibility = 'visible';
10509         }
10510     },
10511
10512     // private
10513     
10514     onHide : function(){
10515         var ae = this.getActionEl();
10516         ae.dom.style.display = 'none';
10517     },
10518
10519     /**
10520      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10521      * by an implementing function.
10522      * @method
10523      * @param {EventObject} e
10524      */
10525     onTriggerClick : Roo.emptyFn
10526 });
10527  /*
10528  * Based on:
10529  * Ext JS Library 1.1.1
10530  * Copyright(c) 2006-2007, Ext JS, LLC.
10531  *
10532  * Originally Released Under LGPL - original licence link has changed is not relivant.
10533  *
10534  * Fork - LGPL
10535  * <script type="text/javascript">
10536  */
10537
10538
10539 /**
10540  * @class Roo.data.SortTypes
10541  * @singleton
10542  * Defines the default sorting (casting?) comparison functions used when sorting data.
10543  */
10544 Roo.data.SortTypes = {
10545     /**
10546      * Default sort that does nothing
10547      * @param {Mixed} s The value being converted
10548      * @return {Mixed} The comparison value
10549      */
10550     none : function(s){
10551         return s;
10552     },
10553     
10554     /**
10555      * The regular expression used to strip tags
10556      * @type {RegExp}
10557      * @property
10558      */
10559     stripTagsRE : /<\/?[^>]+>/gi,
10560     
10561     /**
10562      * Strips all HTML tags to sort on text only
10563      * @param {Mixed} s The value being converted
10564      * @return {String} The comparison value
10565      */
10566     asText : function(s){
10567         return String(s).replace(this.stripTagsRE, "");
10568     },
10569     
10570     /**
10571      * Strips all HTML tags to sort on text only - Case insensitive
10572      * @param {Mixed} s The value being converted
10573      * @return {String} The comparison value
10574      */
10575     asUCText : function(s){
10576         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10577     },
10578     
10579     /**
10580      * Case insensitive string
10581      * @param {Mixed} s The value being converted
10582      * @return {String} The comparison value
10583      */
10584     asUCString : function(s) {
10585         return String(s).toUpperCase();
10586     },
10587     
10588     /**
10589      * Date sorting
10590      * @param {Mixed} s The value being converted
10591      * @return {Number} The comparison value
10592      */
10593     asDate : function(s) {
10594         if(!s){
10595             return 0;
10596         }
10597         if(s instanceof Date){
10598             return s.getTime();
10599         }
10600         return Date.parse(String(s));
10601     },
10602     
10603     /**
10604      * Float sorting
10605      * @param {Mixed} s The value being converted
10606      * @return {Float} The comparison value
10607      */
10608     asFloat : function(s) {
10609         var val = parseFloat(String(s).replace(/,/g, ""));
10610         if(isNaN(val)) {
10611             val = 0;
10612         }
10613         return val;
10614     },
10615     
10616     /**
10617      * Integer sorting
10618      * @param {Mixed} s The value being converted
10619      * @return {Number} The comparison value
10620      */
10621     asInt : function(s) {
10622         var val = parseInt(String(s).replace(/,/g, ""));
10623         if(isNaN(val)) {
10624             val = 0;
10625         }
10626         return val;
10627     }
10628 };/*
10629  * Based on:
10630  * Ext JS Library 1.1.1
10631  * Copyright(c) 2006-2007, Ext JS, LLC.
10632  *
10633  * Originally Released Under LGPL - original licence link has changed is not relivant.
10634  *
10635  * Fork - LGPL
10636  * <script type="text/javascript">
10637  */
10638
10639 /**
10640 * @class Roo.data.Record
10641  * Instances of this class encapsulate both record <em>definition</em> information, and record
10642  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10643  * to access Records cached in an {@link Roo.data.Store} object.<br>
10644  * <p>
10645  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10646  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10647  * objects.<br>
10648  * <p>
10649  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10650  * @constructor
10651  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10652  * {@link #create}. The parameters are the same.
10653  * @param {Array} data An associative Array of data values keyed by the field name.
10654  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10655  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10656  * not specified an integer id is generated.
10657  */
10658 Roo.data.Record = function(data, id){
10659     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10660     this.data = data;
10661 };
10662
10663 /**
10664  * Generate a constructor for a specific record layout.
10665  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10666  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10667  * Each field definition object may contain the following properties: <ul>
10668  * <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,
10669  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10670  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10671  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10672  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10673  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10674  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10675  * this may be omitted.</p></li>
10676  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10677  * <ul><li>auto (Default, implies no conversion)</li>
10678  * <li>string</li>
10679  * <li>int</li>
10680  * <li>float</li>
10681  * <li>boolean</li>
10682  * <li>date</li></ul></p></li>
10683  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10684  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10685  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10686  * by the Reader into an object that will be stored in the Record. It is passed the
10687  * following parameters:<ul>
10688  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10689  * </ul></p></li>
10690  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10691  * </ul>
10692  * <br>usage:<br><pre><code>
10693 var TopicRecord = Roo.data.Record.create(
10694     {name: 'title', mapping: 'topic_title'},
10695     {name: 'author', mapping: 'username'},
10696     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10697     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10698     {name: 'lastPoster', mapping: 'user2'},
10699     {name: 'excerpt', mapping: 'post_text'}
10700 );
10701
10702 var myNewRecord = new TopicRecord({
10703     title: 'Do my job please',
10704     author: 'noobie',
10705     totalPosts: 1,
10706     lastPost: new Date(),
10707     lastPoster: 'Animal',
10708     excerpt: 'No way dude!'
10709 });
10710 myStore.add(myNewRecord);
10711 </code></pre>
10712  * @method create
10713  * @static
10714  */
10715 Roo.data.Record.create = function(o){
10716     var f = function(){
10717         f.superclass.constructor.apply(this, arguments);
10718     };
10719     Roo.extend(f, Roo.data.Record);
10720     var p = f.prototype;
10721     p.fields = new Roo.util.MixedCollection(false, function(field){
10722         return field.name;
10723     });
10724     for(var i = 0, len = o.length; i < len; i++){
10725         p.fields.add(new Roo.data.Field(o[i]));
10726     }
10727     f.getField = function(name){
10728         return p.fields.get(name);  
10729     };
10730     return f;
10731 };
10732
10733 Roo.data.Record.AUTO_ID = 1000;
10734 Roo.data.Record.EDIT = 'edit';
10735 Roo.data.Record.REJECT = 'reject';
10736 Roo.data.Record.COMMIT = 'commit';
10737
10738 Roo.data.Record.prototype = {
10739     /**
10740      * Readonly flag - true if this record has been modified.
10741      * @type Boolean
10742      */
10743     dirty : false,
10744     editing : false,
10745     error: null,
10746     modified: null,
10747
10748     // private
10749     join : function(store){
10750         this.store = store;
10751     },
10752
10753     /**
10754      * Set the named field to the specified value.
10755      * @param {String} name The name of the field to set.
10756      * @param {Object} value The value to set the field to.
10757      */
10758     set : function(name, value){
10759         if(this.data[name] == value){
10760             return;
10761         }
10762         this.dirty = true;
10763         if(!this.modified){
10764             this.modified = {};
10765         }
10766         if(typeof this.modified[name] == 'undefined'){
10767             this.modified[name] = this.data[name];
10768         }
10769         this.data[name] = value;
10770         if(!this.editing && this.store){
10771             this.store.afterEdit(this);
10772         }       
10773     },
10774
10775     /**
10776      * Get the value of the named field.
10777      * @param {String} name The name of the field to get the value of.
10778      * @return {Object} The value of the field.
10779      */
10780     get : function(name){
10781         return this.data[name]; 
10782     },
10783
10784     // private
10785     beginEdit : function(){
10786         this.editing = true;
10787         this.modified = {}; 
10788     },
10789
10790     // private
10791     cancelEdit : function(){
10792         this.editing = false;
10793         delete this.modified;
10794     },
10795
10796     // private
10797     endEdit : function(){
10798         this.editing = false;
10799         if(this.dirty && this.store){
10800             this.store.afterEdit(this);
10801         }
10802     },
10803
10804     /**
10805      * Usually called by the {@link Roo.data.Store} which owns the Record.
10806      * Rejects all changes made to the Record since either creation, or the last commit operation.
10807      * Modified fields are reverted to their original values.
10808      * <p>
10809      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10810      * of reject operations.
10811      */
10812     reject : function(){
10813         var m = this.modified;
10814         for(var n in m){
10815             if(typeof m[n] != "function"){
10816                 this.data[n] = m[n];
10817             }
10818         }
10819         this.dirty = false;
10820         delete this.modified;
10821         this.editing = false;
10822         if(this.store){
10823             this.store.afterReject(this);
10824         }
10825     },
10826
10827     /**
10828      * Usually called by the {@link Roo.data.Store} which owns the Record.
10829      * Commits all changes made to the Record since either creation, or the last commit operation.
10830      * <p>
10831      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10832      * of commit operations.
10833      */
10834     commit : function(){
10835         this.dirty = false;
10836         delete this.modified;
10837         this.editing = false;
10838         if(this.store){
10839             this.store.afterCommit(this);
10840         }
10841     },
10842
10843     // private
10844     hasError : function(){
10845         return this.error != null;
10846     },
10847
10848     // private
10849     clearError : function(){
10850         this.error = null;
10851     },
10852
10853     /**
10854      * Creates a copy of this record.
10855      * @param {String} id (optional) A new record id if you don't want to use this record's id
10856      * @return {Record}
10857      */
10858     copy : function(newId) {
10859         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10860     }
10861 };/*
10862  * Based on:
10863  * Ext JS Library 1.1.1
10864  * Copyright(c) 2006-2007, Ext JS, LLC.
10865  *
10866  * Originally Released Under LGPL - original licence link has changed is not relivant.
10867  *
10868  * Fork - LGPL
10869  * <script type="text/javascript">
10870  */
10871
10872
10873
10874 /**
10875  * @class Roo.data.Store
10876  * @extends Roo.util.Observable
10877  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10878  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10879  * <p>
10880  * 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
10881  * has no knowledge of the format of the data returned by the Proxy.<br>
10882  * <p>
10883  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10884  * instances from the data object. These records are cached and made available through accessor functions.
10885  * @constructor
10886  * Creates a new Store.
10887  * @param {Object} config A config object containing the objects needed for the Store to access data,
10888  * and read the data into Records.
10889  */
10890 Roo.data.Store = function(config){
10891     this.data = new Roo.util.MixedCollection(false);
10892     this.data.getKey = function(o){
10893         return o.id;
10894     };
10895     this.baseParams = {};
10896     // private
10897     this.paramNames = {
10898         "start" : "start",
10899         "limit" : "limit",
10900         "sort" : "sort",
10901         "dir" : "dir",
10902         "multisort" : "_multisort"
10903     };
10904
10905     if(config && config.data){
10906         this.inlineData = config.data;
10907         delete config.data;
10908     }
10909
10910     Roo.apply(this, config);
10911     
10912     if(this.reader){ // reader passed
10913         this.reader = Roo.factory(this.reader, Roo.data);
10914         this.reader.xmodule = this.xmodule || false;
10915         if(!this.recordType){
10916             this.recordType = this.reader.recordType;
10917         }
10918         if(this.reader.onMetaChange){
10919             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10920         }
10921     }
10922
10923     if(this.recordType){
10924         this.fields = this.recordType.prototype.fields;
10925     }
10926     this.modified = [];
10927
10928     this.addEvents({
10929         /**
10930          * @event datachanged
10931          * Fires when the data cache has changed, and a widget which is using this Store
10932          * as a Record cache should refresh its view.
10933          * @param {Store} this
10934          */
10935         datachanged : true,
10936         /**
10937          * @event metachange
10938          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10939          * @param {Store} this
10940          * @param {Object} meta The JSON metadata
10941          */
10942         metachange : true,
10943         /**
10944          * @event add
10945          * Fires when Records have been added to the Store
10946          * @param {Store} this
10947          * @param {Roo.data.Record[]} records The array of Records added
10948          * @param {Number} index The index at which the record(s) were added
10949          */
10950         add : true,
10951         /**
10952          * @event remove
10953          * Fires when a Record has been removed from the Store
10954          * @param {Store} this
10955          * @param {Roo.data.Record} record The Record that was removed
10956          * @param {Number} index The index at which the record was removed
10957          */
10958         remove : true,
10959         /**
10960          * @event update
10961          * Fires when a Record has been updated
10962          * @param {Store} this
10963          * @param {Roo.data.Record} record The Record that was updated
10964          * @param {String} operation The update operation being performed.  Value may be one of:
10965          * <pre><code>
10966  Roo.data.Record.EDIT
10967  Roo.data.Record.REJECT
10968  Roo.data.Record.COMMIT
10969          * </code></pre>
10970          */
10971         update : true,
10972         /**
10973          * @event clear
10974          * Fires when the data cache has been cleared.
10975          * @param {Store} this
10976          */
10977         clear : true,
10978         /**
10979          * @event beforeload
10980          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10981          * the load action will be canceled.
10982          * @param {Store} this
10983          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10984          */
10985         beforeload : true,
10986         /**
10987          * @event beforeloadadd
10988          * Fires after a new set of Records has been loaded.
10989          * @param {Store} this
10990          * @param {Roo.data.Record[]} records The Records that were loaded
10991          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10992          */
10993         beforeloadadd : true,
10994         /**
10995          * @event load
10996          * Fires after a new set of Records has been loaded, before they are added to the store.
10997          * @param {Store} this
10998          * @param {Roo.data.Record[]} records The Records that were loaded
10999          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11000          * @params {Object} return from reader
11001          */
11002         load : true,
11003         /**
11004          * @event loadexception
11005          * Fires if an exception occurs in the Proxy during loading.
11006          * Called with the signature of the Proxy's "loadexception" event.
11007          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11008          * 
11009          * @param {Proxy} 
11010          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11011          * @param {Object} load options 
11012          * @param {Object} jsonData from your request (normally this contains the Exception)
11013          */
11014         loadexception : true
11015     });
11016     
11017     if(this.proxy){
11018         this.proxy = Roo.factory(this.proxy, Roo.data);
11019         this.proxy.xmodule = this.xmodule || false;
11020         this.relayEvents(this.proxy,  ["loadexception"]);
11021     }
11022     this.sortToggle = {};
11023     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11024
11025     Roo.data.Store.superclass.constructor.call(this);
11026
11027     if(this.inlineData){
11028         this.loadData(this.inlineData);
11029         delete this.inlineData;
11030     }
11031 };
11032
11033 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11034      /**
11035     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11036     * without a remote query - used by combo/forms at present.
11037     */
11038     
11039     /**
11040     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11041     */
11042     /**
11043     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11044     */
11045     /**
11046     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11047     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11048     */
11049     /**
11050     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11051     * on any HTTP request
11052     */
11053     /**
11054     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11055     */
11056     /**
11057     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11058     */
11059     multiSort: false,
11060     /**
11061     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11062     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11063     */
11064     remoteSort : false,
11065
11066     /**
11067     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11068      * loaded or when a record is removed. (defaults to false).
11069     */
11070     pruneModifiedRecords : false,
11071
11072     // private
11073     lastOptions : null,
11074
11075     /**
11076      * Add Records to the Store and fires the add event.
11077      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11078      */
11079     add : function(records){
11080         records = [].concat(records);
11081         for(var i = 0, len = records.length; i < len; i++){
11082             records[i].join(this);
11083         }
11084         var index = this.data.length;
11085         this.data.addAll(records);
11086         this.fireEvent("add", this, records, index);
11087     },
11088
11089     /**
11090      * Remove a Record from the Store and fires the remove event.
11091      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11092      */
11093     remove : function(record){
11094         var index = this.data.indexOf(record);
11095         this.data.removeAt(index);
11096         if(this.pruneModifiedRecords){
11097             this.modified.remove(record);
11098         }
11099         this.fireEvent("remove", this, record, index);
11100     },
11101
11102     /**
11103      * Remove all Records from the Store and fires the clear event.
11104      */
11105     removeAll : function(){
11106         this.data.clear();
11107         if(this.pruneModifiedRecords){
11108             this.modified = [];
11109         }
11110         this.fireEvent("clear", this);
11111     },
11112
11113     /**
11114      * Inserts Records to the Store at the given index and fires the add event.
11115      * @param {Number} index The start index at which to insert the passed Records.
11116      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11117      */
11118     insert : function(index, records){
11119         records = [].concat(records);
11120         for(var i = 0, len = records.length; i < len; i++){
11121             this.data.insert(index, records[i]);
11122             records[i].join(this);
11123         }
11124         this.fireEvent("add", this, records, index);
11125     },
11126
11127     /**
11128      * Get the index within the cache of the passed Record.
11129      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11130      * @return {Number} The index of the passed Record. Returns -1 if not found.
11131      */
11132     indexOf : function(record){
11133         return this.data.indexOf(record);
11134     },
11135
11136     /**
11137      * Get the index within the cache of the Record with the passed id.
11138      * @param {String} id The id of the Record to find.
11139      * @return {Number} The index of the Record. Returns -1 if not found.
11140      */
11141     indexOfId : function(id){
11142         return this.data.indexOfKey(id);
11143     },
11144
11145     /**
11146      * Get the Record with the specified id.
11147      * @param {String} id The id of the Record to find.
11148      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11149      */
11150     getById : function(id){
11151         return this.data.key(id);
11152     },
11153
11154     /**
11155      * Get the Record at the specified index.
11156      * @param {Number} index The index of the Record to find.
11157      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11158      */
11159     getAt : function(index){
11160         return this.data.itemAt(index);
11161     },
11162
11163     /**
11164      * Returns a range of Records between specified indices.
11165      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11166      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11167      * @return {Roo.data.Record[]} An array of Records
11168      */
11169     getRange : function(start, end){
11170         return this.data.getRange(start, end);
11171     },
11172
11173     // private
11174     storeOptions : function(o){
11175         o = Roo.apply({}, o);
11176         delete o.callback;
11177         delete o.scope;
11178         this.lastOptions = o;
11179     },
11180
11181     /**
11182      * Loads the Record cache from the configured Proxy using the configured Reader.
11183      * <p>
11184      * If using remote paging, then the first load call must specify the <em>start</em>
11185      * and <em>limit</em> properties in the options.params property to establish the initial
11186      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11187      * <p>
11188      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11189      * and this call will return before the new data has been loaded. Perform any post-processing
11190      * in a callback function, or in a "load" event handler.</strong>
11191      * <p>
11192      * @param {Object} options An object containing properties which control loading options:<ul>
11193      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11194      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11195      * passed the following arguments:<ul>
11196      * <li>r : Roo.data.Record[]</li>
11197      * <li>options: Options object from the load call</li>
11198      * <li>success: Boolean success indicator</li></ul></li>
11199      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11200      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11201      * </ul>
11202      */
11203     load : function(options){
11204         options = options || {};
11205         if(this.fireEvent("beforeload", this, options) !== false){
11206             this.storeOptions(options);
11207             var p = Roo.apply(options.params || {}, this.baseParams);
11208             // if meta was not loaded from remote source.. try requesting it.
11209             if (!this.reader.metaFromRemote) {
11210                 p._requestMeta = 1;
11211             }
11212             if(this.sortInfo && this.remoteSort){
11213                 var pn = this.paramNames;
11214                 p[pn["sort"]] = this.sortInfo.field;
11215                 p[pn["dir"]] = this.sortInfo.direction;
11216             }
11217             if (this.multiSort) {
11218                 var pn = this.paramNames;
11219                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11220             }
11221             
11222             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11223         }
11224     },
11225
11226     /**
11227      * Reloads the Record cache from the configured Proxy using the configured Reader and
11228      * the options from the last load operation performed.
11229      * @param {Object} options (optional) An object containing properties which may override the options
11230      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11231      * the most recently used options are reused).
11232      */
11233     reload : function(options){
11234         this.load(Roo.applyIf(options||{}, this.lastOptions));
11235     },
11236
11237     // private
11238     // Called as a callback by the Reader during a load operation.
11239     loadRecords : function(o, options, success){
11240         if(!o || success === false){
11241             if(success !== false){
11242                 this.fireEvent("load", this, [], options, o);
11243             }
11244             if(options.callback){
11245                 options.callback.call(options.scope || this, [], options, false);
11246             }
11247             return;
11248         }
11249         // if data returned failure - throw an exception.
11250         if (o.success === false) {
11251             // show a message if no listener is registered.
11252             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11253                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11254             }
11255             // loadmask wil be hooked into this..
11256             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11257             return;
11258         }
11259         var r = o.records, t = o.totalRecords || r.length;
11260         
11261         this.fireEvent("beforeloadadd", this, r, options, o);
11262         
11263         if(!options || options.add !== true){
11264             if(this.pruneModifiedRecords){
11265                 this.modified = [];
11266             }
11267             for(var i = 0, len = r.length; i < len; i++){
11268                 r[i].join(this);
11269             }
11270             if(this.snapshot){
11271                 this.data = this.snapshot;
11272                 delete this.snapshot;
11273             }
11274             this.data.clear();
11275             this.data.addAll(r);
11276             this.totalLength = t;
11277             this.applySort();
11278             this.fireEvent("datachanged", this);
11279         }else{
11280             this.totalLength = Math.max(t, this.data.length+r.length);
11281             this.add(r);
11282         }
11283         
11284         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11285                 
11286             var e = new Roo.data.Record({});
11287
11288             e.set(this.parent.displayField, this.parent.emptyTitle);
11289             e.set(this.parent.valueField, '');
11290
11291             this.insert(0, e);
11292         }
11293             
11294         this.fireEvent("load", this, r, options, o);
11295         if(options.callback){
11296             options.callback.call(options.scope || this, r, options, true);
11297         }
11298     },
11299
11300
11301     /**
11302      * Loads data from a passed data block. A Reader which understands the format of the data
11303      * must have been configured in the constructor.
11304      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11305      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11306      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11307      */
11308     loadData : function(o, append){
11309         var r = this.reader.readRecords(o);
11310         this.loadRecords(r, {add: append}, true);
11311     },
11312
11313     /**
11314      * Gets the number of cached records.
11315      * <p>
11316      * <em>If using paging, this may not be the total size of the dataset. If the data object
11317      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11318      * the data set size</em>
11319      */
11320     getCount : function(){
11321         return this.data.length || 0;
11322     },
11323
11324     /**
11325      * Gets the total number of records in the dataset as returned by the server.
11326      * <p>
11327      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11328      * the dataset size</em>
11329      */
11330     getTotalCount : function(){
11331         return this.totalLength || 0;
11332     },
11333
11334     /**
11335      * Returns the sort state of the Store as an object with two properties:
11336      * <pre><code>
11337  field {String} The name of the field by which the Records are sorted
11338  direction {String} The sort order, "ASC" or "DESC"
11339      * </code></pre>
11340      */
11341     getSortState : function(){
11342         return this.sortInfo;
11343     },
11344
11345     // private
11346     applySort : function(){
11347         if(this.sortInfo && !this.remoteSort){
11348             var s = this.sortInfo, f = s.field;
11349             var st = this.fields.get(f).sortType;
11350             var fn = function(r1, r2){
11351                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11352                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11353             };
11354             this.data.sort(s.direction, fn);
11355             if(this.snapshot && this.snapshot != this.data){
11356                 this.snapshot.sort(s.direction, fn);
11357             }
11358         }
11359     },
11360
11361     /**
11362      * Sets the default sort column and order to be used by the next load operation.
11363      * @param {String} fieldName The name of the field to sort by.
11364      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11365      */
11366     setDefaultSort : function(field, dir){
11367         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11368     },
11369
11370     /**
11371      * Sort the Records.
11372      * If remote sorting is used, the sort is performed on the server, and the cache is
11373      * reloaded. If local sorting is used, the cache is sorted internally.
11374      * @param {String} fieldName The name of the field to sort by.
11375      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11376      */
11377     sort : function(fieldName, dir){
11378         var f = this.fields.get(fieldName);
11379         if(!dir){
11380             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11381             
11382             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11383                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11384             }else{
11385                 dir = f.sortDir;
11386             }
11387         }
11388         this.sortToggle[f.name] = dir;
11389         this.sortInfo = {field: f.name, direction: dir};
11390         if(!this.remoteSort){
11391             this.applySort();
11392             this.fireEvent("datachanged", this);
11393         }else{
11394             this.load(this.lastOptions);
11395         }
11396     },
11397
11398     /**
11399      * Calls the specified function for each of the Records in the cache.
11400      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11401      * Returning <em>false</em> aborts and exits the iteration.
11402      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11403      */
11404     each : function(fn, scope){
11405         this.data.each(fn, scope);
11406     },
11407
11408     /**
11409      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11410      * (e.g., during paging).
11411      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11412      */
11413     getModifiedRecords : function(){
11414         return this.modified;
11415     },
11416
11417     // private
11418     createFilterFn : function(property, value, anyMatch){
11419         if(!value.exec){ // not a regex
11420             value = String(value);
11421             if(value.length == 0){
11422                 return false;
11423             }
11424             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11425         }
11426         return function(r){
11427             return value.test(r.data[property]);
11428         };
11429     },
11430
11431     /**
11432      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11433      * @param {String} property A field on your records
11434      * @param {Number} start The record index to start at (defaults to 0)
11435      * @param {Number} end The last record index to include (defaults to length - 1)
11436      * @return {Number} The sum
11437      */
11438     sum : function(property, start, end){
11439         var rs = this.data.items, v = 0;
11440         start = start || 0;
11441         end = (end || end === 0) ? end : rs.length-1;
11442
11443         for(var i = start; i <= end; i++){
11444             v += (rs[i].data[property] || 0);
11445         }
11446         return v;
11447     },
11448
11449     /**
11450      * Filter the records by a specified property.
11451      * @param {String} field A field on your records
11452      * @param {String/RegExp} value Either a string that the field
11453      * should start with or a RegExp to test against the field
11454      * @param {Boolean} anyMatch True to match any part not just the beginning
11455      */
11456     filter : function(property, value, anyMatch){
11457         var fn = this.createFilterFn(property, value, anyMatch);
11458         return fn ? this.filterBy(fn) : this.clearFilter();
11459     },
11460
11461     /**
11462      * Filter by a function. The specified function will be called with each
11463      * record in this data source. If the function returns true the record is included,
11464      * otherwise it is filtered.
11465      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11466      * @param {Object} scope (optional) The scope of the function (defaults to this)
11467      */
11468     filterBy : function(fn, scope){
11469         this.snapshot = this.snapshot || this.data;
11470         this.data = this.queryBy(fn, scope||this);
11471         this.fireEvent("datachanged", this);
11472     },
11473
11474     /**
11475      * Query the records by a specified property.
11476      * @param {String} field A field on your records
11477      * @param {String/RegExp} value Either a string that the field
11478      * should start with or a RegExp to test against the field
11479      * @param {Boolean} anyMatch True to match any part not just the beginning
11480      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11481      */
11482     query : function(property, value, anyMatch){
11483         var fn = this.createFilterFn(property, value, anyMatch);
11484         return fn ? this.queryBy(fn) : this.data.clone();
11485     },
11486
11487     /**
11488      * Query by a function. The specified function will be called with each
11489      * record in this data source. If the function returns true the record is included
11490      * in the results.
11491      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11492      * @param {Object} scope (optional) The scope of the function (defaults to this)
11493       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11494      **/
11495     queryBy : function(fn, scope){
11496         var data = this.snapshot || this.data;
11497         return data.filterBy(fn, scope||this);
11498     },
11499
11500     /**
11501      * Collects unique values for a particular dataIndex from this store.
11502      * @param {String} dataIndex The property to collect
11503      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11504      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11505      * @return {Array} An array of the unique values
11506      **/
11507     collect : function(dataIndex, allowNull, bypassFilter){
11508         var d = (bypassFilter === true && this.snapshot) ?
11509                 this.snapshot.items : this.data.items;
11510         var v, sv, r = [], l = {};
11511         for(var i = 0, len = d.length; i < len; i++){
11512             v = d[i].data[dataIndex];
11513             sv = String(v);
11514             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11515                 l[sv] = true;
11516                 r[r.length] = v;
11517             }
11518         }
11519         return r;
11520     },
11521
11522     /**
11523      * Revert to a view of the Record cache with no filtering applied.
11524      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11525      */
11526     clearFilter : function(suppressEvent){
11527         if(this.snapshot && this.snapshot != this.data){
11528             this.data = this.snapshot;
11529             delete this.snapshot;
11530             if(suppressEvent !== true){
11531                 this.fireEvent("datachanged", this);
11532             }
11533         }
11534     },
11535
11536     // private
11537     afterEdit : function(record){
11538         if(this.modified.indexOf(record) == -1){
11539             this.modified.push(record);
11540         }
11541         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11542     },
11543     
11544     // private
11545     afterReject : function(record){
11546         this.modified.remove(record);
11547         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11548     },
11549
11550     // private
11551     afterCommit : function(record){
11552         this.modified.remove(record);
11553         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11554     },
11555
11556     /**
11557      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11558      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11559      */
11560     commitChanges : function(){
11561         var m = this.modified.slice(0);
11562         this.modified = [];
11563         for(var i = 0, len = m.length; i < len; i++){
11564             m[i].commit();
11565         }
11566     },
11567
11568     /**
11569      * Cancel outstanding changes on all changed records.
11570      */
11571     rejectChanges : function(){
11572         var m = this.modified.slice(0);
11573         this.modified = [];
11574         for(var i = 0, len = m.length; i < len; i++){
11575             m[i].reject();
11576         }
11577     },
11578
11579     onMetaChange : function(meta, rtype, o){
11580         this.recordType = rtype;
11581         this.fields = rtype.prototype.fields;
11582         delete this.snapshot;
11583         this.sortInfo = meta.sortInfo || this.sortInfo;
11584         this.modified = [];
11585         this.fireEvent('metachange', this, this.reader.meta);
11586     },
11587     
11588     moveIndex : function(data, type)
11589     {
11590         var index = this.indexOf(data);
11591         
11592         var newIndex = index + type;
11593         
11594         this.remove(data);
11595         
11596         this.insert(newIndex, data);
11597         
11598     }
11599 });/*
11600  * Based on:
11601  * Ext JS Library 1.1.1
11602  * Copyright(c) 2006-2007, Ext JS, LLC.
11603  *
11604  * Originally Released Under LGPL - original licence link has changed is not relivant.
11605  *
11606  * Fork - LGPL
11607  * <script type="text/javascript">
11608  */
11609
11610 /**
11611  * @class Roo.data.SimpleStore
11612  * @extends Roo.data.Store
11613  * Small helper class to make creating Stores from Array data easier.
11614  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11615  * @cfg {Array} fields An array of field definition objects, or field name strings.
11616  * @cfg {Array} data The multi-dimensional array of data
11617  * @constructor
11618  * @param {Object} config
11619  */
11620 Roo.data.SimpleStore = function(config){
11621     Roo.data.SimpleStore.superclass.constructor.call(this, {
11622         isLocal : true,
11623         reader: new Roo.data.ArrayReader({
11624                 id: config.id
11625             },
11626             Roo.data.Record.create(config.fields)
11627         ),
11628         proxy : new Roo.data.MemoryProxy(config.data)
11629     });
11630     this.load();
11631 };
11632 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11633  * Based on:
11634  * Ext JS Library 1.1.1
11635  * Copyright(c) 2006-2007, Ext JS, LLC.
11636  *
11637  * Originally Released Under LGPL - original licence link has changed is not relivant.
11638  *
11639  * Fork - LGPL
11640  * <script type="text/javascript">
11641  */
11642
11643 /**
11644 /**
11645  * @extends Roo.data.Store
11646  * @class Roo.data.JsonStore
11647  * Small helper class to make creating Stores for JSON data easier. <br/>
11648 <pre><code>
11649 var store = new Roo.data.JsonStore({
11650     url: 'get-images.php',
11651     root: 'images',
11652     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11653 });
11654 </code></pre>
11655  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11656  * JsonReader and HttpProxy (unless inline data is provided).</b>
11657  * @cfg {Array} fields An array of field definition objects, or field name strings.
11658  * @constructor
11659  * @param {Object} config
11660  */
11661 Roo.data.JsonStore = function(c){
11662     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11663         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11664         reader: new Roo.data.JsonReader(c, c.fields)
11665     }));
11666 };
11667 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11668  * Based on:
11669  * Ext JS Library 1.1.1
11670  * Copyright(c) 2006-2007, Ext JS, LLC.
11671  *
11672  * Originally Released Under LGPL - original licence link has changed is not relivant.
11673  *
11674  * Fork - LGPL
11675  * <script type="text/javascript">
11676  */
11677
11678  
11679 Roo.data.Field = function(config){
11680     if(typeof config == "string"){
11681         config = {name: config};
11682     }
11683     Roo.apply(this, config);
11684     
11685     if(!this.type){
11686         this.type = "auto";
11687     }
11688     
11689     var st = Roo.data.SortTypes;
11690     // named sortTypes are supported, here we look them up
11691     if(typeof this.sortType == "string"){
11692         this.sortType = st[this.sortType];
11693     }
11694     
11695     // set default sortType for strings and dates
11696     if(!this.sortType){
11697         switch(this.type){
11698             case "string":
11699                 this.sortType = st.asUCString;
11700                 break;
11701             case "date":
11702                 this.sortType = st.asDate;
11703                 break;
11704             default:
11705                 this.sortType = st.none;
11706         }
11707     }
11708
11709     // define once
11710     var stripRe = /[\$,%]/g;
11711
11712     // prebuilt conversion function for this field, instead of
11713     // switching every time we're reading a value
11714     if(!this.convert){
11715         var cv, dateFormat = this.dateFormat;
11716         switch(this.type){
11717             case "":
11718             case "auto":
11719             case undefined:
11720                 cv = function(v){ return v; };
11721                 break;
11722             case "string":
11723                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11724                 break;
11725             case "int":
11726                 cv = function(v){
11727                     return v !== undefined && v !== null && v !== '' ?
11728                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11729                     };
11730                 break;
11731             case "float":
11732                 cv = function(v){
11733                     return v !== undefined && v !== null && v !== '' ?
11734                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11735                     };
11736                 break;
11737             case "bool":
11738             case "boolean":
11739                 cv = function(v){ return v === true || v === "true" || v == 1; };
11740                 break;
11741             case "date":
11742                 cv = function(v){
11743                     if(!v){
11744                         return '';
11745                     }
11746                     if(v instanceof Date){
11747                         return v;
11748                     }
11749                     if(dateFormat){
11750                         if(dateFormat == "timestamp"){
11751                             return new Date(v*1000);
11752                         }
11753                         return Date.parseDate(v, dateFormat);
11754                     }
11755                     var parsed = Date.parse(v);
11756                     return parsed ? new Date(parsed) : null;
11757                 };
11758              break;
11759             
11760         }
11761         this.convert = cv;
11762     }
11763 };
11764
11765 Roo.data.Field.prototype = {
11766     dateFormat: null,
11767     defaultValue: "",
11768     mapping: null,
11769     sortType : null,
11770     sortDir : "ASC"
11771 };/*
11772  * Based on:
11773  * Ext JS Library 1.1.1
11774  * Copyright(c) 2006-2007, Ext JS, LLC.
11775  *
11776  * Originally Released Under LGPL - original licence link has changed is not relivant.
11777  *
11778  * Fork - LGPL
11779  * <script type="text/javascript">
11780  */
11781  
11782 // Base class for reading structured data from a data source.  This class is intended to be
11783 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11784
11785 /**
11786  * @class Roo.data.DataReader
11787  * Base class for reading structured data from a data source.  This class is intended to be
11788  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11789  */
11790
11791 Roo.data.DataReader = function(meta, recordType){
11792     
11793     this.meta = meta;
11794     
11795     this.recordType = recordType instanceof Array ? 
11796         Roo.data.Record.create(recordType) : recordType;
11797 };
11798
11799 Roo.data.DataReader.prototype = {
11800      /**
11801      * Create an empty record
11802      * @param {Object} data (optional) - overlay some values
11803      * @return {Roo.data.Record} record created.
11804      */
11805     newRow :  function(d) {
11806         var da =  {};
11807         this.recordType.prototype.fields.each(function(c) {
11808             switch( c.type) {
11809                 case 'int' : da[c.name] = 0; break;
11810                 case 'date' : da[c.name] = new Date(); break;
11811                 case 'float' : da[c.name] = 0.0; break;
11812                 case 'boolean' : da[c.name] = false; break;
11813                 default : da[c.name] = ""; break;
11814             }
11815             
11816         });
11817         return new this.recordType(Roo.apply(da, d));
11818     }
11819     
11820 };/*
11821  * Based on:
11822  * Ext JS Library 1.1.1
11823  * Copyright(c) 2006-2007, Ext JS, LLC.
11824  *
11825  * Originally Released Under LGPL - original licence link has changed is not relivant.
11826  *
11827  * Fork - LGPL
11828  * <script type="text/javascript">
11829  */
11830
11831 /**
11832  * @class Roo.data.DataProxy
11833  * @extends Roo.data.Observable
11834  * This class is an abstract base class for implementations which provide retrieval of
11835  * unformatted data objects.<br>
11836  * <p>
11837  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11838  * (of the appropriate type which knows how to parse the data object) to provide a block of
11839  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11840  * <p>
11841  * Custom implementations must implement the load method as described in
11842  * {@link Roo.data.HttpProxy#load}.
11843  */
11844 Roo.data.DataProxy = function(){
11845     this.addEvents({
11846         /**
11847          * @event beforeload
11848          * Fires before a network request is made to retrieve a data object.
11849          * @param {Object} This DataProxy object.
11850          * @param {Object} params The params parameter to the load function.
11851          */
11852         beforeload : true,
11853         /**
11854          * @event load
11855          * Fires before the load method's callback is called.
11856          * @param {Object} This DataProxy object.
11857          * @param {Object} o The data object.
11858          * @param {Object} arg The callback argument object passed to the load function.
11859          */
11860         load : true,
11861         /**
11862          * @event loadexception
11863          * Fires if an Exception occurs during data retrieval.
11864          * @param {Object} This DataProxy object.
11865          * @param {Object} o The data object.
11866          * @param {Object} arg The callback argument object passed to the load function.
11867          * @param {Object} e The Exception.
11868          */
11869         loadexception : true
11870     });
11871     Roo.data.DataProxy.superclass.constructor.call(this);
11872 };
11873
11874 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11875
11876     /**
11877      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11878      */
11879 /*
11880  * Based on:
11881  * Ext JS Library 1.1.1
11882  * Copyright(c) 2006-2007, Ext JS, LLC.
11883  *
11884  * Originally Released Under LGPL - original licence link has changed is not relivant.
11885  *
11886  * Fork - LGPL
11887  * <script type="text/javascript">
11888  */
11889 /**
11890  * @class Roo.data.MemoryProxy
11891  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11892  * to the Reader when its load method is called.
11893  * @constructor
11894  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11895  */
11896 Roo.data.MemoryProxy = function(data){
11897     if (data.data) {
11898         data = data.data;
11899     }
11900     Roo.data.MemoryProxy.superclass.constructor.call(this);
11901     this.data = data;
11902 };
11903
11904 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11905     
11906     /**
11907      * Load data from the requested source (in this case an in-memory
11908      * data object passed to the constructor), read the data object into
11909      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11910      * process that block using the passed callback.
11911      * @param {Object} params This parameter is not used by the MemoryProxy class.
11912      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11913      * object into a block of Roo.data.Records.
11914      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11915      * The function must be passed <ul>
11916      * <li>The Record block object</li>
11917      * <li>The "arg" argument from the load function</li>
11918      * <li>A boolean success indicator</li>
11919      * </ul>
11920      * @param {Object} scope The scope in which to call the callback
11921      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11922      */
11923     load : function(params, reader, callback, scope, arg){
11924         params = params || {};
11925         var result;
11926         try {
11927             result = reader.readRecords(this.data);
11928         }catch(e){
11929             this.fireEvent("loadexception", this, arg, null, e);
11930             callback.call(scope, null, arg, false);
11931             return;
11932         }
11933         callback.call(scope, result, arg, true);
11934     },
11935     
11936     // private
11937     update : function(params, records){
11938         
11939     }
11940 });/*
11941  * Based on:
11942  * Ext JS Library 1.1.1
11943  * Copyright(c) 2006-2007, Ext JS, LLC.
11944  *
11945  * Originally Released Under LGPL - original licence link has changed is not relivant.
11946  *
11947  * Fork - LGPL
11948  * <script type="text/javascript">
11949  */
11950 /**
11951  * @class Roo.data.HttpProxy
11952  * @extends Roo.data.DataProxy
11953  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11954  * configured to reference a certain URL.<br><br>
11955  * <p>
11956  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11957  * from which the running page was served.<br><br>
11958  * <p>
11959  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11960  * <p>
11961  * Be aware that to enable the browser to parse an XML document, the server must set
11962  * the Content-Type header in the HTTP response to "text/xml".
11963  * @constructor
11964  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11965  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11966  * will be used to make the request.
11967  */
11968 Roo.data.HttpProxy = function(conn){
11969     Roo.data.HttpProxy.superclass.constructor.call(this);
11970     // is conn a conn config or a real conn?
11971     this.conn = conn;
11972     this.useAjax = !conn || !conn.events;
11973   
11974 };
11975
11976 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11977     // thse are take from connection...
11978     
11979     /**
11980      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11981      */
11982     /**
11983      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11984      * extra parameters to each request made by this object. (defaults to undefined)
11985      */
11986     /**
11987      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11988      *  to each request made by this object. (defaults to undefined)
11989      */
11990     /**
11991      * @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)
11992      */
11993     /**
11994      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11995      */
11996      /**
11997      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11998      * @type Boolean
11999      */
12000   
12001
12002     /**
12003      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12004      * @type Boolean
12005      */
12006     /**
12007      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12008      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12009      * a finer-grained basis than the DataProxy events.
12010      */
12011     getConnection : function(){
12012         return this.useAjax ? Roo.Ajax : this.conn;
12013     },
12014
12015     /**
12016      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12017      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12018      * process that block using the passed callback.
12019      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12020      * for the request to the remote server.
12021      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12022      * object into a block of Roo.data.Records.
12023      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12024      * The function must be passed <ul>
12025      * <li>The Record block object</li>
12026      * <li>The "arg" argument from the load function</li>
12027      * <li>A boolean success indicator</li>
12028      * </ul>
12029      * @param {Object} scope The scope in which to call the callback
12030      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12031      */
12032     load : function(params, reader, callback, scope, arg){
12033         if(this.fireEvent("beforeload", this, params) !== false){
12034             var  o = {
12035                 params : params || {},
12036                 request: {
12037                     callback : callback,
12038                     scope : scope,
12039                     arg : arg
12040                 },
12041                 reader: reader,
12042                 callback : this.loadResponse,
12043                 scope: this
12044             };
12045             if(this.useAjax){
12046                 Roo.applyIf(o, this.conn);
12047                 if(this.activeRequest){
12048                     Roo.Ajax.abort(this.activeRequest);
12049                 }
12050                 this.activeRequest = Roo.Ajax.request(o);
12051             }else{
12052                 this.conn.request(o);
12053             }
12054         }else{
12055             callback.call(scope||this, null, arg, false);
12056         }
12057     },
12058
12059     // private
12060     loadResponse : function(o, success, response){
12061         delete this.activeRequest;
12062         if(!success){
12063             this.fireEvent("loadexception", this, o, response);
12064             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12065             return;
12066         }
12067         var result;
12068         try {
12069             result = o.reader.read(response);
12070         }catch(e){
12071             this.fireEvent("loadexception", this, o, response, e);
12072             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12073             return;
12074         }
12075         
12076         this.fireEvent("load", this, o, o.request.arg);
12077         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12078     },
12079
12080     // private
12081     update : function(dataSet){
12082
12083     },
12084
12085     // private
12086     updateResponse : function(dataSet){
12087
12088     }
12089 });/*
12090  * Based on:
12091  * Ext JS Library 1.1.1
12092  * Copyright(c) 2006-2007, Ext JS, LLC.
12093  *
12094  * Originally Released Under LGPL - original licence link has changed is not relivant.
12095  *
12096  * Fork - LGPL
12097  * <script type="text/javascript">
12098  */
12099
12100 /**
12101  * @class Roo.data.ScriptTagProxy
12102  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12103  * other than the originating domain of the running page.<br><br>
12104  * <p>
12105  * <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
12106  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12107  * <p>
12108  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12109  * source code that is used as the source inside a &lt;script> tag.<br><br>
12110  * <p>
12111  * In order for the browser to process the returned data, the server must wrap the data object
12112  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12113  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12114  * depending on whether the callback name was passed:
12115  * <p>
12116  * <pre><code>
12117 boolean scriptTag = false;
12118 String cb = request.getParameter("callback");
12119 if (cb != null) {
12120     scriptTag = true;
12121     response.setContentType("text/javascript");
12122 } else {
12123     response.setContentType("application/x-json");
12124 }
12125 Writer out = response.getWriter();
12126 if (scriptTag) {
12127     out.write(cb + "(");
12128 }
12129 out.print(dataBlock.toJsonString());
12130 if (scriptTag) {
12131     out.write(");");
12132 }
12133 </pre></code>
12134  *
12135  * @constructor
12136  * @param {Object} config A configuration object.
12137  */
12138 Roo.data.ScriptTagProxy = function(config){
12139     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12140     Roo.apply(this, config);
12141     this.head = document.getElementsByTagName("head")[0];
12142 };
12143
12144 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12145
12146 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12147     /**
12148      * @cfg {String} url The URL from which to request the data object.
12149      */
12150     /**
12151      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12152      */
12153     timeout : 30000,
12154     /**
12155      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12156      * the server the name of the callback function set up by the load call to process the returned data object.
12157      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12158      * javascript output which calls this named function passing the data object as its only parameter.
12159      */
12160     callbackParam : "callback",
12161     /**
12162      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12163      * name to the request.
12164      */
12165     nocache : true,
12166
12167     /**
12168      * Load data from the configured URL, read the data object into
12169      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12170      * process that block using the passed callback.
12171      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12172      * for the request to the remote server.
12173      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12174      * object into a block of Roo.data.Records.
12175      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12176      * The function must be passed <ul>
12177      * <li>The Record block object</li>
12178      * <li>The "arg" argument from the load function</li>
12179      * <li>A boolean success indicator</li>
12180      * </ul>
12181      * @param {Object} scope The scope in which to call the callback
12182      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12183      */
12184     load : function(params, reader, callback, scope, arg){
12185         if(this.fireEvent("beforeload", this, params) !== false){
12186
12187             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12188
12189             var url = this.url;
12190             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12191             if(this.nocache){
12192                 url += "&_dc=" + (new Date().getTime());
12193             }
12194             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12195             var trans = {
12196                 id : transId,
12197                 cb : "stcCallback"+transId,
12198                 scriptId : "stcScript"+transId,
12199                 params : params,
12200                 arg : arg,
12201                 url : url,
12202                 callback : callback,
12203                 scope : scope,
12204                 reader : reader
12205             };
12206             var conn = this;
12207
12208             window[trans.cb] = function(o){
12209                 conn.handleResponse(o, trans);
12210             };
12211
12212             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12213
12214             if(this.autoAbort !== false){
12215                 this.abort();
12216             }
12217
12218             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12219
12220             var script = document.createElement("script");
12221             script.setAttribute("src", url);
12222             script.setAttribute("type", "text/javascript");
12223             script.setAttribute("id", trans.scriptId);
12224             this.head.appendChild(script);
12225
12226             this.trans = trans;
12227         }else{
12228             callback.call(scope||this, null, arg, false);
12229         }
12230     },
12231
12232     // private
12233     isLoading : function(){
12234         return this.trans ? true : false;
12235     },
12236
12237     /**
12238      * Abort the current server request.
12239      */
12240     abort : function(){
12241         if(this.isLoading()){
12242             this.destroyTrans(this.trans);
12243         }
12244     },
12245
12246     // private
12247     destroyTrans : function(trans, isLoaded){
12248         this.head.removeChild(document.getElementById(trans.scriptId));
12249         clearTimeout(trans.timeoutId);
12250         if(isLoaded){
12251             window[trans.cb] = undefined;
12252             try{
12253                 delete window[trans.cb];
12254             }catch(e){}
12255         }else{
12256             // if hasn't been loaded, wait for load to remove it to prevent script error
12257             window[trans.cb] = function(){
12258                 window[trans.cb] = undefined;
12259                 try{
12260                     delete window[trans.cb];
12261                 }catch(e){}
12262             };
12263         }
12264     },
12265
12266     // private
12267     handleResponse : function(o, trans){
12268         this.trans = false;
12269         this.destroyTrans(trans, true);
12270         var result;
12271         try {
12272             result = trans.reader.readRecords(o);
12273         }catch(e){
12274             this.fireEvent("loadexception", this, o, trans.arg, e);
12275             trans.callback.call(trans.scope||window, null, trans.arg, false);
12276             return;
12277         }
12278         this.fireEvent("load", this, o, trans.arg);
12279         trans.callback.call(trans.scope||window, result, trans.arg, true);
12280     },
12281
12282     // private
12283     handleFailure : function(trans){
12284         this.trans = false;
12285         this.destroyTrans(trans, false);
12286         this.fireEvent("loadexception", this, null, trans.arg);
12287         trans.callback.call(trans.scope||window, null, trans.arg, false);
12288     }
12289 });/*
12290  * Based on:
12291  * Ext JS Library 1.1.1
12292  * Copyright(c) 2006-2007, Ext JS, LLC.
12293  *
12294  * Originally Released Under LGPL - original licence link has changed is not relivant.
12295  *
12296  * Fork - LGPL
12297  * <script type="text/javascript">
12298  */
12299
12300 /**
12301  * @class Roo.data.JsonReader
12302  * @extends Roo.data.DataReader
12303  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12304  * based on mappings in a provided Roo.data.Record constructor.
12305  * 
12306  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12307  * in the reply previously. 
12308  * 
12309  * <p>
12310  * Example code:
12311  * <pre><code>
12312 var RecordDef = Roo.data.Record.create([
12313     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12314     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12315 ]);
12316 var myReader = new Roo.data.JsonReader({
12317     totalProperty: "results",    // The property which contains the total dataset size (optional)
12318     root: "rows",                // The property which contains an Array of row objects
12319     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12320 }, RecordDef);
12321 </code></pre>
12322  * <p>
12323  * This would consume a JSON file like this:
12324  * <pre><code>
12325 { 'results': 2, 'rows': [
12326     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12327     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12328 }
12329 </code></pre>
12330  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12331  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12332  * paged from the remote server.
12333  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12334  * @cfg {String} root name of the property which contains the Array of row objects.
12335  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12336  * @cfg {Array} fields Array of field definition objects
12337  * @constructor
12338  * Create a new JsonReader
12339  * @param {Object} meta Metadata configuration options
12340  * @param {Object} recordType Either an Array of field definition objects,
12341  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12342  */
12343 Roo.data.JsonReader = function(meta, recordType){
12344     
12345     meta = meta || {};
12346     // set some defaults:
12347     Roo.applyIf(meta, {
12348         totalProperty: 'total',
12349         successProperty : 'success',
12350         root : 'data',
12351         id : 'id'
12352     });
12353     
12354     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12355 };
12356 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12357     
12358     /**
12359      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12360      * Used by Store query builder to append _requestMeta to params.
12361      * 
12362      */
12363     metaFromRemote : false,
12364     /**
12365      * This method is only used by a DataProxy which has retrieved data from a remote server.
12366      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12367      * @return {Object} data A data block which is used by an Roo.data.Store object as
12368      * a cache of Roo.data.Records.
12369      */
12370     read : function(response){
12371         var json = response.responseText;
12372        
12373         var o = /* eval:var:o */ eval("("+json+")");
12374         if(!o) {
12375             throw {message: "JsonReader.read: Json object not found"};
12376         }
12377         
12378         if(o.metaData){
12379             
12380             delete this.ef;
12381             this.metaFromRemote = true;
12382             this.meta = o.metaData;
12383             this.recordType = Roo.data.Record.create(o.metaData.fields);
12384             this.onMetaChange(this.meta, this.recordType, o);
12385         }
12386         return this.readRecords(o);
12387     },
12388
12389     // private function a store will implement
12390     onMetaChange : function(meta, recordType, o){
12391
12392     },
12393
12394     /**
12395          * @ignore
12396          */
12397     simpleAccess: function(obj, subsc) {
12398         return obj[subsc];
12399     },
12400
12401         /**
12402          * @ignore
12403          */
12404     getJsonAccessor: function(){
12405         var re = /[\[\.]/;
12406         return function(expr) {
12407             try {
12408                 return(re.test(expr))
12409                     ? new Function("obj", "return obj." + expr)
12410                     : function(obj){
12411                         return obj[expr];
12412                     };
12413             } catch(e){}
12414             return Roo.emptyFn;
12415         };
12416     }(),
12417
12418     /**
12419      * Create a data block containing Roo.data.Records from an XML document.
12420      * @param {Object} o An object which contains an Array of row objects in the property specified
12421      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12422      * which contains the total size of the dataset.
12423      * @return {Object} data A data block which is used by an Roo.data.Store object as
12424      * a cache of Roo.data.Records.
12425      */
12426     readRecords : function(o){
12427         /**
12428          * After any data loads, the raw JSON data is available for further custom processing.
12429          * @type Object
12430          */
12431         this.o = o;
12432         var s = this.meta, Record = this.recordType,
12433             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12434
12435 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12436         if (!this.ef) {
12437             if(s.totalProperty) {
12438                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12439                 }
12440                 if(s.successProperty) {
12441                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12442                 }
12443                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12444                 if (s.id) {
12445                         var g = this.getJsonAccessor(s.id);
12446                         this.getId = function(rec) {
12447                                 var r = g(rec);  
12448                                 return (r === undefined || r === "") ? null : r;
12449                         };
12450                 } else {
12451                         this.getId = function(){return null;};
12452                 }
12453             this.ef = [];
12454             for(var jj = 0; jj < fl; jj++){
12455                 f = fi[jj];
12456                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12457                 this.ef[jj] = this.getJsonAccessor(map);
12458             }
12459         }
12460
12461         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12462         if(s.totalProperty){
12463             var vt = parseInt(this.getTotal(o), 10);
12464             if(!isNaN(vt)){
12465                 totalRecords = vt;
12466             }
12467         }
12468         if(s.successProperty){
12469             var vs = this.getSuccess(o);
12470             if(vs === false || vs === 'false'){
12471                 success = false;
12472             }
12473         }
12474         var records = [];
12475         for(var i = 0; i < c; i++){
12476                 var n = root[i];
12477             var values = {};
12478             var id = this.getId(n);
12479             for(var j = 0; j < fl; j++){
12480                 f = fi[j];
12481             var v = this.ef[j](n);
12482             if (!f.convert) {
12483                 Roo.log('missing convert for ' + f.name);
12484                 Roo.log(f);
12485                 continue;
12486             }
12487             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12488             }
12489             var record = new Record(values, id);
12490             record.json = n;
12491             records[i] = record;
12492         }
12493         return {
12494             raw : o,
12495             success : success,
12496             records : records,
12497             totalRecords : totalRecords
12498         };
12499     }
12500 });/*
12501  * Based on:
12502  * Ext JS Library 1.1.1
12503  * Copyright(c) 2006-2007, Ext JS, LLC.
12504  *
12505  * Originally Released Under LGPL - original licence link has changed is not relivant.
12506  *
12507  * Fork - LGPL
12508  * <script type="text/javascript">
12509  */
12510
12511 /**
12512  * @class Roo.data.ArrayReader
12513  * @extends Roo.data.DataReader
12514  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12515  * Each element of that Array represents a row of data fields. The
12516  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12517  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12518  * <p>
12519  * Example code:.
12520  * <pre><code>
12521 var RecordDef = Roo.data.Record.create([
12522     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12523     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12524 ]);
12525 var myReader = new Roo.data.ArrayReader({
12526     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12527 }, RecordDef);
12528 </code></pre>
12529  * <p>
12530  * This would consume an Array like this:
12531  * <pre><code>
12532 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12533   </code></pre>
12534  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12535  * @constructor
12536  * Create a new JsonReader
12537  * @param {Object} meta Metadata configuration options.
12538  * @param {Object} recordType Either an Array of field definition objects
12539  * as specified to {@link Roo.data.Record#create},
12540  * or an {@link Roo.data.Record} object
12541  * created using {@link Roo.data.Record#create}.
12542  */
12543 Roo.data.ArrayReader = function(meta, recordType){
12544     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12545 };
12546
12547 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12548     /**
12549      * Create a data block containing Roo.data.Records from an XML document.
12550      * @param {Object} o An Array of row objects which represents the dataset.
12551      * @return {Object} data A data block which is used by an Roo.data.Store object as
12552      * a cache of Roo.data.Records.
12553      */
12554     readRecords : function(o){
12555         var sid = this.meta ? this.meta.id : null;
12556         var recordType = this.recordType, fields = recordType.prototype.fields;
12557         var records = [];
12558         var root = o;
12559             for(var i = 0; i < root.length; i++){
12560                     var n = root[i];
12561                 var values = {};
12562                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12563                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12564                 var f = fields.items[j];
12565                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12566                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12567                 v = f.convert(v);
12568                 values[f.name] = v;
12569             }
12570                 var record = new recordType(values, id);
12571                 record.json = n;
12572                 records[records.length] = record;
12573             }
12574             return {
12575                 records : records,
12576                 totalRecords : records.length
12577             };
12578     }
12579 });/*
12580  * - LGPL
12581  * * 
12582  */
12583
12584 /**
12585  * @class Roo.bootstrap.ComboBox
12586  * @extends Roo.bootstrap.TriggerField
12587  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12588  * @cfg {Boolean} append (true|false) default false
12589  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12590  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12591  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12592  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12593  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12594  * @cfg {Boolean} animate default true
12595  * @cfg {Boolean} emptyResultText only for touch device
12596  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12597  * @cfg {String} emptyTitle default ''
12598  * @constructor
12599  * Create a new ComboBox.
12600  * @param {Object} config Configuration options
12601  */
12602 Roo.bootstrap.ComboBox = function(config){
12603     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12604     this.addEvents({
12605         /**
12606          * @event expand
12607          * Fires when the dropdown list is expanded
12608         * @param {Roo.bootstrap.ComboBox} combo This combo box
12609         */
12610         'expand' : true,
12611         /**
12612          * @event collapse
12613          * Fires when the dropdown list is collapsed
12614         * @param {Roo.bootstrap.ComboBox} combo This combo box
12615         */
12616         'collapse' : true,
12617         /**
12618          * @event beforeselect
12619          * Fires before a list item is selected. Return false to cancel the selection.
12620         * @param {Roo.bootstrap.ComboBox} combo This combo box
12621         * @param {Roo.data.Record} record The data record returned from the underlying store
12622         * @param {Number} index The index of the selected item in the dropdown list
12623         */
12624         'beforeselect' : true,
12625         /**
12626          * @event select
12627          * Fires when a list item is selected
12628         * @param {Roo.bootstrap.ComboBox} combo This combo box
12629         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12630         * @param {Number} index The index of the selected item in the dropdown list
12631         */
12632         'select' : true,
12633         /**
12634          * @event beforequery
12635          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12636          * The event object passed has these properties:
12637         * @param {Roo.bootstrap.ComboBox} combo This combo box
12638         * @param {String} query The query
12639         * @param {Boolean} forceAll true to force "all" query
12640         * @param {Boolean} cancel true to cancel the query
12641         * @param {Object} e The query event object
12642         */
12643         'beforequery': true,
12644          /**
12645          * @event add
12646          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12647         * @param {Roo.bootstrap.ComboBox} combo This combo box
12648         */
12649         'add' : true,
12650         /**
12651          * @event edit
12652          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12653         * @param {Roo.bootstrap.ComboBox} combo This combo box
12654         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12655         */
12656         'edit' : true,
12657         /**
12658          * @event remove
12659          * Fires when the remove value from the combobox array
12660         * @param {Roo.bootstrap.ComboBox} combo This combo box
12661         */
12662         'remove' : true,
12663         /**
12664          * @event afterremove
12665          * Fires when the remove value from the combobox array
12666         * @param {Roo.bootstrap.ComboBox} combo This combo box
12667         */
12668         'afterremove' : true,
12669         /**
12670          * @event specialfilter
12671          * Fires when specialfilter
12672             * @param {Roo.bootstrap.ComboBox} combo This combo box
12673             */
12674         'specialfilter' : true,
12675         /**
12676          * @event tick
12677          * Fires when tick the element
12678             * @param {Roo.bootstrap.ComboBox} combo This combo box
12679             */
12680         'tick' : true,
12681         /**
12682          * @event touchviewdisplay
12683          * Fires when touch view require special display (default is using displayField)
12684             * @param {Roo.bootstrap.ComboBox} combo This combo box
12685             * @param {Object} cfg set html .
12686             */
12687         'touchviewdisplay' : true
12688         
12689     });
12690     
12691     this.item = [];
12692     this.tickItems = [];
12693     
12694     this.selectedIndex = -1;
12695     if(this.mode == 'local'){
12696         if(config.queryDelay === undefined){
12697             this.queryDelay = 10;
12698         }
12699         if(config.minChars === undefined){
12700             this.minChars = 0;
12701         }
12702     }
12703 };
12704
12705 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12706      
12707     /**
12708      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12709      * rendering into an Roo.Editor, defaults to false)
12710      */
12711     /**
12712      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12713      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12714      */
12715     /**
12716      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12717      */
12718     /**
12719      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12720      * the dropdown list (defaults to undefined, with no header element)
12721      */
12722
12723      /**
12724      * @cfg {String/Roo.Template} tpl The template to use to render the output
12725      */
12726      
12727      /**
12728      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12729      */
12730     listWidth: undefined,
12731     /**
12732      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12733      * mode = 'remote' or 'text' if mode = 'local')
12734      */
12735     displayField: undefined,
12736     
12737     /**
12738      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12739      * mode = 'remote' or 'value' if mode = 'local'). 
12740      * Note: use of a valueField requires the user make a selection
12741      * in order for a value to be mapped.
12742      */
12743     valueField: undefined,
12744     /**
12745      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12746      */
12747     modalTitle : '',
12748     
12749     /**
12750      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12751      * field's data value (defaults to the underlying DOM element's name)
12752      */
12753     hiddenName: undefined,
12754     /**
12755      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12756      */
12757     listClass: '',
12758     /**
12759      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12760      */
12761     selectedClass: 'active',
12762     
12763     /**
12764      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12765      */
12766     shadow:'sides',
12767     /**
12768      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12769      * anchor positions (defaults to 'tl-bl')
12770      */
12771     listAlign: 'tl-bl?',
12772     /**
12773      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12774      */
12775     maxHeight: 300,
12776     /**
12777      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12778      * query specified by the allQuery config option (defaults to 'query')
12779      */
12780     triggerAction: 'query',
12781     /**
12782      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12783      * (defaults to 4, does not apply if editable = false)
12784      */
12785     minChars : 4,
12786     /**
12787      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12788      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12789      */
12790     typeAhead: false,
12791     /**
12792      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12793      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12794      */
12795     queryDelay: 500,
12796     /**
12797      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12798      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12799      */
12800     pageSize: 0,
12801     /**
12802      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12803      * when editable = true (defaults to false)
12804      */
12805     selectOnFocus:false,
12806     /**
12807      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12808      */
12809     queryParam: 'query',
12810     /**
12811      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12812      * when mode = 'remote' (defaults to 'Loading...')
12813      */
12814     loadingText: 'Loading...',
12815     /**
12816      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12817      */
12818     resizable: false,
12819     /**
12820      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12821      */
12822     handleHeight : 8,
12823     /**
12824      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12825      * traditional select (defaults to true)
12826      */
12827     editable: true,
12828     /**
12829      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12830      */
12831     allQuery: '',
12832     /**
12833      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12834      */
12835     mode: 'remote',
12836     /**
12837      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12838      * listWidth has a higher value)
12839      */
12840     minListWidth : 70,
12841     /**
12842      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12843      * allow the user to set arbitrary text into the field (defaults to false)
12844      */
12845     forceSelection:false,
12846     /**
12847      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12848      * if typeAhead = true (defaults to 250)
12849      */
12850     typeAheadDelay : 250,
12851     /**
12852      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12853      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12854      */
12855     valueNotFoundText : undefined,
12856     /**
12857      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12858      */
12859     blockFocus : false,
12860     
12861     /**
12862      * @cfg {Boolean} disableClear Disable showing of clear button.
12863      */
12864     disableClear : false,
12865     /**
12866      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12867      */
12868     alwaysQuery : false,
12869     
12870     /**
12871      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12872      */
12873     multiple : false,
12874     
12875     /**
12876      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12877      */
12878     invalidClass : "has-warning",
12879     
12880     /**
12881      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12882      */
12883     validClass : "has-success",
12884     
12885     /**
12886      * @cfg {Boolean} specialFilter (true|false) special filter default false
12887      */
12888     specialFilter : false,
12889     
12890     /**
12891      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12892      */
12893     mobileTouchView : true,
12894     
12895     /**
12896      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12897      */
12898     useNativeIOS : false,
12899     
12900     ios_options : false,
12901     
12902     //private
12903     addicon : false,
12904     editicon: false,
12905     
12906     page: 0,
12907     hasQuery: false,
12908     append: false,
12909     loadNext: false,
12910     autoFocus : true,
12911     tickable : false,
12912     btnPosition : 'right',
12913     triggerList : true,
12914     showToggleBtn : true,
12915     animate : true,
12916     emptyResultText: 'Empty',
12917     triggerText : 'Select',
12918     emptyTitle : '',
12919     
12920     // element that contains real text value.. (when hidden is used..)
12921     
12922     getAutoCreate : function()
12923     {   
12924         var cfg = false;
12925         //render
12926         /*
12927          * Render classic select for iso
12928          */
12929         
12930         if(Roo.isIOS && this.useNativeIOS){
12931             cfg = this.getAutoCreateNativeIOS();
12932             return cfg;
12933         }
12934         
12935         /*
12936          * Touch Devices
12937          */
12938         
12939         if(Roo.isTouch && this.mobileTouchView){
12940             cfg = this.getAutoCreateTouchView();
12941             return cfg;;
12942         }
12943         
12944         /*
12945          *  Normal ComboBox
12946          */
12947         if(!this.tickable){
12948             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12949             return cfg;
12950         }
12951         
12952         /*
12953          *  ComboBox with tickable selections
12954          */
12955              
12956         var align = this.labelAlign || this.parentLabelAlign();
12957         
12958         cfg = {
12959             cls : 'form-group roo-combobox-tickable' //input-group
12960         };
12961         
12962         var btn_text_select = '';
12963         var btn_text_done = '';
12964         var btn_text_cancel = '';
12965         
12966         if (this.btn_text_show) {
12967             btn_text_select = 'Select';
12968             btn_text_done = 'Done';
12969             btn_text_cancel = 'Cancel'; 
12970         }
12971         
12972         var buttons = {
12973             tag : 'div',
12974             cls : 'tickable-buttons',
12975             cn : [
12976                 {
12977                     tag : 'button',
12978                     type : 'button',
12979                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12980                     //html : this.triggerText
12981                     html: btn_text_select
12982                 },
12983                 {
12984                     tag : 'button',
12985                     type : 'button',
12986                     name : 'ok',
12987                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12988                     //html : 'Done'
12989                     html: btn_text_done
12990                 },
12991                 {
12992                     tag : 'button',
12993                     type : 'button',
12994                     name : 'cancel',
12995                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12996                     //html : 'Cancel'
12997                     html: btn_text_cancel
12998                 }
12999             ]
13000         };
13001         
13002         if(this.editable){
13003             buttons.cn.unshift({
13004                 tag: 'input',
13005                 cls: 'roo-select2-search-field-input'
13006             });
13007         }
13008         
13009         var _this = this;
13010         
13011         Roo.each(buttons.cn, function(c){
13012             if (_this.size) {
13013                 c.cls += ' btn-' + _this.size;
13014             }
13015
13016             if (_this.disabled) {
13017                 c.disabled = true;
13018             }
13019         });
13020         
13021         var box = {
13022             tag: 'div',
13023             cn: [
13024                 {
13025                     tag: 'input',
13026                     type : 'hidden',
13027                     cls: 'form-hidden-field'
13028                 },
13029                 {
13030                     tag: 'ul',
13031                     cls: 'roo-select2-choices',
13032                     cn:[
13033                         {
13034                             tag: 'li',
13035                             cls: 'roo-select2-search-field',
13036                             cn: [
13037                                 buttons
13038                             ]
13039                         }
13040                     ]
13041                 }
13042             ]
13043         };
13044         
13045         var combobox = {
13046             cls: 'roo-select2-container input-group roo-select2-container-multi',
13047             cn: [
13048                 box
13049 //                {
13050 //                    tag: 'ul',
13051 //                    cls: 'typeahead typeahead-long dropdown-menu',
13052 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13053 //                }
13054             ]
13055         };
13056         
13057         if(this.hasFeedback && !this.allowBlank){
13058             
13059             var feedback = {
13060                 tag: 'span',
13061                 cls: 'glyphicon form-control-feedback'
13062             };
13063
13064             combobox.cn.push(feedback);
13065         }
13066         
13067         
13068         if (align ==='left' && this.fieldLabel.length) {
13069             
13070             cfg.cls += ' roo-form-group-label-left';
13071             
13072             cfg.cn = [
13073                 {
13074                     tag : 'i',
13075                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13076                     tooltip : 'This field is required'
13077                 },
13078                 {
13079                     tag: 'label',
13080                     'for' :  id,
13081                     cls : 'control-label',
13082                     html : this.fieldLabel
13083
13084                 },
13085                 {
13086                     cls : "", 
13087                     cn: [
13088                         combobox
13089                     ]
13090                 }
13091
13092             ];
13093             
13094             var labelCfg = cfg.cn[1];
13095             var contentCfg = cfg.cn[2];
13096             
13097
13098             if(this.indicatorpos == 'right'){
13099                 
13100                 cfg.cn = [
13101                     {
13102                         tag: 'label',
13103                         'for' :  id,
13104                         cls : 'control-label',
13105                         cn : [
13106                             {
13107                                 tag : 'span',
13108                                 html : this.fieldLabel
13109                             },
13110                             {
13111                                 tag : 'i',
13112                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13113                                 tooltip : 'This field is required'
13114                             }
13115                         ]
13116                     },
13117                     {
13118                         cls : "",
13119                         cn: [
13120                             combobox
13121                         ]
13122                     }
13123
13124                 ];
13125                 
13126                 
13127                 
13128                 labelCfg = cfg.cn[0];
13129                 contentCfg = cfg.cn[1];
13130             
13131             }
13132             
13133             if(this.labelWidth > 12){
13134                 labelCfg.style = "width: " + this.labelWidth + 'px';
13135             }
13136             
13137             if(this.labelWidth < 13 && this.labelmd == 0){
13138                 this.labelmd = this.labelWidth;
13139             }
13140             
13141             if(this.labellg > 0){
13142                 labelCfg.cls += ' col-lg-' + this.labellg;
13143                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13144             }
13145             
13146             if(this.labelmd > 0){
13147                 labelCfg.cls += ' col-md-' + this.labelmd;
13148                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13149             }
13150             
13151             if(this.labelsm > 0){
13152                 labelCfg.cls += ' col-sm-' + this.labelsm;
13153                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13154             }
13155             
13156             if(this.labelxs > 0){
13157                 labelCfg.cls += ' col-xs-' + this.labelxs;
13158                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13159             }
13160                 
13161                 
13162         } else if ( this.fieldLabel.length) {
13163 //                Roo.log(" label");
13164                  cfg.cn = [
13165                     {
13166                         tag : 'i',
13167                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13168                         tooltip : 'This field is required'
13169                     },
13170                     {
13171                         tag: 'label',
13172                         //cls : 'input-group-addon',
13173                         html : this.fieldLabel
13174                     },
13175                     combobox
13176                 ];
13177                 
13178                 if(this.indicatorpos == 'right'){
13179                     cfg.cn = [
13180                         {
13181                             tag: 'label',
13182                             //cls : 'input-group-addon',
13183                             html : this.fieldLabel
13184                         },
13185                         {
13186                             tag : 'i',
13187                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13188                             tooltip : 'This field is required'
13189                         },
13190                         combobox
13191                     ];
13192                     
13193                 }
13194
13195         } else {
13196             
13197 //                Roo.log(" no label && no align");
13198                 cfg = combobox
13199                      
13200                 
13201         }
13202          
13203         var settings=this;
13204         ['xs','sm','md','lg'].map(function(size){
13205             if (settings[size]) {
13206                 cfg.cls += ' col-' + size + '-' + settings[size];
13207             }
13208         });
13209         
13210         return cfg;
13211         
13212     },
13213     
13214     _initEventsCalled : false,
13215     
13216     // private
13217     initEvents: function()
13218     {   
13219         if (this._initEventsCalled) { // as we call render... prevent looping...
13220             return;
13221         }
13222         this._initEventsCalled = true;
13223         
13224         if (!this.store) {
13225             throw "can not find store for combo";
13226         }
13227         
13228         this.indicator = this.indicatorEl();
13229         
13230         this.store = Roo.factory(this.store, Roo.data);
13231         this.store.parent = this;
13232         
13233         // if we are building from html. then this element is so complex, that we can not really
13234         // use the rendered HTML.
13235         // so we have to trash and replace the previous code.
13236         if (Roo.XComponent.build_from_html) {
13237             // remove this element....
13238             var e = this.el.dom, k=0;
13239             while (e ) { e = e.previousSibling;  ++k;}
13240
13241             this.el.remove();
13242             
13243             this.el=false;
13244             this.rendered = false;
13245             
13246             this.render(this.parent().getChildContainer(true), k);
13247         }
13248         
13249         if(Roo.isIOS && this.useNativeIOS){
13250             this.initIOSView();
13251             return;
13252         }
13253         
13254         /*
13255          * Touch Devices
13256          */
13257         
13258         if(Roo.isTouch && this.mobileTouchView){
13259             this.initTouchView();
13260             return;
13261         }
13262         
13263         if(this.tickable){
13264             this.initTickableEvents();
13265             return;
13266         }
13267         
13268         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13269         
13270         if(this.hiddenName){
13271             
13272             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13273             
13274             this.hiddenField.dom.value =
13275                 this.hiddenValue !== undefined ? this.hiddenValue :
13276                 this.value !== undefined ? this.value : '';
13277
13278             // prevent input submission
13279             this.el.dom.removeAttribute('name');
13280             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13281              
13282              
13283         }
13284         //if(Roo.isGecko){
13285         //    this.el.dom.setAttribute('autocomplete', 'off');
13286         //}
13287         
13288         var cls = 'x-combo-list';
13289         
13290         //this.list = new Roo.Layer({
13291         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13292         //});
13293         
13294         var _this = this;
13295         
13296         (function(){
13297             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13298             _this.list.setWidth(lw);
13299         }).defer(100);
13300         
13301         this.list.on('mouseover', this.onViewOver, this);
13302         this.list.on('mousemove', this.onViewMove, this);
13303         this.list.on('scroll', this.onViewScroll, this);
13304         
13305         /*
13306         this.list.swallowEvent('mousewheel');
13307         this.assetHeight = 0;
13308
13309         if(this.title){
13310             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13311             this.assetHeight += this.header.getHeight();
13312         }
13313
13314         this.innerList = this.list.createChild({cls:cls+'-inner'});
13315         this.innerList.on('mouseover', this.onViewOver, this);
13316         this.innerList.on('mousemove', this.onViewMove, this);
13317         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13318         
13319         if(this.allowBlank && !this.pageSize && !this.disableClear){
13320             this.footer = this.list.createChild({cls:cls+'-ft'});
13321             this.pageTb = new Roo.Toolbar(this.footer);
13322            
13323         }
13324         if(this.pageSize){
13325             this.footer = this.list.createChild({cls:cls+'-ft'});
13326             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13327                     {pageSize: this.pageSize});
13328             
13329         }
13330         
13331         if (this.pageTb && this.allowBlank && !this.disableClear) {
13332             var _this = this;
13333             this.pageTb.add(new Roo.Toolbar.Fill(), {
13334                 cls: 'x-btn-icon x-btn-clear',
13335                 text: '&#160;',
13336                 handler: function()
13337                 {
13338                     _this.collapse();
13339                     _this.clearValue();
13340                     _this.onSelect(false, -1);
13341                 }
13342             });
13343         }
13344         if (this.footer) {
13345             this.assetHeight += this.footer.getHeight();
13346         }
13347         */
13348             
13349         if(!this.tpl){
13350             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13351         }
13352
13353         this.view = new Roo.View(this.list, this.tpl, {
13354             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13355         });
13356         //this.view.wrapEl.setDisplayed(false);
13357         this.view.on('click', this.onViewClick, this);
13358         
13359         
13360         this.store.on('beforeload', this.onBeforeLoad, this);
13361         this.store.on('load', this.onLoad, this);
13362         this.store.on('loadexception', this.onLoadException, this);
13363         /*
13364         if(this.resizable){
13365             this.resizer = new Roo.Resizable(this.list,  {
13366                pinned:true, handles:'se'
13367             });
13368             this.resizer.on('resize', function(r, w, h){
13369                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13370                 this.listWidth = w;
13371                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13372                 this.restrictHeight();
13373             }, this);
13374             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13375         }
13376         */
13377         if(!this.editable){
13378             this.editable = true;
13379             this.setEditable(false);
13380         }
13381         
13382         /*
13383         
13384         if (typeof(this.events.add.listeners) != 'undefined') {
13385             
13386             this.addicon = this.wrap.createChild(
13387                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13388        
13389             this.addicon.on('click', function(e) {
13390                 this.fireEvent('add', this);
13391             }, this);
13392         }
13393         if (typeof(this.events.edit.listeners) != 'undefined') {
13394             
13395             this.editicon = this.wrap.createChild(
13396                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13397             if (this.addicon) {
13398                 this.editicon.setStyle('margin-left', '40px');
13399             }
13400             this.editicon.on('click', function(e) {
13401                 
13402                 // we fire even  if inothing is selected..
13403                 this.fireEvent('edit', this, this.lastData );
13404                 
13405             }, this);
13406         }
13407         */
13408         
13409         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13410             "up" : function(e){
13411                 this.inKeyMode = true;
13412                 this.selectPrev();
13413             },
13414
13415             "down" : function(e){
13416                 if(!this.isExpanded()){
13417                     this.onTriggerClick();
13418                 }else{
13419                     this.inKeyMode = true;
13420                     this.selectNext();
13421                 }
13422             },
13423
13424             "enter" : function(e){
13425 //                this.onViewClick();
13426                 //return true;
13427                 this.collapse();
13428                 
13429                 if(this.fireEvent("specialkey", this, e)){
13430                     this.onViewClick(false);
13431                 }
13432                 
13433                 return true;
13434             },
13435
13436             "esc" : function(e){
13437                 this.collapse();
13438             },
13439
13440             "tab" : function(e){
13441                 this.collapse();
13442                 
13443                 if(this.fireEvent("specialkey", this, e)){
13444                     this.onViewClick(false);
13445                 }
13446                 
13447                 return true;
13448             },
13449
13450             scope : this,
13451
13452             doRelay : function(foo, bar, hname){
13453                 if(hname == 'down' || this.scope.isExpanded()){
13454                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13455                 }
13456                 return true;
13457             },
13458
13459             forceKeyDown: true
13460         });
13461         
13462         
13463         this.queryDelay = Math.max(this.queryDelay || 10,
13464                 this.mode == 'local' ? 10 : 250);
13465         
13466         
13467         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13468         
13469         if(this.typeAhead){
13470             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13471         }
13472         if(this.editable !== false){
13473             this.inputEl().on("keyup", this.onKeyUp, this);
13474         }
13475         if(this.forceSelection){
13476             this.inputEl().on('blur', this.doForce, this);
13477         }
13478         
13479         if(this.multiple){
13480             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13481             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13482         }
13483     },
13484     
13485     initTickableEvents: function()
13486     {   
13487         this.createList();
13488         
13489         if(this.hiddenName){
13490             
13491             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13492             
13493             this.hiddenField.dom.value =
13494                 this.hiddenValue !== undefined ? this.hiddenValue :
13495                 this.value !== undefined ? this.value : '';
13496
13497             // prevent input submission
13498             this.el.dom.removeAttribute('name');
13499             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13500              
13501              
13502         }
13503         
13504 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13505         
13506         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13507         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13508         if(this.triggerList){
13509             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13510         }
13511          
13512         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13513         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13514         
13515         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13516         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13517         
13518         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13519         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13520         
13521         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13522         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13523         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13524         
13525         this.okBtn.hide();
13526         this.cancelBtn.hide();
13527         
13528         var _this = this;
13529         
13530         (function(){
13531             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13532             _this.list.setWidth(lw);
13533         }).defer(100);
13534         
13535         this.list.on('mouseover', this.onViewOver, this);
13536         this.list.on('mousemove', this.onViewMove, this);
13537         
13538         this.list.on('scroll', this.onViewScroll, this);
13539         
13540         if(!this.tpl){
13541             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>';
13542         }
13543
13544         this.view = new Roo.View(this.list, this.tpl, {
13545             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13546         });
13547         
13548         //this.view.wrapEl.setDisplayed(false);
13549         this.view.on('click', this.onViewClick, this);
13550         
13551         
13552         
13553         this.store.on('beforeload', this.onBeforeLoad, this);
13554         this.store.on('load', this.onLoad, this);
13555         this.store.on('loadexception', this.onLoadException, this);
13556         
13557         if(this.editable){
13558             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13559                 "up" : function(e){
13560                     this.inKeyMode = true;
13561                     this.selectPrev();
13562                 },
13563
13564                 "down" : function(e){
13565                     this.inKeyMode = true;
13566                     this.selectNext();
13567                 },
13568
13569                 "enter" : function(e){
13570                     if(this.fireEvent("specialkey", this, e)){
13571                         this.onViewClick(false);
13572                     }
13573                     
13574                     return true;
13575                 },
13576
13577                 "esc" : function(e){
13578                     this.onTickableFooterButtonClick(e, false, false);
13579                 },
13580
13581                 "tab" : function(e){
13582                     this.fireEvent("specialkey", this, e);
13583                     
13584                     this.onTickableFooterButtonClick(e, false, false);
13585                     
13586                     return true;
13587                 },
13588
13589                 scope : this,
13590
13591                 doRelay : function(e, fn, key){
13592                     if(this.scope.isExpanded()){
13593                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13594                     }
13595                     return true;
13596                 },
13597
13598                 forceKeyDown: true
13599             });
13600         }
13601         
13602         this.queryDelay = Math.max(this.queryDelay || 10,
13603                 this.mode == 'local' ? 10 : 250);
13604         
13605         
13606         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13607         
13608         if(this.typeAhead){
13609             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13610         }
13611         
13612         if(this.editable !== false){
13613             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13614         }
13615         
13616         this.indicator = this.indicatorEl();
13617         
13618         if(this.indicator){
13619             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13620             this.indicator.hide();
13621         }
13622         
13623     },
13624
13625     onDestroy : function(){
13626         if(this.view){
13627             this.view.setStore(null);
13628             this.view.el.removeAllListeners();
13629             this.view.el.remove();
13630             this.view.purgeListeners();
13631         }
13632         if(this.list){
13633             this.list.dom.innerHTML  = '';
13634         }
13635         
13636         if(this.store){
13637             this.store.un('beforeload', this.onBeforeLoad, this);
13638             this.store.un('load', this.onLoad, this);
13639             this.store.un('loadexception', this.onLoadException, this);
13640         }
13641         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13642     },
13643
13644     // private
13645     fireKey : function(e){
13646         if(e.isNavKeyPress() && !this.list.isVisible()){
13647             this.fireEvent("specialkey", this, e);
13648         }
13649     },
13650
13651     // private
13652     onResize: function(w, h){
13653 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13654 //        
13655 //        if(typeof w != 'number'){
13656 //            // we do not handle it!?!?
13657 //            return;
13658 //        }
13659 //        var tw = this.trigger.getWidth();
13660 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13661 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13662 //        var x = w - tw;
13663 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13664 //            
13665 //        //this.trigger.setStyle('left', x+'px');
13666 //        
13667 //        if(this.list && this.listWidth === undefined){
13668 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13669 //            this.list.setWidth(lw);
13670 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13671 //        }
13672         
13673     
13674         
13675     },
13676
13677     /**
13678      * Allow or prevent the user from directly editing the field text.  If false is passed,
13679      * the user will only be able to select from the items defined in the dropdown list.  This method
13680      * is the runtime equivalent of setting the 'editable' config option at config time.
13681      * @param {Boolean} value True to allow the user to directly edit the field text
13682      */
13683     setEditable : function(value){
13684         if(value == this.editable){
13685             return;
13686         }
13687         this.editable = value;
13688         if(!value){
13689             this.inputEl().dom.setAttribute('readOnly', true);
13690             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13691             this.inputEl().addClass('x-combo-noedit');
13692         }else{
13693             this.inputEl().dom.setAttribute('readOnly', false);
13694             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13695             this.inputEl().removeClass('x-combo-noedit');
13696         }
13697     },
13698
13699     // private
13700     
13701     onBeforeLoad : function(combo,opts){
13702         if(!this.hasFocus){
13703             return;
13704         }
13705          if (!opts.add) {
13706             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13707          }
13708         this.restrictHeight();
13709         this.selectedIndex = -1;
13710     },
13711
13712     // private
13713     onLoad : function(){
13714         
13715         this.hasQuery = false;
13716         
13717         if(!this.hasFocus){
13718             return;
13719         }
13720         
13721         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13722             this.loading.hide();
13723         }
13724         
13725         if(this.store.getCount() > 0){
13726             
13727             this.expand();
13728             this.restrictHeight();
13729             if(this.lastQuery == this.allQuery){
13730                 if(this.editable && !this.tickable){
13731                     this.inputEl().dom.select();
13732                 }
13733                 
13734                 if(
13735                     !this.selectByValue(this.value, true) &&
13736                     this.autoFocus && 
13737                     (
13738                         !this.store.lastOptions ||
13739                         typeof(this.store.lastOptions.add) == 'undefined' || 
13740                         this.store.lastOptions.add != true
13741                     )
13742                 ){
13743                     this.select(0, true);
13744                 }
13745             }else{
13746                 if(this.autoFocus){
13747                     this.selectNext();
13748                 }
13749                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13750                     this.taTask.delay(this.typeAheadDelay);
13751                 }
13752             }
13753         }else{
13754             this.onEmptyResults();
13755         }
13756         
13757         //this.el.focus();
13758     },
13759     // private
13760     onLoadException : function()
13761     {
13762         this.hasQuery = false;
13763         
13764         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13765             this.loading.hide();
13766         }
13767         
13768         if(this.tickable && this.editable){
13769             return;
13770         }
13771         
13772         this.collapse();
13773         // only causes errors at present
13774         //Roo.log(this.store.reader.jsonData);
13775         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13776             // fixme
13777             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13778         //}
13779         
13780         
13781     },
13782     // private
13783     onTypeAhead : function(){
13784         if(this.store.getCount() > 0){
13785             var r = this.store.getAt(0);
13786             var newValue = r.data[this.displayField];
13787             var len = newValue.length;
13788             var selStart = this.getRawValue().length;
13789             
13790             if(selStart != len){
13791                 this.setRawValue(newValue);
13792                 this.selectText(selStart, newValue.length);
13793             }
13794         }
13795     },
13796
13797     // private
13798     onSelect : function(record, index){
13799         
13800         if(this.fireEvent('beforeselect', this, record, index) !== false){
13801         
13802             this.setFromData(index > -1 ? record.data : false);
13803             
13804             this.collapse();
13805             this.fireEvent('select', this, record, index);
13806         }
13807     },
13808
13809     /**
13810      * Returns the currently selected field value or empty string if no value is set.
13811      * @return {String} value The selected value
13812      */
13813     getValue : function()
13814     {
13815         if(Roo.isIOS && this.useNativeIOS){
13816             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13817         }
13818         
13819         if(this.multiple){
13820             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13821         }
13822         
13823         if(this.valueField){
13824             return typeof this.value != 'undefined' ? this.value : '';
13825         }else{
13826             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13827         }
13828     },
13829     
13830     getRawValue : function()
13831     {
13832         if(Roo.isIOS && this.useNativeIOS){
13833             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13834         }
13835         
13836         var v = this.inputEl().getValue();
13837         
13838         return v;
13839     },
13840
13841     /**
13842      * Clears any text/value currently set in the field
13843      */
13844     clearValue : function(){
13845         
13846         if(this.hiddenField){
13847             this.hiddenField.dom.value = '';
13848         }
13849         this.value = '';
13850         this.setRawValue('');
13851         this.lastSelectionText = '';
13852         this.lastData = false;
13853         
13854         var close = this.closeTriggerEl();
13855         
13856         if(close){
13857             close.hide();
13858         }
13859         
13860         this.validate();
13861         
13862     },
13863
13864     /**
13865      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13866      * will be displayed in the field.  If the value does not match the data value of an existing item,
13867      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13868      * Otherwise the field will be blank (although the value will still be set).
13869      * @param {String} value The value to match
13870      */
13871     setValue : function(v)
13872     {
13873         if(Roo.isIOS && this.useNativeIOS){
13874             this.setIOSValue(v);
13875             return;
13876         }
13877         
13878         if(this.multiple){
13879             this.syncValue();
13880             return;
13881         }
13882         
13883         var text = v;
13884         if(this.valueField){
13885             var r = this.findRecord(this.valueField, v);
13886             if(r){
13887                 text = r.data[this.displayField];
13888             }else if(this.valueNotFoundText !== undefined){
13889                 text = this.valueNotFoundText;
13890             }
13891         }
13892         this.lastSelectionText = text;
13893         if(this.hiddenField){
13894             this.hiddenField.dom.value = v;
13895         }
13896         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13897         this.value = v;
13898         
13899         var close = this.closeTriggerEl();
13900         
13901         if(close){
13902             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13903         }
13904         
13905         this.validate();
13906     },
13907     /**
13908      * @property {Object} the last set data for the element
13909      */
13910     
13911     lastData : false,
13912     /**
13913      * Sets the value of the field based on a object which is related to the record format for the store.
13914      * @param {Object} value the value to set as. or false on reset?
13915      */
13916     setFromData : function(o){
13917         
13918         if(this.multiple){
13919             this.addItem(o);
13920             return;
13921         }
13922             
13923         var dv = ''; // display value
13924         var vv = ''; // value value..
13925         this.lastData = o;
13926         if (this.displayField) {
13927             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13928         } else {
13929             // this is an error condition!!!
13930             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13931         }
13932         
13933         if(this.valueField){
13934             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13935         }
13936         
13937         var close = this.closeTriggerEl();
13938         
13939         if(close){
13940             if(dv.length || vv * 1 > 0){
13941                 close.show() ;
13942                 this.blockFocus=true;
13943             } else {
13944                 close.hide();
13945             }             
13946         }
13947         
13948         if(this.hiddenField){
13949             this.hiddenField.dom.value = vv;
13950             
13951             this.lastSelectionText = dv;
13952             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13953             this.value = vv;
13954             return;
13955         }
13956         // no hidden field.. - we store the value in 'value', but still display
13957         // display field!!!!
13958         this.lastSelectionText = dv;
13959         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13960         this.value = vv;
13961         
13962         
13963         
13964     },
13965     // private
13966     reset : function(){
13967         // overridden so that last data is reset..
13968         
13969         if(this.multiple){
13970             this.clearItem();
13971             return;
13972         }
13973         
13974         this.setValue(this.originalValue);
13975         //this.clearInvalid();
13976         this.lastData = false;
13977         if (this.view) {
13978             this.view.clearSelections();
13979         }
13980         
13981         this.validate();
13982     },
13983     // private
13984     findRecord : function(prop, value){
13985         var record;
13986         if(this.store.getCount() > 0){
13987             this.store.each(function(r){
13988                 if(r.data[prop] == value){
13989                     record = r;
13990                     return false;
13991                 }
13992                 return true;
13993             });
13994         }
13995         return record;
13996     },
13997     
13998     getName: function()
13999     {
14000         // returns hidden if it's set..
14001         if (!this.rendered) {return ''};
14002         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14003         
14004     },
14005     // private
14006     onViewMove : function(e, t){
14007         this.inKeyMode = false;
14008     },
14009
14010     // private
14011     onViewOver : function(e, t){
14012         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14013             return;
14014         }
14015         var item = this.view.findItemFromChild(t);
14016         
14017         if(item){
14018             var index = this.view.indexOf(item);
14019             this.select(index, false);
14020         }
14021     },
14022
14023     // private
14024     onViewClick : function(view, doFocus, el, e)
14025     {
14026         var index = this.view.getSelectedIndexes()[0];
14027         
14028         var r = this.store.getAt(index);
14029         
14030         if(this.tickable){
14031             
14032             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14033                 return;
14034             }
14035             
14036             var rm = false;
14037             var _this = this;
14038             
14039             Roo.each(this.tickItems, function(v,k){
14040                 
14041                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14042                     Roo.log(v);
14043                     _this.tickItems.splice(k, 1);
14044                     
14045                     if(typeof(e) == 'undefined' && view == false){
14046                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14047                     }
14048                     
14049                     rm = true;
14050                     return;
14051                 }
14052             });
14053             
14054             if(rm){
14055                 return;
14056             }
14057             
14058             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14059                 this.tickItems.push(r.data);
14060             }
14061             
14062             if(typeof(e) == 'undefined' && view == false){
14063                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14064             }
14065                     
14066             return;
14067         }
14068         
14069         if(r){
14070             this.onSelect(r, index);
14071         }
14072         if(doFocus !== false && !this.blockFocus){
14073             this.inputEl().focus();
14074         }
14075     },
14076
14077     // private
14078     restrictHeight : function(){
14079         //this.innerList.dom.style.height = '';
14080         //var inner = this.innerList.dom;
14081         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14082         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14083         //this.list.beginUpdate();
14084         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14085         this.list.alignTo(this.inputEl(), this.listAlign);
14086         this.list.alignTo(this.inputEl(), this.listAlign);
14087         //this.list.endUpdate();
14088     },
14089
14090     // private
14091     onEmptyResults : function(){
14092         
14093         if(this.tickable && this.editable){
14094             this.hasFocus = false;
14095             this.restrictHeight();
14096             return;
14097         }
14098         
14099         this.collapse();
14100     },
14101
14102     /**
14103      * Returns true if the dropdown list is expanded, else false.
14104      */
14105     isExpanded : function(){
14106         return this.list.isVisible();
14107     },
14108
14109     /**
14110      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14111      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14112      * @param {String} value The data value of the item to select
14113      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14114      * selected item if it is not currently in view (defaults to true)
14115      * @return {Boolean} True if the value matched an item in the list, else false
14116      */
14117     selectByValue : function(v, scrollIntoView){
14118         if(v !== undefined && v !== null){
14119             var r = this.findRecord(this.valueField || this.displayField, v);
14120             if(r){
14121                 this.select(this.store.indexOf(r), scrollIntoView);
14122                 return true;
14123             }
14124         }
14125         return false;
14126     },
14127
14128     /**
14129      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14130      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14131      * @param {Number} index The zero-based index of the list item to select
14132      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14133      * selected item if it is not currently in view (defaults to true)
14134      */
14135     select : function(index, scrollIntoView){
14136         this.selectedIndex = index;
14137         this.view.select(index);
14138         if(scrollIntoView !== false){
14139             var el = this.view.getNode(index);
14140             /*
14141              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14142              */
14143             if(el){
14144                 this.list.scrollChildIntoView(el, false);
14145             }
14146         }
14147     },
14148
14149     // private
14150     selectNext : function(){
14151         var ct = this.store.getCount();
14152         if(ct > 0){
14153             if(this.selectedIndex == -1){
14154                 this.select(0);
14155             }else if(this.selectedIndex < ct-1){
14156                 this.select(this.selectedIndex+1);
14157             }
14158         }
14159     },
14160
14161     // private
14162     selectPrev : function(){
14163         var ct = this.store.getCount();
14164         if(ct > 0){
14165             if(this.selectedIndex == -1){
14166                 this.select(0);
14167             }else if(this.selectedIndex != 0){
14168                 this.select(this.selectedIndex-1);
14169             }
14170         }
14171     },
14172
14173     // private
14174     onKeyUp : function(e){
14175         if(this.editable !== false && !e.isSpecialKey()){
14176             this.lastKey = e.getKey();
14177             this.dqTask.delay(this.queryDelay);
14178         }
14179     },
14180
14181     // private
14182     validateBlur : function(){
14183         return !this.list || !this.list.isVisible();   
14184     },
14185
14186     // private
14187     initQuery : function(){
14188         
14189         var v = this.getRawValue();
14190         
14191         if(this.tickable && this.editable){
14192             v = this.tickableInputEl().getValue();
14193         }
14194         
14195         this.doQuery(v);
14196     },
14197
14198     // private
14199     doForce : function(){
14200         if(this.inputEl().dom.value.length > 0){
14201             this.inputEl().dom.value =
14202                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14203              
14204         }
14205     },
14206
14207     /**
14208      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14209      * query allowing the query action to be canceled if needed.
14210      * @param {String} query The SQL query to execute
14211      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14212      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14213      * saved in the current store (defaults to false)
14214      */
14215     doQuery : function(q, forceAll){
14216         
14217         if(q === undefined || q === null){
14218             q = '';
14219         }
14220         var qe = {
14221             query: q,
14222             forceAll: forceAll,
14223             combo: this,
14224             cancel:false
14225         };
14226         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14227             return false;
14228         }
14229         q = qe.query;
14230         
14231         forceAll = qe.forceAll;
14232         if(forceAll === true || (q.length >= this.minChars)){
14233             
14234             this.hasQuery = true;
14235             
14236             if(this.lastQuery != q || this.alwaysQuery){
14237                 this.lastQuery = q;
14238                 if(this.mode == 'local'){
14239                     this.selectedIndex = -1;
14240                     if(forceAll){
14241                         this.store.clearFilter();
14242                     }else{
14243                         
14244                         if(this.specialFilter){
14245                             this.fireEvent('specialfilter', this);
14246                             this.onLoad();
14247                             return;
14248                         }
14249                         
14250                         this.store.filter(this.displayField, q);
14251                     }
14252                     
14253                     this.store.fireEvent("datachanged", this.store);
14254                     
14255                     this.onLoad();
14256                     
14257                     
14258                 }else{
14259                     
14260                     this.store.baseParams[this.queryParam] = q;
14261                     
14262                     var options = {params : this.getParams(q)};
14263                     
14264                     if(this.loadNext){
14265                         options.add = true;
14266                         options.params.start = this.page * this.pageSize;
14267                     }
14268                     
14269                     this.store.load(options);
14270                     
14271                     /*
14272                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14273                      *  we should expand the list on onLoad
14274                      *  so command out it
14275                      */
14276 //                    this.expand();
14277                 }
14278             }else{
14279                 this.selectedIndex = -1;
14280                 this.onLoad();   
14281             }
14282         }
14283         
14284         this.loadNext = false;
14285     },
14286     
14287     // private
14288     getParams : function(q){
14289         var p = {};
14290         //p[this.queryParam] = q;
14291         
14292         if(this.pageSize){
14293             p.start = 0;
14294             p.limit = this.pageSize;
14295         }
14296         return p;
14297     },
14298
14299     /**
14300      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14301      */
14302     collapse : function(){
14303         if(!this.isExpanded()){
14304             return;
14305         }
14306         
14307         this.list.hide();
14308         
14309         this.hasFocus = false;
14310         
14311         if(this.tickable){
14312             this.okBtn.hide();
14313             this.cancelBtn.hide();
14314             this.trigger.show();
14315             
14316             if(this.editable){
14317                 this.tickableInputEl().dom.value = '';
14318                 this.tickableInputEl().blur();
14319             }
14320             
14321         }
14322         
14323         Roo.get(document).un('mousedown', this.collapseIf, this);
14324         Roo.get(document).un('mousewheel', this.collapseIf, this);
14325         if (!this.editable) {
14326             Roo.get(document).un('keydown', this.listKeyPress, this);
14327         }
14328         this.fireEvent('collapse', this);
14329         
14330         this.validate();
14331     },
14332
14333     // private
14334     collapseIf : function(e){
14335         var in_combo  = e.within(this.el);
14336         var in_list =  e.within(this.list);
14337         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14338         
14339         if (in_combo || in_list || is_list) {
14340             //e.stopPropagation();
14341             return;
14342         }
14343         
14344         if(this.tickable){
14345             this.onTickableFooterButtonClick(e, false, false);
14346         }
14347
14348         this.collapse();
14349         
14350     },
14351
14352     /**
14353      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14354      */
14355     expand : function(){
14356        
14357         if(this.isExpanded() || !this.hasFocus){
14358             return;
14359         }
14360         
14361         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14362         this.list.setWidth(lw);
14363         
14364         Roo.log('expand');
14365         
14366         this.list.show();
14367         
14368         this.restrictHeight();
14369         
14370         if(this.tickable){
14371             
14372             this.tickItems = Roo.apply([], this.item);
14373             
14374             this.okBtn.show();
14375             this.cancelBtn.show();
14376             this.trigger.hide();
14377             
14378             if(this.editable){
14379                 this.tickableInputEl().focus();
14380             }
14381             
14382         }
14383         
14384         Roo.get(document).on('mousedown', this.collapseIf, this);
14385         Roo.get(document).on('mousewheel', this.collapseIf, this);
14386         if (!this.editable) {
14387             Roo.get(document).on('keydown', this.listKeyPress, this);
14388         }
14389         
14390         this.fireEvent('expand', this);
14391     },
14392
14393     // private
14394     // Implements the default empty TriggerField.onTriggerClick function
14395     onTriggerClick : function(e)
14396     {
14397         Roo.log('trigger click');
14398         
14399         if(this.disabled || !this.triggerList){
14400             return;
14401         }
14402         
14403         this.page = 0;
14404         this.loadNext = false;
14405         
14406         if(this.isExpanded()){
14407             this.collapse();
14408             if (!this.blockFocus) {
14409                 this.inputEl().focus();
14410             }
14411             
14412         }else {
14413             this.hasFocus = true;
14414             if(this.triggerAction == 'all') {
14415                 this.doQuery(this.allQuery, true);
14416             } else {
14417                 this.doQuery(this.getRawValue());
14418             }
14419             if (!this.blockFocus) {
14420                 this.inputEl().focus();
14421             }
14422         }
14423     },
14424     
14425     onTickableTriggerClick : function(e)
14426     {
14427         if(this.disabled){
14428             return;
14429         }
14430         
14431         this.page = 0;
14432         this.loadNext = false;
14433         this.hasFocus = true;
14434         
14435         if(this.triggerAction == 'all') {
14436             this.doQuery(this.allQuery, true);
14437         } else {
14438             this.doQuery(this.getRawValue());
14439         }
14440     },
14441     
14442     onSearchFieldClick : function(e)
14443     {
14444         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14445             this.onTickableFooterButtonClick(e, false, false);
14446             return;
14447         }
14448         
14449         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14450             return;
14451         }
14452         
14453         this.page = 0;
14454         this.loadNext = false;
14455         this.hasFocus = true;
14456         
14457         if(this.triggerAction == 'all') {
14458             this.doQuery(this.allQuery, true);
14459         } else {
14460             this.doQuery(this.getRawValue());
14461         }
14462     },
14463     
14464     listKeyPress : function(e)
14465     {
14466         //Roo.log('listkeypress');
14467         // scroll to first matching element based on key pres..
14468         if (e.isSpecialKey()) {
14469             return false;
14470         }
14471         var k = String.fromCharCode(e.getKey()).toUpperCase();
14472         //Roo.log(k);
14473         var match  = false;
14474         var csel = this.view.getSelectedNodes();
14475         var cselitem = false;
14476         if (csel.length) {
14477             var ix = this.view.indexOf(csel[0]);
14478             cselitem  = this.store.getAt(ix);
14479             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14480                 cselitem = false;
14481             }
14482             
14483         }
14484         
14485         this.store.each(function(v) { 
14486             if (cselitem) {
14487                 // start at existing selection.
14488                 if (cselitem.id == v.id) {
14489                     cselitem = false;
14490                 }
14491                 return true;
14492             }
14493                 
14494             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14495                 match = this.store.indexOf(v);
14496                 return false;
14497             }
14498             return true;
14499         }, this);
14500         
14501         if (match === false) {
14502             return true; // no more action?
14503         }
14504         // scroll to?
14505         this.view.select(match);
14506         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14507         sn.scrollIntoView(sn.dom.parentNode, false);
14508     },
14509     
14510     onViewScroll : function(e, t){
14511         
14512         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){
14513             return;
14514         }
14515         
14516         this.hasQuery = true;
14517         
14518         this.loading = this.list.select('.loading', true).first();
14519         
14520         if(this.loading === null){
14521             this.list.createChild({
14522                 tag: 'div',
14523                 cls: 'loading roo-select2-more-results roo-select2-active',
14524                 html: 'Loading more results...'
14525             });
14526             
14527             this.loading = this.list.select('.loading', true).first();
14528             
14529             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14530             
14531             this.loading.hide();
14532         }
14533         
14534         this.loading.show();
14535         
14536         var _combo = this;
14537         
14538         this.page++;
14539         this.loadNext = true;
14540         
14541         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14542         
14543         return;
14544     },
14545     
14546     addItem : function(o)
14547     {   
14548         var dv = ''; // display value
14549         
14550         if (this.displayField) {
14551             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14552         } else {
14553             // this is an error condition!!!
14554             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14555         }
14556         
14557         if(!dv.length){
14558             return;
14559         }
14560         
14561         var choice = this.choices.createChild({
14562             tag: 'li',
14563             cls: 'roo-select2-search-choice',
14564             cn: [
14565                 {
14566                     tag: 'div',
14567                     html: dv
14568                 },
14569                 {
14570                     tag: 'a',
14571                     href: '#',
14572                     cls: 'roo-select2-search-choice-close fa fa-times',
14573                     tabindex: '-1'
14574                 }
14575             ]
14576             
14577         }, this.searchField);
14578         
14579         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14580         
14581         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14582         
14583         this.item.push(o);
14584         
14585         this.lastData = o;
14586         
14587         this.syncValue();
14588         
14589         this.inputEl().dom.value = '';
14590         
14591         this.validate();
14592     },
14593     
14594     onRemoveItem : function(e, _self, o)
14595     {
14596         e.preventDefault();
14597         
14598         this.lastItem = Roo.apply([], this.item);
14599         
14600         var index = this.item.indexOf(o.data) * 1;
14601         
14602         if( index < 0){
14603             Roo.log('not this item?!');
14604             return;
14605         }
14606         
14607         this.item.splice(index, 1);
14608         o.item.remove();
14609         
14610         this.syncValue();
14611         
14612         this.fireEvent('remove', this, e);
14613         
14614         this.validate();
14615         
14616     },
14617     
14618     syncValue : function()
14619     {
14620         if(!this.item.length){
14621             this.clearValue();
14622             return;
14623         }
14624             
14625         var value = [];
14626         var _this = this;
14627         Roo.each(this.item, function(i){
14628             if(_this.valueField){
14629                 value.push(i[_this.valueField]);
14630                 return;
14631             }
14632
14633             value.push(i);
14634         });
14635
14636         this.value = value.join(',');
14637
14638         if(this.hiddenField){
14639             this.hiddenField.dom.value = this.value;
14640         }
14641         
14642         this.store.fireEvent("datachanged", this.store);
14643         
14644         this.validate();
14645     },
14646     
14647     clearItem : function()
14648     {
14649         if(!this.multiple){
14650             return;
14651         }
14652         
14653         this.item = [];
14654         
14655         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14656            c.remove();
14657         });
14658         
14659         this.syncValue();
14660         
14661         this.validate();
14662         
14663         if(this.tickable && !Roo.isTouch){
14664             this.view.refresh();
14665         }
14666     },
14667     
14668     inputEl: function ()
14669     {
14670         if(Roo.isIOS && this.useNativeIOS){
14671             return this.el.select('select.roo-ios-select', true).first();
14672         }
14673         
14674         if(Roo.isTouch && this.mobileTouchView){
14675             return this.el.select('input.form-control',true).first();
14676         }
14677         
14678         if(this.tickable){
14679             return this.searchField;
14680         }
14681         
14682         return this.el.select('input.form-control',true).first();
14683     },
14684     
14685     onTickableFooterButtonClick : function(e, btn, el)
14686     {
14687         e.preventDefault();
14688         
14689         this.lastItem = Roo.apply([], this.item);
14690         
14691         if(btn && btn.name == 'cancel'){
14692             this.tickItems = Roo.apply([], this.item);
14693             this.collapse();
14694             return;
14695         }
14696         
14697         this.clearItem();
14698         
14699         var _this = this;
14700         
14701         Roo.each(this.tickItems, function(o){
14702             _this.addItem(o);
14703         });
14704         
14705         this.collapse();
14706         
14707     },
14708     
14709     validate : function()
14710     {
14711         if(this.getEl().hasClass('hidden')){
14712             return true;
14713         }
14714         
14715         var v = this.getRawValue();
14716         
14717         if(this.multiple){
14718             v = this.getValue();
14719         }
14720         
14721         if(this.disabled || this.allowBlank || v.length){
14722             this.markValid();
14723             return true;
14724         }
14725         
14726         this.markInvalid();
14727         return false;
14728     },
14729     
14730     tickableInputEl : function()
14731     {
14732         if(!this.tickable || !this.editable){
14733             return this.inputEl();
14734         }
14735         
14736         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14737     },
14738     
14739     
14740     getAutoCreateTouchView : function()
14741     {
14742         var id = Roo.id();
14743         
14744         var cfg = {
14745             cls: 'form-group' //input-group
14746         };
14747         
14748         var input =  {
14749             tag: 'input',
14750             id : id,
14751             type : this.inputType,
14752             cls : 'form-control x-combo-noedit',
14753             autocomplete: 'new-password',
14754             placeholder : this.placeholder || '',
14755             readonly : true
14756         };
14757         
14758         if (this.name) {
14759             input.name = this.name;
14760         }
14761         
14762         if (this.size) {
14763             input.cls += ' input-' + this.size;
14764         }
14765         
14766         if (this.disabled) {
14767             input.disabled = true;
14768         }
14769         
14770         var inputblock = {
14771             cls : '',
14772             cn : [
14773                 input
14774             ]
14775         };
14776         
14777         if(this.before){
14778             inputblock.cls += ' input-group';
14779             
14780             inputblock.cn.unshift({
14781                 tag :'span',
14782                 cls : 'input-group-addon',
14783                 html : this.before
14784             });
14785         }
14786         
14787         if(this.removable && !this.multiple){
14788             inputblock.cls += ' roo-removable';
14789             
14790             inputblock.cn.push({
14791                 tag: 'button',
14792                 html : 'x',
14793                 cls : 'roo-combo-removable-btn close'
14794             });
14795         }
14796
14797         if(this.hasFeedback && !this.allowBlank){
14798             
14799             inputblock.cls += ' has-feedback';
14800             
14801             inputblock.cn.push({
14802                 tag: 'span',
14803                 cls: 'glyphicon form-control-feedback'
14804             });
14805             
14806         }
14807         
14808         if (this.after) {
14809             
14810             inputblock.cls += (this.before) ? '' : ' input-group';
14811             
14812             inputblock.cn.push({
14813                 tag :'span',
14814                 cls : 'input-group-addon',
14815                 html : this.after
14816             });
14817         }
14818
14819         var box = {
14820             tag: 'div',
14821             cn: [
14822                 {
14823                     tag: 'input',
14824                     type : 'hidden',
14825                     cls: 'form-hidden-field'
14826                 },
14827                 inputblock
14828             ]
14829             
14830         };
14831         
14832         if(this.multiple){
14833             box = {
14834                 tag: 'div',
14835                 cn: [
14836                     {
14837                         tag: 'input',
14838                         type : 'hidden',
14839                         cls: 'form-hidden-field'
14840                     },
14841                     {
14842                         tag: 'ul',
14843                         cls: 'roo-select2-choices',
14844                         cn:[
14845                             {
14846                                 tag: 'li',
14847                                 cls: 'roo-select2-search-field',
14848                                 cn: [
14849
14850                                     inputblock
14851                                 ]
14852                             }
14853                         ]
14854                     }
14855                 ]
14856             }
14857         };
14858         
14859         var combobox = {
14860             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14861             cn: [
14862                 box
14863             ]
14864         };
14865         
14866         if(!this.multiple && this.showToggleBtn){
14867             
14868             var caret = {
14869                         tag: 'span',
14870                         cls: 'caret'
14871             };
14872             
14873             if (this.caret != false) {
14874                 caret = {
14875                      tag: 'i',
14876                      cls: 'fa fa-' + this.caret
14877                 };
14878                 
14879             }
14880             
14881             combobox.cn.push({
14882                 tag :'span',
14883                 cls : 'input-group-addon btn dropdown-toggle',
14884                 cn : [
14885                     caret,
14886                     {
14887                         tag: 'span',
14888                         cls: 'combobox-clear',
14889                         cn  : [
14890                             {
14891                                 tag : 'i',
14892                                 cls: 'icon-remove'
14893                             }
14894                         ]
14895                     }
14896                 ]
14897
14898             })
14899         }
14900         
14901         if(this.multiple){
14902             combobox.cls += ' roo-select2-container-multi';
14903         }
14904         
14905         var align = this.labelAlign || this.parentLabelAlign();
14906         
14907         if (align ==='left' && this.fieldLabel.length) {
14908
14909             cfg.cn = [
14910                 {
14911                    tag : 'i',
14912                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14913                    tooltip : 'This field is required'
14914                 },
14915                 {
14916                     tag: 'label',
14917                     cls : 'control-label',
14918                     html : this.fieldLabel
14919
14920                 },
14921                 {
14922                     cls : '', 
14923                     cn: [
14924                         combobox
14925                     ]
14926                 }
14927             ];
14928             
14929             var labelCfg = cfg.cn[1];
14930             var contentCfg = cfg.cn[2];
14931             
14932
14933             if(this.indicatorpos == 'right'){
14934                 cfg.cn = [
14935                     {
14936                         tag: 'label',
14937                         'for' :  id,
14938                         cls : 'control-label',
14939                         cn : [
14940                             {
14941                                 tag : 'span',
14942                                 html : this.fieldLabel
14943                             },
14944                             {
14945                                 tag : 'i',
14946                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14947                                 tooltip : 'This field is required'
14948                             }
14949                         ]
14950                     },
14951                     {
14952                         cls : "",
14953                         cn: [
14954                             combobox
14955                         ]
14956                     }
14957
14958                 ];
14959                 
14960                 labelCfg = cfg.cn[0];
14961                 contentCfg = cfg.cn[1];
14962             }
14963             
14964            
14965             
14966             if(this.labelWidth > 12){
14967                 labelCfg.style = "width: " + this.labelWidth + 'px';
14968             }
14969             
14970             if(this.labelWidth < 13 && this.labelmd == 0){
14971                 this.labelmd = this.labelWidth;
14972             }
14973             
14974             if(this.labellg > 0){
14975                 labelCfg.cls += ' col-lg-' + this.labellg;
14976                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14977             }
14978             
14979             if(this.labelmd > 0){
14980                 labelCfg.cls += ' col-md-' + this.labelmd;
14981                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14982             }
14983             
14984             if(this.labelsm > 0){
14985                 labelCfg.cls += ' col-sm-' + this.labelsm;
14986                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14987             }
14988             
14989             if(this.labelxs > 0){
14990                 labelCfg.cls += ' col-xs-' + this.labelxs;
14991                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14992             }
14993                 
14994                 
14995         } else if ( this.fieldLabel.length) {
14996             cfg.cn = [
14997                 {
14998                    tag : 'i',
14999                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15000                    tooltip : 'This field is required'
15001                 },
15002                 {
15003                     tag: 'label',
15004                     cls : 'control-label',
15005                     html : this.fieldLabel
15006
15007                 },
15008                 {
15009                     cls : '', 
15010                     cn: [
15011                         combobox
15012                     ]
15013                 }
15014             ];
15015             
15016             if(this.indicatorpos == 'right'){
15017                 cfg.cn = [
15018                     {
15019                         tag: 'label',
15020                         cls : 'control-label',
15021                         html : this.fieldLabel,
15022                         cn : [
15023                             {
15024                                tag : 'i',
15025                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15026                                tooltip : 'This field is required'
15027                             }
15028                         ]
15029                     },
15030                     {
15031                         cls : '', 
15032                         cn: [
15033                             combobox
15034                         ]
15035                     }
15036                 ];
15037             }
15038         } else {
15039             cfg.cn = combobox;    
15040         }
15041         
15042         
15043         var settings = this;
15044         
15045         ['xs','sm','md','lg'].map(function(size){
15046             if (settings[size]) {
15047                 cfg.cls += ' col-' + size + '-' + settings[size];
15048             }
15049         });
15050         
15051         return cfg;
15052     },
15053     
15054     initTouchView : function()
15055     {
15056         this.renderTouchView();
15057         
15058         this.touchViewEl.on('scroll', function(){
15059             this.el.dom.scrollTop = 0;
15060         }, this);
15061         
15062         this.originalValue = this.getValue();
15063         
15064         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15065         
15066         this.inputEl().on("click", this.showTouchView, this);
15067         if (this.triggerEl) {
15068             this.triggerEl.on("click", this.showTouchView, this);
15069         }
15070         
15071         
15072         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15073         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15074         
15075         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15076         
15077         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15078         this.store.on('load', this.onTouchViewLoad, this);
15079         this.store.on('loadexception', this.onTouchViewLoadException, this);
15080         
15081         if(this.hiddenName){
15082             
15083             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15084             
15085             this.hiddenField.dom.value =
15086                 this.hiddenValue !== undefined ? this.hiddenValue :
15087                 this.value !== undefined ? this.value : '';
15088         
15089             this.el.dom.removeAttribute('name');
15090             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15091         }
15092         
15093         if(this.multiple){
15094             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15095             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15096         }
15097         
15098         if(this.removable && !this.multiple){
15099             var close = this.closeTriggerEl();
15100             if(close){
15101                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15102                 close.on('click', this.removeBtnClick, this, close);
15103             }
15104         }
15105         /*
15106          * fix the bug in Safari iOS8
15107          */
15108         this.inputEl().on("focus", function(e){
15109             document.activeElement.blur();
15110         }, this);
15111         
15112         return;
15113         
15114         
15115     },
15116     
15117     renderTouchView : function()
15118     {
15119         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15120         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15121         
15122         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15123         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15124         
15125         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15126         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15127         this.touchViewBodyEl.setStyle('overflow', 'auto');
15128         
15129         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15130         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15131         
15132         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15133         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15134         
15135     },
15136     
15137     showTouchView : function()
15138     {
15139         if(this.disabled){
15140             return;
15141         }
15142         
15143         this.touchViewHeaderEl.hide();
15144
15145         if(this.modalTitle.length){
15146             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15147             this.touchViewHeaderEl.show();
15148         }
15149
15150         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15151         this.touchViewEl.show();
15152
15153         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15154         
15155         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15156         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15157
15158         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15159
15160         if(this.modalTitle.length){
15161             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15162         }
15163         
15164         this.touchViewBodyEl.setHeight(bodyHeight);
15165
15166         if(this.animate){
15167             var _this = this;
15168             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15169         }else{
15170             this.touchViewEl.addClass('in');
15171         }
15172
15173         this.doTouchViewQuery();
15174         
15175     },
15176     
15177     hideTouchView : function()
15178     {
15179         this.touchViewEl.removeClass('in');
15180
15181         if(this.animate){
15182             var _this = this;
15183             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15184         }else{
15185             this.touchViewEl.setStyle('display', 'none');
15186         }
15187         
15188     },
15189     
15190     setTouchViewValue : function()
15191     {
15192         if(this.multiple){
15193             this.clearItem();
15194         
15195             var _this = this;
15196
15197             Roo.each(this.tickItems, function(o){
15198                 this.addItem(o);
15199             }, this);
15200         }
15201         
15202         this.hideTouchView();
15203     },
15204     
15205     doTouchViewQuery : function()
15206     {
15207         var qe = {
15208             query: '',
15209             forceAll: true,
15210             combo: this,
15211             cancel:false
15212         };
15213         
15214         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15215             return false;
15216         }
15217         
15218         if(!this.alwaysQuery || this.mode == 'local'){
15219             this.onTouchViewLoad();
15220             return;
15221         }
15222         
15223         this.store.load();
15224     },
15225     
15226     onTouchViewBeforeLoad : function(combo,opts)
15227     {
15228         return;
15229     },
15230
15231     // private
15232     onTouchViewLoad : function()
15233     {
15234         if(this.store.getCount() < 1){
15235             this.onTouchViewEmptyResults();
15236             return;
15237         }
15238         
15239         this.clearTouchView();
15240         
15241         var rawValue = this.getRawValue();
15242         
15243         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15244         
15245         this.tickItems = [];
15246         
15247         this.store.data.each(function(d, rowIndex){
15248             var row = this.touchViewListGroup.createChild(template);
15249             
15250             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15251                 row.addClass(d.data.cls);
15252             }
15253             
15254             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15255                 var cfg = {
15256                     data : d.data,
15257                     html : d.data[this.displayField]
15258                 };
15259                 
15260                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15261                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15262                 }
15263             }
15264             row.removeClass('selected');
15265             if(!this.multiple && this.valueField &&
15266                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15267             {
15268                 // radio buttons..
15269                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15270                 row.addClass('selected');
15271             }
15272             
15273             if(this.multiple && this.valueField &&
15274                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15275             {
15276                 
15277                 // checkboxes...
15278                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15279                 this.tickItems.push(d.data);
15280             }
15281             
15282             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15283             
15284         }, this);
15285         
15286         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15287         
15288         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15289
15290         if(this.modalTitle.length){
15291             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15292         }
15293
15294         var listHeight = this.touchViewListGroup.getHeight();
15295         
15296         var _this = this;
15297         
15298         if(firstChecked && listHeight > bodyHeight){
15299             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15300         }
15301         
15302     },
15303     
15304     onTouchViewLoadException : function()
15305     {
15306         this.hideTouchView();
15307     },
15308     
15309     onTouchViewEmptyResults : function()
15310     {
15311         this.clearTouchView();
15312         
15313         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15314         
15315         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15316         
15317     },
15318     
15319     clearTouchView : function()
15320     {
15321         this.touchViewListGroup.dom.innerHTML = '';
15322     },
15323     
15324     onTouchViewClick : function(e, el, o)
15325     {
15326         e.preventDefault();
15327         
15328         var row = o.row;
15329         var rowIndex = o.rowIndex;
15330         
15331         var r = this.store.getAt(rowIndex);
15332         
15333         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15334             
15335             if(!this.multiple){
15336                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15337                     c.dom.removeAttribute('checked');
15338                 }, this);
15339
15340                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15341
15342                 this.setFromData(r.data);
15343
15344                 var close = this.closeTriggerEl();
15345
15346                 if(close){
15347                     close.show();
15348                 }
15349
15350                 this.hideTouchView();
15351
15352                 this.fireEvent('select', this, r, rowIndex);
15353
15354                 return;
15355             }
15356
15357             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15358                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15359                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15360                 return;
15361             }
15362
15363             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15364             this.addItem(r.data);
15365             this.tickItems.push(r.data);
15366         }
15367     },
15368     
15369     getAutoCreateNativeIOS : function()
15370     {
15371         var cfg = {
15372             cls: 'form-group' //input-group,
15373         };
15374         
15375         var combobox =  {
15376             tag: 'select',
15377             cls : 'roo-ios-select'
15378         };
15379         
15380         if (this.name) {
15381             combobox.name = this.name;
15382         }
15383         
15384         if (this.disabled) {
15385             combobox.disabled = true;
15386         }
15387         
15388         var settings = this;
15389         
15390         ['xs','sm','md','lg'].map(function(size){
15391             if (settings[size]) {
15392                 cfg.cls += ' col-' + size + '-' + settings[size];
15393             }
15394         });
15395         
15396         cfg.cn = combobox;
15397         
15398         return cfg;
15399         
15400     },
15401     
15402     initIOSView : function()
15403     {
15404         this.store.on('load', this.onIOSViewLoad, this);
15405         
15406         return;
15407     },
15408     
15409     onIOSViewLoad : function()
15410     {
15411         if(this.store.getCount() < 1){
15412             return;
15413         }
15414         
15415         this.clearIOSView();
15416         
15417         if(this.allowBlank) {
15418             
15419             var default_text = '-- SELECT --';
15420             
15421             if(this.placeholder.length){
15422                 default_text = this.placeholder;
15423             }
15424             
15425             if(this.emptyTitle.length){
15426                 default_text += ' - ' + this.emptyTitle + ' -';
15427             }
15428             
15429             var opt = this.inputEl().createChild({
15430                 tag: 'option',
15431                 value : 0,
15432                 html : default_text
15433             });
15434             
15435             var o = {};
15436             o[this.valueField] = 0;
15437             o[this.displayField] = default_text;
15438             
15439             this.ios_options.push({
15440                 data : o,
15441                 el : opt
15442             });
15443             
15444         }
15445         
15446         this.store.data.each(function(d, rowIndex){
15447             
15448             var html = '';
15449             
15450             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15451                 html = d.data[this.displayField];
15452             }
15453             
15454             var value = '';
15455             
15456             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15457                 value = d.data[this.valueField];
15458             }
15459             
15460             var option = {
15461                 tag: 'option',
15462                 value : value,
15463                 html : html
15464             };
15465             
15466             if(this.value == d.data[this.valueField]){
15467                 option['selected'] = true;
15468             }
15469             
15470             var opt = this.inputEl().createChild(option);
15471             
15472             this.ios_options.push({
15473                 data : d.data,
15474                 el : opt
15475             });
15476             
15477         }, this);
15478         
15479         this.inputEl().on('change', function(){
15480            this.fireEvent('select', this);
15481         }, this);
15482         
15483     },
15484     
15485     clearIOSView: function()
15486     {
15487         this.inputEl().dom.innerHTML = '';
15488         
15489         this.ios_options = [];
15490     },
15491     
15492     setIOSValue: function(v)
15493     {
15494         this.value = v;
15495         
15496         if(!this.ios_options){
15497             return;
15498         }
15499         
15500         Roo.each(this.ios_options, function(opts){
15501            
15502            opts.el.dom.removeAttribute('selected');
15503            
15504            if(opts.data[this.valueField] != v){
15505                return;
15506            }
15507            
15508            opts.el.dom.setAttribute('selected', true);
15509            
15510         }, this);
15511     }
15512
15513     /** 
15514     * @cfg {Boolean} grow 
15515     * @hide 
15516     */
15517     /** 
15518     * @cfg {Number} growMin 
15519     * @hide 
15520     */
15521     /** 
15522     * @cfg {Number} growMax 
15523     * @hide 
15524     */
15525     /**
15526      * @hide
15527      * @method autoSize
15528      */
15529 });
15530
15531 Roo.apply(Roo.bootstrap.ComboBox,  {
15532     
15533     header : {
15534         tag: 'div',
15535         cls: 'modal-header',
15536         cn: [
15537             {
15538                 tag: 'h4',
15539                 cls: 'modal-title'
15540             }
15541         ]
15542     },
15543     
15544     body : {
15545         tag: 'div',
15546         cls: 'modal-body',
15547         cn: [
15548             {
15549                 tag: 'ul',
15550                 cls: 'list-group'
15551             }
15552         ]
15553     },
15554     
15555     listItemRadio : {
15556         tag: 'li',
15557         cls: 'list-group-item',
15558         cn: [
15559             {
15560                 tag: 'span',
15561                 cls: 'roo-combobox-list-group-item-value'
15562             },
15563             {
15564                 tag: 'div',
15565                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15566                 cn: [
15567                     {
15568                         tag: 'input',
15569                         type: 'radio'
15570                     },
15571                     {
15572                         tag: 'label'
15573                     }
15574                 ]
15575             }
15576         ]
15577     },
15578     
15579     listItemCheckbox : {
15580         tag: 'li',
15581         cls: 'list-group-item',
15582         cn: [
15583             {
15584                 tag: 'span',
15585                 cls: 'roo-combobox-list-group-item-value'
15586             },
15587             {
15588                 tag: 'div',
15589                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15590                 cn: [
15591                     {
15592                         tag: 'input',
15593                         type: 'checkbox'
15594                     },
15595                     {
15596                         tag: 'label'
15597                     }
15598                 ]
15599             }
15600         ]
15601     },
15602     
15603     emptyResult : {
15604         tag: 'div',
15605         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15606     },
15607     
15608     footer : {
15609         tag: 'div',
15610         cls: 'modal-footer',
15611         cn: [
15612             {
15613                 tag: 'div',
15614                 cls: 'row',
15615                 cn: [
15616                     {
15617                         tag: 'div',
15618                         cls: 'col-xs-6 text-left',
15619                         cn: {
15620                             tag: 'button',
15621                             cls: 'btn btn-danger roo-touch-view-cancel',
15622                             html: 'Cancel'
15623                         }
15624                     },
15625                     {
15626                         tag: 'div',
15627                         cls: 'col-xs-6 text-right',
15628                         cn: {
15629                             tag: 'button',
15630                             cls: 'btn btn-success roo-touch-view-ok',
15631                             html: 'OK'
15632                         }
15633                     }
15634                 ]
15635             }
15636         ]
15637         
15638     }
15639 });
15640
15641 Roo.apply(Roo.bootstrap.ComboBox,  {
15642     
15643     touchViewTemplate : {
15644         tag: 'div',
15645         cls: 'modal fade roo-combobox-touch-view',
15646         cn: [
15647             {
15648                 tag: 'div',
15649                 cls: 'modal-dialog',
15650                 style : 'position:fixed', // we have to fix position....
15651                 cn: [
15652                     {
15653                         tag: 'div',
15654                         cls: 'modal-content',
15655                         cn: [
15656                             Roo.bootstrap.ComboBox.header,
15657                             Roo.bootstrap.ComboBox.body,
15658                             Roo.bootstrap.ComboBox.footer
15659                         ]
15660                     }
15661                 ]
15662             }
15663         ]
15664     }
15665 });/*
15666  * Based on:
15667  * Ext JS Library 1.1.1
15668  * Copyright(c) 2006-2007, Ext JS, LLC.
15669  *
15670  * Originally Released Under LGPL - original licence link has changed is not relivant.
15671  *
15672  * Fork - LGPL
15673  * <script type="text/javascript">
15674  */
15675
15676 /**
15677  * @class Roo.View
15678  * @extends Roo.util.Observable
15679  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15680  * This class also supports single and multi selection modes. <br>
15681  * Create a data model bound view:
15682  <pre><code>
15683  var store = new Roo.data.Store(...);
15684
15685  var view = new Roo.View({
15686     el : "my-element",
15687     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15688  
15689     singleSelect: true,
15690     selectedClass: "ydataview-selected",
15691     store: store
15692  });
15693
15694  // listen for node click?
15695  view.on("click", function(vw, index, node, e){
15696  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15697  });
15698
15699  // load XML data
15700  dataModel.load("foobar.xml");
15701  </code></pre>
15702  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15703  * <br><br>
15704  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15705  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15706  * 
15707  * Note: old style constructor is still suported (container, template, config)
15708  * 
15709  * @constructor
15710  * Create a new View
15711  * @param {Object} config The config object
15712  * 
15713  */
15714 Roo.View = function(config, depreciated_tpl, depreciated_config){
15715     
15716     this.parent = false;
15717     
15718     if (typeof(depreciated_tpl) == 'undefined') {
15719         // new way.. - universal constructor.
15720         Roo.apply(this, config);
15721         this.el  = Roo.get(this.el);
15722     } else {
15723         // old format..
15724         this.el  = Roo.get(config);
15725         this.tpl = depreciated_tpl;
15726         Roo.apply(this, depreciated_config);
15727     }
15728     this.wrapEl  = this.el.wrap().wrap();
15729     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15730     
15731     
15732     if(typeof(this.tpl) == "string"){
15733         this.tpl = new Roo.Template(this.tpl);
15734     } else {
15735         // support xtype ctors..
15736         this.tpl = new Roo.factory(this.tpl, Roo);
15737     }
15738     
15739     
15740     this.tpl.compile();
15741     
15742     /** @private */
15743     this.addEvents({
15744         /**
15745          * @event beforeclick
15746          * Fires before a click is processed. Returns false to cancel the default action.
15747          * @param {Roo.View} this
15748          * @param {Number} index The index of the target node
15749          * @param {HTMLElement} node The target node
15750          * @param {Roo.EventObject} e The raw event object
15751          */
15752             "beforeclick" : true,
15753         /**
15754          * @event click
15755          * Fires when a template node is clicked.
15756          * @param {Roo.View} this
15757          * @param {Number} index The index of the target node
15758          * @param {HTMLElement} node The target node
15759          * @param {Roo.EventObject} e The raw event object
15760          */
15761             "click" : true,
15762         /**
15763          * @event dblclick
15764          * Fires when a template node is double clicked.
15765          * @param {Roo.View} this
15766          * @param {Number} index The index of the target node
15767          * @param {HTMLElement} node The target node
15768          * @param {Roo.EventObject} e The raw event object
15769          */
15770             "dblclick" : true,
15771         /**
15772          * @event contextmenu
15773          * Fires when a template node is right clicked.
15774          * @param {Roo.View} this
15775          * @param {Number} index The index of the target node
15776          * @param {HTMLElement} node The target node
15777          * @param {Roo.EventObject} e The raw event object
15778          */
15779             "contextmenu" : true,
15780         /**
15781          * @event selectionchange
15782          * Fires when the selected nodes change.
15783          * @param {Roo.View} this
15784          * @param {Array} selections Array of the selected nodes
15785          */
15786             "selectionchange" : true,
15787     
15788         /**
15789          * @event beforeselect
15790          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15791          * @param {Roo.View} this
15792          * @param {HTMLElement} node The node to be selected
15793          * @param {Array} selections Array of currently selected nodes
15794          */
15795             "beforeselect" : true,
15796         /**
15797          * @event preparedata
15798          * Fires on every row to render, to allow you to change the data.
15799          * @param {Roo.View} this
15800          * @param {Object} data to be rendered (change this)
15801          */
15802           "preparedata" : true
15803           
15804           
15805         });
15806
15807
15808
15809     this.el.on({
15810         "click": this.onClick,
15811         "dblclick": this.onDblClick,
15812         "contextmenu": this.onContextMenu,
15813         scope:this
15814     });
15815
15816     this.selections = [];
15817     this.nodes = [];
15818     this.cmp = new Roo.CompositeElementLite([]);
15819     if(this.store){
15820         this.store = Roo.factory(this.store, Roo.data);
15821         this.setStore(this.store, true);
15822     }
15823     
15824     if ( this.footer && this.footer.xtype) {
15825            
15826          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15827         
15828         this.footer.dataSource = this.store;
15829         this.footer.container = fctr;
15830         this.footer = Roo.factory(this.footer, Roo);
15831         fctr.insertFirst(this.el);
15832         
15833         // this is a bit insane - as the paging toolbar seems to detach the el..
15834 //        dom.parentNode.parentNode.parentNode
15835          // they get detached?
15836     }
15837     
15838     
15839     Roo.View.superclass.constructor.call(this);
15840     
15841     
15842 };
15843
15844 Roo.extend(Roo.View, Roo.util.Observable, {
15845     
15846      /**
15847      * @cfg {Roo.data.Store} store Data store to load data from.
15848      */
15849     store : false,
15850     
15851     /**
15852      * @cfg {String|Roo.Element} el The container element.
15853      */
15854     el : '',
15855     
15856     /**
15857      * @cfg {String|Roo.Template} tpl The template used by this View 
15858      */
15859     tpl : false,
15860     /**
15861      * @cfg {String} dataName the named area of the template to use as the data area
15862      *                          Works with domtemplates roo-name="name"
15863      */
15864     dataName: false,
15865     /**
15866      * @cfg {String} selectedClass The css class to add to selected nodes
15867      */
15868     selectedClass : "x-view-selected",
15869      /**
15870      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15871      */
15872     emptyText : "",
15873     
15874     /**
15875      * @cfg {String} text to display on mask (default Loading)
15876      */
15877     mask : false,
15878     /**
15879      * @cfg {Boolean} multiSelect Allow multiple selection
15880      */
15881     multiSelect : false,
15882     /**
15883      * @cfg {Boolean} singleSelect Allow single selection
15884      */
15885     singleSelect:  false,
15886     
15887     /**
15888      * @cfg {Boolean} toggleSelect - selecting 
15889      */
15890     toggleSelect : false,
15891     
15892     /**
15893      * @cfg {Boolean} tickable - selecting 
15894      */
15895     tickable : false,
15896     
15897     /**
15898      * Returns the element this view is bound to.
15899      * @return {Roo.Element}
15900      */
15901     getEl : function(){
15902         return this.wrapEl;
15903     },
15904     
15905     
15906
15907     /**
15908      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15909      */
15910     refresh : function(){
15911         //Roo.log('refresh');
15912         var t = this.tpl;
15913         
15914         // if we are using something like 'domtemplate', then
15915         // the what gets used is:
15916         // t.applySubtemplate(NAME, data, wrapping data..)
15917         // the outer template then get' applied with
15918         //     the store 'extra data'
15919         // and the body get's added to the
15920         //      roo-name="data" node?
15921         //      <span class='roo-tpl-{name}'></span> ?????
15922         
15923         
15924         
15925         this.clearSelections();
15926         this.el.update("");
15927         var html = [];
15928         var records = this.store.getRange();
15929         if(records.length < 1) {
15930             
15931             // is this valid??  = should it render a template??
15932             
15933             this.el.update(this.emptyText);
15934             return;
15935         }
15936         var el = this.el;
15937         if (this.dataName) {
15938             this.el.update(t.apply(this.store.meta)); //????
15939             el = this.el.child('.roo-tpl-' + this.dataName);
15940         }
15941         
15942         for(var i = 0, len = records.length; i < len; i++){
15943             var data = this.prepareData(records[i].data, i, records[i]);
15944             this.fireEvent("preparedata", this, data, i, records[i]);
15945             
15946             var d = Roo.apply({}, data);
15947             
15948             if(this.tickable){
15949                 Roo.apply(d, {'roo-id' : Roo.id()});
15950                 
15951                 var _this = this;
15952             
15953                 Roo.each(this.parent.item, function(item){
15954                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15955                         return;
15956                     }
15957                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15958                 });
15959             }
15960             
15961             html[html.length] = Roo.util.Format.trim(
15962                 this.dataName ?
15963                     t.applySubtemplate(this.dataName, d, this.store.meta) :
15964                     t.apply(d)
15965             );
15966         }
15967         
15968         
15969         
15970         el.update(html.join(""));
15971         this.nodes = el.dom.childNodes;
15972         this.updateIndexes(0);
15973     },
15974     
15975
15976     /**
15977      * Function to override to reformat the data that is sent to
15978      * the template for each node.
15979      * DEPRICATED - use the preparedata event handler.
15980      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15981      * a JSON object for an UpdateManager bound view).
15982      */
15983     prepareData : function(data, index, record)
15984     {
15985         this.fireEvent("preparedata", this, data, index, record);
15986         return data;
15987     },
15988
15989     onUpdate : function(ds, record){
15990         // Roo.log('on update');   
15991         this.clearSelections();
15992         var index = this.store.indexOf(record);
15993         var n = this.nodes[index];
15994         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15995         n.parentNode.removeChild(n);
15996         this.updateIndexes(index, index);
15997     },
15998
15999     
16000     
16001 // --------- FIXME     
16002     onAdd : function(ds, records, index)
16003     {
16004         //Roo.log(['on Add', ds, records, index] );        
16005         this.clearSelections();
16006         if(this.nodes.length == 0){
16007             this.refresh();
16008             return;
16009         }
16010         var n = this.nodes[index];
16011         for(var i = 0, len = records.length; i < len; i++){
16012             var d = this.prepareData(records[i].data, i, records[i]);
16013             if(n){
16014                 this.tpl.insertBefore(n, d);
16015             }else{
16016                 
16017                 this.tpl.append(this.el, d);
16018             }
16019         }
16020         this.updateIndexes(index);
16021     },
16022
16023     onRemove : function(ds, record, index){
16024        // Roo.log('onRemove');
16025         this.clearSelections();
16026         var el = this.dataName  ?
16027             this.el.child('.roo-tpl-' + this.dataName) :
16028             this.el; 
16029         
16030         el.dom.removeChild(this.nodes[index]);
16031         this.updateIndexes(index);
16032     },
16033
16034     /**
16035      * Refresh an individual node.
16036      * @param {Number} index
16037      */
16038     refreshNode : function(index){
16039         this.onUpdate(this.store, this.store.getAt(index));
16040     },
16041
16042     updateIndexes : function(startIndex, endIndex){
16043         var ns = this.nodes;
16044         startIndex = startIndex || 0;
16045         endIndex = endIndex || ns.length - 1;
16046         for(var i = startIndex; i <= endIndex; i++){
16047             ns[i].nodeIndex = i;
16048         }
16049     },
16050
16051     /**
16052      * Changes the data store this view uses and refresh the view.
16053      * @param {Store} store
16054      */
16055     setStore : function(store, initial){
16056         if(!initial && this.store){
16057             this.store.un("datachanged", this.refresh);
16058             this.store.un("add", this.onAdd);
16059             this.store.un("remove", this.onRemove);
16060             this.store.un("update", this.onUpdate);
16061             this.store.un("clear", this.refresh);
16062             this.store.un("beforeload", this.onBeforeLoad);
16063             this.store.un("load", this.onLoad);
16064             this.store.un("loadexception", this.onLoad);
16065         }
16066         if(store){
16067           
16068             store.on("datachanged", this.refresh, this);
16069             store.on("add", this.onAdd, this);
16070             store.on("remove", this.onRemove, this);
16071             store.on("update", this.onUpdate, this);
16072             store.on("clear", this.refresh, this);
16073             store.on("beforeload", this.onBeforeLoad, this);
16074             store.on("load", this.onLoad, this);
16075             store.on("loadexception", this.onLoad, this);
16076         }
16077         
16078         if(store){
16079             this.refresh();
16080         }
16081     },
16082     /**
16083      * onbeforeLoad - masks the loading area.
16084      *
16085      */
16086     onBeforeLoad : function(store,opts)
16087     {
16088          //Roo.log('onBeforeLoad');   
16089         if (!opts.add) {
16090             this.el.update("");
16091         }
16092         this.el.mask(this.mask ? this.mask : "Loading" ); 
16093     },
16094     onLoad : function ()
16095     {
16096         this.el.unmask();
16097     },
16098     
16099
16100     /**
16101      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16102      * @param {HTMLElement} node
16103      * @return {HTMLElement} The template node
16104      */
16105     findItemFromChild : function(node){
16106         var el = this.dataName  ?
16107             this.el.child('.roo-tpl-' + this.dataName,true) :
16108             this.el.dom; 
16109         
16110         if(!node || node.parentNode == el){
16111                     return node;
16112             }
16113             var p = node.parentNode;
16114             while(p && p != el){
16115             if(p.parentNode == el){
16116                 return p;
16117             }
16118             p = p.parentNode;
16119         }
16120             return null;
16121     },
16122
16123     /** @ignore */
16124     onClick : function(e){
16125         var item = this.findItemFromChild(e.getTarget());
16126         if(item){
16127             var index = this.indexOf(item);
16128             if(this.onItemClick(item, index, e) !== false){
16129                 this.fireEvent("click", this, index, item, e);
16130             }
16131         }else{
16132             this.clearSelections();
16133         }
16134     },
16135
16136     /** @ignore */
16137     onContextMenu : function(e){
16138         var item = this.findItemFromChild(e.getTarget());
16139         if(item){
16140             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16141         }
16142     },
16143
16144     /** @ignore */
16145     onDblClick : function(e){
16146         var item = this.findItemFromChild(e.getTarget());
16147         if(item){
16148             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16149         }
16150     },
16151
16152     onItemClick : function(item, index, e)
16153     {
16154         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16155             return false;
16156         }
16157         if (this.toggleSelect) {
16158             var m = this.isSelected(item) ? 'unselect' : 'select';
16159             //Roo.log(m);
16160             var _t = this;
16161             _t[m](item, true, false);
16162             return true;
16163         }
16164         if(this.multiSelect || this.singleSelect){
16165             if(this.multiSelect && e.shiftKey && this.lastSelection){
16166                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16167             }else{
16168                 this.select(item, this.multiSelect && e.ctrlKey);
16169                 this.lastSelection = item;
16170             }
16171             
16172             if(!this.tickable){
16173                 e.preventDefault();
16174             }
16175             
16176         }
16177         return true;
16178     },
16179
16180     /**
16181      * Get the number of selected nodes.
16182      * @return {Number}
16183      */
16184     getSelectionCount : function(){
16185         return this.selections.length;
16186     },
16187
16188     /**
16189      * Get the currently selected nodes.
16190      * @return {Array} An array of HTMLElements
16191      */
16192     getSelectedNodes : function(){
16193         return this.selections;
16194     },
16195
16196     /**
16197      * Get the indexes of the selected nodes.
16198      * @return {Array}
16199      */
16200     getSelectedIndexes : function(){
16201         var indexes = [], s = this.selections;
16202         for(var i = 0, len = s.length; i < len; i++){
16203             indexes.push(s[i].nodeIndex);
16204         }
16205         return indexes;
16206     },
16207
16208     /**
16209      * Clear all selections
16210      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16211      */
16212     clearSelections : function(suppressEvent){
16213         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16214             this.cmp.elements = this.selections;
16215             this.cmp.removeClass(this.selectedClass);
16216             this.selections = [];
16217             if(!suppressEvent){
16218                 this.fireEvent("selectionchange", this, this.selections);
16219             }
16220         }
16221     },
16222
16223     /**
16224      * Returns true if the passed node is selected
16225      * @param {HTMLElement/Number} node The node or node index
16226      * @return {Boolean}
16227      */
16228     isSelected : function(node){
16229         var s = this.selections;
16230         if(s.length < 1){
16231             return false;
16232         }
16233         node = this.getNode(node);
16234         return s.indexOf(node) !== -1;
16235     },
16236
16237     /**
16238      * Selects nodes.
16239      * @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
16240      * @param {Boolean} keepExisting (optional) true to keep existing selections
16241      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16242      */
16243     select : function(nodeInfo, keepExisting, suppressEvent){
16244         if(nodeInfo instanceof Array){
16245             if(!keepExisting){
16246                 this.clearSelections(true);
16247             }
16248             for(var i = 0, len = nodeInfo.length; i < len; i++){
16249                 this.select(nodeInfo[i], true, true);
16250             }
16251             return;
16252         } 
16253         var node = this.getNode(nodeInfo);
16254         if(!node || this.isSelected(node)){
16255             return; // already selected.
16256         }
16257         if(!keepExisting){
16258             this.clearSelections(true);
16259         }
16260         
16261         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16262             Roo.fly(node).addClass(this.selectedClass);
16263             this.selections.push(node);
16264             if(!suppressEvent){
16265                 this.fireEvent("selectionchange", this, this.selections);
16266             }
16267         }
16268         
16269         
16270     },
16271       /**
16272      * Unselects nodes.
16273      * @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
16274      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16275      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16276      */
16277     unselect : function(nodeInfo, keepExisting, suppressEvent)
16278     {
16279         if(nodeInfo instanceof Array){
16280             Roo.each(this.selections, function(s) {
16281                 this.unselect(s, nodeInfo);
16282             }, this);
16283             return;
16284         }
16285         var node = this.getNode(nodeInfo);
16286         if(!node || !this.isSelected(node)){
16287             //Roo.log("not selected");
16288             return; // not selected.
16289         }
16290         // fireevent???
16291         var ns = [];
16292         Roo.each(this.selections, function(s) {
16293             if (s == node ) {
16294                 Roo.fly(node).removeClass(this.selectedClass);
16295
16296                 return;
16297             }
16298             ns.push(s);
16299         },this);
16300         
16301         this.selections= ns;
16302         this.fireEvent("selectionchange", this, this.selections);
16303     },
16304
16305     /**
16306      * Gets a template node.
16307      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16308      * @return {HTMLElement} The node or null if it wasn't found
16309      */
16310     getNode : function(nodeInfo){
16311         if(typeof nodeInfo == "string"){
16312             return document.getElementById(nodeInfo);
16313         }else if(typeof nodeInfo == "number"){
16314             return this.nodes[nodeInfo];
16315         }
16316         return nodeInfo;
16317     },
16318
16319     /**
16320      * Gets a range template nodes.
16321      * @param {Number} startIndex
16322      * @param {Number} endIndex
16323      * @return {Array} An array of nodes
16324      */
16325     getNodes : function(start, end){
16326         var ns = this.nodes;
16327         start = start || 0;
16328         end = typeof end == "undefined" ? ns.length - 1 : end;
16329         var nodes = [];
16330         if(start <= end){
16331             for(var i = start; i <= end; i++){
16332                 nodes.push(ns[i]);
16333             }
16334         } else{
16335             for(var i = start; i >= end; i--){
16336                 nodes.push(ns[i]);
16337             }
16338         }
16339         return nodes;
16340     },
16341
16342     /**
16343      * Finds the index of the passed node
16344      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16345      * @return {Number} The index of the node or -1
16346      */
16347     indexOf : function(node){
16348         node = this.getNode(node);
16349         if(typeof node.nodeIndex == "number"){
16350             return node.nodeIndex;
16351         }
16352         var ns = this.nodes;
16353         for(var i = 0, len = ns.length; i < len; i++){
16354             if(ns[i] == node){
16355                 return i;
16356             }
16357         }
16358         return -1;
16359     }
16360 });
16361 /*
16362  * - LGPL
16363  *
16364  * based on jquery fullcalendar
16365  * 
16366  */
16367
16368 Roo.bootstrap = Roo.bootstrap || {};
16369 /**
16370  * @class Roo.bootstrap.Calendar
16371  * @extends Roo.bootstrap.Component
16372  * Bootstrap Calendar class
16373  * @cfg {Boolean} loadMask (true|false) default false
16374  * @cfg {Object} header generate the user specific header of the calendar, default false
16375
16376  * @constructor
16377  * Create a new Container
16378  * @param {Object} config The config object
16379  */
16380
16381
16382
16383 Roo.bootstrap.Calendar = function(config){
16384     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16385      this.addEvents({
16386         /**
16387              * @event select
16388              * Fires when a date is selected
16389              * @param {DatePicker} this
16390              * @param {Date} date The selected date
16391              */
16392         'select': true,
16393         /**
16394              * @event monthchange
16395              * Fires when the displayed month changes 
16396              * @param {DatePicker} this
16397              * @param {Date} date The selected month
16398              */
16399         'monthchange': true,
16400         /**
16401              * @event evententer
16402              * Fires when mouse over an event
16403              * @param {Calendar} this
16404              * @param {event} Event
16405              */
16406         'evententer': true,
16407         /**
16408              * @event eventleave
16409              * Fires when the mouse leaves an
16410              * @param {Calendar} this
16411              * @param {event}
16412              */
16413         'eventleave': true,
16414         /**
16415              * @event eventclick
16416              * Fires when the mouse click an
16417              * @param {Calendar} this
16418              * @param {event}
16419              */
16420         'eventclick': true
16421         
16422     });
16423
16424 };
16425
16426 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16427     
16428      /**
16429      * @cfg {Number} startDay
16430      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16431      */
16432     startDay : 0,
16433     
16434     loadMask : false,
16435     
16436     header : false,
16437       
16438     getAutoCreate : function(){
16439         
16440         
16441         var fc_button = function(name, corner, style, content ) {
16442             return Roo.apply({},{
16443                 tag : 'span',
16444                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16445                          (corner.length ?
16446                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16447                             ''
16448                         ),
16449                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16450                 unselectable: 'on'
16451             });
16452         };
16453         
16454         var header = {};
16455         
16456         if(!this.header){
16457             header = {
16458                 tag : 'table',
16459                 cls : 'fc-header',
16460                 style : 'width:100%',
16461                 cn : [
16462                     {
16463                         tag: 'tr',
16464                         cn : [
16465                             {
16466                                 tag : 'td',
16467                                 cls : 'fc-header-left',
16468                                 cn : [
16469                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16470                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16471                                     { tag: 'span', cls: 'fc-header-space' },
16472                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16473
16474
16475                                 ]
16476                             },
16477
16478                             {
16479                                 tag : 'td',
16480                                 cls : 'fc-header-center',
16481                                 cn : [
16482                                     {
16483                                         tag: 'span',
16484                                         cls: 'fc-header-title',
16485                                         cn : {
16486                                             tag: 'H2',
16487                                             html : 'month / year'
16488                                         }
16489                                     }
16490
16491                                 ]
16492                             },
16493                             {
16494                                 tag : 'td',
16495                                 cls : 'fc-header-right',
16496                                 cn : [
16497                               /*      fc_button('month', 'left', '', 'month' ),
16498                                     fc_button('week', '', '', 'week' ),
16499                                     fc_button('day', 'right', '', 'day' )
16500                                 */    
16501
16502                                 ]
16503                             }
16504
16505                         ]
16506                     }
16507                 ]
16508             };
16509         }
16510         
16511         header = this.header;
16512         
16513        
16514         var cal_heads = function() {
16515             var ret = [];
16516             // fixme - handle this.
16517             
16518             for (var i =0; i < Date.dayNames.length; i++) {
16519                 var d = Date.dayNames[i];
16520                 ret.push({
16521                     tag: 'th',
16522                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16523                     html : d.substring(0,3)
16524                 });
16525                 
16526             }
16527             ret[0].cls += ' fc-first';
16528             ret[6].cls += ' fc-last';
16529             return ret;
16530         };
16531         var cal_cell = function(n) {
16532             return  {
16533                 tag: 'td',
16534                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16535                 cn : [
16536                     {
16537                         cn : [
16538                             {
16539                                 cls: 'fc-day-number',
16540                                 html: 'D'
16541                             },
16542                             {
16543                                 cls: 'fc-day-content',
16544                              
16545                                 cn : [
16546                                      {
16547                                         style: 'position: relative;' // height: 17px;
16548                                     }
16549                                 ]
16550                             }
16551                             
16552                             
16553                         ]
16554                     }
16555                 ]
16556                 
16557             }
16558         };
16559         var cal_rows = function() {
16560             
16561             var ret = [];
16562             for (var r = 0; r < 6; r++) {
16563                 var row= {
16564                     tag : 'tr',
16565                     cls : 'fc-week',
16566                     cn : []
16567                 };
16568                 
16569                 for (var i =0; i < Date.dayNames.length; i++) {
16570                     var d = Date.dayNames[i];
16571                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16572
16573                 }
16574                 row.cn[0].cls+=' fc-first';
16575                 row.cn[0].cn[0].style = 'min-height:90px';
16576                 row.cn[6].cls+=' fc-last';
16577                 ret.push(row);
16578                 
16579             }
16580             ret[0].cls += ' fc-first';
16581             ret[4].cls += ' fc-prev-last';
16582             ret[5].cls += ' fc-last';
16583             return ret;
16584             
16585         };
16586         
16587         var cal_table = {
16588             tag: 'table',
16589             cls: 'fc-border-separate',
16590             style : 'width:100%',
16591             cellspacing  : 0,
16592             cn : [
16593                 { 
16594                     tag: 'thead',
16595                     cn : [
16596                         { 
16597                             tag: 'tr',
16598                             cls : 'fc-first fc-last',
16599                             cn : cal_heads()
16600                         }
16601                     ]
16602                 },
16603                 { 
16604                     tag: 'tbody',
16605                     cn : cal_rows()
16606                 }
16607                   
16608             ]
16609         };
16610          
16611          var cfg = {
16612             cls : 'fc fc-ltr',
16613             cn : [
16614                 header,
16615                 {
16616                     cls : 'fc-content',
16617                     style : "position: relative;",
16618                     cn : [
16619                         {
16620                             cls : 'fc-view fc-view-month fc-grid',
16621                             style : 'position: relative',
16622                             unselectable : 'on',
16623                             cn : [
16624                                 {
16625                                     cls : 'fc-event-container',
16626                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16627                                 },
16628                                 cal_table
16629                             ]
16630                         }
16631                     ]
16632     
16633                 }
16634            ] 
16635             
16636         };
16637         
16638          
16639         
16640         return cfg;
16641     },
16642     
16643     
16644     initEvents : function()
16645     {
16646         if(!this.store){
16647             throw "can not find store for calendar";
16648         }
16649         
16650         var mark = {
16651             tag: "div",
16652             cls:"x-dlg-mask",
16653             style: "text-align:center",
16654             cn: [
16655                 {
16656                     tag: "div",
16657                     style: "background-color:white;width:50%;margin:250 auto",
16658                     cn: [
16659                         {
16660                             tag: "img",
16661                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16662                         },
16663                         {
16664                             tag: "span",
16665                             html: "Loading"
16666                         }
16667                         
16668                     ]
16669                 }
16670             ]
16671         };
16672         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16673         
16674         var size = this.el.select('.fc-content', true).first().getSize();
16675         this.maskEl.setSize(size.width, size.height);
16676         this.maskEl.enableDisplayMode("block");
16677         if(!this.loadMask){
16678             this.maskEl.hide();
16679         }
16680         
16681         this.store = Roo.factory(this.store, Roo.data);
16682         this.store.on('load', this.onLoad, this);
16683         this.store.on('beforeload', this.onBeforeLoad, this);
16684         
16685         this.resize();
16686         
16687         this.cells = this.el.select('.fc-day',true);
16688         //Roo.log(this.cells);
16689         this.textNodes = this.el.query('.fc-day-number');
16690         this.cells.addClassOnOver('fc-state-hover');
16691         
16692         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16693         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16694         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16695         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16696         
16697         this.on('monthchange', this.onMonthChange, this);
16698         
16699         this.update(new Date().clearTime());
16700     },
16701     
16702     resize : function() {
16703         var sz  = this.el.getSize();
16704         
16705         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16706         this.el.select('.fc-day-content div',true).setHeight(34);
16707     },
16708     
16709     
16710     // private
16711     showPrevMonth : function(e){
16712         this.update(this.activeDate.add("mo", -1));
16713     },
16714     showToday : function(e){
16715         this.update(new Date().clearTime());
16716     },
16717     // private
16718     showNextMonth : function(e){
16719         this.update(this.activeDate.add("mo", 1));
16720     },
16721
16722     // private
16723     showPrevYear : function(){
16724         this.update(this.activeDate.add("y", -1));
16725     },
16726
16727     // private
16728     showNextYear : function(){
16729         this.update(this.activeDate.add("y", 1));
16730     },
16731
16732     
16733    // private
16734     update : function(date)
16735     {
16736         var vd = this.activeDate;
16737         this.activeDate = date;
16738 //        if(vd && this.el){
16739 //            var t = date.getTime();
16740 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16741 //                Roo.log('using add remove');
16742 //                
16743 //                this.fireEvent('monthchange', this, date);
16744 //                
16745 //                this.cells.removeClass("fc-state-highlight");
16746 //                this.cells.each(function(c){
16747 //                   if(c.dateValue == t){
16748 //                       c.addClass("fc-state-highlight");
16749 //                       setTimeout(function(){
16750 //                            try{c.dom.firstChild.focus();}catch(e){}
16751 //                       }, 50);
16752 //                       return false;
16753 //                   }
16754 //                   return true;
16755 //                });
16756 //                return;
16757 //            }
16758 //        }
16759         
16760         var days = date.getDaysInMonth();
16761         
16762         var firstOfMonth = date.getFirstDateOfMonth();
16763         var startingPos = firstOfMonth.getDay()-this.startDay;
16764         
16765         if(startingPos < this.startDay){
16766             startingPos += 7;
16767         }
16768         
16769         var pm = date.add(Date.MONTH, -1);
16770         var prevStart = pm.getDaysInMonth()-startingPos;
16771 //        
16772         this.cells = this.el.select('.fc-day',true);
16773         this.textNodes = this.el.query('.fc-day-number');
16774         this.cells.addClassOnOver('fc-state-hover');
16775         
16776         var cells = this.cells.elements;
16777         var textEls = this.textNodes;
16778         
16779         Roo.each(cells, function(cell){
16780             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16781         });
16782         
16783         days += startingPos;
16784
16785         // convert everything to numbers so it's fast
16786         var day = 86400000;
16787         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16788         //Roo.log(d);
16789         //Roo.log(pm);
16790         //Roo.log(prevStart);
16791         
16792         var today = new Date().clearTime().getTime();
16793         var sel = date.clearTime().getTime();
16794         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16795         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16796         var ddMatch = this.disabledDatesRE;
16797         var ddText = this.disabledDatesText;
16798         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16799         var ddaysText = this.disabledDaysText;
16800         var format = this.format;
16801         
16802         var setCellClass = function(cal, cell){
16803             cell.row = 0;
16804             cell.events = [];
16805             cell.more = [];
16806             //Roo.log('set Cell Class');
16807             cell.title = "";
16808             var t = d.getTime();
16809             
16810             //Roo.log(d);
16811             
16812             cell.dateValue = t;
16813             if(t == today){
16814                 cell.className += " fc-today";
16815                 cell.className += " fc-state-highlight";
16816                 cell.title = cal.todayText;
16817             }
16818             if(t == sel){
16819                 // disable highlight in other month..
16820                 //cell.className += " fc-state-highlight";
16821                 
16822             }
16823             // disabling
16824             if(t < min) {
16825                 cell.className = " fc-state-disabled";
16826                 cell.title = cal.minText;
16827                 return;
16828             }
16829             if(t > max) {
16830                 cell.className = " fc-state-disabled";
16831                 cell.title = cal.maxText;
16832                 return;
16833             }
16834             if(ddays){
16835                 if(ddays.indexOf(d.getDay()) != -1){
16836                     cell.title = ddaysText;
16837                     cell.className = " fc-state-disabled";
16838                 }
16839             }
16840             if(ddMatch && format){
16841                 var fvalue = d.dateFormat(format);
16842                 if(ddMatch.test(fvalue)){
16843                     cell.title = ddText.replace("%0", fvalue);
16844                     cell.className = " fc-state-disabled";
16845                 }
16846             }
16847             
16848             if (!cell.initialClassName) {
16849                 cell.initialClassName = cell.dom.className;
16850             }
16851             
16852             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16853         };
16854
16855         var i = 0;
16856         
16857         for(; i < startingPos; i++) {
16858             textEls[i].innerHTML = (++prevStart);
16859             d.setDate(d.getDate()+1);
16860             
16861             cells[i].className = "fc-past fc-other-month";
16862             setCellClass(this, cells[i]);
16863         }
16864         
16865         var intDay = 0;
16866         
16867         for(; i < days; i++){
16868             intDay = i - startingPos + 1;
16869             textEls[i].innerHTML = (intDay);
16870             d.setDate(d.getDate()+1);
16871             
16872             cells[i].className = ''; // "x-date-active";
16873             setCellClass(this, cells[i]);
16874         }
16875         var extraDays = 0;
16876         
16877         for(; i < 42; i++) {
16878             textEls[i].innerHTML = (++extraDays);
16879             d.setDate(d.getDate()+1);
16880             
16881             cells[i].className = "fc-future fc-other-month";
16882             setCellClass(this, cells[i]);
16883         }
16884         
16885         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16886         
16887         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16888         
16889         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16890         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16891         
16892         if(totalRows != 6){
16893             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16894             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16895         }
16896         
16897         this.fireEvent('monthchange', this, date);
16898         
16899         
16900         /*
16901         if(!this.internalRender){
16902             var main = this.el.dom.firstChild;
16903             var w = main.offsetWidth;
16904             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16905             Roo.fly(main).setWidth(w);
16906             this.internalRender = true;
16907             // opera does not respect the auto grow header center column
16908             // then, after it gets a width opera refuses to recalculate
16909             // without a second pass
16910             if(Roo.isOpera && !this.secondPass){
16911                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16912                 this.secondPass = true;
16913                 this.update.defer(10, this, [date]);
16914             }
16915         }
16916         */
16917         
16918     },
16919     
16920     findCell : function(dt) {
16921         dt = dt.clearTime().getTime();
16922         var ret = false;
16923         this.cells.each(function(c){
16924             //Roo.log("check " +c.dateValue + '?=' + dt);
16925             if(c.dateValue == dt){
16926                 ret = c;
16927                 return false;
16928             }
16929             return true;
16930         });
16931         
16932         return ret;
16933     },
16934     
16935     findCells : function(ev) {
16936         var s = ev.start.clone().clearTime().getTime();
16937        // Roo.log(s);
16938         var e= ev.end.clone().clearTime().getTime();
16939        // Roo.log(e);
16940         var ret = [];
16941         this.cells.each(function(c){
16942              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16943             
16944             if(c.dateValue > e){
16945                 return ;
16946             }
16947             if(c.dateValue < s){
16948                 return ;
16949             }
16950             ret.push(c);
16951         });
16952         
16953         return ret;    
16954     },
16955     
16956 //    findBestRow: function(cells)
16957 //    {
16958 //        var ret = 0;
16959 //        
16960 //        for (var i =0 ; i < cells.length;i++) {
16961 //            ret  = Math.max(cells[i].rows || 0,ret);
16962 //        }
16963 //        return ret;
16964 //        
16965 //    },
16966     
16967     
16968     addItem : function(ev)
16969     {
16970         // look for vertical location slot in
16971         var cells = this.findCells(ev);
16972         
16973 //        ev.row = this.findBestRow(cells);
16974         
16975         // work out the location.
16976         
16977         var crow = false;
16978         var rows = [];
16979         for(var i =0; i < cells.length; i++) {
16980             
16981             cells[i].row = cells[0].row;
16982             
16983             if(i == 0){
16984                 cells[i].row = cells[i].row + 1;
16985             }
16986             
16987             if (!crow) {
16988                 crow = {
16989                     start : cells[i],
16990                     end :  cells[i]
16991                 };
16992                 continue;
16993             }
16994             if (crow.start.getY() == cells[i].getY()) {
16995                 // on same row.
16996                 crow.end = cells[i];
16997                 continue;
16998             }
16999             // different row.
17000             rows.push(crow);
17001             crow = {
17002                 start: cells[i],
17003                 end : cells[i]
17004             };
17005             
17006         }
17007         
17008         rows.push(crow);
17009         ev.els = [];
17010         ev.rows = rows;
17011         ev.cells = cells;
17012         
17013         cells[0].events.push(ev);
17014         
17015         this.calevents.push(ev);
17016     },
17017     
17018     clearEvents: function() {
17019         
17020         if(!this.calevents){
17021             return;
17022         }
17023         
17024         Roo.each(this.cells.elements, function(c){
17025             c.row = 0;
17026             c.events = [];
17027             c.more = [];
17028         });
17029         
17030         Roo.each(this.calevents, function(e) {
17031             Roo.each(e.els, function(el) {
17032                 el.un('mouseenter' ,this.onEventEnter, this);
17033                 el.un('mouseleave' ,this.onEventLeave, this);
17034                 el.remove();
17035             },this);
17036         },this);
17037         
17038         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17039             e.remove();
17040         });
17041         
17042     },
17043     
17044     renderEvents: function()
17045     {   
17046         var _this = this;
17047         
17048         this.cells.each(function(c) {
17049             
17050             if(c.row < 5){
17051                 return;
17052             }
17053             
17054             var ev = c.events;
17055             
17056             var r = 4;
17057             if(c.row != c.events.length){
17058                 r = 4 - (4 - (c.row - c.events.length));
17059             }
17060             
17061             c.events = ev.slice(0, r);
17062             c.more = ev.slice(r);
17063             
17064             if(c.more.length && c.more.length == 1){
17065                 c.events.push(c.more.pop());
17066             }
17067             
17068             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17069             
17070         });
17071             
17072         this.cells.each(function(c) {
17073             
17074             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17075             
17076             
17077             for (var e = 0; e < c.events.length; e++){
17078                 var ev = c.events[e];
17079                 var rows = ev.rows;
17080                 
17081                 for(var i = 0; i < rows.length; i++) {
17082                 
17083                     // how many rows should it span..
17084
17085                     var  cfg = {
17086                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17087                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17088
17089                         unselectable : "on",
17090                         cn : [
17091                             {
17092                                 cls: 'fc-event-inner',
17093                                 cn : [
17094     //                                {
17095     //                                  tag:'span',
17096     //                                  cls: 'fc-event-time',
17097     //                                  html : cells.length > 1 ? '' : ev.time
17098     //                                },
17099                                     {
17100                                       tag:'span',
17101                                       cls: 'fc-event-title',
17102                                       html : String.format('{0}', ev.title)
17103                                     }
17104
17105
17106                                 ]
17107                             },
17108                             {
17109                                 cls: 'ui-resizable-handle ui-resizable-e',
17110                                 html : '&nbsp;&nbsp;&nbsp'
17111                             }
17112
17113                         ]
17114                     };
17115
17116                     if (i == 0) {
17117                         cfg.cls += ' fc-event-start';
17118                     }
17119                     if ((i+1) == rows.length) {
17120                         cfg.cls += ' fc-event-end';
17121                     }
17122
17123                     var ctr = _this.el.select('.fc-event-container',true).first();
17124                     var cg = ctr.createChild(cfg);
17125
17126                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17127                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17128
17129                     var r = (c.more.length) ? 1 : 0;
17130                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17131                     cg.setWidth(ebox.right - sbox.x -2);
17132
17133                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17134                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17135                     cg.on('click', _this.onEventClick, _this, ev);
17136
17137                     ev.els.push(cg);
17138                     
17139                 }
17140                 
17141             }
17142             
17143             
17144             if(c.more.length){
17145                 var  cfg = {
17146                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17147                     style : 'position: absolute',
17148                     unselectable : "on",
17149                     cn : [
17150                         {
17151                             cls: 'fc-event-inner',
17152                             cn : [
17153                                 {
17154                                   tag:'span',
17155                                   cls: 'fc-event-title',
17156                                   html : 'More'
17157                                 }
17158
17159
17160                             ]
17161                         },
17162                         {
17163                             cls: 'ui-resizable-handle ui-resizable-e',
17164                             html : '&nbsp;&nbsp;&nbsp'
17165                         }
17166
17167                     ]
17168                 };
17169
17170                 var ctr = _this.el.select('.fc-event-container',true).first();
17171                 var cg = ctr.createChild(cfg);
17172
17173                 var sbox = c.select('.fc-day-content',true).first().getBox();
17174                 var ebox = c.select('.fc-day-content',true).first().getBox();
17175                 //Roo.log(cg);
17176                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17177                 cg.setWidth(ebox.right - sbox.x -2);
17178
17179                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17180                 
17181             }
17182             
17183         });
17184         
17185         
17186         
17187     },
17188     
17189     onEventEnter: function (e, el,event,d) {
17190         this.fireEvent('evententer', this, el, event);
17191     },
17192     
17193     onEventLeave: function (e, el,event,d) {
17194         this.fireEvent('eventleave', this, el, event);
17195     },
17196     
17197     onEventClick: function (e, el,event,d) {
17198         this.fireEvent('eventclick', this, el, event);
17199     },
17200     
17201     onMonthChange: function () {
17202         this.store.load();
17203     },
17204     
17205     onMoreEventClick: function(e, el, more)
17206     {
17207         var _this = this;
17208         
17209         this.calpopover.placement = 'right';
17210         this.calpopover.setTitle('More');
17211         
17212         this.calpopover.setContent('');
17213         
17214         var ctr = this.calpopover.el.select('.popover-content', true).first();
17215         
17216         Roo.each(more, function(m){
17217             var cfg = {
17218                 cls : 'fc-event-hori fc-event-draggable',
17219                 html : m.title
17220             };
17221             var cg = ctr.createChild(cfg);
17222             
17223             cg.on('click', _this.onEventClick, _this, m);
17224         });
17225         
17226         this.calpopover.show(el);
17227         
17228         
17229     },
17230     
17231     onLoad: function () 
17232     {   
17233         this.calevents = [];
17234         var cal = this;
17235         
17236         if(this.store.getCount() > 0){
17237             this.store.data.each(function(d){
17238                cal.addItem({
17239                     id : d.data.id,
17240                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17241                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17242                     time : d.data.start_time,
17243                     title : d.data.title,
17244                     description : d.data.description,
17245                     venue : d.data.venue
17246                 });
17247             });
17248         }
17249         
17250         this.renderEvents();
17251         
17252         if(this.calevents.length && this.loadMask){
17253             this.maskEl.hide();
17254         }
17255     },
17256     
17257     onBeforeLoad: function()
17258     {
17259         this.clearEvents();
17260         if(this.loadMask){
17261             this.maskEl.show();
17262         }
17263     }
17264 });
17265
17266  
17267  /*
17268  * - LGPL
17269  *
17270  * element
17271  * 
17272  */
17273
17274 /**
17275  * @class Roo.bootstrap.Popover
17276  * @extends Roo.bootstrap.Component
17277  * Bootstrap Popover class
17278  * @cfg {String} html contents of the popover   (or false to use children..)
17279  * @cfg {String} title of popover (or false to hide)
17280  * @cfg {String} placement how it is placed
17281  * @cfg {String} trigger click || hover (or false to trigger manually)
17282  * @cfg {String} over what (parent or false to trigger manually.)
17283  * @cfg {Number} delay - delay before showing
17284  
17285  * @constructor
17286  * Create a new Popover
17287  * @param {Object} config The config object
17288  */
17289
17290 Roo.bootstrap.Popover = function(config){
17291     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17292     
17293     this.addEvents({
17294         // raw events
17295          /**
17296          * @event show
17297          * After the popover show
17298          * 
17299          * @param {Roo.bootstrap.Popover} this
17300          */
17301         "show" : true,
17302         /**
17303          * @event hide
17304          * After the popover hide
17305          * 
17306          * @param {Roo.bootstrap.Popover} this
17307          */
17308         "hide" : true
17309     });
17310 };
17311
17312 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17313     
17314     title: 'Fill in a title',
17315     html: false,
17316     
17317     placement : 'right',
17318     trigger : 'hover', // hover
17319     
17320     delay : 0,
17321     
17322     over: 'parent',
17323     
17324     can_build_overlaid : false,
17325     
17326     getChildContainer : function()
17327     {
17328         return this.el.select('.popover-content',true).first();
17329     },
17330     
17331     getAutoCreate : function(){
17332          
17333         var cfg = {
17334            cls : 'popover roo-dynamic',
17335            style: 'display:block',
17336            cn : [
17337                 {
17338                     cls : 'arrow'
17339                 },
17340                 {
17341                     cls : 'popover-inner',
17342                     cn : [
17343                         {
17344                             tag: 'h3',
17345                             cls: 'popover-title',
17346                             html : this.title
17347                         },
17348                         {
17349                             cls : 'popover-content',
17350                             html : this.html
17351                         }
17352                     ]
17353                     
17354                 }
17355            ]
17356         };
17357         
17358         return cfg;
17359     },
17360     setTitle: function(str)
17361     {
17362         this.title = str;
17363         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17364     },
17365     setContent: function(str)
17366     {
17367         this.html = str;
17368         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17369     },
17370     // as it get's added to the bottom of the page.
17371     onRender : function(ct, position)
17372     {
17373         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17374         if(!this.el){
17375             var cfg = Roo.apply({},  this.getAutoCreate());
17376             cfg.id = Roo.id();
17377             
17378             if (this.cls) {
17379                 cfg.cls += ' ' + this.cls;
17380             }
17381             if (this.style) {
17382                 cfg.style = this.style;
17383             }
17384             //Roo.log("adding to ");
17385             this.el = Roo.get(document.body).createChild(cfg, position);
17386 //            Roo.log(this.el);
17387         }
17388         this.initEvents();
17389     },
17390     
17391     initEvents : function()
17392     {
17393         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17394         this.el.enableDisplayMode('block');
17395         this.el.hide();
17396         if (this.over === false) {
17397             return; 
17398         }
17399         if (this.triggers === false) {
17400             return;
17401         }
17402         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17403         var triggers = this.trigger ? this.trigger.split(' ') : [];
17404         Roo.each(triggers, function(trigger) {
17405         
17406             if (trigger == 'click') {
17407                 on_el.on('click', this.toggle, this);
17408             } else if (trigger != 'manual') {
17409                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17410                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17411       
17412                 on_el.on(eventIn  ,this.enter, this);
17413                 on_el.on(eventOut, this.leave, this);
17414             }
17415         }, this);
17416         
17417     },
17418     
17419     
17420     // private
17421     timeout : null,
17422     hoverState : null,
17423     
17424     toggle : function () {
17425         this.hoverState == 'in' ? this.leave() : this.enter();
17426     },
17427     
17428     enter : function () {
17429         
17430         clearTimeout(this.timeout);
17431     
17432         this.hoverState = 'in';
17433     
17434         if (!this.delay || !this.delay.show) {
17435             this.show();
17436             return;
17437         }
17438         var _t = this;
17439         this.timeout = setTimeout(function () {
17440             if (_t.hoverState == 'in') {
17441                 _t.show();
17442             }
17443         }, this.delay.show)
17444     },
17445     
17446     leave : function() {
17447         clearTimeout(this.timeout);
17448     
17449         this.hoverState = 'out';
17450     
17451         if (!this.delay || !this.delay.hide) {
17452             this.hide();
17453             return;
17454         }
17455         var _t = this;
17456         this.timeout = setTimeout(function () {
17457             if (_t.hoverState == 'out') {
17458                 _t.hide();
17459             }
17460         }, this.delay.hide)
17461     },
17462     
17463     show : function (on_el)
17464     {
17465         if (!on_el) {
17466             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17467         }
17468         
17469         // set content.
17470         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17471         if (this.html !== false) {
17472             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17473         }
17474         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17475         if (!this.title.length) {
17476             this.el.select('.popover-title',true).hide();
17477         }
17478         
17479         var placement = typeof this.placement == 'function' ?
17480             this.placement.call(this, this.el, on_el) :
17481             this.placement;
17482             
17483         var autoToken = /\s?auto?\s?/i;
17484         var autoPlace = autoToken.test(placement);
17485         if (autoPlace) {
17486             placement = placement.replace(autoToken, '') || 'top';
17487         }
17488         
17489         //this.el.detach()
17490         //this.el.setXY([0,0]);
17491         this.el.show();
17492         this.el.dom.style.display='block';
17493         this.el.addClass(placement);
17494         
17495         //this.el.appendTo(on_el);
17496         
17497         var p = this.getPosition();
17498         var box = this.el.getBox();
17499         
17500         if (autoPlace) {
17501             // fixme..
17502         }
17503         var align = Roo.bootstrap.Popover.alignment[placement];
17504         
17505 //        Roo.log(align);
17506         this.el.alignTo(on_el, align[0],align[1]);
17507         //var arrow = this.el.select('.arrow',true).first();
17508         //arrow.set(align[2], 
17509         
17510         this.el.addClass('in');
17511         
17512         
17513         if (this.el.hasClass('fade')) {
17514             // fade it?
17515         }
17516         
17517         this.hoverState = 'in';
17518         
17519         this.fireEvent('show', this);
17520         
17521     },
17522     hide : function()
17523     {
17524         this.el.setXY([0,0]);
17525         this.el.removeClass('in');
17526         this.el.hide();
17527         this.hoverState = null;
17528         
17529         this.fireEvent('hide', this);
17530     }
17531     
17532 });
17533
17534 Roo.bootstrap.Popover.alignment = {
17535     'left' : ['r-l', [-10,0], 'right'],
17536     'right' : ['l-r', [10,0], 'left'],
17537     'bottom' : ['t-b', [0,10], 'top'],
17538     'top' : [ 'b-t', [0,-10], 'bottom']
17539 };
17540
17541  /*
17542  * - LGPL
17543  *
17544  * Progress
17545  * 
17546  */
17547
17548 /**
17549  * @class Roo.bootstrap.Progress
17550  * @extends Roo.bootstrap.Component
17551  * Bootstrap Progress class
17552  * @cfg {Boolean} striped striped of the progress bar
17553  * @cfg {Boolean} active animated of the progress bar
17554  * 
17555  * 
17556  * @constructor
17557  * Create a new Progress
17558  * @param {Object} config The config object
17559  */
17560
17561 Roo.bootstrap.Progress = function(config){
17562     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17563 };
17564
17565 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17566     
17567     striped : false,
17568     active: false,
17569     
17570     getAutoCreate : function(){
17571         var cfg = {
17572             tag: 'div',
17573             cls: 'progress'
17574         };
17575         
17576         
17577         if(this.striped){
17578             cfg.cls += ' progress-striped';
17579         }
17580       
17581         if(this.active){
17582             cfg.cls += ' active';
17583         }
17584         
17585         
17586         return cfg;
17587     }
17588    
17589 });
17590
17591  
17592
17593  /*
17594  * - LGPL
17595  *
17596  * ProgressBar
17597  * 
17598  */
17599
17600 /**
17601  * @class Roo.bootstrap.ProgressBar
17602  * @extends Roo.bootstrap.Component
17603  * Bootstrap ProgressBar class
17604  * @cfg {Number} aria_valuenow aria-value now
17605  * @cfg {Number} aria_valuemin aria-value min
17606  * @cfg {Number} aria_valuemax aria-value max
17607  * @cfg {String} label label for the progress bar
17608  * @cfg {String} panel (success | info | warning | danger )
17609  * @cfg {String} role role of the progress bar
17610  * @cfg {String} sr_only text
17611  * 
17612  * 
17613  * @constructor
17614  * Create a new ProgressBar
17615  * @param {Object} config The config object
17616  */
17617
17618 Roo.bootstrap.ProgressBar = function(config){
17619     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17620 };
17621
17622 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17623     
17624     aria_valuenow : 0,
17625     aria_valuemin : 0,
17626     aria_valuemax : 100,
17627     label : false,
17628     panel : false,
17629     role : false,
17630     sr_only: false,
17631     
17632     getAutoCreate : function()
17633     {
17634         
17635         var cfg = {
17636             tag: 'div',
17637             cls: 'progress-bar',
17638             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17639         };
17640         
17641         if(this.sr_only){
17642             cfg.cn = {
17643                 tag: 'span',
17644                 cls: 'sr-only',
17645                 html: this.sr_only
17646             }
17647         }
17648         
17649         if(this.role){
17650             cfg.role = this.role;
17651         }
17652         
17653         if(this.aria_valuenow){
17654             cfg['aria-valuenow'] = this.aria_valuenow;
17655         }
17656         
17657         if(this.aria_valuemin){
17658             cfg['aria-valuemin'] = this.aria_valuemin;
17659         }
17660         
17661         if(this.aria_valuemax){
17662             cfg['aria-valuemax'] = this.aria_valuemax;
17663         }
17664         
17665         if(this.label && !this.sr_only){
17666             cfg.html = this.label;
17667         }
17668         
17669         if(this.panel){
17670             cfg.cls += ' progress-bar-' + this.panel;
17671         }
17672         
17673         return cfg;
17674     },
17675     
17676     update : function(aria_valuenow)
17677     {
17678         this.aria_valuenow = aria_valuenow;
17679         
17680         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17681     }
17682    
17683 });
17684
17685  
17686
17687  /*
17688  * - LGPL
17689  *
17690  * column
17691  * 
17692  */
17693
17694 /**
17695  * @class Roo.bootstrap.TabGroup
17696  * @extends Roo.bootstrap.Column
17697  * Bootstrap Column class
17698  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17699  * @cfg {Boolean} carousel true to make the group behave like a carousel
17700  * @cfg {Boolean} bullets show bullets for the panels
17701  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17702  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17703  * @cfg {Boolean} showarrow (true|false) show arrow default true
17704  * 
17705  * @constructor
17706  * Create a new TabGroup
17707  * @param {Object} config The config object
17708  */
17709
17710 Roo.bootstrap.TabGroup = function(config){
17711     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17712     if (!this.navId) {
17713         this.navId = Roo.id();
17714     }
17715     this.tabs = [];
17716     Roo.bootstrap.TabGroup.register(this);
17717     
17718 };
17719
17720 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17721     
17722     carousel : false,
17723     transition : false,
17724     bullets : 0,
17725     timer : 0,
17726     autoslide : false,
17727     slideFn : false,
17728     slideOnTouch : false,
17729     showarrow : true,
17730     
17731     getAutoCreate : function()
17732     {
17733         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17734         
17735         cfg.cls += ' tab-content';
17736         
17737         if (this.carousel) {
17738             cfg.cls += ' carousel slide';
17739             
17740             cfg.cn = [{
17741                cls : 'carousel-inner',
17742                cn : []
17743             }];
17744         
17745             if(this.bullets  && !Roo.isTouch){
17746                 
17747                 var bullets = {
17748                     cls : 'carousel-bullets',
17749                     cn : []
17750                 };
17751                
17752                 if(this.bullets_cls){
17753                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17754                 }
17755                 
17756                 bullets.cn.push({
17757                     cls : 'clear'
17758                 });
17759                 
17760                 cfg.cn[0].cn.push(bullets);
17761             }
17762             
17763             if(this.showarrow){
17764                 cfg.cn[0].cn.push({
17765                     tag : 'div',
17766                     class : 'carousel-arrow',
17767                     cn : [
17768                         {
17769                             tag : 'div',
17770                             class : 'carousel-prev',
17771                             cn : [
17772                                 {
17773                                     tag : 'i',
17774                                     class : 'fa fa-chevron-left'
17775                                 }
17776                             ]
17777                         },
17778                         {
17779                             tag : 'div',
17780                             class : 'carousel-next',
17781                             cn : [
17782                                 {
17783                                     tag : 'i',
17784                                     class : 'fa fa-chevron-right'
17785                                 }
17786                             ]
17787                         }
17788                     ]
17789                 });
17790             }
17791             
17792         }
17793         
17794         return cfg;
17795     },
17796     
17797     initEvents:  function()
17798     {
17799 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17800 //            this.el.on("touchstart", this.onTouchStart, this);
17801 //        }
17802         
17803         if(this.autoslide){
17804             var _this = this;
17805             
17806             this.slideFn = window.setInterval(function() {
17807                 _this.showPanelNext();
17808             }, this.timer);
17809         }
17810         
17811         if(this.showarrow){
17812             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17813             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17814         }
17815         
17816         
17817     },
17818     
17819 //    onTouchStart : function(e, el, o)
17820 //    {
17821 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17822 //            return;
17823 //        }
17824 //        
17825 //        this.showPanelNext();
17826 //    },
17827     
17828     
17829     getChildContainer : function()
17830     {
17831         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17832     },
17833     
17834     /**
17835     * register a Navigation item
17836     * @param {Roo.bootstrap.NavItem} the navitem to add
17837     */
17838     register : function(item)
17839     {
17840         this.tabs.push( item);
17841         item.navId = this.navId; // not really needed..
17842         this.addBullet();
17843     
17844     },
17845     
17846     getActivePanel : function()
17847     {
17848         var r = false;
17849         Roo.each(this.tabs, function(t) {
17850             if (t.active) {
17851                 r = t;
17852                 return false;
17853             }
17854             return null;
17855         });
17856         return r;
17857         
17858     },
17859     getPanelByName : function(n)
17860     {
17861         var r = false;
17862         Roo.each(this.tabs, function(t) {
17863             if (t.tabId == n) {
17864                 r = t;
17865                 return false;
17866             }
17867             return null;
17868         });
17869         return r;
17870     },
17871     indexOfPanel : function(p)
17872     {
17873         var r = false;
17874         Roo.each(this.tabs, function(t,i) {
17875             if (t.tabId == p.tabId) {
17876                 r = i;
17877                 return false;
17878             }
17879             return null;
17880         });
17881         return r;
17882     },
17883     /**
17884      * show a specific panel
17885      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17886      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17887      */
17888     showPanel : function (pan)
17889     {
17890         if(this.transition || typeof(pan) == 'undefined'){
17891             Roo.log("waiting for the transitionend");
17892             return;
17893         }
17894         
17895         if (typeof(pan) == 'number') {
17896             pan = this.tabs[pan];
17897         }
17898         
17899         if (typeof(pan) == 'string') {
17900             pan = this.getPanelByName(pan);
17901         }
17902         
17903         var cur = this.getActivePanel();
17904         
17905         if(!pan || !cur){
17906             Roo.log('pan or acitve pan is undefined');
17907             return false;
17908         }
17909         
17910         if (pan.tabId == this.getActivePanel().tabId) {
17911             return true;
17912         }
17913         
17914         if (false === cur.fireEvent('beforedeactivate')) {
17915             return false;
17916         }
17917         
17918         if(this.bullets > 0 && !Roo.isTouch){
17919             this.setActiveBullet(this.indexOfPanel(pan));
17920         }
17921         
17922         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17923             
17924             this.transition = true;
17925             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17926             var lr = dir == 'next' ? 'left' : 'right';
17927             pan.el.addClass(dir); // or prev
17928             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17929             cur.el.addClass(lr); // or right
17930             pan.el.addClass(lr);
17931             
17932             var _this = this;
17933             cur.el.on('transitionend', function() {
17934                 Roo.log("trans end?");
17935                 
17936                 pan.el.removeClass([lr,dir]);
17937                 pan.setActive(true);
17938                 
17939                 cur.el.removeClass([lr]);
17940                 cur.setActive(false);
17941                 
17942                 _this.transition = false;
17943                 
17944             }, this, { single:  true } );
17945             
17946             return true;
17947         }
17948         
17949         cur.setActive(false);
17950         pan.setActive(true);
17951         
17952         return true;
17953         
17954     },
17955     showPanelNext : function()
17956     {
17957         var i = this.indexOfPanel(this.getActivePanel());
17958         
17959         if (i >= this.tabs.length - 1 && !this.autoslide) {
17960             return;
17961         }
17962         
17963         if (i >= this.tabs.length - 1 && this.autoslide) {
17964             i = -1;
17965         }
17966         
17967         this.showPanel(this.tabs[i+1]);
17968     },
17969     
17970     showPanelPrev : function()
17971     {
17972         var i = this.indexOfPanel(this.getActivePanel());
17973         
17974         if (i  < 1 && !this.autoslide) {
17975             return;
17976         }
17977         
17978         if (i < 1 && this.autoslide) {
17979             i = this.tabs.length;
17980         }
17981         
17982         this.showPanel(this.tabs[i-1]);
17983     },
17984     
17985     
17986     addBullet: function()
17987     {
17988         if(!this.bullets || Roo.isTouch){
17989             return;
17990         }
17991         var ctr = this.el.select('.carousel-bullets',true).first();
17992         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17993         var bullet = ctr.createChild({
17994             cls : 'bullet bullet-' + i
17995         },ctr.dom.lastChild);
17996         
17997         
17998         var _this = this;
17999         
18000         bullet.on('click', (function(e, el, o, ii, t){
18001
18002             e.preventDefault();
18003
18004             this.showPanel(ii);
18005
18006             if(this.autoslide && this.slideFn){
18007                 clearInterval(this.slideFn);
18008                 this.slideFn = window.setInterval(function() {
18009                     _this.showPanelNext();
18010                 }, this.timer);
18011             }
18012
18013         }).createDelegate(this, [i, bullet], true));
18014                 
18015         
18016     },
18017      
18018     setActiveBullet : function(i)
18019     {
18020         if(Roo.isTouch){
18021             return;
18022         }
18023         
18024         Roo.each(this.el.select('.bullet', true).elements, function(el){
18025             el.removeClass('selected');
18026         });
18027
18028         var bullet = this.el.select('.bullet-' + i, true).first();
18029         
18030         if(!bullet){
18031             return;
18032         }
18033         
18034         bullet.addClass('selected');
18035     }
18036     
18037     
18038   
18039 });
18040
18041  
18042
18043  
18044  
18045 Roo.apply(Roo.bootstrap.TabGroup, {
18046     
18047     groups: {},
18048      /**
18049     * register a Navigation Group
18050     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18051     */
18052     register : function(navgrp)
18053     {
18054         this.groups[navgrp.navId] = navgrp;
18055         
18056     },
18057     /**
18058     * fetch a Navigation Group based on the navigation ID
18059     * if one does not exist , it will get created.
18060     * @param {string} the navgroup to add
18061     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18062     */
18063     get: function(navId) {
18064         if (typeof(this.groups[navId]) == 'undefined') {
18065             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18066         }
18067         return this.groups[navId] ;
18068     }
18069     
18070     
18071     
18072 });
18073
18074  /*
18075  * - LGPL
18076  *
18077  * TabPanel
18078  * 
18079  */
18080
18081 /**
18082  * @class Roo.bootstrap.TabPanel
18083  * @extends Roo.bootstrap.Component
18084  * Bootstrap TabPanel class
18085  * @cfg {Boolean} active panel active
18086  * @cfg {String} html panel content
18087  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18088  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18089  * @cfg {String} href click to link..
18090  * 
18091  * 
18092  * @constructor
18093  * Create a new TabPanel
18094  * @param {Object} config The config object
18095  */
18096
18097 Roo.bootstrap.TabPanel = function(config){
18098     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18099     this.addEvents({
18100         /**
18101              * @event changed
18102              * Fires when the active status changes
18103              * @param {Roo.bootstrap.TabPanel} this
18104              * @param {Boolean} state the new state
18105             
18106          */
18107         'changed': true,
18108         /**
18109              * @event beforedeactivate
18110              * Fires before a tab is de-activated - can be used to do validation on a form.
18111              * @param {Roo.bootstrap.TabPanel} this
18112              * @return {Boolean} false if there is an error
18113             
18114          */
18115         'beforedeactivate': true
18116      });
18117     
18118     this.tabId = this.tabId || Roo.id();
18119   
18120 };
18121
18122 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18123     
18124     active: false,
18125     html: false,
18126     tabId: false,
18127     navId : false,
18128     href : '',
18129     
18130     getAutoCreate : function(){
18131         var cfg = {
18132             tag: 'div',
18133             // item is needed for carousel - not sure if it has any effect otherwise
18134             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18135             html: this.html || ''
18136         };
18137         
18138         if(this.active){
18139             cfg.cls += ' active';
18140         }
18141         
18142         if(this.tabId){
18143             cfg.tabId = this.tabId;
18144         }
18145         
18146         
18147         return cfg;
18148     },
18149     
18150     initEvents:  function()
18151     {
18152         var p = this.parent();
18153         
18154         this.navId = this.navId || p.navId;
18155         
18156         if (typeof(this.navId) != 'undefined') {
18157             // not really needed.. but just in case.. parent should be a NavGroup.
18158             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18159             
18160             tg.register(this);
18161             
18162             var i = tg.tabs.length - 1;
18163             
18164             if(this.active && tg.bullets > 0 && i < tg.bullets){
18165                 tg.setActiveBullet(i);
18166             }
18167         }
18168         
18169         this.el.on('click', this.onClick, this);
18170         
18171         if(Roo.isTouch){
18172             this.el.on("touchstart", this.onTouchStart, this);
18173             this.el.on("touchmove", this.onTouchMove, this);
18174             this.el.on("touchend", this.onTouchEnd, this);
18175         }
18176         
18177     },
18178     
18179     onRender : function(ct, position)
18180     {
18181         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18182     },
18183     
18184     setActive : function(state)
18185     {
18186         Roo.log("panel - set active " + this.tabId + "=" + state);
18187         
18188         this.active = state;
18189         if (!state) {
18190             this.el.removeClass('active');
18191             
18192         } else  if (!this.el.hasClass('active')) {
18193             this.el.addClass('active');
18194         }
18195         
18196         this.fireEvent('changed', this, state);
18197     },
18198     
18199     onClick : function(e)
18200     {
18201         e.preventDefault();
18202         
18203         if(!this.href.length){
18204             return;
18205         }
18206         
18207         window.location.href = this.href;
18208     },
18209     
18210     startX : 0,
18211     startY : 0,
18212     endX : 0,
18213     endY : 0,
18214     swiping : false,
18215     
18216     onTouchStart : function(e)
18217     {
18218         this.swiping = false;
18219         
18220         this.startX = e.browserEvent.touches[0].clientX;
18221         this.startY = e.browserEvent.touches[0].clientY;
18222     },
18223     
18224     onTouchMove : function(e)
18225     {
18226         this.swiping = true;
18227         
18228         this.endX = e.browserEvent.touches[0].clientX;
18229         this.endY = e.browserEvent.touches[0].clientY;
18230     },
18231     
18232     onTouchEnd : function(e)
18233     {
18234         if(!this.swiping){
18235             this.onClick(e);
18236             return;
18237         }
18238         
18239         var tabGroup = this.parent();
18240         
18241         if(this.endX > this.startX){ // swiping right
18242             tabGroup.showPanelPrev();
18243             return;
18244         }
18245         
18246         if(this.startX > this.endX){ // swiping left
18247             tabGroup.showPanelNext();
18248             return;
18249         }
18250     }
18251     
18252     
18253 });
18254  
18255
18256  
18257
18258  /*
18259  * - LGPL
18260  *
18261  * DateField
18262  * 
18263  */
18264
18265 /**
18266  * @class Roo.bootstrap.DateField
18267  * @extends Roo.bootstrap.Input
18268  * Bootstrap DateField class
18269  * @cfg {Number} weekStart default 0
18270  * @cfg {String} viewMode default empty, (months|years)
18271  * @cfg {String} minViewMode default empty, (months|years)
18272  * @cfg {Number} startDate default -Infinity
18273  * @cfg {Number} endDate default Infinity
18274  * @cfg {Boolean} todayHighlight default false
18275  * @cfg {Boolean} todayBtn default false
18276  * @cfg {Boolean} calendarWeeks default false
18277  * @cfg {Object} daysOfWeekDisabled default empty
18278  * @cfg {Boolean} singleMode default false (true | false)
18279  * 
18280  * @cfg {Boolean} keyboardNavigation default true
18281  * @cfg {String} language default en
18282  * 
18283  * @constructor
18284  * Create a new DateField
18285  * @param {Object} config The config object
18286  */
18287
18288 Roo.bootstrap.DateField = function(config){
18289     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18290      this.addEvents({
18291             /**
18292              * @event show
18293              * Fires when this field show.
18294              * @param {Roo.bootstrap.DateField} this
18295              * @param {Mixed} date The date value
18296              */
18297             show : true,
18298             /**
18299              * @event show
18300              * Fires when this field hide.
18301              * @param {Roo.bootstrap.DateField} this
18302              * @param {Mixed} date The date value
18303              */
18304             hide : true,
18305             /**
18306              * @event select
18307              * Fires when select a date.
18308              * @param {Roo.bootstrap.DateField} this
18309              * @param {Mixed} date The date value
18310              */
18311             select : true,
18312             /**
18313              * @event beforeselect
18314              * Fires when before select a date.
18315              * @param {Roo.bootstrap.DateField} this
18316              * @param {Mixed} date The date value
18317              */
18318             beforeselect : true
18319         });
18320 };
18321
18322 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18323     
18324     /**
18325      * @cfg {String} format
18326      * The default date format string which can be overriden for localization support.  The format must be
18327      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18328      */
18329     format : "m/d/y",
18330     /**
18331      * @cfg {String} altFormats
18332      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18333      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18334      */
18335     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18336     
18337     weekStart : 0,
18338     
18339     viewMode : '',
18340     
18341     minViewMode : '',
18342     
18343     todayHighlight : false,
18344     
18345     todayBtn: false,
18346     
18347     language: 'en',
18348     
18349     keyboardNavigation: true,
18350     
18351     calendarWeeks: false,
18352     
18353     startDate: -Infinity,
18354     
18355     endDate: Infinity,
18356     
18357     daysOfWeekDisabled: [],
18358     
18359     _events: [],
18360     
18361     singleMode : false,
18362     
18363     UTCDate: function()
18364     {
18365         return new Date(Date.UTC.apply(Date, arguments));
18366     },
18367     
18368     UTCToday: function()
18369     {
18370         var today = new Date();
18371         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18372     },
18373     
18374     getDate: function() {
18375             var d = this.getUTCDate();
18376             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18377     },
18378     
18379     getUTCDate: function() {
18380             return this.date;
18381     },
18382     
18383     setDate: function(d) {
18384             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18385     },
18386     
18387     setUTCDate: function(d) {
18388             this.date = d;
18389             this.setValue(this.formatDate(this.date));
18390     },
18391         
18392     onRender: function(ct, position)
18393     {
18394         
18395         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18396         
18397         this.language = this.language || 'en';
18398         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18399         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18400         
18401         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18402         this.format = this.format || 'm/d/y';
18403         this.isInline = false;
18404         this.isInput = true;
18405         this.component = this.el.select('.add-on', true).first() || false;
18406         this.component = (this.component && this.component.length === 0) ? false : this.component;
18407         this.hasInput = this.component && this.inputEl().length;
18408         
18409         if (typeof(this.minViewMode === 'string')) {
18410             switch (this.minViewMode) {
18411                 case 'months':
18412                     this.minViewMode = 1;
18413                     break;
18414                 case 'years':
18415                     this.minViewMode = 2;
18416                     break;
18417                 default:
18418                     this.minViewMode = 0;
18419                     break;
18420             }
18421         }
18422         
18423         if (typeof(this.viewMode === 'string')) {
18424             switch (this.viewMode) {
18425                 case 'months':
18426                     this.viewMode = 1;
18427                     break;
18428                 case 'years':
18429                     this.viewMode = 2;
18430                     break;
18431                 default:
18432                     this.viewMode = 0;
18433                     break;
18434             }
18435         }
18436                 
18437         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18438         
18439 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18440         
18441         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18442         
18443         this.picker().on('mousedown', this.onMousedown, this);
18444         this.picker().on('click', this.onClick, this);
18445         
18446         this.picker().addClass('datepicker-dropdown');
18447         
18448         this.startViewMode = this.viewMode;
18449         
18450         if(this.singleMode){
18451             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18452                 v.setVisibilityMode(Roo.Element.DISPLAY);
18453                 v.hide();
18454             });
18455             
18456             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18457                 v.setStyle('width', '189px');
18458             });
18459         }
18460         
18461         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18462             if(!this.calendarWeeks){
18463                 v.remove();
18464                 return;
18465             }
18466             
18467             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18468             v.attr('colspan', function(i, val){
18469                 return parseInt(val) + 1;
18470             });
18471         });
18472                         
18473         
18474         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18475         
18476         this.setStartDate(this.startDate);
18477         this.setEndDate(this.endDate);
18478         
18479         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18480         
18481         this.fillDow();
18482         this.fillMonths();
18483         this.update();
18484         this.showMode();
18485         
18486         if(this.isInline) {
18487             this.show();
18488         }
18489     },
18490     
18491     picker : function()
18492     {
18493         return this.pickerEl;
18494 //        return this.el.select('.datepicker', true).first();
18495     },
18496     
18497     fillDow: function()
18498     {
18499         var dowCnt = this.weekStart;
18500         
18501         var dow = {
18502             tag: 'tr',
18503             cn: [
18504                 
18505             ]
18506         };
18507         
18508         if(this.calendarWeeks){
18509             dow.cn.push({
18510                 tag: 'th',
18511                 cls: 'cw',
18512                 html: '&nbsp;'
18513             })
18514         }
18515         
18516         while (dowCnt < this.weekStart + 7) {
18517             dow.cn.push({
18518                 tag: 'th',
18519                 cls: 'dow',
18520                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18521             });
18522         }
18523         
18524         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18525     },
18526     
18527     fillMonths: function()
18528     {    
18529         var i = 0;
18530         var months = this.picker().select('>.datepicker-months td', true).first();
18531         
18532         months.dom.innerHTML = '';
18533         
18534         while (i < 12) {
18535             var month = {
18536                 tag: 'span',
18537                 cls: 'month',
18538                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18539             };
18540             
18541             months.createChild(month);
18542         }
18543         
18544     },
18545     
18546     update: function()
18547     {
18548         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;
18549         
18550         if (this.date < this.startDate) {
18551             this.viewDate = new Date(this.startDate);
18552         } else if (this.date > this.endDate) {
18553             this.viewDate = new Date(this.endDate);
18554         } else {
18555             this.viewDate = new Date(this.date);
18556         }
18557         
18558         this.fill();
18559     },
18560     
18561     fill: function() 
18562     {
18563         var d = new Date(this.viewDate),
18564                 year = d.getUTCFullYear(),
18565                 month = d.getUTCMonth(),
18566                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18567                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18568                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18569                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18570                 currentDate = this.date && this.date.valueOf(),
18571                 today = this.UTCToday();
18572         
18573         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18574         
18575 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18576         
18577 //        this.picker.select('>tfoot th.today').
18578 //                                              .text(dates[this.language].today)
18579 //                                              .toggle(this.todayBtn !== false);
18580     
18581         this.updateNavArrows();
18582         this.fillMonths();
18583                                                 
18584         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18585         
18586         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18587          
18588         prevMonth.setUTCDate(day);
18589         
18590         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18591         
18592         var nextMonth = new Date(prevMonth);
18593         
18594         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18595         
18596         nextMonth = nextMonth.valueOf();
18597         
18598         var fillMonths = false;
18599         
18600         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18601         
18602         while(prevMonth.valueOf() < nextMonth) {
18603             var clsName = '';
18604             
18605             if (prevMonth.getUTCDay() === this.weekStart) {
18606                 if(fillMonths){
18607                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18608                 }
18609                     
18610                 fillMonths = {
18611                     tag: 'tr',
18612                     cn: []
18613                 };
18614                 
18615                 if(this.calendarWeeks){
18616                     // ISO 8601: First week contains first thursday.
18617                     // ISO also states week starts on Monday, but we can be more abstract here.
18618                     var
18619                     // Start of current week: based on weekstart/current date
18620                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18621                     // Thursday of this week
18622                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18623                     // First Thursday of year, year from thursday
18624                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18625                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18626                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18627                     
18628                     fillMonths.cn.push({
18629                         tag: 'td',
18630                         cls: 'cw',
18631                         html: calWeek
18632                     });
18633                 }
18634             }
18635             
18636             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18637                 clsName += ' old';
18638             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18639                 clsName += ' new';
18640             }
18641             if (this.todayHighlight &&
18642                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18643                 prevMonth.getUTCMonth() == today.getMonth() &&
18644                 prevMonth.getUTCDate() == today.getDate()) {
18645                 clsName += ' today';
18646             }
18647             
18648             if (currentDate && prevMonth.valueOf() === currentDate) {
18649                 clsName += ' active';
18650             }
18651             
18652             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18653                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18654                     clsName += ' disabled';
18655             }
18656             
18657             fillMonths.cn.push({
18658                 tag: 'td',
18659                 cls: 'day ' + clsName,
18660                 html: prevMonth.getDate()
18661             });
18662             
18663             prevMonth.setDate(prevMonth.getDate()+1);
18664         }
18665           
18666         var currentYear = this.date && this.date.getUTCFullYear();
18667         var currentMonth = this.date && this.date.getUTCMonth();
18668         
18669         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18670         
18671         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18672             v.removeClass('active');
18673             
18674             if(currentYear === year && k === currentMonth){
18675                 v.addClass('active');
18676             }
18677             
18678             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18679                 v.addClass('disabled');
18680             }
18681             
18682         });
18683         
18684         
18685         year = parseInt(year/10, 10) * 10;
18686         
18687         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18688         
18689         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18690         
18691         year -= 1;
18692         for (var i = -1; i < 11; i++) {
18693             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18694                 tag: 'span',
18695                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18696                 html: year
18697             });
18698             
18699             year += 1;
18700         }
18701     },
18702     
18703     showMode: function(dir) 
18704     {
18705         if (dir) {
18706             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18707         }
18708         
18709         Roo.each(this.picker().select('>div',true).elements, function(v){
18710             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18711             v.hide();
18712         });
18713         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18714     },
18715     
18716     place: function()
18717     {
18718         if(this.isInline) {
18719             return;
18720         }
18721         
18722         this.picker().removeClass(['bottom', 'top']);
18723         
18724         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18725             /*
18726              * place to the top of element!
18727              *
18728              */
18729             
18730             this.picker().addClass('top');
18731             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18732             
18733             return;
18734         }
18735         
18736         this.picker().addClass('bottom');
18737         
18738         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18739     },
18740     
18741     parseDate : function(value)
18742     {
18743         if(!value || value instanceof Date){
18744             return value;
18745         }
18746         var v = Date.parseDate(value, this.format);
18747         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18748             v = Date.parseDate(value, 'Y-m-d');
18749         }
18750         if(!v && this.altFormats){
18751             if(!this.altFormatsArray){
18752                 this.altFormatsArray = this.altFormats.split("|");
18753             }
18754             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18755                 v = Date.parseDate(value, this.altFormatsArray[i]);
18756             }
18757         }
18758         return v;
18759     },
18760     
18761     formatDate : function(date, fmt)
18762     {   
18763         return (!date || !(date instanceof Date)) ?
18764         date : date.dateFormat(fmt || this.format);
18765     },
18766     
18767     onFocus : function()
18768     {
18769         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18770         this.show();
18771     },
18772     
18773     onBlur : function()
18774     {
18775         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18776         
18777         var d = this.inputEl().getValue();
18778         
18779         this.setValue(d);
18780                 
18781         this.hide();
18782     },
18783     
18784     show : function()
18785     {
18786         this.picker().show();
18787         this.update();
18788         this.place();
18789         
18790         this.fireEvent('show', this, this.date);
18791     },
18792     
18793     hide : function()
18794     {
18795         if(this.isInline) {
18796             return;
18797         }
18798         this.picker().hide();
18799         this.viewMode = this.startViewMode;
18800         this.showMode();
18801         
18802         this.fireEvent('hide', this, this.date);
18803         
18804     },
18805     
18806     onMousedown: function(e)
18807     {
18808         e.stopPropagation();
18809         e.preventDefault();
18810     },
18811     
18812     keyup: function(e)
18813     {
18814         Roo.bootstrap.DateField.superclass.keyup.call(this);
18815         this.update();
18816     },
18817
18818     setValue: function(v)
18819     {
18820         if(this.fireEvent('beforeselect', this, v) !== false){
18821             var d = new Date(this.parseDate(v) ).clearTime();
18822         
18823             if(isNaN(d.getTime())){
18824                 this.date = this.viewDate = '';
18825                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18826                 return;
18827             }
18828
18829             v = this.formatDate(d);
18830
18831             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18832
18833             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18834
18835             this.update();
18836
18837             this.fireEvent('select', this, this.date);
18838         }
18839     },
18840     
18841     getValue: function()
18842     {
18843         return this.formatDate(this.date);
18844     },
18845     
18846     fireKey: function(e)
18847     {
18848         if (!this.picker().isVisible()){
18849             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18850                 this.show();
18851             }
18852             return;
18853         }
18854         
18855         var dateChanged = false,
18856         dir, day, month,
18857         newDate, newViewDate;
18858         
18859         switch(e.keyCode){
18860             case 27: // escape
18861                 this.hide();
18862                 e.preventDefault();
18863                 break;
18864             case 37: // left
18865             case 39: // right
18866                 if (!this.keyboardNavigation) {
18867                     break;
18868                 }
18869                 dir = e.keyCode == 37 ? -1 : 1;
18870                 
18871                 if (e.ctrlKey){
18872                     newDate = this.moveYear(this.date, dir);
18873                     newViewDate = this.moveYear(this.viewDate, dir);
18874                 } else if (e.shiftKey){
18875                     newDate = this.moveMonth(this.date, dir);
18876                     newViewDate = this.moveMonth(this.viewDate, dir);
18877                 } else {
18878                     newDate = new Date(this.date);
18879                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18880                     newViewDate = new Date(this.viewDate);
18881                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18882                 }
18883                 if (this.dateWithinRange(newDate)){
18884                     this.date = newDate;
18885                     this.viewDate = newViewDate;
18886                     this.setValue(this.formatDate(this.date));
18887 //                    this.update();
18888                     e.preventDefault();
18889                     dateChanged = true;
18890                 }
18891                 break;
18892             case 38: // up
18893             case 40: // down
18894                 if (!this.keyboardNavigation) {
18895                     break;
18896                 }
18897                 dir = e.keyCode == 38 ? -1 : 1;
18898                 if (e.ctrlKey){
18899                     newDate = this.moveYear(this.date, dir);
18900                     newViewDate = this.moveYear(this.viewDate, dir);
18901                 } else if (e.shiftKey){
18902                     newDate = this.moveMonth(this.date, dir);
18903                     newViewDate = this.moveMonth(this.viewDate, dir);
18904                 } else {
18905                     newDate = new Date(this.date);
18906                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18907                     newViewDate = new Date(this.viewDate);
18908                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18909                 }
18910                 if (this.dateWithinRange(newDate)){
18911                     this.date = newDate;
18912                     this.viewDate = newViewDate;
18913                     this.setValue(this.formatDate(this.date));
18914 //                    this.update();
18915                     e.preventDefault();
18916                     dateChanged = true;
18917                 }
18918                 break;
18919             case 13: // enter
18920                 this.setValue(this.formatDate(this.date));
18921                 this.hide();
18922                 e.preventDefault();
18923                 break;
18924             case 9: // tab
18925                 this.setValue(this.formatDate(this.date));
18926                 this.hide();
18927                 break;
18928             case 16: // shift
18929             case 17: // ctrl
18930             case 18: // alt
18931                 break;
18932             default :
18933                 this.hide();
18934                 
18935         }
18936     },
18937     
18938     
18939     onClick: function(e) 
18940     {
18941         e.stopPropagation();
18942         e.preventDefault();
18943         
18944         var target = e.getTarget();
18945         
18946         if(target.nodeName.toLowerCase() === 'i'){
18947             target = Roo.get(target).dom.parentNode;
18948         }
18949         
18950         var nodeName = target.nodeName;
18951         var className = target.className;
18952         var html = target.innerHTML;
18953         //Roo.log(nodeName);
18954         
18955         switch(nodeName.toLowerCase()) {
18956             case 'th':
18957                 switch(className) {
18958                     case 'switch':
18959                         this.showMode(1);
18960                         break;
18961                     case 'prev':
18962                     case 'next':
18963                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18964                         switch(this.viewMode){
18965                                 case 0:
18966                                         this.viewDate = this.moveMonth(this.viewDate, dir);
18967                                         break;
18968                                 case 1:
18969                                 case 2:
18970                                         this.viewDate = this.moveYear(this.viewDate, dir);
18971                                         break;
18972                         }
18973                         this.fill();
18974                         break;
18975                     case 'today':
18976                         var date = new Date();
18977                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18978 //                        this.fill()
18979                         this.setValue(this.formatDate(this.date));
18980                         
18981                         this.hide();
18982                         break;
18983                 }
18984                 break;
18985             case 'span':
18986                 if (className.indexOf('disabled') < 0) {
18987                     this.viewDate.setUTCDate(1);
18988                     if (className.indexOf('month') > -1) {
18989                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18990                     } else {
18991                         var year = parseInt(html, 10) || 0;
18992                         this.viewDate.setUTCFullYear(year);
18993                         
18994                     }
18995                     
18996                     if(this.singleMode){
18997                         this.setValue(this.formatDate(this.viewDate));
18998                         this.hide();
18999                         return;
19000                     }
19001                     
19002                     this.showMode(-1);
19003                     this.fill();
19004                 }
19005                 break;
19006                 
19007             case 'td':
19008                 //Roo.log(className);
19009                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19010                     var day = parseInt(html, 10) || 1;
19011                     var year = this.viewDate.getUTCFullYear(),
19012                         month = this.viewDate.getUTCMonth();
19013
19014                     if (className.indexOf('old') > -1) {
19015                         if(month === 0 ){
19016                             month = 11;
19017                             year -= 1;
19018                         }else{
19019                             month -= 1;
19020                         }
19021                     } else if (className.indexOf('new') > -1) {
19022                         if (month == 11) {
19023                             month = 0;
19024                             year += 1;
19025                         } else {
19026                             month += 1;
19027                         }
19028                     }
19029                     //Roo.log([year,month,day]);
19030                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19031                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19032 //                    this.fill();
19033                     //Roo.log(this.formatDate(this.date));
19034                     this.setValue(this.formatDate(this.date));
19035                     this.hide();
19036                 }
19037                 break;
19038         }
19039     },
19040     
19041     setStartDate: function(startDate)
19042     {
19043         this.startDate = startDate || -Infinity;
19044         if (this.startDate !== -Infinity) {
19045             this.startDate = this.parseDate(this.startDate);
19046         }
19047         this.update();
19048         this.updateNavArrows();
19049     },
19050
19051     setEndDate: function(endDate)
19052     {
19053         this.endDate = endDate || Infinity;
19054         if (this.endDate !== Infinity) {
19055             this.endDate = this.parseDate(this.endDate);
19056         }
19057         this.update();
19058         this.updateNavArrows();
19059     },
19060     
19061     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19062     {
19063         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19064         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19065             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19066         }
19067         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19068             return parseInt(d, 10);
19069         });
19070         this.update();
19071         this.updateNavArrows();
19072     },
19073     
19074     updateNavArrows: function() 
19075     {
19076         if(this.singleMode){
19077             return;
19078         }
19079         
19080         var d = new Date(this.viewDate),
19081         year = d.getUTCFullYear(),
19082         month = d.getUTCMonth();
19083         
19084         Roo.each(this.picker().select('.prev', true).elements, function(v){
19085             v.show();
19086             switch (this.viewMode) {
19087                 case 0:
19088
19089                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19090                         v.hide();
19091                     }
19092                     break;
19093                 case 1:
19094                 case 2:
19095                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19096                         v.hide();
19097                     }
19098                     break;
19099             }
19100         });
19101         
19102         Roo.each(this.picker().select('.next', true).elements, function(v){
19103             v.show();
19104             switch (this.viewMode) {
19105                 case 0:
19106
19107                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19108                         v.hide();
19109                     }
19110                     break;
19111                 case 1:
19112                 case 2:
19113                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19114                         v.hide();
19115                     }
19116                     break;
19117             }
19118         })
19119     },
19120     
19121     moveMonth: function(date, dir)
19122     {
19123         if (!dir) {
19124             return date;
19125         }
19126         var new_date = new Date(date.valueOf()),
19127         day = new_date.getUTCDate(),
19128         month = new_date.getUTCMonth(),
19129         mag = Math.abs(dir),
19130         new_month, test;
19131         dir = dir > 0 ? 1 : -1;
19132         if (mag == 1){
19133             test = dir == -1
19134             // If going back one month, make sure month is not current month
19135             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19136             ? function(){
19137                 return new_date.getUTCMonth() == month;
19138             }
19139             // If going forward one month, make sure month is as expected
19140             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19141             : function(){
19142                 return new_date.getUTCMonth() != new_month;
19143             };
19144             new_month = month + dir;
19145             new_date.setUTCMonth(new_month);
19146             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19147             if (new_month < 0 || new_month > 11) {
19148                 new_month = (new_month + 12) % 12;
19149             }
19150         } else {
19151             // For magnitudes >1, move one month at a time...
19152             for (var i=0; i<mag; i++) {
19153                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19154                 new_date = this.moveMonth(new_date, dir);
19155             }
19156             // ...then reset the day, keeping it in the new month
19157             new_month = new_date.getUTCMonth();
19158             new_date.setUTCDate(day);
19159             test = function(){
19160                 return new_month != new_date.getUTCMonth();
19161             };
19162         }
19163         // Common date-resetting loop -- if date is beyond end of month, make it
19164         // end of month
19165         while (test()){
19166             new_date.setUTCDate(--day);
19167             new_date.setUTCMonth(new_month);
19168         }
19169         return new_date;
19170     },
19171
19172     moveYear: function(date, dir)
19173     {
19174         return this.moveMonth(date, dir*12);
19175     },
19176
19177     dateWithinRange: function(date)
19178     {
19179         return date >= this.startDate && date <= this.endDate;
19180     },
19181
19182     
19183     remove: function() 
19184     {
19185         this.picker().remove();
19186     },
19187     
19188     validateValue : function(value)
19189     {
19190         if(this.getEl().hasClass('hidden')){
19191             return true;
19192         }
19193         
19194         if(value.length < 1)  {
19195             if(this.allowBlank){
19196                 return true;
19197             }
19198             return false;
19199         }
19200         
19201         if(value.length < this.minLength){
19202             return false;
19203         }
19204         if(value.length > this.maxLength){
19205             return false;
19206         }
19207         if(this.vtype){
19208             var vt = Roo.form.VTypes;
19209             if(!vt[this.vtype](value, this)){
19210                 return false;
19211             }
19212         }
19213         if(typeof this.validator == "function"){
19214             var msg = this.validator(value);
19215             if(msg !== true){
19216                 return false;
19217             }
19218         }
19219         
19220         if(this.regex && !this.regex.test(value)){
19221             return false;
19222         }
19223         
19224         if(typeof(this.parseDate(value)) == 'undefined'){
19225             return false;
19226         }
19227         
19228         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19229             return false;
19230         }      
19231         
19232         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19233             return false;
19234         } 
19235         
19236         
19237         return true;
19238     },
19239     
19240     setVisible : function(visible)
19241     {
19242         if(!this.getEl()){
19243             return;
19244         }
19245         
19246         this.getEl().removeClass('hidden');
19247         
19248         if(visible){
19249             return;
19250         }
19251         
19252         this.getEl().addClass('hidden');
19253     }
19254    
19255 });
19256
19257 Roo.apply(Roo.bootstrap.DateField,  {
19258     
19259     head : {
19260         tag: 'thead',
19261         cn: [
19262         {
19263             tag: 'tr',
19264             cn: [
19265             {
19266                 tag: 'th',
19267                 cls: 'prev',
19268                 html: '<i class="fa fa-arrow-left"/>'
19269             },
19270             {
19271                 tag: 'th',
19272                 cls: 'switch',
19273                 colspan: '5'
19274             },
19275             {
19276                 tag: 'th',
19277                 cls: 'next',
19278                 html: '<i class="fa fa-arrow-right"/>'
19279             }
19280
19281             ]
19282         }
19283         ]
19284     },
19285     
19286     content : {
19287         tag: 'tbody',
19288         cn: [
19289         {
19290             tag: 'tr',
19291             cn: [
19292             {
19293                 tag: 'td',
19294                 colspan: '7'
19295             }
19296             ]
19297         }
19298         ]
19299     },
19300     
19301     footer : {
19302         tag: 'tfoot',
19303         cn: [
19304         {
19305             tag: 'tr',
19306             cn: [
19307             {
19308                 tag: 'th',
19309                 colspan: '7',
19310                 cls: 'today'
19311             }
19312                     
19313             ]
19314         }
19315         ]
19316     },
19317     
19318     dates:{
19319         en: {
19320             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19321             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19322             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19323             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19324             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19325             today: "Today"
19326         }
19327     },
19328     
19329     modes: [
19330     {
19331         clsName: 'days',
19332         navFnc: 'Month',
19333         navStep: 1
19334     },
19335     {
19336         clsName: 'months',
19337         navFnc: 'FullYear',
19338         navStep: 1
19339     },
19340     {
19341         clsName: 'years',
19342         navFnc: 'FullYear',
19343         navStep: 10
19344     }]
19345 });
19346
19347 Roo.apply(Roo.bootstrap.DateField,  {
19348   
19349     template : {
19350         tag: 'div',
19351         cls: 'datepicker dropdown-menu roo-dynamic',
19352         cn: [
19353         {
19354             tag: 'div',
19355             cls: 'datepicker-days',
19356             cn: [
19357             {
19358                 tag: 'table',
19359                 cls: 'table-condensed',
19360                 cn:[
19361                 Roo.bootstrap.DateField.head,
19362                 {
19363                     tag: 'tbody'
19364                 },
19365                 Roo.bootstrap.DateField.footer
19366                 ]
19367             }
19368             ]
19369         },
19370         {
19371             tag: 'div',
19372             cls: 'datepicker-months',
19373             cn: [
19374             {
19375                 tag: 'table',
19376                 cls: 'table-condensed',
19377                 cn:[
19378                 Roo.bootstrap.DateField.head,
19379                 Roo.bootstrap.DateField.content,
19380                 Roo.bootstrap.DateField.footer
19381                 ]
19382             }
19383             ]
19384         },
19385         {
19386             tag: 'div',
19387             cls: 'datepicker-years',
19388             cn: [
19389             {
19390                 tag: 'table',
19391                 cls: 'table-condensed',
19392                 cn:[
19393                 Roo.bootstrap.DateField.head,
19394                 Roo.bootstrap.DateField.content,
19395                 Roo.bootstrap.DateField.footer
19396                 ]
19397             }
19398             ]
19399         }
19400         ]
19401     }
19402 });
19403
19404  
19405
19406  /*
19407  * - LGPL
19408  *
19409  * TimeField
19410  * 
19411  */
19412
19413 /**
19414  * @class Roo.bootstrap.TimeField
19415  * @extends Roo.bootstrap.Input
19416  * Bootstrap DateField class
19417  * 
19418  * 
19419  * @constructor
19420  * Create a new TimeField
19421  * @param {Object} config The config object
19422  */
19423
19424 Roo.bootstrap.TimeField = function(config){
19425     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19426     this.addEvents({
19427             /**
19428              * @event show
19429              * Fires when this field show.
19430              * @param {Roo.bootstrap.DateField} thisthis
19431              * @param {Mixed} date The date value
19432              */
19433             show : true,
19434             /**
19435              * @event show
19436              * Fires when this field hide.
19437              * @param {Roo.bootstrap.DateField} this
19438              * @param {Mixed} date The date value
19439              */
19440             hide : true,
19441             /**
19442              * @event select
19443              * Fires when select a date.
19444              * @param {Roo.bootstrap.DateField} this
19445              * @param {Mixed} date The date value
19446              */
19447             select : true
19448         });
19449 };
19450
19451 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19452     
19453     /**
19454      * @cfg {String} format
19455      * The default time format string which can be overriden for localization support.  The format must be
19456      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19457      */
19458     format : "H:i",
19459        
19460     onRender: function(ct, position)
19461     {
19462         
19463         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19464                 
19465         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19466         
19467         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19468         
19469         this.pop = this.picker().select('>.datepicker-time',true).first();
19470         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19471         
19472         this.picker().on('mousedown', this.onMousedown, this);
19473         this.picker().on('click', this.onClick, this);
19474         
19475         this.picker().addClass('datepicker-dropdown');
19476     
19477         this.fillTime();
19478         this.update();
19479             
19480         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19481         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19482         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19483         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19484         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19485         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19486
19487     },
19488     
19489     fireKey: function(e){
19490         if (!this.picker().isVisible()){
19491             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19492                 this.show();
19493             }
19494             return;
19495         }
19496
19497         e.preventDefault();
19498         
19499         switch(e.keyCode){
19500             case 27: // escape
19501                 this.hide();
19502                 break;
19503             case 37: // left
19504             case 39: // right
19505                 this.onTogglePeriod();
19506                 break;
19507             case 38: // up
19508                 this.onIncrementMinutes();
19509                 break;
19510             case 40: // down
19511                 this.onDecrementMinutes();
19512                 break;
19513             case 13: // enter
19514             case 9: // tab
19515                 this.setTime();
19516                 break;
19517         }
19518     },
19519     
19520     onClick: function(e) {
19521         e.stopPropagation();
19522         e.preventDefault();
19523     },
19524     
19525     picker : function()
19526     {
19527         return this.el.select('.datepicker', true).first();
19528     },
19529     
19530     fillTime: function()
19531     {    
19532         var time = this.pop.select('tbody', true).first();
19533         
19534         time.dom.innerHTML = '';
19535         
19536         time.createChild({
19537             tag: 'tr',
19538             cn: [
19539                 {
19540                     tag: 'td',
19541                     cn: [
19542                         {
19543                             tag: 'a',
19544                             href: '#',
19545                             cls: 'btn',
19546                             cn: [
19547                                 {
19548                                     tag: 'span',
19549                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19550                                 }
19551                             ]
19552                         } 
19553                     ]
19554                 },
19555                 {
19556                     tag: 'td',
19557                     cls: 'separator'
19558                 },
19559                 {
19560                     tag: 'td',
19561                     cn: [
19562                         {
19563                             tag: 'a',
19564                             href: '#',
19565                             cls: 'btn',
19566                             cn: [
19567                                 {
19568                                     tag: 'span',
19569                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19570                                 }
19571                             ]
19572                         }
19573                     ]
19574                 },
19575                 {
19576                     tag: 'td',
19577                     cls: 'separator'
19578                 }
19579             ]
19580         });
19581         
19582         time.createChild({
19583             tag: 'tr',
19584             cn: [
19585                 {
19586                     tag: 'td',
19587                     cn: [
19588                         {
19589                             tag: 'span',
19590                             cls: 'timepicker-hour',
19591                             html: '00'
19592                         }  
19593                     ]
19594                 },
19595                 {
19596                     tag: 'td',
19597                     cls: 'separator',
19598                     html: ':'
19599                 },
19600                 {
19601                     tag: 'td',
19602                     cn: [
19603                         {
19604                             tag: 'span',
19605                             cls: 'timepicker-minute',
19606                             html: '00'
19607                         }  
19608                     ]
19609                 },
19610                 {
19611                     tag: 'td',
19612                     cls: 'separator'
19613                 },
19614                 {
19615                     tag: 'td',
19616                     cn: [
19617                         {
19618                             tag: 'button',
19619                             type: 'button',
19620                             cls: 'btn btn-primary period',
19621                             html: 'AM'
19622                             
19623                         }
19624                     ]
19625                 }
19626             ]
19627         });
19628         
19629         time.createChild({
19630             tag: 'tr',
19631             cn: [
19632                 {
19633                     tag: 'td',
19634                     cn: [
19635                         {
19636                             tag: 'a',
19637                             href: '#',
19638                             cls: 'btn',
19639                             cn: [
19640                                 {
19641                                     tag: 'span',
19642                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19643                                 }
19644                             ]
19645                         }
19646                     ]
19647                 },
19648                 {
19649                     tag: 'td',
19650                     cls: 'separator'
19651                 },
19652                 {
19653                     tag: 'td',
19654                     cn: [
19655                         {
19656                             tag: 'a',
19657                             href: '#',
19658                             cls: 'btn',
19659                             cn: [
19660                                 {
19661                                     tag: 'span',
19662                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19663                                 }
19664                             ]
19665                         }
19666                     ]
19667                 },
19668                 {
19669                     tag: 'td',
19670                     cls: 'separator'
19671                 }
19672             ]
19673         });
19674         
19675     },
19676     
19677     update: function()
19678     {
19679         
19680         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19681         
19682         this.fill();
19683     },
19684     
19685     fill: function() 
19686     {
19687         var hours = this.time.getHours();
19688         var minutes = this.time.getMinutes();
19689         var period = 'AM';
19690         
19691         if(hours > 11){
19692             period = 'PM';
19693         }
19694         
19695         if(hours == 0){
19696             hours = 12;
19697         }
19698         
19699         
19700         if(hours > 12){
19701             hours = hours - 12;
19702         }
19703         
19704         if(hours < 10){
19705             hours = '0' + hours;
19706         }
19707         
19708         if(minutes < 10){
19709             minutes = '0' + minutes;
19710         }
19711         
19712         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19713         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19714         this.pop.select('button', true).first().dom.innerHTML = period;
19715         
19716     },
19717     
19718     place: function()
19719     {   
19720         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19721         
19722         var cls = ['bottom'];
19723         
19724         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19725             cls.pop();
19726             cls.push('top');
19727         }
19728         
19729         cls.push('right');
19730         
19731         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19732             cls.pop();
19733             cls.push('left');
19734         }
19735         
19736         this.picker().addClass(cls.join('-'));
19737         
19738         var _this = this;
19739         
19740         Roo.each(cls, function(c){
19741             if(c == 'bottom'){
19742                 _this.picker().setTop(_this.inputEl().getHeight());
19743                 return;
19744             }
19745             if(c == 'top'){
19746                 _this.picker().setTop(0 - _this.picker().getHeight());
19747                 return;
19748             }
19749             
19750             if(c == 'left'){
19751                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19752                 return;
19753             }
19754             if(c == 'right'){
19755                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19756                 return;
19757             }
19758         });
19759         
19760     },
19761   
19762     onFocus : function()
19763     {
19764         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19765         this.show();
19766     },
19767     
19768     onBlur : function()
19769     {
19770         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19771         this.hide();
19772     },
19773     
19774     show : function()
19775     {
19776         this.picker().show();
19777         this.pop.show();
19778         this.update();
19779         this.place();
19780         
19781         this.fireEvent('show', this, this.date);
19782     },
19783     
19784     hide : function()
19785     {
19786         this.picker().hide();
19787         this.pop.hide();
19788         
19789         this.fireEvent('hide', this, this.date);
19790     },
19791     
19792     setTime : function()
19793     {
19794         this.hide();
19795         this.setValue(this.time.format(this.format));
19796         
19797         this.fireEvent('select', this, this.date);
19798         
19799         
19800     },
19801     
19802     onMousedown: function(e){
19803         e.stopPropagation();
19804         e.preventDefault();
19805     },
19806     
19807     onIncrementHours: function()
19808     {
19809         Roo.log('onIncrementHours');
19810         this.time = this.time.add(Date.HOUR, 1);
19811         this.update();
19812         
19813     },
19814     
19815     onDecrementHours: function()
19816     {
19817         Roo.log('onDecrementHours');
19818         this.time = this.time.add(Date.HOUR, -1);
19819         this.update();
19820     },
19821     
19822     onIncrementMinutes: function()
19823     {
19824         Roo.log('onIncrementMinutes');
19825         this.time = this.time.add(Date.MINUTE, 1);
19826         this.update();
19827     },
19828     
19829     onDecrementMinutes: function()
19830     {
19831         Roo.log('onDecrementMinutes');
19832         this.time = this.time.add(Date.MINUTE, -1);
19833         this.update();
19834     },
19835     
19836     onTogglePeriod: function()
19837     {
19838         Roo.log('onTogglePeriod');
19839         this.time = this.time.add(Date.HOUR, 12);
19840         this.update();
19841     }
19842     
19843    
19844 });
19845
19846 Roo.apply(Roo.bootstrap.TimeField,  {
19847     
19848     content : {
19849         tag: 'tbody',
19850         cn: [
19851             {
19852                 tag: 'tr',
19853                 cn: [
19854                 {
19855                     tag: 'td',
19856                     colspan: '7'
19857                 }
19858                 ]
19859             }
19860         ]
19861     },
19862     
19863     footer : {
19864         tag: 'tfoot',
19865         cn: [
19866             {
19867                 tag: 'tr',
19868                 cn: [
19869                 {
19870                     tag: 'th',
19871                     colspan: '7',
19872                     cls: '',
19873                     cn: [
19874                         {
19875                             tag: 'button',
19876                             cls: 'btn btn-info ok',
19877                             html: 'OK'
19878                         }
19879                     ]
19880                 }
19881
19882                 ]
19883             }
19884         ]
19885     }
19886 });
19887
19888 Roo.apply(Roo.bootstrap.TimeField,  {
19889   
19890     template : {
19891         tag: 'div',
19892         cls: 'datepicker dropdown-menu',
19893         cn: [
19894             {
19895                 tag: 'div',
19896                 cls: 'datepicker-time',
19897                 cn: [
19898                 {
19899                     tag: 'table',
19900                     cls: 'table-condensed',
19901                     cn:[
19902                     Roo.bootstrap.TimeField.content,
19903                     Roo.bootstrap.TimeField.footer
19904                     ]
19905                 }
19906                 ]
19907             }
19908         ]
19909     }
19910 });
19911
19912  
19913
19914  /*
19915  * - LGPL
19916  *
19917  * MonthField
19918  * 
19919  */
19920
19921 /**
19922  * @class Roo.bootstrap.MonthField
19923  * @extends Roo.bootstrap.Input
19924  * Bootstrap MonthField class
19925  * 
19926  * @cfg {String} language default en
19927  * 
19928  * @constructor
19929  * Create a new MonthField
19930  * @param {Object} config The config object
19931  */
19932
19933 Roo.bootstrap.MonthField = function(config){
19934     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19935     
19936     this.addEvents({
19937         /**
19938          * @event show
19939          * Fires when this field show.
19940          * @param {Roo.bootstrap.MonthField} this
19941          * @param {Mixed} date The date value
19942          */
19943         show : true,
19944         /**
19945          * @event show
19946          * Fires when this field hide.
19947          * @param {Roo.bootstrap.MonthField} this
19948          * @param {Mixed} date The date value
19949          */
19950         hide : true,
19951         /**
19952          * @event select
19953          * Fires when select a date.
19954          * @param {Roo.bootstrap.MonthField} this
19955          * @param {String} oldvalue The old value
19956          * @param {String} newvalue The new value
19957          */
19958         select : true
19959     });
19960 };
19961
19962 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
19963     
19964     onRender: function(ct, position)
19965     {
19966         
19967         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19968         
19969         this.language = this.language || 'en';
19970         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19971         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19972         
19973         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19974         this.isInline = false;
19975         this.isInput = true;
19976         this.component = this.el.select('.add-on', true).first() || false;
19977         this.component = (this.component && this.component.length === 0) ? false : this.component;
19978         this.hasInput = this.component && this.inputEL().length;
19979         
19980         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19981         
19982         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19983         
19984         this.picker().on('mousedown', this.onMousedown, this);
19985         this.picker().on('click', this.onClick, this);
19986         
19987         this.picker().addClass('datepicker-dropdown');
19988         
19989         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19990             v.setStyle('width', '189px');
19991         });
19992         
19993         this.fillMonths();
19994         
19995         this.update();
19996         
19997         if(this.isInline) {
19998             this.show();
19999         }
20000         
20001     },
20002     
20003     setValue: function(v, suppressEvent)
20004     {   
20005         var o = this.getValue();
20006         
20007         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20008         
20009         this.update();
20010
20011         if(suppressEvent !== true){
20012             this.fireEvent('select', this, o, v);
20013         }
20014         
20015     },
20016     
20017     getValue: function()
20018     {
20019         return this.value;
20020     },
20021     
20022     onClick: function(e) 
20023     {
20024         e.stopPropagation();
20025         e.preventDefault();
20026         
20027         var target = e.getTarget();
20028         
20029         if(target.nodeName.toLowerCase() === 'i'){
20030             target = Roo.get(target).dom.parentNode;
20031         }
20032         
20033         var nodeName = target.nodeName;
20034         var className = target.className;
20035         var html = target.innerHTML;
20036         
20037         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20038             return;
20039         }
20040         
20041         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20042         
20043         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20044         
20045         this.hide();
20046                         
20047     },
20048     
20049     picker : function()
20050     {
20051         return this.pickerEl;
20052     },
20053     
20054     fillMonths: function()
20055     {    
20056         var i = 0;
20057         var months = this.picker().select('>.datepicker-months td', true).first();
20058         
20059         months.dom.innerHTML = '';
20060         
20061         while (i < 12) {
20062             var month = {
20063                 tag: 'span',
20064                 cls: 'month',
20065                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20066             };
20067             
20068             months.createChild(month);
20069         }
20070         
20071     },
20072     
20073     update: function()
20074     {
20075         var _this = this;
20076         
20077         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20078             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20079         }
20080         
20081         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20082             e.removeClass('active');
20083             
20084             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20085                 e.addClass('active');
20086             }
20087         })
20088     },
20089     
20090     place: function()
20091     {
20092         if(this.isInline) {
20093             return;
20094         }
20095         
20096         this.picker().removeClass(['bottom', 'top']);
20097         
20098         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20099             /*
20100              * place to the top of element!
20101              *
20102              */
20103             
20104             this.picker().addClass('top');
20105             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20106             
20107             return;
20108         }
20109         
20110         this.picker().addClass('bottom');
20111         
20112         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20113     },
20114     
20115     onFocus : function()
20116     {
20117         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20118         this.show();
20119     },
20120     
20121     onBlur : function()
20122     {
20123         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20124         
20125         var d = this.inputEl().getValue();
20126         
20127         this.setValue(d);
20128                 
20129         this.hide();
20130     },
20131     
20132     show : function()
20133     {
20134         this.picker().show();
20135         this.picker().select('>.datepicker-months', true).first().show();
20136         this.update();
20137         this.place();
20138         
20139         this.fireEvent('show', this, this.date);
20140     },
20141     
20142     hide : function()
20143     {
20144         if(this.isInline) {
20145             return;
20146         }
20147         this.picker().hide();
20148         this.fireEvent('hide', this, this.date);
20149         
20150     },
20151     
20152     onMousedown: function(e)
20153     {
20154         e.stopPropagation();
20155         e.preventDefault();
20156     },
20157     
20158     keyup: function(e)
20159     {
20160         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20161         this.update();
20162     },
20163
20164     fireKey: function(e)
20165     {
20166         if (!this.picker().isVisible()){
20167             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20168                 this.show();
20169             }
20170             return;
20171         }
20172         
20173         var dir;
20174         
20175         switch(e.keyCode){
20176             case 27: // escape
20177                 this.hide();
20178                 e.preventDefault();
20179                 break;
20180             case 37: // left
20181             case 39: // right
20182                 dir = e.keyCode == 37 ? -1 : 1;
20183                 
20184                 this.vIndex = this.vIndex + dir;
20185                 
20186                 if(this.vIndex < 0){
20187                     this.vIndex = 0;
20188                 }
20189                 
20190                 if(this.vIndex > 11){
20191                     this.vIndex = 11;
20192                 }
20193                 
20194                 if(isNaN(this.vIndex)){
20195                     this.vIndex = 0;
20196                 }
20197                 
20198                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20199                 
20200                 break;
20201             case 38: // up
20202             case 40: // down
20203                 
20204                 dir = e.keyCode == 38 ? -1 : 1;
20205                 
20206                 this.vIndex = this.vIndex + dir * 4;
20207                 
20208                 if(this.vIndex < 0){
20209                     this.vIndex = 0;
20210                 }
20211                 
20212                 if(this.vIndex > 11){
20213                     this.vIndex = 11;
20214                 }
20215                 
20216                 if(isNaN(this.vIndex)){
20217                     this.vIndex = 0;
20218                 }
20219                 
20220                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20221                 break;
20222                 
20223             case 13: // enter
20224                 
20225                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20226                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20227                 }
20228                 
20229                 this.hide();
20230                 e.preventDefault();
20231                 break;
20232             case 9: // tab
20233                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20234                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20235                 }
20236                 this.hide();
20237                 break;
20238             case 16: // shift
20239             case 17: // ctrl
20240             case 18: // alt
20241                 break;
20242             default :
20243                 this.hide();
20244                 
20245         }
20246     },
20247     
20248     remove: function() 
20249     {
20250         this.picker().remove();
20251     }
20252    
20253 });
20254
20255 Roo.apply(Roo.bootstrap.MonthField,  {
20256     
20257     content : {
20258         tag: 'tbody',
20259         cn: [
20260         {
20261             tag: 'tr',
20262             cn: [
20263             {
20264                 tag: 'td',
20265                 colspan: '7'
20266             }
20267             ]
20268         }
20269         ]
20270     },
20271     
20272     dates:{
20273         en: {
20274             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20275             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20276         }
20277     }
20278 });
20279
20280 Roo.apply(Roo.bootstrap.MonthField,  {
20281   
20282     template : {
20283         tag: 'div',
20284         cls: 'datepicker dropdown-menu roo-dynamic',
20285         cn: [
20286             {
20287                 tag: 'div',
20288                 cls: 'datepicker-months',
20289                 cn: [
20290                 {
20291                     tag: 'table',
20292                     cls: 'table-condensed',
20293                     cn:[
20294                         Roo.bootstrap.DateField.content
20295                     ]
20296                 }
20297                 ]
20298             }
20299         ]
20300     }
20301 });
20302
20303  
20304
20305  
20306  /*
20307  * - LGPL
20308  *
20309  * CheckBox
20310  * 
20311  */
20312
20313 /**
20314  * @class Roo.bootstrap.CheckBox
20315  * @extends Roo.bootstrap.Input
20316  * Bootstrap CheckBox class
20317  * 
20318  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20319  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20320  * @cfg {String} boxLabel The text that appears beside the checkbox
20321  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20322  * @cfg {Boolean} checked initnal the element
20323  * @cfg {Boolean} inline inline the element (default false)
20324  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20325  * @cfg {String} tooltip label tooltip
20326  * 
20327  * @constructor
20328  * Create a new CheckBox
20329  * @param {Object} config The config object
20330  */
20331
20332 Roo.bootstrap.CheckBox = function(config){
20333     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20334    
20335     this.addEvents({
20336         /**
20337         * @event check
20338         * Fires when the element is checked or unchecked.
20339         * @param {Roo.bootstrap.CheckBox} this This input
20340         * @param {Boolean} checked The new checked value
20341         */
20342        check : true,
20343        /**
20344         * @event click
20345         * Fires when the element is click.
20346         * @param {Roo.bootstrap.CheckBox} this This input
20347         */
20348        click : true
20349     });
20350     
20351 };
20352
20353 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20354   
20355     inputType: 'checkbox',
20356     inputValue: 1,
20357     valueOff: 0,
20358     boxLabel: false,
20359     checked: false,
20360     weight : false,
20361     inline: false,
20362     tooltip : '',
20363     
20364     getAutoCreate : function()
20365     {
20366         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20367         
20368         var id = Roo.id();
20369         
20370         var cfg = {};
20371         
20372         cfg.cls = 'form-group ' + this.inputType; //input-group
20373         
20374         if(this.inline){
20375             cfg.cls += ' ' + this.inputType + '-inline';
20376         }
20377         
20378         var input =  {
20379             tag: 'input',
20380             id : id,
20381             type : this.inputType,
20382             value : this.inputValue,
20383             cls : 'roo-' + this.inputType, //'form-box',
20384             placeholder : this.placeholder || ''
20385             
20386         };
20387         
20388         if(this.inputType != 'radio'){
20389             var hidden =  {
20390                 tag: 'input',
20391                 type : 'hidden',
20392                 cls : 'roo-hidden-value',
20393                 value : this.checked ? this.inputValue : this.valueOff
20394             };
20395         }
20396         
20397             
20398         if (this.weight) { // Validity check?
20399             cfg.cls += " " + this.inputType + "-" + this.weight;
20400         }
20401         
20402         if (this.disabled) {
20403             input.disabled=true;
20404         }
20405         
20406         if(this.checked){
20407             input.checked = this.checked;
20408         }
20409         
20410         if (this.name) {
20411             
20412             input.name = this.name;
20413             
20414             if(this.inputType != 'radio'){
20415                 hidden.name = this.name;
20416                 input.name = '_hidden_' + this.name;
20417             }
20418         }
20419         
20420         if (this.size) {
20421             input.cls += ' input-' + this.size;
20422         }
20423         
20424         var settings=this;
20425         
20426         ['xs','sm','md','lg'].map(function(size){
20427             if (settings[size]) {
20428                 cfg.cls += ' col-' + size + '-' + settings[size];
20429             }
20430         });
20431         
20432         var inputblock = input;
20433          
20434         if (this.before || this.after) {
20435             
20436             inputblock = {
20437                 cls : 'input-group',
20438                 cn :  [] 
20439             };
20440             
20441             if (this.before) {
20442                 inputblock.cn.push({
20443                     tag :'span',
20444                     cls : 'input-group-addon',
20445                     html : this.before
20446                 });
20447             }
20448             
20449             inputblock.cn.push(input);
20450             
20451             if(this.inputType != 'radio'){
20452                 inputblock.cn.push(hidden);
20453             }
20454             
20455             if (this.after) {
20456                 inputblock.cn.push({
20457                     tag :'span',
20458                     cls : 'input-group-addon',
20459                     html : this.after
20460                 });
20461             }
20462             
20463         }
20464         
20465         if (align ==='left' && this.fieldLabel.length) {
20466 //                Roo.log("left and has label");
20467             cfg.cn = [
20468                 {
20469                     tag: 'label',
20470                     'for' :  id,
20471                     cls : 'control-label',
20472                     html : this.fieldLabel
20473                 },
20474                 {
20475                     cls : "", 
20476                     cn: [
20477                         inputblock
20478                     ]
20479                 }
20480             ];
20481             
20482             if(this.labelWidth > 12){
20483                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20484             }
20485             
20486             if(this.labelWidth < 13 && this.labelmd == 0){
20487                 this.labelmd = this.labelWidth;
20488             }
20489             
20490             if(this.labellg > 0){
20491                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20492                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20493             }
20494             
20495             if(this.labelmd > 0){
20496                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20497                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20498             }
20499             
20500             if(this.labelsm > 0){
20501                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20502                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20503             }
20504             
20505             if(this.labelxs > 0){
20506                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20507                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20508             }
20509             
20510         } else if ( this.fieldLabel.length) {
20511 //                Roo.log(" label");
20512                 cfg.cn = [
20513                    
20514                     {
20515                         tag: this.boxLabel ? 'span' : 'label',
20516                         'for': id,
20517                         cls: 'control-label box-input-label',
20518                         //cls : 'input-group-addon',
20519                         html : this.fieldLabel
20520                     },
20521                     
20522                     inputblock
20523                     
20524                 ];
20525
20526         } else {
20527             
20528 //                Roo.log(" no label && no align");
20529                 cfg.cn = [  inputblock ] ;
20530                 
20531                 
20532         }
20533         
20534         if(this.boxLabel){
20535              var boxLabelCfg = {
20536                 tag: 'label',
20537                 //'for': id, // box label is handled by onclick - so no for...
20538                 cls: 'box-label',
20539                 html: this.boxLabel
20540             };
20541             
20542             if(this.tooltip){
20543                 boxLabelCfg.tooltip = this.tooltip;
20544             }
20545              
20546             cfg.cn.push(boxLabelCfg);
20547         }
20548         
20549         if(this.inputType != 'radio'){
20550             cfg.cn.push(hidden);
20551         }
20552         
20553         return cfg;
20554         
20555     },
20556     
20557     /**
20558      * return the real input element.
20559      */
20560     inputEl: function ()
20561     {
20562         return this.el.select('input.roo-' + this.inputType,true).first();
20563     },
20564     hiddenEl: function ()
20565     {
20566         return this.el.select('input.roo-hidden-value',true).first();
20567     },
20568     
20569     labelEl: function()
20570     {
20571         return this.el.select('label.control-label',true).first();
20572     },
20573     /* depricated... */
20574     
20575     label: function()
20576     {
20577         return this.labelEl();
20578     },
20579     
20580     boxLabelEl: function()
20581     {
20582         return this.el.select('label.box-label',true).first();
20583     },
20584     
20585     initEvents : function()
20586     {
20587 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20588         
20589         this.inputEl().on('click', this.onClick,  this);
20590         
20591         if (this.boxLabel) { 
20592             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20593         }
20594         
20595         this.startValue = this.getValue();
20596         
20597         if(this.groupId){
20598             Roo.bootstrap.CheckBox.register(this);
20599         }
20600     },
20601     
20602     onClick : function(e)
20603     {   
20604         if(this.fireEvent('click', this, e) !== false){
20605             this.setChecked(!this.checked);
20606         }
20607         
20608     },
20609     
20610     setChecked : function(state,suppressEvent)
20611     {
20612         this.startValue = this.getValue();
20613
20614         if(this.inputType == 'radio'){
20615             
20616             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20617                 e.dom.checked = false;
20618             });
20619             
20620             this.inputEl().dom.checked = true;
20621             
20622             this.inputEl().dom.value = this.inputValue;
20623             
20624             if(suppressEvent !== true){
20625                 this.fireEvent('check', this, true);
20626             }
20627             
20628             this.validate();
20629             
20630             return;
20631         }
20632         
20633         this.checked = state;
20634         
20635         this.inputEl().dom.checked = state;
20636         
20637         
20638         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20639         
20640         if(suppressEvent !== true){
20641             this.fireEvent('check', this, state);
20642         }
20643         
20644         this.validate();
20645     },
20646     
20647     getValue : function()
20648     {
20649         if(this.inputType == 'radio'){
20650             return this.getGroupValue();
20651         }
20652         
20653         return this.hiddenEl().dom.value;
20654         
20655     },
20656     
20657     getGroupValue : function()
20658     {
20659         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20660             return '';
20661         }
20662         
20663         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20664     },
20665     
20666     setValue : function(v,suppressEvent)
20667     {
20668         if(this.inputType == 'radio'){
20669             this.setGroupValue(v, suppressEvent);
20670             return;
20671         }
20672         
20673         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20674         
20675         this.validate();
20676     },
20677     
20678     setGroupValue : function(v, suppressEvent)
20679     {
20680         this.startValue = this.getValue();
20681         
20682         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20683             e.dom.checked = false;
20684             
20685             if(e.dom.value == v){
20686                 e.dom.checked = true;
20687             }
20688         });
20689         
20690         if(suppressEvent !== true){
20691             this.fireEvent('check', this, true);
20692         }
20693
20694         this.validate();
20695         
20696         return;
20697     },
20698     
20699     validate : function()
20700     {
20701         if(this.getEl().hasClass('hidden')){
20702             return true;
20703         }
20704         
20705         if(
20706                 this.disabled || 
20707                 (this.inputType == 'radio' && this.validateRadio()) ||
20708                 (this.inputType == 'checkbox' && this.validateCheckbox())
20709         ){
20710             this.markValid();
20711             return true;
20712         }
20713         
20714         this.markInvalid();
20715         return false;
20716     },
20717     
20718     validateRadio : function()
20719     {
20720         if(this.getEl().hasClass('hidden')){
20721             return true;
20722         }
20723         
20724         if(this.allowBlank){
20725             return true;
20726         }
20727         
20728         var valid = false;
20729         
20730         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20731             if(!e.dom.checked){
20732                 return;
20733             }
20734             
20735             valid = true;
20736             
20737             return false;
20738         });
20739         
20740         return valid;
20741     },
20742     
20743     validateCheckbox : function()
20744     {
20745         if(!this.groupId){
20746             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20747             //return (this.getValue() == this.inputValue) ? true : false;
20748         }
20749         
20750         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20751         
20752         if(!group){
20753             return false;
20754         }
20755         
20756         var r = false;
20757         
20758         for(var i in group){
20759             if(group[i].el.isVisible(true)){
20760                 r = false;
20761                 break;
20762             }
20763             
20764             r = true;
20765         }
20766         
20767         for(var i in group){
20768             if(r){
20769                 break;
20770             }
20771             
20772             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20773         }
20774         
20775         return r;
20776     },
20777     
20778     /**
20779      * Mark this field as valid
20780      */
20781     markValid : function()
20782     {
20783         var _this = this;
20784         
20785         this.fireEvent('valid', this);
20786         
20787         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20788         
20789         if(this.groupId){
20790             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20791         }
20792         
20793         if(label){
20794             label.markValid();
20795         }
20796
20797         if(this.inputType == 'radio'){
20798             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20799                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20800                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20801             });
20802             
20803             return;
20804         }
20805
20806         if(!this.groupId){
20807             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20808             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20809             return;
20810         }
20811         
20812         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20813         
20814         if(!group){
20815             return;
20816         }
20817         
20818         for(var i in group){
20819             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20820             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20821         }
20822     },
20823     
20824      /**
20825      * Mark this field as invalid
20826      * @param {String} msg The validation message
20827      */
20828     markInvalid : function(msg)
20829     {
20830         if(this.allowBlank){
20831             return;
20832         }
20833         
20834         var _this = this;
20835         
20836         this.fireEvent('invalid', this, msg);
20837         
20838         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20839         
20840         if(this.groupId){
20841             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20842         }
20843         
20844         if(label){
20845             label.markInvalid();
20846         }
20847             
20848         if(this.inputType == 'radio'){
20849             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20850                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20851                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20852             });
20853             
20854             return;
20855         }
20856         
20857         if(!this.groupId){
20858             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20859             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20860             return;
20861         }
20862         
20863         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20864         
20865         if(!group){
20866             return;
20867         }
20868         
20869         for(var i in group){
20870             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20871             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20872         }
20873         
20874     },
20875     
20876     clearInvalid : function()
20877     {
20878         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20879         
20880         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20881         
20882         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20883         
20884         if (label && label.iconEl) {
20885             label.iconEl.removeClass(label.validClass);
20886             label.iconEl.removeClass(label.invalidClass);
20887         }
20888     },
20889     
20890     disable : function()
20891     {
20892         if(this.inputType != 'radio'){
20893             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20894             return;
20895         }
20896         
20897         var _this = this;
20898         
20899         if(this.rendered){
20900             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20901                 _this.getActionEl().addClass(this.disabledClass);
20902                 e.dom.disabled = true;
20903             });
20904         }
20905         
20906         this.disabled = true;
20907         this.fireEvent("disable", this);
20908         return this;
20909     },
20910
20911     enable : function()
20912     {
20913         if(this.inputType != 'radio'){
20914             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20915             return;
20916         }
20917         
20918         var _this = this;
20919         
20920         if(this.rendered){
20921             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20922                 _this.getActionEl().removeClass(this.disabledClass);
20923                 e.dom.disabled = false;
20924             });
20925         }
20926         
20927         this.disabled = false;
20928         this.fireEvent("enable", this);
20929         return this;
20930     },
20931     
20932     setBoxLabel : function(v)
20933     {
20934         this.boxLabel = v;
20935         
20936         if(this.rendered){
20937             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
20938         }
20939     }
20940
20941 });
20942
20943 Roo.apply(Roo.bootstrap.CheckBox, {
20944     
20945     groups: {},
20946     
20947      /**
20948     * register a CheckBox Group
20949     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20950     */
20951     register : function(checkbox)
20952     {
20953         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20954             this.groups[checkbox.groupId] = {};
20955         }
20956         
20957         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20958             return;
20959         }
20960         
20961         this.groups[checkbox.groupId][checkbox.name] = checkbox;
20962         
20963     },
20964     /**
20965     * fetch a CheckBox Group based on the group ID
20966     * @param {string} the group ID
20967     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20968     */
20969     get: function(groupId) {
20970         if (typeof(this.groups[groupId]) == 'undefined') {
20971             return false;
20972         }
20973         
20974         return this.groups[groupId] ;
20975     }
20976     
20977     
20978 });
20979 /*
20980  * - LGPL
20981  *
20982  * RadioItem
20983  * 
20984  */
20985
20986 /**
20987  * @class Roo.bootstrap.Radio
20988  * @extends Roo.bootstrap.Component
20989  * Bootstrap Radio class
20990  * @cfg {String} boxLabel - the label associated
20991  * @cfg {String} value - the value of radio
20992  * 
20993  * @constructor
20994  * Create a new Radio
20995  * @param {Object} config The config object
20996  */
20997 Roo.bootstrap.Radio = function(config){
20998     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20999     
21000 };
21001
21002 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21003     
21004     boxLabel : '',
21005     
21006     value : '',
21007     
21008     getAutoCreate : function()
21009     {
21010         var cfg = {
21011             tag : 'div',
21012             cls : 'form-group radio',
21013             cn : [
21014                 {
21015                     tag : 'label',
21016                     cls : 'box-label',
21017                     html : this.boxLabel
21018                 }
21019             ]
21020         };
21021         
21022         return cfg;
21023     },
21024     
21025     initEvents : function() 
21026     {
21027         this.parent().register(this);
21028         
21029         this.el.on('click', this.onClick, this);
21030         
21031     },
21032     
21033     onClick : function(e)
21034     {
21035         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21036             this.setChecked(true);
21037         }
21038     },
21039     
21040     setChecked : function(state, suppressEvent)
21041     {
21042         this.parent().setValue(this.value, suppressEvent);
21043         
21044     },
21045     
21046     setBoxLabel : function(v)
21047     {
21048         this.boxLabel = v;
21049         
21050         if(this.rendered){
21051             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21052         }
21053     }
21054     
21055 });
21056  
21057
21058  /*
21059  * - LGPL
21060  *
21061  * Input
21062  * 
21063  */
21064
21065 /**
21066  * @class Roo.bootstrap.SecurePass
21067  * @extends Roo.bootstrap.Input
21068  * Bootstrap SecurePass class
21069  *
21070  * 
21071  * @constructor
21072  * Create a new SecurePass
21073  * @param {Object} config The config object
21074  */
21075  
21076 Roo.bootstrap.SecurePass = function (config) {
21077     // these go here, so the translation tool can replace them..
21078     this.errors = {
21079         PwdEmpty: "Please type a password, and then retype it to confirm.",
21080         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21081         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21082         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21083         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21084         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21085         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21086         TooWeak: "Your password is Too Weak."
21087     },
21088     this.meterLabel = "Password strength:";
21089     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21090     this.meterClass = [
21091         "roo-password-meter-tooweak", 
21092         "roo-password-meter-weak", 
21093         "roo-password-meter-medium", 
21094         "roo-password-meter-strong", 
21095         "roo-password-meter-grey"
21096     ];
21097     
21098     this.errors = {};
21099     
21100     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21101 }
21102
21103 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21104     /**
21105      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21106      * {
21107      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21108      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21109      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21110      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21111      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21112      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21113      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21114      * })
21115      */
21116     // private
21117     
21118     meterWidth: 300,
21119     errorMsg :'',    
21120     errors: false,
21121     imageRoot: '/',
21122     /**
21123      * @cfg {String/Object} Label for the strength meter (defaults to
21124      * 'Password strength:')
21125      */
21126     // private
21127     meterLabel: '',
21128     /**
21129      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21130      * ['Weak', 'Medium', 'Strong'])
21131      */
21132     // private    
21133     pwdStrengths: false,    
21134     // private
21135     strength: 0,
21136     // private
21137     _lastPwd: null,
21138     // private
21139     kCapitalLetter: 0,
21140     kSmallLetter: 1,
21141     kDigit: 2,
21142     kPunctuation: 3,
21143     
21144     insecure: false,
21145     // private
21146     initEvents: function ()
21147     {
21148         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21149
21150         if (this.el.is('input[type=password]') && Roo.isSafari) {
21151             this.el.on('keydown', this.SafariOnKeyDown, this);
21152         }
21153
21154         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21155     },
21156     // private
21157     onRender: function (ct, position)
21158     {
21159         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21160         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21161         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21162
21163         this.trigger.createChild({
21164                    cn: [
21165                     {
21166                     //id: 'PwdMeter',
21167                     tag: 'div',
21168                     cls: 'roo-password-meter-grey col-xs-12',
21169                     style: {
21170                         //width: 0,
21171                         //width: this.meterWidth + 'px'                                                
21172                         }
21173                     },
21174                     {                            
21175                          cls: 'roo-password-meter-text'                          
21176                     }
21177                 ]            
21178         });
21179
21180          
21181         if (this.hideTrigger) {
21182             this.trigger.setDisplayed(false);
21183         }
21184         this.setSize(this.width || '', this.height || '');
21185     },
21186     // private
21187     onDestroy: function ()
21188     {
21189         if (this.trigger) {
21190             this.trigger.removeAllListeners();
21191             this.trigger.remove();
21192         }
21193         if (this.wrap) {
21194             this.wrap.remove();
21195         }
21196         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21197     },
21198     // private
21199     checkStrength: function ()
21200     {
21201         var pwd = this.inputEl().getValue();
21202         if (pwd == this._lastPwd) {
21203             return;
21204         }
21205
21206         var strength;
21207         if (this.ClientSideStrongPassword(pwd)) {
21208             strength = 3;
21209         } else if (this.ClientSideMediumPassword(pwd)) {
21210             strength = 2;
21211         } else if (this.ClientSideWeakPassword(pwd)) {
21212             strength = 1;
21213         } else {
21214             strength = 0;
21215         }
21216         
21217         Roo.log('strength1: ' + strength);
21218         
21219         //var pm = this.trigger.child('div/div/div').dom;
21220         var pm = this.trigger.child('div/div');
21221         pm.removeClass(this.meterClass);
21222         pm.addClass(this.meterClass[strength]);
21223                 
21224         
21225         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21226                 
21227         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21228         
21229         this._lastPwd = pwd;
21230     },
21231     reset: function ()
21232     {
21233         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21234         
21235         this._lastPwd = '';
21236         
21237         var pm = this.trigger.child('div/div');
21238         pm.removeClass(this.meterClass);
21239         pm.addClass('roo-password-meter-grey');        
21240         
21241         
21242         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21243         
21244         pt.innerHTML = '';
21245         this.inputEl().dom.type='password';
21246     },
21247     // private
21248     validateValue: function (value)
21249     {
21250         
21251         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21252             return false;
21253         }
21254         if (value.length == 0) {
21255             if (this.allowBlank) {
21256                 this.clearInvalid();
21257                 return true;
21258             }
21259
21260             this.markInvalid(this.errors.PwdEmpty);
21261             this.errorMsg = this.errors.PwdEmpty;
21262             return false;
21263         }
21264         
21265         if(this.insecure){
21266             return true;
21267         }
21268         
21269         if ('[\x21-\x7e]*'.match(value)) {
21270             this.markInvalid(this.errors.PwdBadChar);
21271             this.errorMsg = this.errors.PwdBadChar;
21272             return false;
21273         }
21274         if (value.length < 6) {
21275             this.markInvalid(this.errors.PwdShort);
21276             this.errorMsg = this.errors.PwdShort;
21277             return false;
21278         }
21279         if (value.length > 16) {
21280             this.markInvalid(this.errors.PwdLong);
21281             this.errorMsg = this.errors.PwdLong;
21282             return false;
21283         }
21284         var strength;
21285         if (this.ClientSideStrongPassword(value)) {
21286             strength = 3;
21287         } else if (this.ClientSideMediumPassword(value)) {
21288             strength = 2;
21289         } else if (this.ClientSideWeakPassword(value)) {
21290             strength = 1;
21291         } else {
21292             strength = 0;
21293         }
21294
21295         
21296         if (strength < 2) {
21297             //this.markInvalid(this.errors.TooWeak);
21298             this.errorMsg = this.errors.TooWeak;
21299             //return false;
21300         }
21301         
21302         
21303         console.log('strength2: ' + strength);
21304         
21305         //var pm = this.trigger.child('div/div/div').dom;
21306         
21307         var pm = this.trigger.child('div/div');
21308         pm.removeClass(this.meterClass);
21309         pm.addClass(this.meterClass[strength]);
21310                 
21311         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21312                 
21313         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21314         
21315         this.errorMsg = ''; 
21316         return true;
21317     },
21318     // private
21319     CharacterSetChecks: function (type)
21320     {
21321         this.type = type;
21322         this.fResult = false;
21323     },
21324     // private
21325     isctype: function (character, type)
21326     {
21327         switch (type) {  
21328             case this.kCapitalLetter:
21329                 if (character >= 'A' && character <= 'Z') {
21330                     return true;
21331                 }
21332                 break;
21333             
21334             case this.kSmallLetter:
21335                 if (character >= 'a' && character <= 'z') {
21336                     return true;
21337                 }
21338                 break;
21339             
21340             case this.kDigit:
21341                 if (character >= '0' && character <= '9') {
21342                     return true;
21343                 }
21344                 break;
21345             
21346             case this.kPunctuation:
21347                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21348                     return true;
21349                 }
21350                 break;
21351             
21352             default:
21353                 return false;
21354         }
21355
21356     },
21357     // private
21358     IsLongEnough: function (pwd, size)
21359     {
21360         return !(pwd == null || isNaN(size) || pwd.length < size);
21361     },
21362     // private
21363     SpansEnoughCharacterSets: function (word, nb)
21364     {
21365         if (!this.IsLongEnough(word, nb))
21366         {
21367             return false;
21368         }
21369
21370         var characterSetChecks = new Array(
21371             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21372             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21373         );
21374         
21375         for (var index = 0; index < word.length; ++index) {
21376             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21377                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21378                     characterSetChecks[nCharSet].fResult = true;
21379                     break;
21380                 }
21381             }
21382         }
21383
21384         var nCharSets = 0;
21385         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21386             if (characterSetChecks[nCharSet].fResult) {
21387                 ++nCharSets;
21388             }
21389         }
21390
21391         if (nCharSets < nb) {
21392             return false;
21393         }
21394         return true;
21395     },
21396     // private
21397     ClientSideStrongPassword: function (pwd)
21398     {
21399         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21400     },
21401     // private
21402     ClientSideMediumPassword: function (pwd)
21403     {
21404         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21405     },
21406     // private
21407     ClientSideWeakPassword: function (pwd)
21408     {
21409         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21410     }
21411           
21412 })//<script type="text/javascript">
21413
21414 /*
21415  * Based  Ext JS Library 1.1.1
21416  * Copyright(c) 2006-2007, Ext JS, LLC.
21417  * LGPL
21418  *
21419  */
21420  
21421 /**
21422  * @class Roo.HtmlEditorCore
21423  * @extends Roo.Component
21424  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21425  *
21426  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21427  */
21428
21429 Roo.HtmlEditorCore = function(config){
21430     
21431     
21432     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21433     
21434     
21435     this.addEvents({
21436         /**
21437          * @event initialize
21438          * Fires when the editor is fully initialized (including the iframe)
21439          * @param {Roo.HtmlEditorCore} this
21440          */
21441         initialize: true,
21442         /**
21443          * @event activate
21444          * Fires when the editor is first receives the focus. Any insertion must wait
21445          * until after this event.
21446          * @param {Roo.HtmlEditorCore} this
21447          */
21448         activate: true,
21449          /**
21450          * @event beforesync
21451          * Fires before the textarea is updated with content from the editor iframe. Return false
21452          * to cancel the sync.
21453          * @param {Roo.HtmlEditorCore} this
21454          * @param {String} html
21455          */
21456         beforesync: true,
21457          /**
21458          * @event beforepush
21459          * Fires before the iframe editor is updated with content from the textarea. Return false
21460          * to cancel the push.
21461          * @param {Roo.HtmlEditorCore} this
21462          * @param {String} html
21463          */
21464         beforepush: true,
21465          /**
21466          * @event sync
21467          * Fires when the textarea is updated with content from the editor iframe.
21468          * @param {Roo.HtmlEditorCore} this
21469          * @param {String} html
21470          */
21471         sync: true,
21472          /**
21473          * @event push
21474          * Fires when the iframe editor is updated with content from the textarea.
21475          * @param {Roo.HtmlEditorCore} this
21476          * @param {String} html
21477          */
21478         push: true,
21479         
21480         /**
21481          * @event editorevent
21482          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21483          * @param {Roo.HtmlEditorCore} this
21484          */
21485         editorevent: true
21486         
21487     });
21488     
21489     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21490     
21491     // defaults : white / black...
21492     this.applyBlacklists();
21493     
21494     
21495     
21496 };
21497
21498
21499 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21500
21501
21502      /**
21503      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21504      */
21505     
21506     owner : false,
21507     
21508      /**
21509      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21510      *                        Roo.resizable.
21511      */
21512     resizable : false,
21513      /**
21514      * @cfg {Number} height (in pixels)
21515      */   
21516     height: 300,
21517    /**
21518      * @cfg {Number} width (in pixels)
21519      */   
21520     width: 500,
21521     
21522     /**
21523      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21524      * 
21525      */
21526     stylesheets: false,
21527     
21528     // id of frame..
21529     frameId: false,
21530     
21531     // private properties
21532     validationEvent : false,
21533     deferHeight: true,
21534     initialized : false,
21535     activated : false,
21536     sourceEditMode : false,
21537     onFocus : Roo.emptyFn,
21538     iframePad:3,
21539     hideMode:'offsets',
21540     
21541     clearUp: true,
21542     
21543     // blacklist + whitelisted elements..
21544     black: false,
21545     white: false,
21546      
21547     bodyCls : '',
21548
21549     /**
21550      * Protected method that will not generally be called directly. It
21551      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21552      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21553      */
21554     getDocMarkup : function(){
21555         // body styles..
21556         var st = '';
21557         
21558         // inherit styels from page...?? 
21559         if (this.stylesheets === false) {
21560             
21561             Roo.get(document.head).select('style').each(function(node) {
21562                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21563             });
21564             
21565             Roo.get(document.head).select('link').each(function(node) { 
21566                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21567             });
21568             
21569         } else if (!this.stylesheets.length) {
21570                 // simple..
21571                 st = '<style type="text/css">' +
21572                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21573                    '</style>';
21574         } else { 
21575             st = '<style type="text/css">' +
21576                     this.stylesheets +
21577                 '</style>';
21578         }
21579         
21580         st +=  '<style type="text/css">' +
21581             'IMG { cursor: pointer } ' +
21582         '</style>';
21583
21584         var cls = 'roo-htmleditor-body';
21585         
21586         if(this.bodyCls.length){
21587             cls += ' ' + this.bodyCls;
21588         }
21589         
21590         return '<html><head>' + st  +
21591             //<style type="text/css">' +
21592             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21593             //'</style>' +
21594             ' </head><body class="' +  cls + '"></body></html>';
21595     },
21596
21597     // private
21598     onRender : function(ct, position)
21599     {
21600         var _t = this;
21601         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21602         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21603         
21604         
21605         this.el.dom.style.border = '0 none';
21606         this.el.dom.setAttribute('tabIndex', -1);
21607         this.el.addClass('x-hidden hide');
21608         
21609         
21610         
21611         if(Roo.isIE){ // fix IE 1px bogus margin
21612             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21613         }
21614        
21615         
21616         this.frameId = Roo.id();
21617         
21618          
21619         
21620         var iframe = this.owner.wrap.createChild({
21621             tag: 'iframe',
21622             cls: 'form-control', // bootstrap..
21623             id: this.frameId,
21624             name: this.frameId,
21625             frameBorder : 'no',
21626             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21627         }, this.el
21628         );
21629         
21630         
21631         this.iframe = iframe.dom;
21632
21633          this.assignDocWin();
21634         
21635         this.doc.designMode = 'on';
21636        
21637         this.doc.open();
21638         this.doc.write(this.getDocMarkup());
21639         this.doc.close();
21640
21641         
21642         var task = { // must defer to wait for browser to be ready
21643             run : function(){
21644                 //console.log("run task?" + this.doc.readyState);
21645                 this.assignDocWin();
21646                 if(this.doc.body || this.doc.readyState == 'complete'){
21647                     try {
21648                         this.doc.designMode="on";
21649                     } catch (e) {
21650                         return;
21651                     }
21652                     Roo.TaskMgr.stop(task);
21653                     this.initEditor.defer(10, this);
21654                 }
21655             },
21656             interval : 10,
21657             duration: 10000,
21658             scope: this
21659         };
21660         Roo.TaskMgr.start(task);
21661
21662     },
21663
21664     // private
21665     onResize : function(w, h)
21666     {
21667          Roo.log('resize: ' +w + ',' + h );
21668         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21669         if(!this.iframe){
21670             return;
21671         }
21672         if(typeof w == 'number'){
21673             
21674             this.iframe.style.width = w + 'px';
21675         }
21676         if(typeof h == 'number'){
21677             
21678             this.iframe.style.height = h + 'px';
21679             if(this.doc){
21680                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21681             }
21682         }
21683         
21684     },
21685
21686     /**
21687      * Toggles the editor between standard and source edit mode.
21688      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21689      */
21690     toggleSourceEdit : function(sourceEditMode){
21691         
21692         this.sourceEditMode = sourceEditMode === true;
21693         
21694         if(this.sourceEditMode){
21695  
21696             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21697             
21698         }else{
21699             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21700             //this.iframe.className = '';
21701             this.deferFocus();
21702         }
21703         //this.setSize(this.owner.wrap.getSize());
21704         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21705     },
21706
21707     
21708   
21709
21710     /**
21711      * Protected method that will not generally be called directly. If you need/want
21712      * custom HTML cleanup, this is the method you should override.
21713      * @param {String} html The HTML to be cleaned
21714      * return {String} The cleaned HTML
21715      */
21716     cleanHtml : function(html){
21717         html = String(html);
21718         if(html.length > 5){
21719             if(Roo.isSafari){ // strip safari nonsense
21720                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21721             }
21722         }
21723         if(html == '&nbsp;'){
21724             html = '';
21725         }
21726         return html;
21727     },
21728
21729     /**
21730      * HTML Editor -> Textarea
21731      * Protected method that will not generally be called directly. Syncs the contents
21732      * of the editor iframe with the textarea.
21733      */
21734     syncValue : function(){
21735         if(this.initialized){
21736             var bd = (this.doc.body || this.doc.documentElement);
21737             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21738             var html = bd.innerHTML;
21739             if(Roo.isSafari){
21740                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21741                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21742                 if(m && m[1]){
21743                     html = '<div style="'+m[0]+'">' + html + '</div>';
21744                 }
21745             }
21746             html = this.cleanHtml(html);
21747             // fix up the special chars.. normaly like back quotes in word...
21748             // however we do not want to do this with chinese..
21749             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21750                 var cc = b.charCodeAt();
21751                 if (
21752                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21753                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21754                     (cc >= 0xf900 && cc < 0xfb00 )
21755                 ) {
21756                         return b;
21757                 }
21758                 return "&#"+cc+";" 
21759             });
21760             if(this.owner.fireEvent('beforesync', this, html) !== false){
21761                 this.el.dom.value = html;
21762                 this.owner.fireEvent('sync', this, html);
21763             }
21764         }
21765     },
21766
21767     /**
21768      * Protected method that will not generally be called directly. Pushes the value of the textarea
21769      * into the iframe editor.
21770      */
21771     pushValue : function(){
21772         if(this.initialized){
21773             var v = this.el.dom.value.trim();
21774             
21775 //            if(v.length < 1){
21776 //                v = '&#160;';
21777 //            }
21778             
21779             if(this.owner.fireEvent('beforepush', this, v) !== false){
21780                 var d = (this.doc.body || this.doc.documentElement);
21781                 d.innerHTML = v;
21782                 this.cleanUpPaste();
21783                 this.el.dom.value = d.innerHTML;
21784                 this.owner.fireEvent('push', this, v);
21785             }
21786         }
21787     },
21788
21789     // private
21790     deferFocus : function(){
21791         this.focus.defer(10, this);
21792     },
21793
21794     // doc'ed in Field
21795     focus : function(){
21796         if(this.win && !this.sourceEditMode){
21797             this.win.focus();
21798         }else{
21799             this.el.focus();
21800         }
21801     },
21802     
21803     assignDocWin: function()
21804     {
21805         var iframe = this.iframe;
21806         
21807          if(Roo.isIE){
21808             this.doc = iframe.contentWindow.document;
21809             this.win = iframe.contentWindow;
21810         } else {
21811 //            if (!Roo.get(this.frameId)) {
21812 //                return;
21813 //            }
21814 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21815 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21816             
21817             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21818                 return;
21819             }
21820             
21821             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21822             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21823         }
21824     },
21825     
21826     // private
21827     initEditor : function(){
21828         //console.log("INIT EDITOR");
21829         this.assignDocWin();
21830         
21831         
21832         
21833         this.doc.designMode="on";
21834         this.doc.open();
21835         this.doc.write(this.getDocMarkup());
21836         this.doc.close();
21837         
21838         var dbody = (this.doc.body || this.doc.documentElement);
21839         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21840         // this copies styles from the containing element into thsi one..
21841         // not sure why we need all of this..
21842         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21843         
21844         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21845         //ss['background-attachment'] = 'fixed'; // w3c
21846         dbody.bgProperties = 'fixed'; // ie
21847         //Roo.DomHelper.applyStyles(dbody, ss);
21848         Roo.EventManager.on(this.doc, {
21849             //'mousedown': this.onEditorEvent,
21850             'mouseup': this.onEditorEvent,
21851             'dblclick': this.onEditorEvent,
21852             'click': this.onEditorEvent,
21853             'keyup': this.onEditorEvent,
21854             buffer:100,
21855             scope: this
21856         });
21857         if(Roo.isGecko){
21858             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21859         }
21860         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21861             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21862         }
21863         this.initialized = true;
21864
21865         this.owner.fireEvent('initialize', this);
21866         this.pushValue();
21867     },
21868
21869     // private
21870     onDestroy : function(){
21871         
21872         
21873         
21874         if(this.rendered){
21875             
21876             //for (var i =0; i < this.toolbars.length;i++) {
21877             //    // fixme - ask toolbars for heights?
21878             //    this.toolbars[i].onDestroy();
21879            // }
21880             
21881             //this.wrap.dom.innerHTML = '';
21882             //this.wrap.remove();
21883         }
21884     },
21885
21886     // private
21887     onFirstFocus : function(){
21888         
21889         this.assignDocWin();
21890         
21891         
21892         this.activated = true;
21893          
21894     
21895         if(Roo.isGecko){ // prevent silly gecko errors
21896             this.win.focus();
21897             var s = this.win.getSelection();
21898             if(!s.focusNode || s.focusNode.nodeType != 3){
21899                 var r = s.getRangeAt(0);
21900                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21901                 r.collapse(true);
21902                 this.deferFocus();
21903             }
21904             try{
21905                 this.execCmd('useCSS', true);
21906                 this.execCmd('styleWithCSS', false);
21907             }catch(e){}
21908         }
21909         this.owner.fireEvent('activate', this);
21910     },
21911
21912     // private
21913     adjustFont: function(btn){
21914         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21915         //if(Roo.isSafari){ // safari
21916         //    adjust *= 2;
21917        // }
21918         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21919         if(Roo.isSafari){ // safari
21920             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21921             v =  (v < 10) ? 10 : v;
21922             v =  (v > 48) ? 48 : v;
21923             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21924             
21925         }
21926         
21927         
21928         v = Math.max(1, v+adjust);
21929         
21930         this.execCmd('FontSize', v  );
21931     },
21932
21933     onEditorEvent : function(e)
21934     {
21935         this.owner.fireEvent('editorevent', this, e);
21936       //  this.updateToolbar();
21937         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21938     },
21939
21940     insertTag : function(tg)
21941     {
21942         // could be a bit smarter... -> wrap the current selected tRoo..
21943         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21944             
21945             range = this.createRange(this.getSelection());
21946             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21947             wrappingNode.appendChild(range.extractContents());
21948             range.insertNode(wrappingNode);
21949
21950             return;
21951             
21952             
21953             
21954         }
21955         this.execCmd("formatblock",   tg);
21956         
21957     },
21958     
21959     insertText : function(txt)
21960     {
21961         
21962         
21963         var range = this.createRange();
21964         range.deleteContents();
21965                //alert(Sender.getAttribute('label'));
21966                
21967         range.insertNode(this.doc.createTextNode(txt));
21968     } ,
21969     
21970      
21971
21972     /**
21973      * Executes a Midas editor command on the editor document and performs necessary focus and
21974      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21975      * @param {String} cmd The Midas command
21976      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21977      */
21978     relayCmd : function(cmd, value){
21979         this.win.focus();
21980         this.execCmd(cmd, value);
21981         this.owner.fireEvent('editorevent', this);
21982         //this.updateToolbar();
21983         this.owner.deferFocus();
21984     },
21985
21986     /**
21987      * Executes a Midas editor command directly on the editor document.
21988      * For visual commands, you should use {@link #relayCmd} instead.
21989      * <b>This should only be called after the editor is initialized.</b>
21990      * @param {String} cmd The Midas command
21991      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21992      */
21993     execCmd : function(cmd, value){
21994         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21995         this.syncValue();
21996     },
21997  
21998  
21999    
22000     /**
22001      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22002      * to insert tRoo.
22003      * @param {String} text | dom node.. 
22004      */
22005     insertAtCursor : function(text)
22006     {
22007         
22008         if(!this.activated){
22009             return;
22010         }
22011         /*
22012         if(Roo.isIE){
22013             this.win.focus();
22014             var r = this.doc.selection.createRange();
22015             if(r){
22016                 r.collapse(true);
22017                 r.pasteHTML(text);
22018                 this.syncValue();
22019                 this.deferFocus();
22020             
22021             }
22022             return;
22023         }
22024         */
22025         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22026             this.win.focus();
22027             
22028             
22029             // from jquery ui (MIT licenced)
22030             var range, node;
22031             var win = this.win;
22032             
22033             if (win.getSelection && win.getSelection().getRangeAt) {
22034                 range = win.getSelection().getRangeAt(0);
22035                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22036                 range.insertNode(node);
22037             } else if (win.document.selection && win.document.selection.createRange) {
22038                 // no firefox support
22039                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22040                 win.document.selection.createRange().pasteHTML(txt);
22041             } else {
22042                 // no firefox support
22043                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22044                 this.execCmd('InsertHTML', txt);
22045             } 
22046             
22047             this.syncValue();
22048             
22049             this.deferFocus();
22050         }
22051     },
22052  // private
22053     mozKeyPress : function(e){
22054         if(e.ctrlKey){
22055             var c = e.getCharCode(), cmd;
22056           
22057             if(c > 0){
22058                 c = String.fromCharCode(c).toLowerCase();
22059                 switch(c){
22060                     case 'b':
22061                         cmd = 'bold';
22062                         break;
22063                     case 'i':
22064                         cmd = 'italic';
22065                         break;
22066                     
22067                     case 'u':
22068                         cmd = 'underline';
22069                         break;
22070                     
22071                     case 'v':
22072                         this.cleanUpPaste.defer(100, this);
22073                         return;
22074                         
22075                 }
22076                 if(cmd){
22077                     this.win.focus();
22078                     this.execCmd(cmd);
22079                     this.deferFocus();
22080                     e.preventDefault();
22081                 }
22082                 
22083             }
22084         }
22085     },
22086
22087     // private
22088     fixKeys : function(){ // load time branching for fastest keydown performance
22089         if(Roo.isIE){
22090             return function(e){
22091                 var k = e.getKey(), r;
22092                 if(k == e.TAB){
22093                     e.stopEvent();
22094                     r = this.doc.selection.createRange();
22095                     if(r){
22096                         r.collapse(true);
22097                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22098                         this.deferFocus();
22099                     }
22100                     return;
22101                 }
22102                 
22103                 if(k == e.ENTER){
22104                     r = this.doc.selection.createRange();
22105                     if(r){
22106                         var target = r.parentElement();
22107                         if(!target || target.tagName.toLowerCase() != 'li'){
22108                             e.stopEvent();
22109                             r.pasteHTML('<br />');
22110                             r.collapse(false);
22111                             r.select();
22112                         }
22113                     }
22114                 }
22115                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22116                     this.cleanUpPaste.defer(100, this);
22117                     return;
22118                 }
22119                 
22120                 
22121             };
22122         }else if(Roo.isOpera){
22123             return function(e){
22124                 var k = e.getKey();
22125                 if(k == e.TAB){
22126                     e.stopEvent();
22127                     this.win.focus();
22128                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22129                     this.deferFocus();
22130                 }
22131                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22132                     this.cleanUpPaste.defer(100, this);
22133                     return;
22134                 }
22135                 
22136             };
22137         }else if(Roo.isSafari){
22138             return function(e){
22139                 var k = e.getKey();
22140                 
22141                 if(k == e.TAB){
22142                     e.stopEvent();
22143                     this.execCmd('InsertText','\t');
22144                     this.deferFocus();
22145                     return;
22146                 }
22147                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22148                     this.cleanUpPaste.defer(100, this);
22149                     return;
22150                 }
22151                 
22152              };
22153         }
22154     }(),
22155     
22156     getAllAncestors: function()
22157     {
22158         var p = this.getSelectedNode();
22159         var a = [];
22160         if (!p) {
22161             a.push(p); // push blank onto stack..
22162             p = this.getParentElement();
22163         }
22164         
22165         
22166         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22167             a.push(p);
22168             p = p.parentNode;
22169         }
22170         a.push(this.doc.body);
22171         return a;
22172     },
22173     lastSel : false,
22174     lastSelNode : false,
22175     
22176     
22177     getSelection : function() 
22178     {
22179         this.assignDocWin();
22180         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22181     },
22182     
22183     getSelectedNode: function() 
22184     {
22185         // this may only work on Gecko!!!
22186         
22187         // should we cache this!!!!
22188         
22189         
22190         
22191          
22192         var range = this.createRange(this.getSelection()).cloneRange();
22193         
22194         if (Roo.isIE) {
22195             var parent = range.parentElement();
22196             while (true) {
22197                 var testRange = range.duplicate();
22198                 testRange.moveToElementText(parent);
22199                 if (testRange.inRange(range)) {
22200                     break;
22201                 }
22202                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22203                     break;
22204                 }
22205                 parent = parent.parentElement;
22206             }
22207             return parent;
22208         }
22209         
22210         // is ancestor a text element.
22211         var ac =  range.commonAncestorContainer;
22212         if (ac.nodeType == 3) {
22213             ac = ac.parentNode;
22214         }
22215         
22216         var ar = ac.childNodes;
22217          
22218         var nodes = [];
22219         var other_nodes = [];
22220         var has_other_nodes = false;
22221         for (var i=0;i<ar.length;i++) {
22222             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22223                 continue;
22224             }
22225             // fullly contained node.
22226             
22227             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22228                 nodes.push(ar[i]);
22229                 continue;
22230             }
22231             
22232             // probably selected..
22233             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22234                 other_nodes.push(ar[i]);
22235                 continue;
22236             }
22237             // outer..
22238             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22239                 continue;
22240             }
22241             
22242             
22243             has_other_nodes = true;
22244         }
22245         if (!nodes.length && other_nodes.length) {
22246             nodes= other_nodes;
22247         }
22248         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22249             return false;
22250         }
22251         
22252         return nodes[0];
22253     },
22254     createRange: function(sel)
22255     {
22256         // this has strange effects when using with 
22257         // top toolbar - not sure if it's a great idea.
22258         //this.editor.contentWindow.focus();
22259         if (typeof sel != "undefined") {
22260             try {
22261                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22262             } catch(e) {
22263                 return this.doc.createRange();
22264             }
22265         } else {
22266             return this.doc.createRange();
22267         }
22268     },
22269     getParentElement: function()
22270     {
22271         
22272         this.assignDocWin();
22273         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22274         
22275         var range = this.createRange(sel);
22276          
22277         try {
22278             var p = range.commonAncestorContainer;
22279             while (p.nodeType == 3) { // text node
22280                 p = p.parentNode;
22281             }
22282             return p;
22283         } catch (e) {
22284             return null;
22285         }
22286     
22287     },
22288     /***
22289      *
22290      * Range intersection.. the hard stuff...
22291      *  '-1' = before
22292      *  '0' = hits..
22293      *  '1' = after.
22294      *         [ -- selected range --- ]
22295      *   [fail]                        [fail]
22296      *
22297      *    basically..
22298      *      if end is before start or  hits it. fail.
22299      *      if start is after end or hits it fail.
22300      *
22301      *   if either hits (but other is outside. - then it's not 
22302      *   
22303      *    
22304      **/
22305     
22306     
22307     // @see http://www.thismuchiknow.co.uk/?p=64.
22308     rangeIntersectsNode : function(range, node)
22309     {
22310         var nodeRange = node.ownerDocument.createRange();
22311         try {
22312             nodeRange.selectNode(node);
22313         } catch (e) {
22314             nodeRange.selectNodeContents(node);
22315         }
22316     
22317         var rangeStartRange = range.cloneRange();
22318         rangeStartRange.collapse(true);
22319     
22320         var rangeEndRange = range.cloneRange();
22321         rangeEndRange.collapse(false);
22322     
22323         var nodeStartRange = nodeRange.cloneRange();
22324         nodeStartRange.collapse(true);
22325     
22326         var nodeEndRange = nodeRange.cloneRange();
22327         nodeEndRange.collapse(false);
22328     
22329         return rangeStartRange.compareBoundaryPoints(
22330                  Range.START_TO_START, nodeEndRange) == -1 &&
22331                rangeEndRange.compareBoundaryPoints(
22332                  Range.START_TO_START, nodeStartRange) == 1;
22333         
22334          
22335     },
22336     rangeCompareNode : function(range, node)
22337     {
22338         var nodeRange = node.ownerDocument.createRange();
22339         try {
22340             nodeRange.selectNode(node);
22341         } catch (e) {
22342             nodeRange.selectNodeContents(node);
22343         }
22344         
22345         
22346         range.collapse(true);
22347     
22348         nodeRange.collapse(true);
22349      
22350         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22351         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22352          
22353         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22354         
22355         var nodeIsBefore   =  ss == 1;
22356         var nodeIsAfter    = ee == -1;
22357         
22358         if (nodeIsBefore && nodeIsAfter) {
22359             return 0; // outer
22360         }
22361         if (!nodeIsBefore && nodeIsAfter) {
22362             return 1; //right trailed.
22363         }
22364         
22365         if (nodeIsBefore && !nodeIsAfter) {
22366             return 2;  // left trailed.
22367         }
22368         // fully contined.
22369         return 3;
22370     },
22371
22372     // private? - in a new class?
22373     cleanUpPaste :  function()
22374     {
22375         // cleans up the whole document..
22376         Roo.log('cleanuppaste');
22377         
22378         this.cleanUpChildren(this.doc.body);
22379         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22380         if (clean != this.doc.body.innerHTML) {
22381             this.doc.body.innerHTML = clean;
22382         }
22383         
22384     },
22385     
22386     cleanWordChars : function(input) {// change the chars to hex code
22387         var he = Roo.HtmlEditorCore;
22388         
22389         var output = input;
22390         Roo.each(he.swapCodes, function(sw) { 
22391             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22392             
22393             output = output.replace(swapper, sw[1]);
22394         });
22395         
22396         return output;
22397     },
22398     
22399     
22400     cleanUpChildren : function (n)
22401     {
22402         if (!n.childNodes.length) {
22403             return;
22404         }
22405         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22406            this.cleanUpChild(n.childNodes[i]);
22407         }
22408     },
22409     
22410     
22411         
22412     
22413     cleanUpChild : function (node)
22414     {
22415         var ed = this;
22416         //console.log(node);
22417         if (node.nodeName == "#text") {
22418             // clean up silly Windows -- stuff?
22419             return; 
22420         }
22421         if (node.nodeName == "#comment") {
22422             node.parentNode.removeChild(node);
22423             // clean up silly Windows -- stuff?
22424             return; 
22425         }
22426         var lcname = node.tagName.toLowerCase();
22427         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22428         // whitelist of tags..
22429         
22430         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22431             // remove node.
22432             node.parentNode.removeChild(node);
22433             return;
22434             
22435         }
22436         
22437         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22438         
22439         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22440         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22441         
22442         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22443         //    remove_keep_children = true;
22444         //}
22445         
22446         if (remove_keep_children) {
22447             this.cleanUpChildren(node);
22448             // inserts everything just before this node...
22449             while (node.childNodes.length) {
22450                 var cn = node.childNodes[0];
22451                 node.removeChild(cn);
22452                 node.parentNode.insertBefore(cn, node);
22453             }
22454             node.parentNode.removeChild(node);
22455             return;
22456         }
22457         
22458         if (!node.attributes || !node.attributes.length) {
22459             this.cleanUpChildren(node);
22460             return;
22461         }
22462         
22463         function cleanAttr(n,v)
22464         {
22465             
22466             if (v.match(/^\./) || v.match(/^\//)) {
22467                 return;
22468             }
22469             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
22470                 return;
22471             }
22472             if (v.match(/^#/)) {
22473                 return;
22474             }
22475 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22476             node.removeAttribute(n);
22477             
22478         }
22479         
22480         var cwhite = this.cwhite;
22481         var cblack = this.cblack;
22482             
22483         function cleanStyle(n,v)
22484         {
22485             if (v.match(/expression/)) { //XSS?? should we even bother..
22486                 node.removeAttribute(n);
22487                 return;
22488             }
22489             
22490             var parts = v.split(/;/);
22491             var clean = [];
22492             
22493             Roo.each(parts, function(p) {
22494                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22495                 if (!p.length) {
22496                     return true;
22497                 }
22498                 var l = p.split(':').shift().replace(/\s+/g,'');
22499                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22500                 
22501                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22502 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22503                     //node.removeAttribute(n);
22504                     return true;
22505                 }
22506                 //Roo.log()
22507                 // only allow 'c whitelisted system attributes'
22508                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22509 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22510                     //node.removeAttribute(n);
22511                     return true;
22512                 }
22513                 
22514                 
22515                  
22516                 
22517                 clean.push(p);
22518                 return true;
22519             });
22520             if (clean.length) { 
22521                 node.setAttribute(n, clean.join(';'));
22522             } else {
22523                 node.removeAttribute(n);
22524             }
22525             
22526         }
22527         
22528         
22529         for (var i = node.attributes.length-1; i > -1 ; i--) {
22530             var a = node.attributes[i];
22531             //console.log(a);
22532             
22533             if (a.name.toLowerCase().substr(0,2)=='on')  {
22534                 node.removeAttribute(a.name);
22535                 continue;
22536             }
22537             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22538                 node.removeAttribute(a.name);
22539                 continue;
22540             }
22541             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22542                 cleanAttr(a.name,a.value); // fixme..
22543                 continue;
22544             }
22545             if (a.name == 'style') {
22546                 cleanStyle(a.name,a.value);
22547                 continue;
22548             }
22549             /// clean up MS crap..
22550             // tecnically this should be a list of valid class'es..
22551             
22552             
22553             if (a.name == 'class') {
22554                 if (a.value.match(/^Mso/)) {
22555                     node.className = '';
22556                 }
22557                 
22558                 if (a.value.match(/^body$/)) {
22559                     node.className = '';
22560                 }
22561                 continue;
22562             }
22563             
22564             // style cleanup!?
22565             // class cleanup?
22566             
22567         }
22568         
22569         
22570         this.cleanUpChildren(node);
22571         
22572         
22573     },
22574     
22575     /**
22576      * Clean up MS wordisms...
22577      */
22578     cleanWord : function(node)
22579     {
22580         
22581         
22582         if (!node) {
22583             this.cleanWord(this.doc.body);
22584             return;
22585         }
22586         if (node.nodeName == "#text") {
22587             // clean up silly Windows -- stuff?
22588             return; 
22589         }
22590         if (node.nodeName == "#comment") {
22591             node.parentNode.removeChild(node);
22592             // clean up silly Windows -- stuff?
22593             return; 
22594         }
22595         
22596         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22597             node.parentNode.removeChild(node);
22598             return;
22599         }
22600         
22601         // remove - but keep children..
22602         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22603             while (node.childNodes.length) {
22604                 var cn = node.childNodes[0];
22605                 node.removeChild(cn);
22606                 node.parentNode.insertBefore(cn, node);
22607             }
22608             node.parentNode.removeChild(node);
22609             this.iterateChildren(node, this.cleanWord);
22610             return;
22611         }
22612         // clean styles
22613         if (node.className.length) {
22614             
22615             var cn = node.className.split(/\W+/);
22616             var cna = [];
22617             Roo.each(cn, function(cls) {
22618                 if (cls.match(/Mso[a-zA-Z]+/)) {
22619                     return;
22620                 }
22621                 cna.push(cls);
22622             });
22623             node.className = cna.length ? cna.join(' ') : '';
22624             if (!cna.length) {
22625                 node.removeAttribute("class");
22626             }
22627         }
22628         
22629         if (node.hasAttribute("lang")) {
22630             node.removeAttribute("lang");
22631         }
22632         
22633         if (node.hasAttribute("style")) {
22634             
22635             var styles = node.getAttribute("style").split(";");
22636             var nstyle = [];
22637             Roo.each(styles, function(s) {
22638                 if (!s.match(/:/)) {
22639                     return;
22640                 }
22641                 var kv = s.split(":");
22642                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22643                     return;
22644                 }
22645                 // what ever is left... we allow.
22646                 nstyle.push(s);
22647             });
22648             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22649             if (!nstyle.length) {
22650                 node.removeAttribute('style');
22651             }
22652         }
22653         this.iterateChildren(node, this.cleanWord);
22654         
22655         
22656         
22657     },
22658     /**
22659      * iterateChildren of a Node, calling fn each time, using this as the scole..
22660      * @param {DomNode} node node to iterate children of.
22661      * @param {Function} fn method of this class to call on each item.
22662      */
22663     iterateChildren : function(node, fn)
22664     {
22665         if (!node.childNodes.length) {
22666                 return;
22667         }
22668         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22669            fn.call(this, node.childNodes[i])
22670         }
22671     },
22672     
22673     
22674     /**
22675      * cleanTableWidths.
22676      *
22677      * Quite often pasting from word etc.. results in tables with column and widths.
22678      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22679      *
22680      */
22681     cleanTableWidths : function(node)
22682     {
22683          
22684          
22685         if (!node) {
22686             this.cleanTableWidths(this.doc.body);
22687             return;
22688         }
22689         
22690         // ignore list...
22691         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22692             return; 
22693         }
22694         Roo.log(node.tagName);
22695         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22696             this.iterateChildren(node, this.cleanTableWidths);
22697             return;
22698         }
22699         if (node.hasAttribute('width')) {
22700             node.removeAttribute('width');
22701         }
22702         
22703          
22704         if (node.hasAttribute("style")) {
22705             // pretty basic...
22706             
22707             var styles = node.getAttribute("style").split(";");
22708             var nstyle = [];
22709             Roo.each(styles, function(s) {
22710                 if (!s.match(/:/)) {
22711                     return;
22712                 }
22713                 var kv = s.split(":");
22714                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22715                     return;
22716                 }
22717                 // what ever is left... we allow.
22718                 nstyle.push(s);
22719             });
22720             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22721             if (!nstyle.length) {
22722                 node.removeAttribute('style');
22723             }
22724         }
22725         
22726         this.iterateChildren(node, this.cleanTableWidths);
22727         
22728         
22729     },
22730     
22731     
22732     
22733     
22734     domToHTML : function(currentElement, depth, nopadtext) {
22735         
22736         depth = depth || 0;
22737         nopadtext = nopadtext || false;
22738     
22739         if (!currentElement) {
22740             return this.domToHTML(this.doc.body);
22741         }
22742         
22743         //Roo.log(currentElement);
22744         var j;
22745         var allText = false;
22746         var nodeName = currentElement.nodeName;
22747         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22748         
22749         if  (nodeName == '#text') {
22750             
22751             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22752         }
22753         
22754         
22755         var ret = '';
22756         if (nodeName != 'BODY') {
22757              
22758             var i = 0;
22759             // Prints the node tagName, such as <A>, <IMG>, etc
22760             if (tagName) {
22761                 var attr = [];
22762                 for(i = 0; i < currentElement.attributes.length;i++) {
22763                     // quoting?
22764                     var aname = currentElement.attributes.item(i).name;
22765                     if (!currentElement.attributes.item(i).value.length) {
22766                         continue;
22767                     }
22768                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22769                 }
22770                 
22771                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22772             } 
22773             else {
22774                 
22775                 // eack
22776             }
22777         } else {
22778             tagName = false;
22779         }
22780         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22781             return ret;
22782         }
22783         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22784             nopadtext = true;
22785         }
22786         
22787         
22788         // Traverse the tree
22789         i = 0;
22790         var currentElementChild = currentElement.childNodes.item(i);
22791         var allText = true;
22792         var innerHTML  = '';
22793         lastnode = '';
22794         while (currentElementChild) {
22795             // Formatting code (indent the tree so it looks nice on the screen)
22796             var nopad = nopadtext;
22797             if (lastnode == 'SPAN') {
22798                 nopad  = true;
22799             }
22800             // text
22801             if  (currentElementChild.nodeName == '#text') {
22802                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22803                 toadd = nopadtext ? toadd : toadd.trim();
22804                 if (!nopad && toadd.length > 80) {
22805                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22806                 }
22807                 innerHTML  += toadd;
22808                 
22809                 i++;
22810                 currentElementChild = currentElement.childNodes.item(i);
22811                 lastNode = '';
22812                 continue;
22813             }
22814             allText = false;
22815             
22816             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22817                 
22818             // Recursively traverse the tree structure of the child node
22819             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22820             lastnode = currentElementChild.nodeName;
22821             i++;
22822             currentElementChild=currentElement.childNodes.item(i);
22823         }
22824         
22825         ret += innerHTML;
22826         
22827         if (!allText) {
22828                 // The remaining code is mostly for formatting the tree
22829             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22830         }
22831         
22832         
22833         if (tagName) {
22834             ret+= "</"+tagName+">";
22835         }
22836         return ret;
22837         
22838     },
22839         
22840     applyBlacklists : function()
22841     {
22842         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22843         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22844         
22845         this.white = [];
22846         this.black = [];
22847         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22848             if (b.indexOf(tag) > -1) {
22849                 return;
22850             }
22851             this.white.push(tag);
22852             
22853         }, this);
22854         
22855         Roo.each(w, function(tag) {
22856             if (b.indexOf(tag) > -1) {
22857                 return;
22858             }
22859             if (this.white.indexOf(tag) > -1) {
22860                 return;
22861             }
22862             this.white.push(tag);
22863             
22864         }, this);
22865         
22866         
22867         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22868             if (w.indexOf(tag) > -1) {
22869                 return;
22870             }
22871             this.black.push(tag);
22872             
22873         }, this);
22874         
22875         Roo.each(b, function(tag) {
22876             if (w.indexOf(tag) > -1) {
22877                 return;
22878             }
22879             if (this.black.indexOf(tag) > -1) {
22880                 return;
22881             }
22882             this.black.push(tag);
22883             
22884         }, this);
22885         
22886         
22887         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22888         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22889         
22890         this.cwhite = [];
22891         this.cblack = [];
22892         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22893             if (b.indexOf(tag) > -1) {
22894                 return;
22895             }
22896             this.cwhite.push(tag);
22897             
22898         }, this);
22899         
22900         Roo.each(w, function(tag) {
22901             if (b.indexOf(tag) > -1) {
22902                 return;
22903             }
22904             if (this.cwhite.indexOf(tag) > -1) {
22905                 return;
22906             }
22907             this.cwhite.push(tag);
22908             
22909         }, this);
22910         
22911         
22912         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22913             if (w.indexOf(tag) > -1) {
22914                 return;
22915             }
22916             this.cblack.push(tag);
22917             
22918         }, this);
22919         
22920         Roo.each(b, function(tag) {
22921             if (w.indexOf(tag) > -1) {
22922                 return;
22923             }
22924             if (this.cblack.indexOf(tag) > -1) {
22925                 return;
22926             }
22927             this.cblack.push(tag);
22928             
22929         }, this);
22930     },
22931     
22932     setStylesheets : function(stylesheets)
22933     {
22934         if(typeof(stylesheets) == 'string'){
22935             Roo.get(this.iframe.contentDocument.head).createChild({
22936                 tag : 'link',
22937                 rel : 'stylesheet',
22938                 type : 'text/css',
22939                 href : stylesheets
22940             });
22941             
22942             return;
22943         }
22944         var _this = this;
22945      
22946         Roo.each(stylesheets, function(s) {
22947             if(!s.length){
22948                 return;
22949             }
22950             
22951             Roo.get(_this.iframe.contentDocument.head).createChild({
22952                 tag : 'link',
22953                 rel : 'stylesheet',
22954                 type : 'text/css',
22955                 href : s
22956             });
22957         });
22958
22959         
22960     },
22961     
22962     removeStylesheets : function()
22963     {
22964         var _this = this;
22965         
22966         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22967             s.remove();
22968         });
22969     },
22970     
22971     setStyle : function(style)
22972     {
22973         Roo.get(this.iframe.contentDocument.head).createChild({
22974             tag : 'style',
22975             type : 'text/css',
22976             html : style
22977         });
22978
22979         return;
22980     }
22981     
22982     // hide stuff that is not compatible
22983     /**
22984      * @event blur
22985      * @hide
22986      */
22987     /**
22988      * @event change
22989      * @hide
22990      */
22991     /**
22992      * @event focus
22993      * @hide
22994      */
22995     /**
22996      * @event specialkey
22997      * @hide
22998      */
22999     /**
23000      * @cfg {String} fieldClass @hide
23001      */
23002     /**
23003      * @cfg {String} focusClass @hide
23004      */
23005     /**
23006      * @cfg {String} autoCreate @hide
23007      */
23008     /**
23009      * @cfg {String} inputType @hide
23010      */
23011     /**
23012      * @cfg {String} invalidClass @hide
23013      */
23014     /**
23015      * @cfg {String} invalidText @hide
23016      */
23017     /**
23018      * @cfg {String} msgFx @hide
23019      */
23020     /**
23021      * @cfg {String} validateOnBlur @hide
23022      */
23023 });
23024
23025 Roo.HtmlEditorCore.white = [
23026         'area', 'br', 'img', 'input', 'hr', 'wbr',
23027         
23028        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23029        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23030        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23031        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23032        'table',   'ul',         'xmp', 
23033        
23034        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23035       'thead',   'tr', 
23036      
23037       'dir', 'menu', 'ol', 'ul', 'dl',
23038        
23039       'embed',  'object'
23040 ];
23041
23042
23043 Roo.HtmlEditorCore.black = [
23044     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23045         'applet', // 
23046         'base',   'basefont', 'bgsound', 'blink',  'body', 
23047         'frame',  'frameset', 'head',    'html',   'ilayer', 
23048         'iframe', 'layer',  'link',     'meta',    'object',   
23049         'script', 'style' ,'title',  'xml' // clean later..
23050 ];
23051 Roo.HtmlEditorCore.clean = [
23052     'script', 'style', 'title', 'xml'
23053 ];
23054 Roo.HtmlEditorCore.remove = [
23055     'font'
23056 ];
23057 // attributes..
23058
23059 Roo.HtmlEditorCore.ablack = [
23060     'on'
23061 ];
23062     
23063 Roo.HtmlEditorCore.aclean = [ 
23064     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23065 ];
23066
23067 // protocols..
23068 Roo.HtmlEditorCore.pwhite= [
23069         'http',  'https',  'mailto'
23070 ];
23071
23072 // white listed style attributes.
23073 Roo.HtmlEditorCore.cwhite= [
23074       //  'text-align', /// default is to allow most things..
23075       
23076          
23077 //        'font-size'//??
23078 ];
23079
23080 // black listed style attributes.
23081 Roo.HtmlEditorCore.cblack= [
23082       //  'font-size' -- this can be set by the project 
23083 ];
23084
23085
23086 Roo.HtmlEditorCore.swapCodes   =[ 
23087     [    8211, "--" ], 
23088     [    8212, "--" ], 
23089     [    8216,  "'" ],  
23090     [    8217, "'" ],  
23091     [    8220, '"' ],  
23092     [    8221, '"' ],  
23093     [    8226, "*" ],  
23094     [    8230, "..." ]
23095 ]; 
23096
23097     /*
23098  * - LGPL
23099  *
23100  * HtmlEditor
23101  * 
23102  */
23103
23104 /**
23105  * @class Roo.bootstrap.HtmlEditor
23106  * @extends Roo.bootstrap.TextArea
23107  * Bootstrap HtmlEditor class
23108
23109  * @constructor
23110  * Create a new HtmlEditor
23111  * @param {Object} config The config object
23112  */
23113
23114 Roo.bootstrap.HtmlEditor = function(config){
23115     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23116     if (!this.toolbars) {
23117         this.toolbars = [];
23118     }
23119     
23120     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23121     this.addEvents({
23122             /**
23123              * @event initialize
23124              * Fires when the editor is fully initialized (including the iframe)
23125              * @param {HtmlEditor} this
23126              */
23127             initialize: true,
23128             /**
23129              * @event activate
23130              * Fires when the editor is first receives the focus. Any insertion must wait
23131              * until after this event.
23132              * @param {HtmlEditor} this
23133              */
23134             activate: true,
23135              /**
23136              * @event beforesync
23137              * Fires before the textarea is updated with content from the editor iframe. Return false
23138              * to cancel the sync.
23139              * @param {HtmlEditor} this
23140              * @param {String} html
23141              */
23142             beforesync: true,
23143              /**
23144              * @event beforepush
23145              * Fires before the iframe editor is updated with content from the textarea. Return false
23146              * to cancel the push.
23147              * @param {HtmlEditor} this
23148              * @param {String} html
23149              */
23150             beforepush: true,
23151              /**
23152              * @event sync
23153              * Fires when the textarea is updated with content from the editor iframe.
23154              * @param {HtmlEditor} this
23155              * @param {String} html
23156              */
23157             sync: true,
23158              /**
23159              * @event push
23160              * Fires when the iframe editor is updated with content from the textarea.
23161              * @param {HtmlEditor} this
23162              * @param {String} html
23163              */
23164             push: true,
23165              /**
23166              * @event editmodechange
23167              * Fires when the editor switches edit modes
23168              * @param {HtmlEditor} this
23169              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23170              */
23171             editmodechange: true,
23172             /**
23173              * @event editorevent
23174              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23175              * @param {HtmlEditor} this
23176              */
23177             editorevent: true,
23178             /**
23179              * @event firstfocus
23180              * Fires when on first focus - needed by toolbars..
23181              * @param {HtmlEditor} this
23182              */
23183             firstfocus: true,
23184             /**
23185              * @event autosave
23186              * Auto save the htmlEditor value as a file into Events
23187              * @param {HtmlEditor} this
23188              */
23189             autosave: true,
23190             /**
23191              * @event savedpreview
23192              * preview the saved version of htmlEditor
23193              * @param {HtmlEditor} this
23194              */
23195             savedpreview: true
23196         });
23197 };
23198
23199
23200 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23201     
23202     
23203       /**
23204      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23205      */
23206     toolbars : false,
23207     
23208      /**
23209     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23210     */
23211     btns : [],
23212    
23213      /**
23214      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23215      *                        Roo.resizable.
23216      */
23217     resizable : false,
23218      /**
23219      * @cfg {Number} height (in pixels)
23220      */   
23221     height: 300,
23222    /**
23223      * @cfg {Number} width (in pixels)
23224      */   
23225     width: false,
23226     
23227     /**
23228      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23229      * 
23230      */
23231     stylesheets: false,
23232     
23233     // id of frame..
23234     frameId: false,
23235     
23236     // private properties
23237     validationEvent : false,
23238     deferHeight: true,
23239     initialized : false,
23240     activated : false,
23241     
23242     onFocus : Roo.emptyFn,
23243     iframePad:3,
23244     hideMode:'offsets',
23245     
23246     tbContainer : false,
23247     
23248     bodyCls : '',
23249     
23250     toolbarContainer :function() {
23251         return this.wrap.select('.x-html-editor-tb',true).first();
23252     },
23253
23254     /**
23255      * Protected method that will not generally be called directly. It
23256      * is called when the editor creates its toolbar. Override this method if you need to
23257      * add custom toolbar buttons.
23258      * @param {HtmlEditor} editor
23259      */
23260     createToolbar : function(){
23261         Roo.log('renewing');
23262         Roo.log("create toolbars");
23263         
23264         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23265         this.toolbars[0].render(this.toolbarContainer());
23266         
23267         return;
23268         
23269 //        if (!editor.toolbars || !editor.toolbars.length) {
23270 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23271 //        }
23272 //        
23273 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23274 //            editor.toolbars[i] = Roo.factory(
23275 //                    typeof(editor.toolbars[i]) == 'string' ?
23276 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23277 //                Roo.bootstrap.HtmlEditor);
23278 //            editor.toolbars[i].init(editor);
23279 //        }
23280     },
23281
23282      
23283     // private
23284     onRender : function(ct, position)
23285     {
23286        // Roo.log("Call onRender: " + this.xtype);
23287         var _t = this;
23288         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23289       
23290         this.wrap = this.inputEl().wrap({
23291             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23292         });
23293         
23294         this.editorcore.onRender(ct, position);
23295          
23296         if (this.resizable) {
23297             this.resizeEl = new Roo.Resizable(this.wrap, {
23298                 pinned : true,
23299                 wrap: true,
23300                 dynamic : true,
23301                 minHeight : this.height,
23302                 height: this.height,
23303                 handles : this.resizable,
23304                 width: this.width,
23305                 listeners : {
23306                     resize : function(r, w, h) {
23307                         _t.onResize(w,h); // -something
23308                     }
23309                 }
23310             });
23311             
23312         }
23313         this.createToolbar(this);
23314        
23315         
23316         if(!this.width && this.resizable){
23317             this.setSize(this.wrap.getSize());
23318         }
23319         if (this.resizeEl) {
23320             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23321             // should trigger onReize..
23322         }
23323         
23324     },
23325
23326     // private
23327     onResize : function(w, h)
23328     {
23329         Roo.log('resize: ' +w + ',' + h );
23330         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23331         var ew = false;
23332         var eh = false;
23333         
23334         if(this.inputEl() ){
23335             if(typeof w == 'number'){
23336                 var aw = w - this.wrap.getFrameWidth('lr');
23337                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23338                 ew = aw;
23339             }
23340             if(typeof h == 'number'){
23341                  var tbh = -11;  // fixme it needs to tool bar size!
23342                 for (var i =0; i < this.toolbars.length;i++) {
23343                     // fixme - ask toolbars for heights?
23344                     tbh += this.toolbars[i].el.getHeight();
23345                     //if (this.toolbars[i].footer) {
23346                     //    tbh += this.toolbars[i].footer.el.getHeight();
23347                     //}
23348                 }
23349               
23350                 
23351                 
23352                 
23353                 
23354                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23355                 ah -= 5; // knock a few pixes off for look..
23356                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23357                 var eh = ah;
23358             }
23359         }
23360         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23361         this.editorcore.onResize(ew,eh);
23362         
23363     },
23364
23365     /**
23366      * Toggles the editor between standard and source edit mode.
23367      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23368      */
23369     toggleSourceEdit : function(sourceEditMode)
23370     {
23371         this.editorcore.toggleSourceEdit(sourceEditMode);
23372         
23373         if(this.editorcore.sourceEditMode){
23374             Roo.log('editor - showing textarea');
23375             
23376 //            Roo.log('in');
23377 //            Roo.log(this.syncValue());
23378             this.syncValue();
23379             this.inputEl().removeClass(['hide', 'x-hidden']);
23380             this.inputEl().dom.removeAttribute('tabIndex');
23381             this.inputEl().focus();
23382         }else{
23383             Roo.log('editor - hiding textarea');
23384 //            Roo.log('out')
23385 //            Roo.log(this.pushValue()); 
23386             this.pushValue();
23387             
23388             this.inputEl().addClass(['hide', 'x-hidden']);
23389             this.inputEl().dom.setAttribute('tabIndex', -1);
23390             //this.deferFocus();
23391         }
23392          
23393         if(this.resizable){
23394             this.setSize(this.wrap.getSize());
23395         }
23396         
23397         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23398     },
23399  
23400     // private (for BoxComponent)
23401     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23402
23403     // private (for BoxComponent)
23404     getResizeEl : function(){
23405         return this.wrap;
23406     },
23407
23408     // private (for BoxComponent)
23409     getPositionEl : function(){
23410         return this.wrap;
23411     },
23412
23413     // private
23414     initEvents : function(){
23415         this.originalValue = this.getValue();
23416     },
23417
23418 //    /**
23419 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23420 //     * @method
23421 //     */
23422 //    markInvalid : Roo.emptyFn,
23423 //    /**
23424 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23425 //     * @method
23426 //     */
23427 //    clearInvalid : Roo.emptyFn,
23428
23429     setValue : function(v){
23430         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23431         this.editorcore.pushValue();
23432     },
23433
23434      
23435     // private
23436     deferFocus : function(){
23437         this.focus.defer(10, this);
23438     },
23439
23440     // doc'ed in Field
23441     focus : function(){
23442         this.editorcore.focus();
23443         
23444     },
23445       
23446
23447     // private
23448     onDestroy : function(){
23449         
23450         
23451         
23452         if(this.rendered){
23453             
23454             for (var i =0; i < this.toolbars.length;i++) {
23455                 // fixme - ask toolbars for heights?
23456                 this.toolbars[i].onDestroy();
23457             }
23458             
23459             this.wrap.dom.innerHTML = '';
23460             this.wrap.remove();
23461         }
23462     },
23463
23464     // private
23465     onFirstFocus : function(){
23466         //Roo.log("onFirstFocus");
23467         this.editorcore.onFirstFocus();
23468          for (var i =0; i < this.toolbars.length;i++) {
23469             this.toolbars[i].onFirstFocus();
23470         }
23471         
23472     },
23473     
23474     // private
23475     syncValue : function()
23476     {   
23477         this.editorcore.syncValue();
23478     },
23479     
23480     pushValue : function()
23481     {   
23482         this.editorcore.pushValue();
23483     }
23484      
23485     
23486     // hide stuff that is not compatible
23487     /**
23488      * @event blur
23489      * @hide
23490      */
23491     /**
23492      * @event change
23493      * @hide
23494      */
23495     /**
23496      * @event focus
23497      * @hide
23498      */
23499     /**
23500      * @event specialkey
23501      * @hide
23502      */
23503     /**
23504      * @cfg {String} fieldClass @hide
23505      */
23506     /**
23507      * @cfg {String} focusClass @hide
23508      */
23509     /**
23510      * @cfg {String} autoCreate @hide
23511      */
23512     /**
23513      * @cfg {String} inputType @hide
23514      */
23515     /**
23516      * @cfg {String} invalidClass @hide
23517      */
23518     /**
23519      * @cfg {String} invalidText @hide
23520      */
23521     /**
23522      * @cfg {String} msgFx @hide
23523      */
23524     /**
23525      * @cfg {String} validateOnBlur @hide
23526      */
23527 });
23528  
23529     
23530    
23531    
23532    
23533       
23534 Roo.namespace('Roo.bootstrap.htmleditor');
23535 /**
23536  * @class Roo.bootstrap.HtmlEditorToolbar1
23537  * Basic Toolbar
23538  * 
23539  * Usage:
23540  *
23541  new Roo.bootstrap.HtmlEditor({
23542     ....
23543     toolbars : [
23544         new Roo.bootstrap.HtmlEditorToolbar1({
23545             disable : { fonts: 1 , format: 1, ..., ... , ...],
23546             btns : [ .... ]
23547         })
23548     }
23549      
23550  * 
23551  * @cfg {Object} disable List of elements to disable..
23552  * @cfg {Array} btns List of additional buttons.
23553  * 
23554  * 
23555  * NEEDS Extra CSS? 
23556  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23557  */
23558  
23559 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23560 {
23561     
23562     Roo.apply(this, config);
23563     
23564     // default disabled, based on 'good practice'..
23565     this.disable = this.disable || {};
23566     Roo.applyIf(this.disable, {
23567         fontSize : true,
23568         colors : true,
23569         specialElements : true
23570     });
23571     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23572     
23573     this.editor = config.editor;
23574     this.editorcore = config.editor.editorcore;
23575     
23576     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23577     
23578     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23579     // dont call parent... till later.
23580 }
23581 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23582      
23583     bar : true,
23584     
23585     editor : false,
23586     editorcore : false,
23587     
23588     
23589     formats : [
23590         "p" ,  
23591         "h1","h2","h3","h4","h5","h6", 
23592         "pre", "code", 
23593         "abbr", "acronym", "address", "cite", "samp", "var",
23594         'div','span'
23595     ],
23596     
23597     onRender : function(ct, position)
23598     {
23599        // Roo.log("Call onRender: " + this.xtype);
23600         
23601        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23602        Roo.log(this.el);
23603        this.el.dom.style.marginBottom = '0';
23604        var _this = this;
23605        var editorcore = this.editorcore;
23606        var editor= this.editor;
23607        
23608        var children = [];
23609        var btn = function(id,cmd , toggle, handler, html){
23610        
23611             var  event = toggle ? 'toggle' : 'click';
23612        
23613             var a = {
23614                 size : 'sm',
23615                 xtype: 'Button',
23616                 xns: Roo.bootstrap,
23617                 glyphicon : id,
23618                 cmd : id || cmd,
23619                 enableToggle:toggle !== false,
23620                 html : html || '',
23621                 pressed : toggle ? false : null,
23622                 listeners : {}
23623             };
23624             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23625                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23626             };
23627             children.push(a);
23628             return a;
23629        }
23630        
23631     //    var cb_box = function...
23632         
23633         var style = {
23634                 xtype: 'Button',
23635                 size : 'sm',
23636                 xns: Roo.bootstrap,
23637                 glyphicon : 'font',
23638                 //html : 'submit'
23639                 menu : {
23640                     xtype: 'Menu',
23641                     xns: Roo.bootstrap,
23642                     items:  []
23643                 }
23644         };
23645         Roo.each(this.formats, function(f) {
23646             style.menu.items.push({
23647                 xtype :'MenuItem',
23648                 xns: Roo.bootstrap,
23649                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23650                 tagname : f,
23651                 listeners : {
23652                     click : function()
23653                     {
23654                         editorcore.insertTag(this.tagname);
23655                         editor.focus();
23656                     }
23657                 }
23658                 
23659             });
23660         });
23661         children.push(style);   
23662         
23663         btn('bold',false,true);
23664         btn('italic',false,true);
23665         btn('align-left', 'justifyleft',true);
23666         btn('align-center', 'justifycenter',true);
23667         btn('align-right' , 'justifyright',true);
23668         btn('link', false, false, function(btn) {
23669             //Roo.log("create link?");
23670             var url = prompt(this.createLinkText, this.defaultLinkValue);
23671             if(url && url != 'http:/'+'/'){
23672                 this.editorcore.relayCmd('createlink', url);
23673             }
23674         }),
23675         btn('list','insertunorderedlist',true);
23676         btn('pencil', false,true, function(btn){
23677                 Roo.log(this);
23678                 this.toggleSourceEdit(btn.pressed);
23679         });
23680         
23681         if (this.editor.btns.length > 0) {
23682             for (var i = 0; i<this.editor.btns.length; i++) {
23683                 children.push(this.editor.btns[i]);
23684             }
23685         }
23686         
23687         /*
23688         var cog = {
23689                 xtype: 'Button',
23690                 size : 'sm',
23691                 xns: Roo.bootstrap,
23692                 glyphicon : 'cog',
23693                 //html : 'submit'
23694                 menu : {
23695                     xtype: 'Menu',
23696                     xns: Roo.bootstrap,
23697                     items:  []
23698                 }
23699         };
23700         
23701         cog.menu.items.push({
23702             xtype :'MenuItem',
23703             xns: Roo.bootstrap,
23704             html : Clean styles,
23705             tagname : f,
23706             listeners : {
23707                 click : function()
23708                 {
23709                     editorcore.insertTag(this.tagname);
23710                     editor.focus();
23711                 }
23712             }
23713             
23714         });
23715        */
23716         
23717          
23718        this.xtype = 'NavSimplebar';
23719         
23720         for(var i=0;i< children.length;i++) {
23721             
23722             this.buttons.add(this.addxtypeChild(children[i]));
23723             
23724         }
23725         
23726         editor.on('editorevent', this.updateToolbar, this);
23727     },
23728     onBtnClick : function(id)
23729     {
23730        this.editorcore.relayCmd(id);
23731        this.editorcore.focus();
23732     },
23733     
23734     /**
23735      * Protected method that will not generally be called directly. It triggers
23736      * a toolbar update by reading the markup state of the current selection in the editor.
23737      */
23738     updateToolbar: function(){
23739
23740         if(!this.editorcore.activated){
23741             this.editor.onFirstFocus(); // is this neeed?
23742             return;
23743         }
23744
23745         var btns = this.buttons; 
23746         var doc = this.editorcore.doc;
23747         btns.get('bold').setActive(doc.queryCommandState('bold'));
23748         btns.get('italic').setActive(doc.queryCommandState('italic'));
23749         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23750         
23751         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23752         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23753         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23754         
23755         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23756         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23757          /*
23758         
23759         var ans = this.editorcore.getAllAncestors();
23760         if (this.formatCombo) {
23761             
23762             
23763             var store = this.formatCombo.store;
23764             this.formatCombo.setValue("");
23765             for (var i =0; i < ans.length;i++) {
23766                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23767                     // select it..
23768                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23769                     break;
23770                 }
23771             }
23772         }
23773         
23774         
23775         
23776         // hides menus... - so this cant be on a menu...
23777         Roo.bootstrap.MenuMgr.hideAll();
23778         */
23779         Roo.bootstrap.MenuMgr.hideAll();
23780         //this.editorsyncValue();
23781     },
23782     onFirstFocus: function() {
23783         this.buttons.each(function(item){
23784            item.enable();
23785         });
23786     },
23787     toggleSourceEdit : function(sourceEditMode){
23788         
23789           
23790         if(sourceEditMode){
23791             Roo.log("disabling buttons");
23792            this.buttons.each( function(item){
23793                 if(item.cmd != 'pencil'){
23794                     item.disable();
23795                 }
23796             });
23797           
23798         }else{
23799             Roo.log("enabling buttons");
23800             if(this.editorcore.initialized){
23801                 this.buttons.each( function(item){
23802                     item.enable();
23803                 });
23804             }
23805             
23806         }
23807         Roo.log("calling toggole on editor");
23808         // tell the editor that it's been pressed..
23809         this.editor.toggleSourceEdit(sourceEditMode);
23810        
23811     }
23812 });
23813
23814
23815
23816
23817
23818 /**
23819  * @class Roo.bootstrap.Table.AbstractSelectionModel
23820  * @extends Roo.util.Observable
23821  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23822  * implemented by descendant classes.  This class should not be directly instantiated.
23823  * @constructor
23824  */
23825 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23826     this.locked = false;
23827     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23828 };
23829
23830
23831 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23832     /** @ignore Called by the grid automatically. Do not call directly. */
23833     init : function(grid){
23834         this.grid = grid;
23835         this.initEvents();
23836     },
23837
23838     /**
23839      * Locks the selections.
23840      */
23841     lock : function(){
23842         this.locked = true;
23843     },
23844
23845     /**
23846      * Unlocks the selections.
23847      */
23848     unlock : function(){
23849         this.locked = false;
23850     },
23851
23852     /**
23853      * Returns true if the selections are locked.
23854      * @return {Boolean}
23855      */
23856     isLocked : function(){
23857         return this.locked;
23858     }
23859 });
23860 /**
23861  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23862  * @class Roo.bootstrap.Table.RowSelectionModel
23863  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23864  * It supports multiple selections and keyboard selection/navigation. 
23865  * @constructor
23866  * @param {Object} config
23867  */
23868
23869 Roo.bootstrap.Table.RowSelectionModel = function(config){
23870     Roo.apply(this, config);
23871     this.selections = new Roo.util.MixedCollection(false, function(o){
23872         return o.id;
23873     });
23874
23875     this.last = false;
23876     this.lastActive = false;
23877
23878     this.addEvents({
23879         /**
23880              * @event selectionchange
23881              * Fires when the selection changes
23882              * @param {SelectionModel} this
23883              */
23884             "selectionchange" : true,
23885         /**
23886              * @event afterselectionchange
23887              * Fires after the selection changes (eg. by key press or clicking)
23888              * @param {SelectionModel} this
23889              */
23890             "afterselectionchange" : true,
23891         /**
23892              * @event beforerowselect
23893              * Fires when a row is selected being selected, return false to cancel.
23894              * @param {SelectionModel} this
23895              * @param {Number} rowIndex The selected index
23896              * @param {Boolean} keepExisting False if other selections will be cleared
23897              */
23898             "beforerowselect" : true,
23899         /**
23900              * @event rowselect
23901              * Fires when a row is selected.
23902              * @param {SelectionModel} this
23903              * @param {Number} rowIndex The selected index
23904              * @param {Roo.data.Record} r The record
23905              */
23906             "rowselect" : true,
23907         /**
23908              * @event rowdeselect
23909              * Fires when a row is deselected.
23910              * @param {SelectionModel} this
23911              * @param {Number} rowIndex The selected index
23912              */
23913         "rowdeselect" : true
23914     });
23915     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23916     this.locked = false;
23917  };
23918
23919 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
23920     /**
23921      * @cfg {Boolean} singleSelect
23922      * True to allow selection of only one row at a time (defaults to false)
23923      */
23924     singleSelect : false,
23925
23926     // private
23927     initEvents : function()
23928     {
23929
23930         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23931         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
23932         //}else{ // allow click to work like normal
23933          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
23934         //}
23935         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23936         this.grid.on("rowclick", this.handleMouseDown, this);
23937         
23938         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23939             "up" : function(e){
23940                 if(!e.shiftKey){
23941                     this.selectPrevious(e.shiftKey);
23942                 }else if(this.last !== false && this.lastActive !== false){
23943                     var last = this.last;
23944                     this.selectRange(this.last,  this.lastActive-1);
23945                     this.grid.getView().focusRow(this.lastActive);
23946                     if(last !== false){
23947                         this.last = last;
23948                     }
23949                 }else{
23950                     this.selectFirstRow();
23951                 }
23952                 this.fireEvent("afterselectionchange", this);
23953             },
23954             "down" : function(e){
23955                 if(!e.shiftKey){
23956                     this.selectNext(e.shiftKey);
23957                 }else if(this.last !== false && this.lastActive !== false){
23958                     var last = this.last;
23959                     this.selectRange(this.last,  this.lastActive+1);
23960                     this.grid.getView().focusRow(this.lastActive);
23961                     if(last !== false){
23962                         this.last = last;
23963                     }
23964                 }else{
23965                     this.selectFirstRow();
23966                 }
23967                 this.fireEvent("afterselectionchange", this);
23968             },
23969             scope: this
23970         });
23971         this.grid.store.on('load', function(){
23972             this.selections.clear();
23973         },this);
23974         /*
23975         var view = this.grid.view;
23976         view.on("refresh", this.onRefresh, this);
23977         view.on("rowupdated", this.onRowUpdated, this);
23978         view.on("rowremoved", this.onRemove, this);
23979         */
23980     },
23981
23982     // private
23983     onRefresh : function()
23984     {
23985         var ds = this.grid.store, i, v = this.grid.view;
23986         var s = this.selections;
23987         s.each(function(r){
23988             if((i = ds.indexOfId(r.id)) != -1){
23989                 v.onRowSelect(i);
23990             }else{
23991                 s.remove(r);
23992             }
23993         });
23994     },
23995
23996     // private
23997     onRemove : function(v, index, r){
23998         this.selections.remove(r);
23999     },
24000
24001     // private
24002     onRowUpdated : function(v, index, r){
24003         if(this.isSelected(r)){
24004             v.onRowSelect(index);
24005         }
24006     },
24007
24008     /**
24009      * Select records.
24010      * @param {Array} records The records to select
24011      * @param {Boolean} keepExisting (optional) True to keep existing selections
24012      */
24013     selectRecords : function(records, keepExisting)
24014     {
24015         if(!keepExisting){
24016             this.clearSelections();
24017         }
24018             var ds = this.grid.store;
24019         for(var i = 0, len = records.length; i < len; i++){
24020             this.selectRow(ds.indexOf(records[i]), true);
24021         }
24022     },
24023
24024     /**
24025      * Gets the number of selected rows.
24026      * @return {Number}
24027      */
24028     getCount : function(){
24029         return this.selections.length;
24030     },
24031
24032     /**
24033      * Selects the first row in the grid.
24034      */
24035     selectFirstRow : function(){
24036         this.selectRow(0);
24037     },
24038
24039     /**
24040      * Select the last row.
24041      * @param {Boolean} keepExisting (optional) True to keep existing selections
24042      */
24043     selectLastRow : function(keepExisting){
24044         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24045         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24046     },
24047
24048     /**
24049      * Selects the row immediately following the last selected row.
24050      * @param {Boolean} keepExisting (optional) True to keep existing selections
24051      */
24052     selectNext : function(keepExisting)
24053     {
24054             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24055             this.selectRow(this.last+1, keepExisting);
24056             this.grid.getView().focusRow(this.last);
24057         }
24058     },
24059
24060     /**
24061      * Selects the row that precedes the last selected row.
24062      * @param {Boolean} keepExisting (optional) True to keep existing selections
24063      */
24064     selectPrevious : function(keepExisting){
24065         if(this.last){
24066             this.selectRow(this.last-1, keepExisting);
24067             this.grid.getView().focusRow(this.last);
24068         }
24069     },
24070
24071     /**
24072      * Returns the selected records
24073      * @return {Array} Array of selected records
24074      */
24075     getSelections : function(){
24076         return [].concat(this.selections.items);
24077     },
24078
24079     /**
24080      * Returns the first selected record.
24081      * @return {Record}
24082      */
24083     getSelected : function(){
24084         return this.selections.itemAt(0);
24085     },
24086
24087
24088     /**
24089      * Clears all selections.
24090      */
24091     clearSelections : function(fast)
24092     {
24093         if(this.locked) {
24094             return;
24095         }
24096         if(fast !== true){
24097                 var ds = this.grid.store;
24098             var s = this.selections;
24099             s.each(function(r){
24100                 this.deselectRow(ds.indexOfId(r.id));
24101             }, this);
24102             s.clear();
24103         }else{
24104             this.selections.clear();
24105         }
24106         this.last = false;
24107     },
24108
24109
24110     /**
24111      * Selects all rows.
24112      */
24113     selectAll : function(){
24114         if(this.locked) {
24115             return;
24116         }
24117         this.selections.clear();
24118         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24119             this.selectRow(i, true);
24120         }
24121     },
24122
24123     /**
24124      * Returns True if there is a selection.
24125      * @return {Boolean}
24126      */
24127     hasSelection : function(){
24128         return this.selections.length > 0;
24129     },
24130
24131     /**
24132      * Returns True if the specified row is selected.
24133      * @param {Number/Record} record The record or index of the record to check
24134      * @return {Boolean}
24135      */
24136     isSelected : function(index){
24137             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24138         return (r && this.selections.key(r.id) ? true : false);
24139     },
24140
24141     /**
24142      * Returns True if the specified record id is selected.
24143      * @param {String} id The id of record to check
24144      * @return {Boolean}
24145      */
24146     isIdSelected : function(id){
24147         return (this.selections.key(id) ? true : false);
24148     },
24149
24150
24151     // private
24152     handleMouseDBClick : function(e, t){
24153         
24154     },
24155     // private
24156     handleMouseDown : function(e, t)
24157     {
24158             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24159         if(this.isLocked() || rowIndex < 0 ){
24160             return;
24161         };
24162         if(e.shiftKey && this.last !== false){
24163             var last = this.last;
24164             this.selectRange(last, rowIndex, e.ctrlKey);
24165             this.last = last; // reset the last
24166             t.focus();
24167     
24168         }else{
24169             var isSelected = this.isSelected(rowIndex);
24170             //Roo.log("select row:" + rowIndex);
24171             if(isSelected){
24172                 this.deselectRow(rowIndex);
24173             } else {
24174                         this.selectRow(rowIndex, true);
24175             }
24176     
24177             /*
24178                 if(e.button !== 0 && isSelected){
24179                 alert('rowIndex 2: ' + rowIndex);
24180                     view.focusRow(rowIndex);
24181                 }else if(e.ctrlKey && isSelected){
24182                     this.deselectRow(rowIndex);
24183                 }else if(!isSelected){
24184                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24185                     view.focusRow(rowIndex);
24186                 }
24187             */
24188         }
24189         this.fireEvent("afterselectionchange", this);
24190     },
24191     // private
24192     handleDragableRowClick :  function(grid, rowIndex, e) 
24193     {
24194         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24195             this.selectRow(rowIndex, false);
24196             grid.view.focusRow(rowIndex);
24197              this.fireEvent("afterselectionchange", this);
24198         }
24199     },
24200     
24201     /**
24202      * Selects multiple rows.
24203      * @param {Array} rows Array of the indexes of the row to select
24204      * @param {Boolean} keepExisting (optional) True to keep existing selections
24205      */
24206     selectRows : function(rows, keepExisting){
24207         if(!keepExisting){
24208             this.clearSelections();
24209         }
24210         for(var i = 0, len = rows.length; i < len; i++){
24211             this.selectRow(rows[i], true);
24212         }
24213     },
24214
24215     /**
24216      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24217      * @param {Number} startRow The index of the first row in the range
24218      * @param {Number} endRow The index of the last row in the range
24219      * @param {Boolean} keepExisting (optional) True to retain existing selections
24220      */
24221     selectRange : function(startRow, endRow, keepExisting){
24222         if(this.locked) {
24223             return;
24224         }
24225         if(!keepExisting){
24226             this.clearSelections();
24227         }
24228         if(startRow <= endRow){
24229             for(var i = startRow; i <= endRow; i++){
24230                 this.selectRow(i, true);
24231             }
24232         }else{
24233             for(var i = startRow; i >= endRow; i--){
24234                 this.selectRow(i, true);
24235             }
24236         }
24237     },
24238
24239     /**
24240      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24241      * @param {Number} startRow The index of the first row in the range
24242      * @param {Number} endRow The index of the last row in the range
24243      */
24244     deselectRange : function(startRow, endRow, preventViewNotify){
24245         if(this.locked) {
24246             return;
24247         }
24248         for(var i = startRow; i <= endRow; i++){
24249             this.deselectRow(i, preventViewNotify);
24250         }
24251     },
24252
24253     /**
24254      * Selects a row.
24255      * @param {Number} row The index of the row to select
24256      * @param {Boolean} keepExisting (optional) True to keep existing selections
24257      */
24258     selectRow : function(index, keepExisting, preventViewNotify)
24259     {
24260             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24261             return;
24262         }
24263         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24264             if(!keepExisting || this.singleSelect){
24265                 this.clearSelections();
24266             }
24267             
24268             var r = this.grid.store.getAt(index);
24269             //console.log('selectRow - record id :' + r.id);
24270             
24271             this.selections.add(r);
24272             this.last = this.lastActive = index;
24273             if(!preventViewNotify){
24274                 var proxy = new Roo.Element(
24275                                 this.grid.getRowDom(index)
24276                 );
24277                 proxy.addClass('bg-info info');
24278             }
24279             this.fireEvent("rowselect", this, index, r);
24280             this.fireEvent("selectionchange", this);
24281         }
24282     },
24283
24284     /**
24285      * Deselects a row.
24286      * @param {Number} row The index of the row to deselect
24287      */
24288     deselectRow : function(index, preventViewNotify)
24289     {
24290         if(this.locked) {
24291             return;
24292         }
24293         if(this.last == index){
24294             this.last = false;
24295         }
24296         if(this.lastActive == index){
24297             this.lastActive = false;
24298         }
24299         
24300         var r = this.grid.store.getAt(index);
24301         if (!r) {
24302             return;
24303         }
24304         
24305         this.selections.remove(r);
24306         //.console.log('deselectRow - record id :' + r.id);
24307         if(!preventViewNotify){
24308         
24309             var proxy = new Roo.Element(
24310                 this.grid.getRowDom(index)
24311             );
24312             proxy.removeClass('bg-info info');
24313         }
24314         this.fireEvent("rowdeselect", this, index);
24315         this.fireEvent("selectionchange", this);
24316     },
24317
24318     // private
24319     restoreLast : function(){
24320         if(this._last){
24321             this.last = this._last;
24322         }
24323     },
24324
24325     // private
24326     acceptsNav : function(row, col, cm){
24327         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24328     },
24329
24330     // private
24331     onEditorKey : function(field, e){
24332         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24333         if(k == e.TAB){
24334             e.stopEvent();
24335             ed.completeEdit();
24336             if(e.shiftKey){
24337                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24338             }else{
24339                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24340             }
24341         }else if(k == e.ENTER && !e.ctrlKey){
24342             e.stopEvent();
24343             ed.completeEdit();
24344             if(e.shiftKey){
24345                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24346             }else{
24347                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24348             }
24349         }else if(k == e.ESC){
24350             ed.cancelEdit();
24351         }
24352         if(newCell){
24353             g.startEditing(newCell[0], newCell[1]);
24354         }
24355     }
24356 });
24357 /*
24358  * Based on:
24359  * Ext JS Library 1.1.1
24360  * Copyright(c) 2006-2007, Ext JS, LLC.
24361  *
24362  * Originally Released Under LGPL - original licence link has changed is not relivant.
24363  *
24364  * Fork - LGPL
24365  * <script type="text/javascript">
24366  */
24367  
24368 /**
24369  * @class Roo.bootstrap.PagingToolbar
24370  * @extends Roo.bootstrap.NavSimplebar
24371  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24372  * @constructor
24373  * Create a new PagingToolbar
24374  * @param {Object} config The config object
24375  * @param {Roo.data.Store} store
24376  */
24377 Roo.bootstrap.PagingToolbar = function(config)
24378 {
24379     // old args format still supported... - xtype is prefered..
24380         // created from xtype...
24381     
24382     this.ds = config.dataSource;
24383     
24384     if (config.store && !this.ds) {
24385         this.store= Roo.factory(config.store, Roo.data);
24386         this.ds = this.store;
24387         this.ds.xmodule = this.xmodule || false;
24388     }
24389     
24390     this.toolbarItems = [];
24391     if (config.items) {
24392         this.toolbarItems = config.items;
24393     }
24394     
24395     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24396     
24397     this.cursor = 0;
24398     
24399     if (this.ds) { 
24400         this.bind(this.ds);
24401     }
24402     
24403     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24404     
24405 };
24406
24407 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24408     /**
24409      * @cfg {Roo.data.Store} dataSource
24410      * The underlying data store providing the paged data
24411      */
24412     /**
24413      * @cfg {String/HTMLElement/Element} container
24414      * container The id or element that will contain the toolbar
24415      */
24416     /**
24417      * @cfg {Boolean} displayInfo
24418      * True to display the displayMsg (defaults to false)
24419      */
24420     /**
24421      * @cfg {Number} pageSize
24422      * The number of records to display per page (defaults to 20)
24423      */
24424     pageSize: 20,
24425     /**
24426      * @cfg {String} displayMsg
24427      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24428      */
24429     displayMsg : 'Displaying {0} - {1} of {2}',
24430     /**
24431      * @cfg {String} emptyMsg
24432      * The message to display when no records are found (defaults to "No data to display")
24433      */
24434     emptyMsg : 'No data to display',
24435     /**
24436      * Customizable piece of the default paging text (defaults to "Page")
24437      * @type String
24438      */
24439     beforePageText : "Page",
24440     /**
24441      * Customizable piece of the default paging text (defaults to "of %0")
24442      * @type String
24443      */
24444     afterPageText : "of {0}",
24445     /**
24446      * Customizable piece of the default paging text (defaults to "First Page")
24447      * @type String
24448      */
24449     firstText : "First Page",
24450     /**
24451      * Customizable piece of the default paging text (defaults to "Previous Page")
24452      * @type String
24453      */
24454     prevText : "Previous Page",
24455     /**
24456      * Customizable piece of the default paging text (defaults to "Next Page")
24457      * @type String
24458      */
24459     nextText : "Next Page",
24460     /**
24461      * Customizable piece of the default paging text (defaults to "Last Page")
24462      * @type String
24463      */
24464     lastText : "Last Page",
24465     /**
24466      * Customizable piece of the default paging text (defaults to "Refresh")
24467      * @type String
24468      */
24469     refreshText : "Refresh",
24470
24471     buttons : false,
24472     // private
24473     onRender : function(ct, position) 
24474     {
24475         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24476         this.navgroup.parentId = this.id;
24477         this.navgroup.onRender(this.el, null);
24478         // add the buttons to the navgroup
24479         
24480         if(this.displayInfo){
24481             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24482             this.displayEl = this.el.select('.x-paging-info', true).first();
24483 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24484 //            this.displayEl = navel.el.select('span',true).first();
24485         }
24486         
24487         var _this = this;
24488         
24489         if(this.buttons){
24490             Roo.each(_this.buttons, function(e){ // this might need to use render????
24491                Roo.factory(e).onRender(_this.el, null);
24492             });
24493         }
24494             
24495         Roo.each(_this.toolbarItems, function(e) {
24496             _this.navgroup.addItem(e);
24497         });
24498         
24499         
24500         this.first = this.navgroup.addItem({
24501             tooltip: this.firstText,
24502             cls: "prev",
24503             icon : 'fa fa-backward',
24504             disabled: true,
24505             preventDefault: true,
24506             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24507         });
24508         
24509         this.prev =  this.navgroup.addItem({
24510             tooltip: this.prevText,
24511             cls: "prev",
24512             icon : 'fa fa-step-backward',
24513             disabled: true,
24514             preventDefault: true,
24515             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24516         });
24517     //this.addSeparator();
24518         
24519         
24520         var field = this.navgroup.addItem( {
24521             tagtype : 'span',
24522             cls : 'x-paging-position',
24523             
24524             html : this.beforePageText  +
24525                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24526                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24527          } ); //?? escaped?
24528         
24529         this.field = field.el.select('input', true).first();
24530         this.field.on("keydown", this.onPagingKeydown, this);
24531         this.field.on("focus", function(){this.dom.select();});
24532     
24533     
24534         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24535         //this.field.setHeight(18);
24536         //this.addSeparator();
24537         this.next = this.navgroup.addItem({
24538             tooltip: this.nextText,
24539             cls: "next",
24540             html : ' <i class="fa fa-step-forward">',
24541             disabled: true,
24542             preventDefault: true,
24543             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24544         });
24545         this.last = this.navgroup.addItem({
24546             tooltip: this.lastText,
24547             icon : 'fa fa-forward',
24548             cls: "next",
24549             disabled: true,
24550             preventDefault: true,
24551             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24552         });
24553     //this.addSeparator();
24554         this.loading = this.navgroup.addItem({
24555             tooltip: this.refreshText,
24556             icon: 'fa fa-refresh',
24557             preventDefault: true,
24558             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24559         });
24560         
24561     },
24562
24563     // private
24564     updateInfo : function(){
24565         if(this.displayEl){
24566             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24567             var msg = count == 0 ?
24568                 this.emptyMsg :
24569                 String.format(
24570                     this.displayMsg,
24571                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24572                 );
24573             this.displayEl.update(msg);
24574         }
24575     },
24576
24577     // private
24578     onLoad : function(ds, r, o)
24579     {
24580         this.cursor = o.params.start ? o.params.start : 0;
24581         
24582         var d = this.getPageData(),
24583             ap = d.activePage,
24584             ps = d.pages;
24585         
24586         
24587         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24588         this.field.dom.value = ap;
24589         this.first.setDisabled(ap == 1);
24590         this.prev.setDisabled(ap == 1);
24591         this.next.setDisabled(ap == ps);
24592         this.last.setDisabled(ap == ps);
24593         this.loading.enable();
24594         this.updateInfo();
24595     },
24596
24597     // private
24598     getPageData : function(){
24599         var total = this.ds.getTotalCount();
24600         return {
24601             total : total,
24602             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24603             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24604         };
24605     },
24606
24607     // private
24608     onLoadError : function(){
24609         this.loading.enable();
24610     },
24611
24612     // private
24613     onPagingKeydown : function(e){
24614         var k = e.getKey();
24615         var d = this.getPageData();
24616         if(k == e.RETURN){
24617             var v = this.field.dom.value, pageNum;
24618             if(!v || isNaN(pageNum = parseInt(v, 10))){
24619                 this.field.dom.value = d.activePage;
24620                 return;
24621             }
24622             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24623             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24624             e.stopEvent();
24625         }
24626         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))
24627         {
24628           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24629           this.field.dom.value = pageNum;
24630           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24631           e.stopEvent();
24632         }
24633         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24634         {
24635           var v = this.field.dom.value, pageNum; 
24636           var increment = (e.shiftKey) ? 10 : 1;
24637           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24638                 increment *= -1;
24639           }
24640           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24641             this.field.dom.value = d.activePage;
24642             return;
24643           }
24644           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24645           {
24646             this.field.dom.value = parseInt(v, 10) + increment;
24647             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24648             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24649           }
24650           e.stopEvent();
24651         }
24652     },
24653
24654     // private
24655     beforeLoad : function(){
24656         if(this.loading){
24657             this.loading.disable();
24658         }
24659     },
24660
24661     // private
24662     onClick : function(which){
24663         
24664         var ds = this.ds;
24665         if (!ds) {
24666             return;
24667         }
24668         
24669         switch(which){
24670             case "first":
24671                 ds.load({params:{start: 0, limit: this.pageSize}});
24672             break;
24673             case "prev":
24674                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24675             break;
24676             case "next":
24677                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24678             break;
24679             case "last":
24680                 var total = ds.getTotalCount();
24681                 var extra = total % this.pageSize;
24682                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24683                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24684             break;
24685             case "refresh":
24686                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24687             break;
24688         }
24689     },
24690
24691     /**
24692      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24693      * @param {Roo.data.Store} store The data store to unbind
24694      */
24695     unbind : function(ds){
24696         ds.un("beforeload", this.beforeLoad, this);
24697         ds.un("load", this.onLoad, this);
24698         ds.un("loadexception", this.onLoadError, this);
24699         ds.un("remove", this.updateInfo, this);
24700         ds.un("add", this.updateInfo, this);
24701         this.ds = undefined;
24702     },
24703
24704     /**
24705      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24706      * @param {Roo.data.Store} store The data store to bind
24707      */
24708     bind : function(ds){
24709         ds.on("beforeload", this.beforeLoad, this);
24710         ds.on("load", this.onLoad, this);
24711         ds.on("loadexception", this.onLoadError, this);
24712         ds.on("remove", this.updateInfo, this);
24713         ds.on("add", this.updateInfo, this);
24714         this.ds = ds;
24715     }
24716 });/*
24717  * - LGPL
24718  *
24719  * element
24720  * 
24721  */
24722
24723 /**
24724  * @class Roo.bootstrap.MessageBar
24725  * @extends Roo.bootstrap.Component
24726  * Bootstrap MessageBar class
24727  * @cfg {String} html contents of the MessageBar
24728  * @cfg {String} weight (info | success | warning | danger) default info
24729  * @cfg {String} beforeClass insert the bar before the given class
24730  * @cfg {Boolean} closable (true | false) default false
24731  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24732  * 
24733  * @constructor
24734  * Create a new Element
24735  * @param {Object} config The config object
24736  */
24737
24738 Roo.bootstrap.MessageBar = function(config){
24739     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24740 };
24741
24742 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24743     
24744     html: '',
24745     weight: 'info',
24746     closable: false,
24747     fixed: false,
24748     beforeClass: 'bootstrap-sticky-wrap',
24749     
24750     getAutoCreate : function(){
24751         
24752         var cfg = {
24753             tag: 'div',
24754             cls: 'alert alert-dismissable alert-' + this.weight,
24755             cn: [
24756                 {
24757                     tag: 'span',
24758                     cls: 'message',
24759                     html: this.html || ''
24760                 }
24761             ]
24762         };
24763         
24764         if(this.fixed){
24765             cfg.cls += ' alert-messages-fixed';
24766         }
24767         
24768         if(this.closable){
24769             cfg.cn.push({
24770                 tag: 'button',
24771                 cls: 'close',
24772                 html: 'x'
24773             });
24774         }
24775         
24776         return cfg;
24777     },
24778     
24779     onRender : function(ct, position)
24780     {
24781         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24782         
24783         if(!this.el){
24784             var cfg = Roo.apply({},  this.getAutoCreate());
24785             cfg.id = Roo.id();
24786             
24787             if (this.cls) {
24788                 cfg.cls += ' ' + this.cls;
24789             }
24790             if (this.style) {
24791                 cfg.style = this.style;
24792             }
24793             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24794             
24795             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24796         }
24797         
24798         this.el.select('>button.close').on('click', this.hide, this);
24799         
24800     },
24801     
24802     show : function()
24803     {
24804         if (!this.rendered) {
24805             this.render();
24806         }
24807         
24808         this.el.show();
24809         
24810         this.fireEvent('show', this);
24811         
24812     },
24813     
24814     hide : function()
24815     {
24816         if (!this.rendered) {
24817             this.render();
24818         }
24819         
24820         this.el.hide();
24821         
24822         this.fireEvent('hide', this);
24823     },
24824     
24825     update : function()
24826     {
24827 //        var e = this.el.dom.firstChild;
24828 //        
24829 //        if(this.closable){
24830 //            e = e.nextSibling;
24831 //        }
24832 //        
24833 //        e.data = this.html || '';
24834
24835         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24836     }
24837    
24838 });
24839
24840  
24841
24842      /*
24843  * - LGPL
24844  *
24845  * Graph
24846  * 
24847  */
24848
24849
24850 /**
24851  * @class Roo.bootstrap.Graph
24852  * @extends Roo.bootstrap.Component
24853  * Bootstrap Graph class
24854 > Prameters
24855  -sm {number} sm 4
24856  -md {number} md 5
24857  @cfg {String} graphtype  bar | vbar | pie
24858  @cfg {number} g_x coodinator | centre x (pie)
24859  @cfg {number} g_y coodinator | centre y (pie)
24860  @cfg {number} g_r radius (pie)
24861  @cfg {number} g_height height of the chart (respected by all elements in the set)
24862  @cfg {number} g_width width of the chart (respected by all elements in the set)
24863  @cfg {Object} title The title of the chart
24864     
24865  -{Array}  values
24866  -opts (object) options for the chart 
24867      o {
24868      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24869      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24870      o vgutter (number)
24871      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.
24872      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24873      o to
24874      o stretch (boolean)
24875      o }
24876  -opts (object) options for the pie
24877      o{
24878      o cut
24879      o startAngle (number)
24880      o endAngle (number)
24881      } 
24882  *
24883  * @constructor
24884  * Create a new Input
24885  * @param {Object} config The config object
24886  */
24887
24888 Roo.bootstrap.Graph = function(config){
24889     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24890     
24891     this.addEvents({
24892         // img events
24893         /**
24894          * @event click
24895          * The img click event for the img.
24896          * @param {Roo.EventObject} e
24897          */
24898         "click" : true
24899     });
24900 };
24901
24902 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24903     
24904     sm: 4,
24905     md: 5,
24906     graphtype: 'bar',
24907     g_height: 250,
24908     g_width: 400,
24909     g_x: 50,
24910     g_y: 50,
24911     g_r: 30,
24912     opts:{
24913         //g_colors: this.colors,
24914         g_type: 'soft',
24915         g_gutter: '20%'
24916
24917     },
24918     title : false,
24919
24920     getAutoCreate : function(){
24921         
24922         var cfg = {
24923             tag: 'div',
24924             html : null
24925         };
24926         
24927         
24928         return  cfg;
24929     },
24930
24931     onRender : function(ct,position){
24932         
24933         
24934         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24935         
24936         if (typeof(Raphael) == 'undefined') {
24937             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24938             return;
24939         }
24940         
24941         this.raphael = Raphael(this.el.dom);
24942         
24943                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24944                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24945                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24946                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24947                 /*
24948                 r.text(160, 10, "Single Series Chart").attr(txtattr);
24949                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24950                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24951                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24952                 
24953                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24954                 r.barchart(330, 10, 300, 220, data1);
24955                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
24956                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
24957                 */
24958                 
24959                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24960                 // r.barchart(30, 30, 560, 250,  xdata, {
24961                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
24962                 //     axis : "0 0 1 1",
24963                 //     axisxlabels :  xdata
24964                 //     //yvalues : cols,
24965                    
24966                 // });
24967 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24968 //        
24969 //        this.load(null,xdata,{
24970 //                axis : "0 0 1 1",
24971 //                axisxlabels :  xdata
24972 //                });
24973
24974     },
24975
24976     load : function(graphtype,xdata,opts)
24977     {
24978         this.raphael.clear();
24979         if(!graphtype) {
24980             graphtype = this.graphtype;
24981         }
24982         if(!opts){
24983             opts = this.opts;
24984         }
24985         var r = this.raphael,
24986             fin = function () {
24987                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
24988             },
24989             fout = function () {
24990                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
24991             },
24992             pfin = function() {
24993                 this.sector.stop();
24994                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
24995
24996                 if (this.label) {
24997                     this.label[0].stop();
24998                     this.label[0].attr({ r: 7.5 });
24999                     this.label[1].attr({ "font-weight": 800 });
25000                 }
25001             },
25002             pfout = function() {
25003                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25004
25005                 if (this.label) {
25006                     this.label[0].animate({ r: 5 }, 500, "bounce");
25007                     this.label[1].attr({ "font-weight": 400 });
25008                 }
25009             };
25010
25011         switch(graphtype){
25012             case 'bar':
25013                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25014                 break;
25015             case 'hbar':
25016                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25017                 break;
25018             case 'pie':
25019 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25020 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25021 //            
25022                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25023                 
25024                 break;
25025
25026         }
25027         
25028         if(this.title){
25029             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25030         }
25031         
25032     },
25033     
25034     setTitle: function(o)
25035     {
25036         this.title = o;
25037     },
25038     
25039     initEvents: function() {
25040         
25041         if(!this.href){
25042             this.el.on('click', this.onClick, this);
25043         }
25044     },
25045     
25046     onClick : function(e)
25047     {
25048         Roo.log('img onclick');
25049         this.fireEvent('click', this, e);
25050     }
25051    
25052 });
25053
25054  
25055 /*
25056  * - LGPL
25057  *
25058  * numberBox
25059  * 
25060  */
25061 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25062
25063 /**
25064  * @class Roo.bootstrap.dash.NumberBox
25065  * @extends Roo.bootstrap.Component
25066  * Bootstrap NumberBox class
25067  * @cfg {String} headline Box headline
25068  * @cfg {String} content Box content
25069  * @cfg {String} icon Box icon
25070  * @cfg {String} footer Footer text
25071  * @cfg {String} fhref Footer href
25072  * 
25073  * @constructor
25074  * Create a new NumberBox
25075  * @param {Object} config The config object
25076  */
25077
25078
25079 Roo.bootstrap.dash.NumberBox = function(config){
25080     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25081     
25082 };
25083
25084 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25085     
25086     headline : '',
25087     content : '',
25088     icon : '',
25089     footer : '',
25090     fhref : '',
25091     ficon : '',
25092     
25093     getAutoCreate : function(){
25094         
25095         var cfg = {
25096             tag : 'div',
25097             cls : 'small-box ',
25098             cn : [
25099                 {
25100                     tag : 'div',
25101                     cls : 'inner',
25102                     cn :[
25103                         {
25104                             tag : 'h3',
25105                             cls : 'roo-headline',
25106                             html : this.headline
25107                         },
25108                         {
25109                             tag : 'p',
25110                             cls : 'roo-content',
25111                             html : this.content
25112                         }
25113                     ]
25114                 }
25115             ]
25116         };
25117         
25118         if(this.icon){
25119             cfg.cn.push({
25120                 tag : 'div',
25121                 cls : 'icon',
25122                 cn :[
25123                     {
25124                         tag : 'i',
25125                         cls : 'ion ' + this.icon
25126                     }
25127                 ]
25128             });
25129         }
25130         
25131         if(this.footer){
25132             var footer = {
25133                 tag : 'a',
25134                 cls : 'small-box-footer',
25135                 href : this.fhref || '#',
25136                 html : this.footer
25137             };
25138             
25139             cfg.cn.push(footer);
25140             
25141         }
25142         
25143         return  cfg;
25144     },
25145
25146     onRender : function(ct,position){
25147         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25148
25149
25150        
25151                 
25152     },
25153
25154     setHeadline: function (value)
25155     {
25156         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25157     },
25158     
25159     setFooter: function (value, href)
25160     {
25161         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25162         
25163         if(href){
25164             this.el.select('a.small-box-footer',true).first().attr('href', href);
25165         }
25166         
25167     },
25168
25169     setContent: function (value)
25170     {
25171         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25172     },
25173
25174     initEvents: function() 
25175     {   
25176         
25177     }
25178     
25179 });
25180
25181  
25182 /*
25183  * - LGPL
25184  *
25185  * TabBox
25186  * 
25187  */
25188 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25189
25190 /**
25191  * @class Roo.bootstrap.dash.TabBox
25192  * @extends Roo.bootstrap.Component
25193  * Bootstrap TabBox class
25194  * @cfg {String} title Title of the TabBox
25195  * @cfg {String} icon Icon of the TabBox
25196  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25197  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25198  * 
25199  * @constructor
25200  * Create a new TabBox
25201  * @param {Object} config The config object
25202  */
25203
25204
25205 Roo.bootstrap.dash.TabBox = function(config){
25206     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25207     this.addEvents({
25208         // raw events
25209         /**
25210          * @event addpane
25211          * When a pane is added
25212          * @param {Roo.bootstrap.dash.TabPane} pane
25213          */
25214         "addpane" : true,
25215         /**
25216          * @event activatepane
25217          * When a pane is activated
25218          * @param {Roo.bootstrap.dash.TabPane} pane
25219          */
25220         "activatepane" : true
25221         
25222          
25223     });
25224     
25225     this.panes = [];
25226 };
25227
25228 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25229
25230     title : '',
25231     icon : false,
25232     showtabs : true,
25233     tabScrollable : false,
25234     
25235     getChildContainer : function()
25236     {
25237         return this.el.select('.tab-content', true).first();
25238     },
25239     
25240     getAutoCreate : function(){
25241         
25242         var header = {
25243             tag: 'li',
25244             cls: 'pull-left header',
25245             html: this.title,
25246             cn : []
25247         };
25248         
25249         if(this.icon){
25250             header.cn.push({
25251                 tag: 'i',
25252                 cls: 'fa ' + this.icon
25253             });
25254         }
25255         
25256         var h = {
25257             tag: 'ul',
25258             cls: 'nav nav-tabs pull-right',
25259             cn: [
25260                 header
25261             ]
25262         };
25263         
25264         if(this.tabScrollable){
25265             h = {
25266                 tag: 'div',
25267                 cls: 'tab-header',
25268                 cn: [
25269                     {
25270                         tag: 'ul',
25271                         cls: 'nav nav-tabs pull-right',
25272                         cn: [
25273                             header
25274                         ]
25275                     }
25276                 ]
25277             };
25278         }
25279         
25280         var cfg = {
25281             tag: 'div',
25282             cls: 'nav-tabs-custom',
25283             cn: [
25284                 h,
25285                 {
25286                     tag: 'div',
25287                     cls: 'tab-content no-padding',
25288                     cn: []
25289                 }
25290             ]
25291         };
25292
25293         return  cfg;
25294     },
25295     initEvents : function()
25296     {
25297         //Roo.log('add add pane handler');
25298         this.on('addpane', this.onAddPane, this);
25299     },
25300      /**
25301      * Updates the box title
25302      * @param {String} html to set the title to.
25303      */
25304     setTitle : function(value)
25305     {
25306         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25307     },
25308     onAddPane : function(pane)
25309     {
25310         this.panes.push(pane);
25311         //Roo.log('addpane');
25312         //Roo.log(pane);
25313         // tabs are rendere left to right..
25314         if(!this.showtabs){
25315             return;
25316         }
25317         
25318         var ctr = this.el.select('.nav-tabs', true).first();
25319          
25320          
25321         var existing = ctr.select('.nav-tab',true);
25322         var qty = existing.getCount();;
25323         
25324         
25325         var tab = ctr.createChild({
25326             tag : 'li',
25327             cls : 'nav-tab' + (qty ? '' : ' active'),
25328             cn : [
25329                 {
25330                     tag : 'a',
25331                     href:'#',
25332                     html : pane.title
25333                 }
25334             ]
25335         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25336         pane.tab = tab;
25337         
25338         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25339         if (!qty) {
25340             pane.el.addClass('active');
25341         }
25342         
25343                 
25344     },
25345     onTabClick : function(ev,un,ob,pane)
25346     {
25347         //Roo.log('tab - prev default');
25348         ev.preventDefault();
25349         
25350         
25351         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25352         pane.tab.addClass('active');
25353         //Roo.log(pane.title);
25354         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25355         // technically we should have a deactivate event.. but maybe add later.
25356         // and it should not de-activate the selected tab...
25357         this.fireEvent('activatepane', pane);
25358         pane.el.addClass('active');
25359         pane.fireEvent('activate');
25360         
25361         
25362     },
25363     
25364     getActivePane : function()
25365     {
25366         var r = false;
25367         Roo.each(this.panes, function(p) {
25368             if(p.el.hasClass('active')){
25369                 r = p;
25370                 return false;
25371             }
25372             
25373             return;
25374         });
25375         
25376         return r;
25377     }
25378     
25379     
25380 });
25381
25382  
25383 /*
25384  * - LGPL
25385  *
25386  * Tab pane
25387  * 
25388  */
25389 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25390 /**
25391  * @class Roo.bootstrap.TabPane
25392  * @extends Roo.bootstrap.Component
25393  * Bootstrap TabPane class
25394  * @cfg {Boolean} active (false | true) Default false
25395  * @cfg {String} title title of panel
25396
25397  * 
25398  * @constructor
25399  * Create a new TabPane
25400  * @param {Object} config The config object
25401  */
25402
25403 Roo.bootstrap.dash.TabPane = function(config){
25404     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25405     
25406     this.addEvents({
25407         // raw events
25408         /**
25409          * @event activate
25410          * When a pane is activated
25411          * @param {Roo.bootstrap.dash.TabPane} pane
25412          */
25413         "activate" : true
25414          
25415     });
25416 };
25417
25418 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25419     
25420     active : false,
25421     title : '',
25422     
25423     // the tabBox that this is attached to.
25424     tab : false,
25425      
25426     getAutoCreate : function() 
25427     {
25428         var cfg = {
25429             tag: 'div',
25430             cls: 'tab-pane'
25431         };
25432         
25433         if(this.active){
25434             cfg.cls += ' active';
25435         }
25436         
25437         return cfg;
25438     },
25439     initEvents  : function()
25440     {
25441         //Roo.log('trigger add pane handler');
25442         this.parent().fireEvent('addpane', this)
25443     },
25444     
25445      /**
25446      * Updates the tab title 
25447      * @param {String} html to set the title to.
25448      */
25449     setTitle: function(str)
25450     {
25451         if (!this.tab) {
25452             return;
25453         }
25454         this.title = str;
25455         this.tab.select('a', true).first().dom.innerHTML = str;
25456         
25457     }
25458     
25459     
25460     
25461 });
25462
25463  
25464
25465
25466  /*
25467  * - LGPL
25468  *
25469  * menu
25470  * 
25471  */
25472 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25473
25474 /**
25475  * @class Roo.bootstrap.menu.Menu
25476  * @extends Roo.bootstrap.Component
25477  * Bootstrap Menu class - container for Menu
25478  * @cfg {String} html Text of the menu
25479  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25480  * @cfg {String} icon Font awesome icon
25481  * @cfg {String} pos Menu align to (top | bottom) default bottom
25482  * 
25483  * 
25484  * @constructor
25485  * Create a new Menu
25486  * @param {Object} config The config object
25487  */
25488
25489
25490 Roo.bootstrap.menu.Menu = function(config){
25491     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25492     
25493     this.addEvents({
25494         /**
25495          * @event beforeshow
25496          * Fires before this menu is displayed
25497          * @param {Roo.bootstrap.menu.Menu} this
25498          */
25499         beforeshow : true,
25500         /**
25501          * @event beforehide
25502          * Fires before this menu is hidden
25503          * @param {Roo.bootstrap.menu.Menu} this
25504          */
25505         beforehide : true,
25506         /**
25507          * @event show
25508          * Fires after this menu is displayed
25509          * @param {Roo.bootstrap.menu.Menu} this
25510          */
25511         show : true,
25512         /**
25513          * @event hide
25514          * Fires after this menu is hidden
25515          * @param {Roo.bootstrap.menu.Menu} this
25516          */
25517         hide : true,
25518         /**
25519          * @event click
25520          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25521          * @param {Roo.bootstrap.menu.Menu} this
25522          * @param {Roo.EventObject} e
25523          */
25524         click : true
25525     });
25526     
25527 };
25528
25529 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25530     
25531     submenu : false,
25532     html : '',
25533     weight : 'default',
25534     icon : false,
25535     pos : 'bottom',
25536     
25537     
25538     getChildContainer : function() {
25539         if(this.isSubMenu){
25540             return this.el;
25541         }
25542         
25543         return this.el.select('ul.dropdown-menu', true).first();  
25544     },
25545     
25546     getAutoCreate : function()
25547     {
25548         var text = [
25549             {
25550                 tag : 'span',
25551                 cls : 'roo-menu-text',
25552                 html : this.html
25553             }
25554         ];
25555         
25556         if(this.icon){
25557             text.unshift({
25558                 tag : 'i',
25559                 cls : 'fa ' + this.icon
25560             })
25561         }
25562         
25563         
25564         var cfg = {
25565             tag : 'div',
25566             cls : 'btn-group',
25567             cn : [
25568                 {
25569                     tag : 'button',
25570                     cls : 'dropdown-button btn btn-' + this.weight,
25571                     cn : text
25572                 },
25573                 {
25574                     tag : 'button',
25575                     cls : 'dropdown-toggle btn btn-' + this.weight,
25576                     cn : [
25577                         {
25578                             tag : 'span',
25579                             cls : 'caret'
25580                         }
25581                     ]
25582                 },
25583                 {
25584                     tag : 'ul',
25585                     cls : 'dropdown-menu'
25586                 }
25587             ]
25588             
25589         };
25590         
25591         if(this.pos == 'top'){
25592             cfg.cls += ' dropup';
25593         }
25594         
25595         if(this.isSubMenu){
25596             cfg = {
25597                 tag : 'ul',
25598                 cls : 'dropdown-menu'
25599             }
25600         }
25601         
25602         return cfg;
25603     },
25604     
25605     onRender : function(ct, position)
25606     {
25607         this.isSubMenu = ct.hasClass('dropdown-submenu');
25608         
25609         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25610     },
25611     
25612     initEvents : function() 
25613     {
25614         if(this.isSubMenu){
25615             return;
25616         }
25617         
25618         this.hidden = true;
25619         
25620         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25621         this.triggerEl.on('click', this.onTriggerPress, this);
25622         
25623         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25624         this.buttonEl.on('click', this.onClick, this);
25625         
25626     },
25627     
25628     list : function()
25629     {
25630         if(this.isSubMenu){
25631             return this.el;
25632         }
25633         
25634         return this.el.select('ul.dropdown-menu', true).first();
25635     },
25636     
25637     onClick : function(e)
25638     {
25639         this.fireEvent("click", this, e);
25640     },
25641     
25642     onTriggerPress  : function(e)
25643     {   
25644         if (this.isVisible()) {
25645             this.hide();
25646         } else {
25647             this.show();
25648         }
25649     },
25650     
25651     isVisible : function(){
25652         return !this.hidden;
25653     },
25654     
25655     show : function()
25656     {
25657         this.fireEvent("beforeshow", this);
25658         
25659         this.hidden = false;
25660         this.el.addClass('open');
25661         
25662         Roo.get(document).on("mouseup", this.onMouseUp, this);
25663         
25664         this.fireEvent("show", this);
25665         
25666         
25667     },
25668     
25669     hide : function()
25670     {
25671         this.fireEvent("beforehide", this);
25672         
25673         this.hidden = true;
25674         this.el.removeClass('open');
25675         
25676         Roo.get(document).un("mouseup", this.onMouseUp);
25677         
25678         this.fireEvent("hide", this);
25679     },
25680     
25681     onMouseUp : function()
25682     {
25683         this.hide();
25684     }
25685     
25686 });
25687
25688  
25689  /*
25690  * - LGPL
25691  *
25692  * menu item
25693  * 
25694  */
25695 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25696
25697 /**
25698  * @class Roo.bootstrap.menu.Item
25699  * @extends Roo.bootstrap.Component
25700  * Bootstrap MenuItem class
25701  * @cfg {Boolean} submenu (true | false) default false
25702  * @cfg {String} html text of the item
25703  * @cfg {String} href the link
25704  * @cfg {Boolean} disable (true | false) default false
25705  * @cfg {Boolean} preventDefault (true | false) default true
25706  * @cfg {String} icon Font awesome icon
25707  * @cfg {String} pos Submenu align to (left | right) default right 
25708  * 
25709  * 
25710  * @constructor
25711  * Create a new Item
25712  * @param {Object} config The config object
25713  */
25714
25715
25716 Roo.bootstrap.menu.Item = function(config){
25717     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25718     this.addEvents({
25719         /**
25720          * @event mouseover
25721          * Fires when the mouse is hovering over this menu
25722          * @param {Roo.bootstrap.menu.Item} this
25723          * @param {Roo.EventObject} e
25724          */
25725         mouseover : true,
25726         /**
25727          * @event mouseout
25728          * Fires when the mouse exits this menu
25729          * @param {Roo.bootstrap.menu.Item} this
25730          * @param {Roo.EventObject} e
25731          */
25732         mouseout : true,
25733         // raw events
25734         /**
25735          * @event click
25736          * The raw click event for the entire grid.
25737          * @param {Roo.EventObject} e
25738          */
25739         click : true
25740     });
25741 };
25742
25743 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25744     
25745     submenu : false,
25746     href : '',
25747     html : '',
25748     preventDefault: true,
25749     disable : false,
25750     icon : false,
25751     pos : 'right',
25752     
25753     getAutoCreate : function()
25754     {
25755         var text = [
25756             {
25757                 tag : 'span',
25758                 cls : 'roo-menu-item-text',
25759                 html : this.html
25760             }
25761         ];
25762         
25763         if(this.icon){
25764             text.unshift({
25765                 tag : 'i',
25766                 cls : 'fa ' + this.icon
25767             })
25768         }
25769         
25770         var cfg = {
25771             tag : 'li',
25772             cn : [
25773                 {
25774                     tag : 'a',
25775                     href : this.href || '#',
25776                     cn : text
25777                 }
25778             ]
25779         };
25780         
25781         if(this.disable){
25782             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25783         }
25784         
25785         if(this.submenu){
25786             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25787             
25788             if(this.pos == 'left'){
25789                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25790             }
25791         }
25792         
25793         return cfg;
25794     },
25795     
25796     initEvents : function() 
25797     {
25798         this.el.on('mouseover', this.onMouseOver, this);
25799         this.el.on('mouseout', this.onMouseOut, this);
25800         
25801         this.el.select('a', true).first().on('click', this.onClick, this);
25802         
25803     },
25804     
25805     onClick : function(e)
25806     {
25807         if(this.preventDefault){
25808             e.preventDefault();
25809         }
25810         
25811         this.fireEvent("click", this, e);
25812     },
25813     
25814     onMouseOver : function(e)
25815     {
25816         if(this.submenu && this.pos == 'left'){
25817             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25818         }
25819         
25820         this.fireEvent("mouseover", this, e);
25821     },
25822     
25823     onMouseOut : function(e)
25824     {
25825         this.fireEvent("mouseout", this, e);
25826     }
25827 });
25828
25829  
25830
25831  /*
25832  * - LGPL
25833  *
25834  * menu separator
25835  * 
25836  */
25837 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25838
25839 /**
25840  * @class Roo.bootstrap.menu.Separator
25841  * @extends Roo.bootstrap.Component
25842  * Bootstrap Separator class
25843  * 
25844  * @constructor
25845  * Create a new Separator
25846  * @param {Object} config The config object
25847  */
25848
25849
25850 Roo.bootstrap.menu.Separator = function(config){
25851     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25852 };
25853
25854 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25855     
25856     getAutoCreate : function(){
25857         var cfg = {
25858             tag : 'li',
25859             cls: 'divider'
25860         };
25861         
25862         return cfg;
25863     }
25864    
25865 });
25866
25867  
25868
25869  /*
25870  * - LGPL
25871  *
25872  * Tooltip
25873  * 
25874  */
25875
25876 /**
25877  * @class Roo.bootstrap.Tooltip
25878  * Bootstrap Tooltip class
25879  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25880  * to determine which dom element triggers the tooltip.
25881  * 
25882  * It needs to add support for additional attributes like tooltip-position
25883  * 
25884  * @constructor
25885  * Create a new Toolti
25886  * @param {Object} config The config object
25887  */
25888
25889 Roo.bootstrap.Tooltip = function(config){
25890     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25891     
25892     this.alignment = Roo.bootstrap.Tooltip.alignment;
25893     
25894     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25895         this.alignment = config.alignment;
25896     }
25897     
25898 };
25899
25900 Roo.apply(Roo.bootstrap.Tooltip, {
25901     /**
25902      * @function init initialize tooltip monitoring.
25903      * @static
25904      */
25905     currentEl : false,
25906     currentTip : false,
25907     currentRegion : false,
25908     
25909     //  init : delay?
25910     
25911     init : function()
25912     {
25913         Roo.get(document).on('mouseover', this.enter ,this);
25914         Roo.get(document).on('mouseout', this.leave, this);
25915          
25916         
25917         this.currentTip = new Roo.bootstrap.Tooltip();
25918     },
25919     
25920     enter : function(ev)
25921     {
25922         var dom = ev.getTarget();
25923         
25924         //Roo.log(['enter',dom]);
25925         var el = Roo.fly(dom);
25926         if (this.currentEl) {
25927             //Roo.log(dom);
25928             //Roo.log(this.currentEl);
25929             //Roo.log(this.currentEl.contains(dom));
25930             if (this.currentEl == el) {
25931                 return;
25932             }
25933             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25934                 return;
25935             }
25936
25937         }
25938         
25939         if (this.currentTip.el) {
25940             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25941         }    
25942         //Roo.log(ev);
25943         
25944         if(!el || el.dom == document){
25945             return;
25946         }
25947         
25948         var bindEl = el;
25949         
25950         // you can not look for children, as if el is the body.. then everythign is the child..
25951         if (!el.attr('tooltip')) { //
25952             if (!el.select("[tooltip]").elements.length) {
25953                 return;
25954             }
25955             // is the mouse over this child...?
25956             bindEl = el.select("[tooltip]").first();
25957             var xy = ev.getXY();
25958             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
25959                 //Roo.log("not in region.");
25960                 return;
25961             }
25962             //Roo.log("child element over..");
25963             
25964         }
25965         this.currentEl = bindEl;
25966         this.currentTip.bind(bindEl);
25967         this.currentRegion = Roo.lib.Region.getRegion(dom);
25968         this.currentTip.enter();
25969         
25970     },
25971     leave : function(ev)
25972     {
25973         var dom = ev.getTarget();
25974         //Roo.log(['leave',dom]);
25975         if (!this.currentEl) {
25976             return;
25977         }
25978         
25979         
25980         if (dom != this.currentEl.dom) {
25981             return;
25982         }
25983         var xy = ev.getXY();
25984         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
25985             return;
25986         }
25987         // only activate leave if mouse cursor is outside... bounding box..
25988         
25989         
25990         
25991         
25992         if (this.currentTip) {
25993             this.currentTip.leave();
25994         }
25995         //Roo.log('clear currentEl');
25996         this.currentEl = false;
25997         
25998         
25999     },
26000     alignment : {
26001         'left' : ['r-l', [-2,0], 'right'],
26002         'right' : ['l-r', [2,0], 'left'],
26003         'bottom' : ['t-b', [0,2], 'top'],
26004         'top' : [ 'b-t', [0,-2], 'bottom']
26005     }
26006     
26007 });
26008
26009
26010 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26011     
26012     
26013     bindEl : false,
26014     
26015     delay : null, // can be { show : 300 , hide: 500}
26016     
26017     timeout : null,
26018     
26019     hoverState : null, //???
26020     
26021     placement : 'bottom', 
26022     
26023     alignment : false,
26024     
26025     getAutoCreate : function(){
26026     
26027         var cfg = {
26028            cls : 'tooltip',
26029            role : 'tooltip',
26030            cn : [
26031                 {
26032                     cls : 'tooltip-arrow'
26033                 },
26034                 {
26035                     cls : 'tooltip-inner'
26036                 }
26037            ]
26038         };
26039         
26040         return cfg;
26041     },
26042     bind : function(el)
26043     {
26044         this.bindEl = el;
26045     },
26046       
26047     
26048     enter : function () {
26049        
26050         if (this.timeout != null) {
26051             clearTimeout(this.timeout);
26052         }
26053         
26054         this.hoverState = 'in';
26055          //Roo.log("enter - show");
26056         if (!this.delay || !this.delay.show) {
26057             this.show();
26058             return;
26059         }
26060         var _t = this;
26061         this.timeout = setTimeout(function () {
26062             if (_t.hoverState == 'in') {
26063                 _t.show();
26064             }
26065         }, this.delay.show);
26066     },
26067     leave : function()
26068     {
26069         clearTimeout(this.timeout);
26070     
26071         this.hoverState = 'out';
26072          if (!this.delay || !this.delay.hide) {
26073             this.hide();
26074             return;
26075         }
26076        
26077         var _t = this;
26078         this.timeout = setTimeout(function () {
26079             //Roo.log("leave - timeout");
26080             
26081             if (_t.hoverState == 'out') {
26082                 _t.hide();
26083                 Roo.bootstrap.Tooltip.currentEl = false;
26084             }
26085         }, delay);
26086     },
26087     
26088     show : function (msg)
26089     {
26090         if (!this.el) {
26091             this.render(document.body);
26092         }
26093         // set content.
26094         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26095         
26096         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26097         
26098         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26099         
26100         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26101         
26102         var placement = typeof this.placement == 'function' ?
26103             this.placement.call(this, this.el, on_el) :
26104             this.placement;
26105             
26106         var autoToken = /\s?auto?\s?/i;
26107         var autoPlace = autoToken.test(placement);
26108         if (autoPlace) {
26109             placement = placement.replace(autoToken, '') || 'top';
26110         }
26111         
26112         //this.el.detach()
26113         //this.el.setXY([0,0]);
26114         this.el.show();
26115         //this.el.dom.style.display='block';
26116         
26117         //this.el.appendTo(on_el);
26118         
26119         var p = this.getPosition();
26120         var box = this.el.getBox();
26121         
26122         if (autoPlace) {
26123             // fixme..
26124         }
26125         
26126         var align = this.alignment[placement];
26127         
26128         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26129         
26130         if(placement == 'top' || placement == 'bottom'){
26131             if(xy[0] < 0){
26132                 placement = 'right';
26133             }
26134             
26135             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26136                 placement = 'left';
26137             }
26138             
26139             var scroll = Roo.select('body', true).first().getScroll();
26140             
26141             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26142                 placement = 'top';
26143             }
26144             
26145         }
26146         
26147         this.el.alignTo(this.bindEl, align[0],align[1]);
26148         //var arrow = this.el.select('.arrow',true).first();
26149         //arrow.set(align[2], 
26150         
26151         this.el.addClass(placement);
26152         
26153         this.el.addClass('in fade');
26154         
26155         this.hoverState = null;
26156         
26157         if (this.el.hasClass('fade')) {
26158             // fade it?
26159         }
26160         
26161     },
26162     hide : function()
26163     {
26164          
26165         if (!this.el) {
26166             return;
26167         }
26168         //this.el.setXY([0,0]);
26169         this.el.removeClass('in');
26170         //this.el.hide();
26171         
26172     }
26173     
26174 });
26175  
26176
26177  /*
26178  * - LGPL
26179  *
26180  * Location Picker
26181  * 
26182  */
26183
26184 /**
26185  * @class Roo.bootstrap.LocationPicker
26186  * @extends Roo.bootstrap.Component
26187  * Bootstrap LocationPicker class
26188  * @cfg {Number} latitude Position when init default 0
26189  * @cfg {Number} longitude Position when init default 0
26190  * @cfg {Number} zoom default 15
26191  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26192  * @cfg {Boolean} mapTypeControl default false
26193  * @cfg {Boolean} disableDoubleClickZoom default false
26194  * @cfg {Boolean} scrollwheel default true
26195  * @cfg {Boolean} streetViewControl default false
26196  * @cfg {Number} radius default 0
26197  * @cfg {String} locationName
26198  * @cfg {Boolean} draggable default true
26199  * @cfg {Boolean} enableAutocomplete default false
26200  * @cfg {Boolean} enableReverseGeocode default true
26201  * @cfg {String} markerTitle
26202  * 
26203  * @constructor
26204  * Create a new LocationPicker
26205  * @param {Object} config The config object
26206  */
26207
26208
26209 Roo.bootstrap.LocationPicker = function(config){
26210     
26211     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26212     
26213     this.addEvents({
26214         /**
26215          * @event initial
26216          * Fires when the picker initialized.
26217          * @param {Roo.bootstrap.LocationPicker} this
26218          * @param {Google Location} location
26219          */
26220         initial : true,
26221         /**
26222          * @event positionchanged
26223          * Fires when the picker position changed.
26224          * @param {Roo.bootstrap.LocationPicker} this
26225          * @param {Google Location} location
26226          */
26227         positionchanged : true,
26228         /**
26229          * @event resize
26230          * Fires when the map resize.
26231          * @param {Roo.bootstrap.LocationPicker} this
26232          */
26233         resize : true,
26234         /**
26235          * @event show
26236          * Fires when the map show.
26237          * @param {Roo.bootstrap.LocationPicker} this
26238          */
26239         show : true,
26240         /**
26241          * @event hide
26242          * Fires when the map hide.
26243          * @param {Roo.bootstrap.LocationPicker} this
26244          */
26245         hide : true,
26246         /**
26247          * @event mapClick
26248          * Fires when click the map.
26249          * @param {Roo.bootstrap.LocationPicker} this
26250          * @param {Map event} e
26251          */
26252         mapClick : true,
26253         /**
26254          * @event mapRightClick
26255          * Fires when right click the map.
26256          * @param {Roo.bootstrap.LocationPicker} this
26257          * @param {Map event} e
26258          */
26259         mapRightClick : true,
26260         /**
26261          * @event markerClick
26262          * Fires when click the marker.
26263          * @param {Roo.bootstrap.LocationPicker} this
26264          * @param {Map event} e
26265          */
26266         markerClick : true,
26267         /**
26268          * @event markerRightClick
26269          * Fires when right click the marker.
26270          * @param {Roo.bootstrap.LocationPicker} this
26271          * @param {Map event} e
26272          */
26273         markerRightClick : true,
26274         /**
26275          * @event OverlayViewDraw
26276          * Fires when OverlayView Draw
26277          * @param {Roo.bootstrap.LocationPicker} this
26278          */
26279         OverlayViewDraw : true,
26280         /**
26281          * @event OverlayViewOnAdd
26282          * Fires when OverlayView Draw
26283          * @param {Roo.bootstrap.LocationPicker} this
26284          */
26285         OverlayViewOnAdd : true,
26286         /**
26287          * @event OverlayViewOnRemove
26288          * Fires when OverlayView Draw
26289          * @param {Roo.bootstrap.LocationPicker} this
26290          */
26291         OverlayViewOnRemove : true,
26292         /**
26293          * @event OverlayViewShow
26294          * Fires when OverlayView Draw
26295          * @param {Roo.bootstrap.LocationPicker} this
26296          * @param {Pixel} cpx
26297          */
26298         OverlayViewShow : true,
26299         /**
26300          * @event OverlayViewHide
26301          * Fires when OverlayView Draw
26302          * @param {Roo.bootstrap.LocationPicker} this
26303          */
26304         OverlayViewHide : true,
26305         /**
26306          * @event loadexception
26307          * Fires when load google lib failed.
26308          * @param {Roo.bootstrap.LocationPicker} this
26309          */
26310         loadexception : true
26311     });
26312         
26313 };
26314
26315 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26316     
26317     gMapContext: false,
26318     
26319     latitude: 0,
26320     longitude: 0,
26321     zoom: 15,
26322     mapTypeId: false,
26323     mapTypeControl: false,
26324     disableDoubleClickZoom: false,
26325     scrollwheel: true,
26326     streetViewControl: false,
26327     radius: 0,
26328     locationName: '',
26329     draggable: true,
26330     enableAutocomplete: false,
26331     enableReverseGeocode: true,
26332     markerTitle: '',
26333     
26334     getAutoCreate: function()
26335     {
26336
26337         var cfg = {
26338             tag: 'div',
26339             cls: 'roo-location-picker'
26340         };
26341         
26342         return cfg
26343     },
26344     
26345     initEvents: function(ct, position)
26346     {       
26347         if(!this.el.getWidth() || this.isApplied()){
26348             return;
26349         }
26350         
26351         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26352         
26353         this.initial();
26354     },
26355     
26356     initial: function()
26357     {
26358         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26359             this.fireEvent('loadexception', this);
26360             return;
26361         }
26362         
26363         if(!this.mapTypeId){
26364             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26365         }
26366         
26367         this.gMapContext = this.GMapContext();
26368         
26369         this.initOverlayView();
26370         
26371         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26372         
26373         var _this = this;
26374                 
26375         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26376             _this.setPosition(_this.gMapContext.marker.position);
26377         });
26378         
26379         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26380             _this.fireEvent('mapClick', this, event);
26381             
26382         });
26383
26384         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26385             _this.fireEvent('mapRightClick', this, event);
26386             
26387         });
26388         
26389         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26390             _this.fireEvent('markerClick', this, event);
26391             
26392         });
26393
26394         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26395             _this.fireEvent('markerRightClick', this, event);
26396             
26397         });
26398         
26399         this.setPosition(this.gMapContext.location);
26400         
26401         this.fireEvent('initial', this, this.gMapContext.location);
26402     },
26403     
26404     initOverlayView: function()
26405     {
26406         var _this = this;
26407         
26408         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26409             
26410             draw: function()
26411             {
26412                 _this.fireEvent('OverlayViewDraw', _this);
26413             },
26414             
26415             onAdd: function()
26416             {
26417                 _this.fireEvent('OverlayViewOnAdd', _this);
26418             },
26419             
26420             onRemove: function()
26421             {
26422                 _this.fireEvent('OverlayViewOnRemove', _this);
26423             },
26424             
26425             show: function(cpx)
26426             {
26427                 _this.fireEvent('OverlayViewShow', _this, cpx);
26428             },
26429             
26430             hide: function()
26431             {
26432                 _this.fireEvent('OverlayViewHide', _this);
26433             }
26434             
26435         });
26436     },
26437     
26438     fromLatLngToContainerPixel: function(event)
26439     {
26440         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26441     },
26442     
26443     isApplied: function() 
26444     {
26445         return this.getGmapContext() == false ? false : true;
26446     },
26447     
26448     getGmapContext: function() 
26449     {
26450         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26451     },
26452     
26453     GMapContext: function() 
26454     {
26455         var position = new google.maps.LatLng(this.latitude, this.longitude);
26456         
26457         var _map = new google.maps.Map(this.el.dom, {
26458             center: position,
26459             zoom: this.zoom,
26460             mapTypeId: this.mapTypeId,
26461             mapTypeControl: this.mapTypeControl,
26462             disableDoubleClickZoom: this.disableDoubleClickZoom,
26463             scrollwheel: this.scrollwheel,
26464             streetViewControl: this.streetViewControl,
26465             locationName: this.locationName,
26466             draggable: this.draggable,
26467             enableAutocomplete: this.enableAutocomplete,
26468             enableReverseGeocode: this.enableReverseGeocode
26469         });
26470         
26471         var _marker = new google.maps.Marker({
26472             position: position,
26473             map: _map,
26474             title: this.markerTitle,
26475             draggable: this.draggable
26476         });
26477         
26478         return {
26479             map: _map,
26480             marker: _marker,
26481             circle: null,
26482             location: position,
26483             radius: this.radius,
26484             locationName: this.locationName,
26485             addressComponents: {
26486                 formatted_address: null,
26487                 addressLine1: null,
26488                 addressLine2: null,
26489                 streetName: null,
26490                 streetNumber: null,
26491                 city: null,
26492                 district: null,
26493                 state: null,
26494                 stateOrProvince: null
26495             },
26496             settings: this,
26497             domContainer: this.el.dom,
26498             geodecoder: new google.maps.Geocoder()
26499         };
26500     },
26501     
26502     drawCircle: function(center, radius, options) 
26503     {
26504         if (this.gMapContext.circle != null) {
26505             this.gMapContext.circle.setMap(null);
26506         }
26507         if (radius > 0) {
26508             radius *= 1;
26509             options = Roo.apply({}, options, {
26510                 strokeColor: "#0000FF",
26511                 strokeOpacity: .35,
26512                 strokeWeight: 2,
26513                 fillColor: "#0000FF",
26514                 fillOpacity: .2
26515             });
26516             
26517             options.map = this.gMapContext.map;
26518             options.radius = radius;
26519             options.center = center;
26520             this.gMapContext.circle = new google.maps.Circle(options);
26521             return this.gMapContext.circle;
26522         }
26523         
26524         return null;
26525     },
26526     
26527     setPosition: function(location) 
26528     {
26529         this.gMapContext.location = location;
26530         this.gMapContext.marker.setPosition(location);
26531         this.gMapContext.map.panTo(location);
26532         this.drawCircle(location, this.gMapContext.radius, {});
26533         
26534         var _this = this;
26535         
26536         if (this.gMapContext.settings.enableReverseGeocode) {
26537             this.gMapContext.geodecoder.geocode({
26538                 latLng: this.gMapContext.location
26539             }, function(results, status) {
26540                 
26541                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26542                     _this.gMapContext.locationName = results[0].formatted_address;
26543                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26544                     
26545                     _this.fireEvent('positionchanged', this, location);
26546                 }
26547             });
26548             
26549             return;
26550         }
26551         
26552         this.fireEvent('positionchanged', this, location);
26553     },
26554     
26555     resize: function()
26556     {
26557         google.maps.event.trigger(this.gMapContext.map, "resize");
26558         
26559         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26560         
26561         this.fireEvent('resize', this);
26562     },
26563     
26564     setPositionByLatLng: function(latitude, longitude)
26565     {
26566         this.setPosition(new google.maps.LatLng(latitude, longitude));
26567     },
26568     
26569     getCurrentPosition: function() 
26570     {
26571         return {
26572             latitude: this.gMapContext.location.lat(),
26573             longitude: this.gMapContext.location.lng()
26574         };
26575     },
26576     
26577     getAddressName: function() 
26578     {
26579         return this.gMapContext.locationName;
26580     },
26581     
26582     getAddressComponents: function() 
26583     {
26584         return this.gMapContext.addressComponents;
26585     },
26586     
26587     address_component_from_google_geocode: function(address_components) 
26588     {
26589         var result = {};
26590         
26591         for (var i = 0; i < address_components.length; i++) {
26592             var component = address_components[i];
26593             if (component.types.indexOf("postal_code") >= 0) {
26594                 result.postalCode = component.short_name;
26595             } else if (component.types.indexOf("street_number") >= 0) {
26596                 result.streetNumber = component.short_name;
26597             } else if (component.types.indexOf("route") >= 0) {
26598                 result.streetName = component.short_name;
26599             } else if (component.types.indexOf("neighborhood") >= 0) {
26600                 result.city = component.short_name;
26601             } else if (component.types.indexOf("locality") >= 0) {
26602                 result.city = component.short_name;
26603             } else if (component.types.indexOf("sublocality") >= 0) {
26604                 result.district = component.short_name;
26605             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26606                 result.stateOrProvince = component.short_name;
26607             } else if (component.types.indexOf("country") >= 0) {
26608                 result.country = component.short_name;
26609             }
26610         }
26611         
26612         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26613         result.addressLine2 = "";
26614         return result;
26615     },
26616     
26617     setZoomLevel: function(zoom)
26618     {
26619         this.gMapContext.map.setZoom(zoom);
26620     },
26621     
26622     show: function()
26623     {
26624         if(!this.el){
26625             return;
26626         }
26627         
26628         this.el.show();
26629         
26630         this.resize();
26631         
26632         this.fireEvent('show', this);
26633     },
26634     
26635     hide: function()
26636     {
26637         if(!this.el){
26638             return;
26639         }
26640         
26641         this.el.hide();
26642         
26643         this.fireEvent('hide', this);
26644     }
26645     
26646 });
26647
26648 Roo.apply(Roo.bootstrap.LocationPicker, {
26649     
26650     OverlayView : function(map, options)
26651     {
26652         options = options || {};
26653         
26654         this.setMap(map);
26655     }
26656     
26657     
26658 });/*
26659  * - LGPL
26660  *
26661  * Alert
26662  * 
26663  */
26664
26665 /**
26666  * @class Roo.bootstrap.Alert
26667  * @extends Roo.bootstrap.Component
26668  * Bootstrap Alert class
26669  * @cfg {String} title The title of alert
26670  * @cfg {String} html The content of alert
26671  * @cfg {String} weight (  success | info | warning | danger )
26672  * @cfg {String} faicon font-awesomeicon
26673  * 
26674  * @constructor
26675  * Create a new alert
26676  * @param {Object} config The config object
26677  */
26678
26679
26680 Roo.bootstrap.Alert = function(config){
26681     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26682     
26683 };
26684
26685 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26686     
26687     title: '',
26688     html: '',
26689     weight: false,
26690     faicon: false,
26691     
26692     getAutoCreate : function()
26693     {
26694         
26695         var cfg = {
26696             tag : 'div',
26697             cls : 'alert',
26698             cn : [
26699                 {
26700                     tag : 'i',
26701                     cls : 'roo-alert-icon'
26702                     
26703                 },
26704                 {
26705                     tag : 'b',
26706                     cls : 'roo-alert-title',
26707                     html : this.title
26708                 },
26709                 {
26710                     tag : 'span',
26711                     cls : 'roo-alert-text',
26712                     html : this.html
26713                 }
26714             ]
26715         };
26716         
26717         if(this.faicon){
26718             cfg.cn[0].cls += ' fa ' + this.faicon;
26719         }
26720         
26721         if(this.weight){
26722             cfg.cls += ' alert-' + this.weight;
26723         }
26724         
26725         return cfg;
26726     },
26727     
26728     initEvents: function() 
26729     {
26730         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26731     },
26732     
26733     setTitle : function(str)
26734     {
26735         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26736     },
26737     
26738     setText : function(str)
26739     {
26740         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26741     },
26742     
26743     setWeight : function(weight)
26744     {
26745         if(this.weight){
26746             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26747         }
26748         
26749         this.weight = weight;
26750         
26751         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26752     },
26753     
26754     setIcon : function(icon)
26755     {
26756         if(this.faicon){
26757             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26758         }
26759         
26760         this.faicon = icon;
26761         
26762         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26763     },
26764     
26765     hide: function() 
26766     {
26767         this.el.hide();   
26768     },
26769     
26770     show: function() 
26771     {  
26772         this.el.show();   
26773     }
26774     
26775 });
26776
26777  
26778 /*
26779 * Licence: LGPL
26780 */
26781
26782 /**
26783  * @class Roo.bootstrap.UploadCropbox
26784  * @extends Roo.bootstrap.Component
26785  * Bootstrap UploadCropbox class
26786  * @cfg {String} emptyText show when image has been loaded
26787  * @cfg {String} rotateNotify show when image too small to rotate
26788  * @cfg {Number} errorTimeout default 3000
26789  * @cfg {Number} minWidth default 300
26790  * @cfg {Number} minHeight default 300
26791  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26792  * @cfg {Boolean} isDocument (true|false) default false
26793  * @cfg {String} url action url
26794  * @cfg {String} paramName default 'imageUpload'
26795  * @cfg {String} method default POST
26796  * @cfg {Boolean} loadMask (true|false) default true
26797  * @cfg {Boolean} loadingText default 'Loading...'
26798  * 
26799  * @constructor
26800  * Create a new UploadCropbox
26801  * @param {Object} config The config object
26802  */
26803
26804 Roo.bootstrap.UploadCropbox = function(config){
26805     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26806     
26807     this.addEvents({
26808         /**
26809          * @event beforeselectfile
26810          * Fire before select file
26811          * @param {Roo.bootstrap.UploadCropbox} this
26812          */
26813         "beforeselectfile" : true,
26814         /**
26815          * @event initial
26816          * Fire after initEvent
26817          * @param {Roo.bootstrap.UploadCropbox} this
26818          */
26819         "initial" : true,
26820         /**
26821          * @event crop
26822          * Fire after initEvent
26823          * @param {Roo.bootstrap.UploadCropbox} this
26824          * @param {String} data
26825          */
26826         "crop" : true,
26827         /**
26828          * @event prepare
26829          * Fire when preparing the file data
26830          * @param {Roo.bootstrap.UploadCropbox} this
26831          * @param {Object} file
26832          */
26833         "prepare" : true,
26834         /**
26835          * @event exception
26836          * Fire when get exception
26837          * @param {Roo.bootstrap.UploadCropbox} this
26838          * @param {XMLHttpRequest} xhr
26839          */
26840         "exception" : true,
26841         /**
26842          * @event beforeloadcanvas
26843          * Fire before load the canvas
26844          * @param {Roo.bootstrap.UploadCropbox} this
26845          * @param {String} src
26846          */
26847         "beforeloadcanvas" : true,
26848         /**
26849          * @event trash
26850          * Fire when trash image
26851          * @param {Roo.bootstrap.UploadCropbox} this
26852          */
26853         "trash" : true,
26854         /**
26855          * @event download
26856          * Fire when download the image
26857          * @param {Roo.bootstrap.UploadCropbox} this
26858          */
26859         "download" : true,
26860         /**
26861          * @event footerbuttonclick
26862          * Fire when footerbuttonclick
26863          * @param {Roo.bootstrap.UploadCropbox} this
26864          * @param {String} type
26865          */
26866         "footerbuttonclick" : true,
26867         /**
26868          * @event resize
26869          * Fire when resize
26870          * @param {Roo.bootstrap.UploadCropbox} this
26871          */
26872         "resize" : true,
26873         /**
26874          * @event rotate
26875          * Fire when rotate the image
26876          * @param {Roo.bootstrap.UploadCropbox} this
26877          * @param {String} pos
26878          */
26879         "rotate" : true,
26880         /**
26881          * @event inspect
26882          * Fire when inspect the file
26883          * @param {Roo.bootstrap.UploadCropbox} this
26884          * @param {Object} file
26885          */
26886         "inspect" : true,
26887         /**
26888          * @event upload
26889          * Fire when xhr upload the file
26890          * @param {Roo.bootstrap.UploadCropbox} this
26891          * @param {Object} data
26892          */
26893         "upload" : true,
26894         /**
26895          * @event arrange
26896          * Fire when arrange the file data
26897          * @param {Roo.bootstrap.UploadCropbox} this
26898          * @param {Object} formData
26899          */
26900         "arrange" : true
26901     });
26902     
26903     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26904 };
26905
26906 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
26907     
26908     emptyText : 'Click to upload image',
26909     rotateNotify : 'Image is too small to rotate',
26910     errorTimeout : 3000,
26911     scale : 0,
26912     baseScale : 1,
26913     rotate : 0,
26914     dragable : false,
26915     pinching : false,
26916     mouseX : 0,
26917     mouseY : 0,
26918     cropData : false,
26919     minWidth : 300,
26920     minHeight : 300,
26921     file : false,
26922     exif : {},
26923     baseRotate : 1,
26924     cropType : 'image/jpeg',
26925     buttons : false,
26926     canvasLoaded : false,
26927     isDocument : false,
26928     method : 'POST',
26929     paramName : 'imageUpload',
26930     loadMask : true,
26931     loadingText : 'Loading...',
26932     maskEl : false,
26933     
26934     getAutoCreate : function()
26935     {
26936         var cfg = {
26937             tag : 'div',
26938             cls : 'roo-upload-cropbox',
26939             cn : [
26940                 {
26941                     tag : 'input',
26942                     cls : 'roo-upload-cropbox-selector',
26943                     type : 'file'
26944                 },
26945                 {
26946                     tag : 'div',
26947                     cls : 'roo-upload-cropbox-body',
26948                     style : 'cursor:pointer',
26949                     cn : [
26950                         {
26951                             tag : 'div',
26952                             cls : 'roo-upload-cropbox-preview'
26953                         },
26954                         {
26955                             tag : 'div',
26956                             cls : 'roo-upload-cropbox-thumb'
26957                         },
26958                         {
26959                             tag : 'div',
26960                             cls : 'roo-upload-cropbox-empty-notify',
26961                             html : this.emptyText
26962                         },
26963                         {
26964                             tag : 'div',
26965                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
26966                             html : this.rotateNotify
26967                         }
26968                     ]
26969                 },
26970                 {
26971                     tag : 'div',
26972                     cls : 'roo-upload-cropbox-footer',
26973                     cn : {
26974                         tag : 'div',
26975                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
26976                         cn : []
26977                     }
26978                 }
26979             ]
26980         };
26981         
26982         return cfg;
26983     },
26984     
26985     onRender : function(ct, position)
26986     {
26987         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
26988         
26989         if (this.buttons.length) {
26990             
26991             Roo.each(this.buttons, function(bb) {
26992                 
26993                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
26994                 
26995                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
26996                 
26997             }, this);
26998         }
26999         
27000         if(this.loadMask){
27001             this.maskEl = this.el;
27002         }
27003     },
27004     
27005     initEvents : function()
27006     {
27007         this.urlAPI = (window.createObjectURL && window) || 
27008                                 (window.URL && URL.revokeObjectURL && URL) || 
27009                                 (window.webkitURL && webkitURL);
27010                         
27011         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27012         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27013         
27014         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27015         this.selectorEl.hide();
27016         
27017         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27018         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27019         
27020         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27021         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27022         this.thumbEl.hide();
27023         
27024         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27025         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27026         
27027         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27028         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27029         this.errorEl.hide();
27030         
27031         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27032         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27033         this.footerEl.hide();
27034         
27035         this.setThumbBoxSize();
27036         
27037         this.bind();
27038         
27039         this.resize();
27040         
27041         this.fireEvent('initial', this);
27042     },
27043
27044     bind : function()
27045     {
27046         var _this = this;
27047         
27048         window.addEventListener("resize", function() { _this.resize(); } );
27049         
27050         this.bodyEl.on('click', this.beforeSelectFile, this);
27051         
27052         if(Roo.isTouch){
27053             this.bodyEl.on('touchstart', this.onTouchStart, this);
27054             this.bodyEl.on('touchmove', this.onTouchMove, this);
27055             this.bodyEl.on('touchend', this.onTouchEnd, this);
27056         }
27057         
27058         if(!Roo.isTouch){
27059             this.bodyEl.on('mousedown', this.onMouseDown, this);
27060             this.bodyEl.on('mousemove', this.onMouseMove, this);
27061             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27062             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27063             Roo.get(document).on('mouseup', this.onMouseUp, this);
27064         }
27065         
27066         this.selectorEl.on('change', this.onFileSelected, this);
27067     },
27068     
27069     reset : function()
27070     {    
27071         this.scale = 0;
27072         this.baseScale = 1;
27073         this.rotate = 0;
27074         this.baseRotate = 1;
27075         this.dragable = false;
27076         this.pinching = false;
27077         this.mouseX = 0;
27078         this.mouseY = 0;
27079         this.cropData = false;
27080         this.notifyEl.dom.innerHTML = this.emptyText;
27081         
27082         this.selectorEl.dom.value = '';
27083         
27084     },
27085     
27086     resize : function()
27087     {
27088         if(this.fireEvent('resize', this) != false){
27089             this.setThumbBoxPosition();
27090             this.setCanvasPosition();
27091         }
27092     },
27093     
27094     onFooterButtonClick : function(e, el, o, type)
27095     {
27096         switch (type) {
27097             case 'rotate-left' :
27098                 this.onRotateLeft(e);
27099                 break;
27100             case 'rotate-right' :
27101                 this.onRotateRight(e);
27102                 break;
27103             case 'picture' :
27104                 this.beforeSelectFile(e);
27105                 break;
27106             case 'trash' :
27107                 this.trash(e);
27108                 break;
27109             case 'crop' :
27110                 this.crop(e);
27111                 break;
27112             case 'download' :
27113                 this.download(e);
27114                 break;
27115             default :
27116                 break;
27117         }
27118         
27119         this.fireEvent('footerbuttonclick', this, type);
27120     },
27121     
27122     beforeSelectFile : function(e)
27123     {
27124         e.preventDefault();
27125         
27126         if(this.fireEvent('beforeselectfile', this) != false){
27127             this.selectorEl.dom.click();
27128         }
27129     },
27130     
27131     onFileSelected : function(e)
27132     {
27133         e.preventDefault();
27134         
27135         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27136             return;
27137         }
27138         
27139         var file = this.selectorEl.dom.files[0];
27140         
27141         if(this.fireEvent('inspect', this, file) != false){
27142             this.prepare(file);
27143         }
27144         
27145     },
27146     
27147     trash : function(e)
27148     {
27149         this.fireEvent('trash', this);
27150     },
27151     
27152     download : function(e)
27153     {
27154         this.fireEvent('download', this);
27155     },
27156     
27157     loadCanvas : function(src)
27158     {   
27159         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27160             
27161             this.reset();
27162             
27163             this.imageEl = document.createElement('img');
27164             
27165             var _this = this;
27166             
27167             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27168             
27169             this.imageEl.src = src;
27170         }
27171     },
27172     
27173     onLoadCanvas : function()
27174     {   
27175         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27176         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27177         
27178         this.bodyEl.un('click', this.beforeSelectFile, this);
27179         
27180         this.notifyEl.hide();
27181         this.thumbEl.show();
27182         this.footerEl.show();
27183         
27184         this.baseRotateLevel();
27185         
27186         if(this.isDocument){
27187             this.setThumbBoxSize();
27188         }
27189         
27190         this.setThumbBoxPosition();
27191         
27192         this.baseScaleLevel();
27193         
27194         this.draw();
27195         
27196         this.resize();
27197         
27198         this.canvasLoaded = true;
27199         
27200         if(this.loadMask){
27201             this.maskEl.unmask();
27202         }
27203         
27204     },
27205     
27206     setCanvasPosition : function()
27207     {   
27208         if(!this.canvasEl){
27209             return;
27210         }
27211         
27212         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27213         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27214         
27215         this.previewEl.setLeft(pw);
27216         this.previewEl.setTop(ph);
27217         
27218     },
27219     
27220     onMouseDown : function(e)
27221     {   
27222         e.stopEvent();
27223         
27224         this.dragable = true;
27225         this.pinching = false;
27226         
27227         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27228             this.dragable = false;
27229             return;
27230         }
27231         
27232         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27233         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27234         
27235     },
27236     
27237     onMouseMove : function(e)
27238     {   
27239         e.stopEvent();
27240         
27241         if(!this.canvasLoaded){
27242             return;
27243         }
27244         
27245         if (!this.dragable){
27246             return;
27247         }
27248         
27249         var minX = Math.ceil(this.thumbEl.getLeft(true));
27250         var minY = Math.ceil(this.thumbEl.getTop(true));
27251         
27252         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27253         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27254         
27255         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27256         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27257         
27258         x = x - this.mouseX;
27259         y = y - this.mouseY;
27260         
27261         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27262         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27263         
27264         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27265         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27266         
27267         this.previewEl.setLeft(bgX);
27268         this.previewEl.setTop(bgY);
27269         
27270         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27271         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27272     },
27273     
27274     onMouseUp : function(e)
27275     {   
27276         e.stopEvent();
27277         
27278         this.dragable = false;
27279     },
27280     
27281     onMouseWheel : function(e)
27282     {   
27283         e.stopEvent();
27284         
27285         this.startScale = this.scale;
27286         
27287         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27288         
27289         if(!this.zoomable()){
27290             this.scale = this.startScale;
27291             return;
27292         }
27293         
27294         this.draw();
27295         
27296         return;
27297     },
27298     
27299     zoomable : function()
27300     {
27301         var minScale = this.thumbEl.getWidth() / this.minWidth;
27302         
27303         if(this.minWidth < this.minHeight){
27304             minScale = this.thumbEl.getHeight() / this.minHeight;
27305         }
27306         
27307         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27308         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27309         
27310         if(
27311                 this.isDocument &&
27312                 (this.rotate == 0 || this.rotate == 180) && 
27313                 (
27314                     width > this.imageEl.OriginWidth || 
27315                     height > this.imageEl.OriginHeight ||
27316                     (width < this.minWidth && height < this.minHeight)
27317                 )
27318         ){
27319             return false;
27320         }
27321         
27322         if(
27323                 this.isDocument &&
27324                 (this.rotate == 90 || this.rotate == 270) && 
27325                 (
27326                     width > this.imageEl.OriginWidth || 
27327                     height > this.imageEl.OriginHeight ||
27328                     (width < this.minHeight && height < this.minWidth)
27329                 )
27330         ){
27331             return false;
27332         }
27333         
27334         if(
27335                 !this.isDocument &&
27336                 (this.rotate == 0 || this.rotate == 180) && 
27337                 (
27338                     width < this.minWidth || 
27339                     width > this.imageEl.OriginWidth || 
27340                     height < this.minHeight || 
27341                     height > this.imageEl.OriginHeight
27342                 )
27343         ){
27344             return false;
27345         }
27346         
27347         if(
27348                 !this.isDocument &&
27349                 (this.rotate == 90 || this.rotate == 270) && 
27350                 (
27351                     width < this.minHeight || 
27352                     width > this.imageEl.OriginWidth || 
27353                     height < this.minWidth || 
27354                     height > this.imageEl.OriginHeight
27355                 )
27356         ){
27357             return false;
27358         }
27359         
27360         return true;
27361         
27362     },
27363     
27364     onRotateLeft : function(e)
27365     {   
27366         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27367             
27368             var minScale = this.thumbEl.getWidth() / this.minWidth;
27369             
27370             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27371             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27372             
27373             this.startScale = this.scale;
27374             
27375             while (this.getScaleLevel() < minScale){
27376             
27377                 this.scale = this.scale + 1;
27378                 
27379                 if(!this.zoomable()){
27380                     break;
27381                 }
27382                 
27383                 if(
27384                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27385                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27386                 ){
27387                     continue;
27388                 }
27389                 
27390                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27391
27392                 this.draw();
27393                 
27394                 return;
27395             }
27396             
27397             this.scale = this.startScale;
27398             
27399             this.onRotateFail();
27400             
27401             return false;
27402         }
27403         
27404         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27405
27406         if(this.isDocument){
27407             this.setThumbBoxSize();
27408             this.setThumbBoxPosition();
27409             this.setCanvasPosition();
27410         }
27411         
27412         this.draw();
27413         
27414         this.fireEvent('rotate', this, 'left');
27415         
27416     },
27417     
27418     onRotateRight : function(e)
27419     {
27420         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27421             
27422             var minScale = this.thumbEl.getWidth() / this.minWidth;
27423         
27424             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27425             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27426             
27427             this.startScale = this.scale;
27428             
27429             while (this.getScaleLevel() < minScale){
27430             
27431                 this.scale = this.scale + 1;
27432                 
27433                 if(!this.zoomable()){
27434                     break;
27435                 }
27436                 
27437                 if(
27438                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27439                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27440                 ){
27441                     continue;
27442                 }
27443                 
27444                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27445
27446                 this.draw();
27447                 
27448                 return;
27449             }
27450             
27451             this.scale = this.startScale;
27452             
27453             this.onRotateFail();
27454             
27455             return false;
27456         }
27457         
27458         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27459
27460         if(this.isDocument){
27461             this.setThumbBoxSize();
27462             this.setThumbBoxPosition();
27463             this.setCanvasPosition();
27464         }
27465         
27466         this.draw();
27467         
27468         this.fireEvent('rotate', this, 'right');
27469     },
27470     
27471     onRotateFail : function()
27472     {
27473         this.errorEl.show(true);
27474         
27475         var _this = this;
27476         
27477         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27478     },
27479     
27480     draw : function()
27481     {
27482         this.previewEl.dom.innerHTML = '';
27483         
27484         var canvasEl = document.createElement("canvas");
27485         
27486         var contextEl = canvasEl.getContext("2d");
27487         
27488         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27489         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27490         var center = this.imageEl.OriginWidth / 2;
27491         
27492         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27493             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27494             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27495             center = this.imageEl.OriginHeight / 2;
27496         }
27497         
27498         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27499         
27500         contextEl.translate(center, center);
27501         contextEl.rotate(this.rotate * Math.PI / 180);
27502
27503         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27504         
27505         this.canvasEl = document.createElement("canvas");
27506         
27507         this.contextEl = this.canvasEl.getContext("2d");
27508         
27509         switch (this.rotate) {
27510             case 0 :
27511                 
27512                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27513                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27514                 
27515                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27516                 
27517                 break;
27518             case 90 : 
27519                 
27520                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27521                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27522                 
27523                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27524                     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);
27525                     break;
27526                 }
27527                 
27528                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27529                 
27530                 break;
27531             case 180 :
27532                 
27533                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27534                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27535                 
27536                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27537                     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);
27538                     break;
27539                 }
27540                 
27541                 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);
27542                 
27543                 break;
27544             case 270 :
27545                 
27546                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27547                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27548         
27549                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27550                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27551                     break;
27552                 }
27553                 
27554                 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);
27555                 
27556                 break;
27557             default : 
27558                 break;
27559         }
27560         
27561         this.previewEl.appendChild(this.canvasEl);
27562         
27563         this.setCanvasPosition();
27564     },
27565     
27566     crop : function()
27567     {
27568         if(!this.canvasLoaded){
27569             return;
27570         }
27571         
27572         var imageCanvas = document.createElement("canvas");
27573         
27574         var imageContext = imageCanvas.getContext("2d");
27575         
27576         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27577         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27578         
27579         var center = imageCanvas.width / 2;
27580         
27581         imageContext.translate(center, center);
27582         
27583         imageContext.rotate(this.rotate * Math.PI / 180);
27584         
27585         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27586         
27587         var canvas = document.createElement("canvas");
27588         
27589         var context = canvas.getContext("2d");
27590                 
27591         canvas.width = this.minWidth;
27592         canvas.height = this.minHeight;
27593
27594         switch (this.rotate) {
27595             case 0 :
27596                 
27597                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27598                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27599                 
27600                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27601                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27602                 
27603                 var targetWidth = this.minWidth - 2 * x;
27604                 var targetHeight = this.minHeight - 2 * y;
27605                 
27606                 var scale = 1;
27607                 
27608                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27609                     scale = targetWidth / width;
27610                 }
27611                 
27612                 if(x > 0 && y == 0){
27613                     scale = targetHeight / height;
27614                 }
27615                 
27616                 if(x > 0 && y > 0){
27617                     scale = targetWidth / width;
27618                     
27619                     if(width < height){
27620                         scale = targetHeight / height;
27621                     }
27622                 }
27623                 
27624                 context.scale(scale, scale);
27625                 
27626                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27627                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27628
27629                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27630                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27631
27632                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27633                 
27634                 break;
27635             case 90 : 
27636                 
27637                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27638                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27639                 
27640                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27641                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27642                 
27643                 var targetWidth = this.minWidth - 2 * x;
27644                 var targetHeight = this.minHeight - 2 * y;
27645                 
27646                 var scale = 1;
27647                 
27648                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27649                     scale = targetWidth / width;
27650                 }
27651                 
27652                 if(x > 0 && y == 0){
27653                     scale = targetHeight / height;
27654                 }
27655                 
27656                 if(x > 0 && y > 0){
27657                     scale = targetWidth / width;
27658                     
27659                     if(width < height){
27660                         scale = targetHeight / height;
27661                     }
27662                 }
27663                 
27664                 context.scale(scale, scale);
27665                 
27666                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27667                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27668
27669                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27670                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27671                 
27672                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27673                 
27674                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27675                 
27676                 break;
27677             case 180 :
27678                 
27679                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27680                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27681                 
27682                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27683                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27684                 
27685                 var targetWidth = this.minWidth - 2 * x;
27686                 var targetHeight = this.minHeight - 2 * y;
27687                 
27688                 var scale = 1;
27689                 
27690                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27691                     scale = targetWidth / width;
27692                 }
27693                 
27694                 if(x > 0 && y == 0){
27695                     scale = targetHeight / height;
27696                 }
27697                 
27698                 if(x > 0 && y > 0){
27699                     scale = targetWidth / width;
27700                     
27701                     if(width < height){
27702                         scale = targetHeight / height;
27703                     }
27704                 }
27705                 
27706                 context.scale(scale, scale);
27707                 
27708                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27709                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27710
27711                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27712                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27713
27714                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27715                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27716                 
27717                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27718                 
27719                 break;
27720             case 270 :
27721                 
27722                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27723                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27724                 
27725                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27726                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27727                 
27728                 var targetWidth = this.minWidth - 2 * x;
27729                 var targetHeight = this.minHeight - 2 * y;
27730                 
27731                 var scale = 1;
27732                 
27733                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27734                     scale = targetWidth / width;
27735                 }
27736                 
27737                 if(x > 0 && y == 0){
27738                     scale = targetHeight / height;
27739                 }
27740                 
27741                 if(x > 0 && y > 0){
27742                     scale = targetWidth / width;
27743                     
27744                     if(width < height){
27745                         scale = targetHeight / height;
27746                     }
27747                 }
27748                 
27749                 context.scale(scale, scale);
27750                 
27751                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27752                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27753
27754                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27755                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27756                 
27757                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27758                 
27759                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27760                 
27761                 break;
27762             default : 
27763                 break;
27764         }
27765         
27766         this.cropData = canvas.toDataURL(this.cropType);
27767         
27768         if(this.fireEvent('crop', this, this.cropData) !== false){
27769             this.process(this.file, this.cropData);
27770         }
27771         
27772         return;
27773         
27774     },
27775     
27776     setThumbBoxSize : function()
27777     {
27778         var width, height;
27779         
27780         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27781             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27782             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27783             
27784             this.minWidth = width;
27785             this.minHeight = height;
27786             
27787             if(this.rotate == 90 || this.rotate == 270){
27788                 this.minWidth = height;
27789                 this.minHeight = width;
27790             }
27791         }
27792         
27793         height = 300;
27794         width = Math.ceil(this.minWidth * height / this.minHeight);
27795         
27796         if(this.minWidth > this.minHeight){
27797             width = 300;
27798             height = Math.ceil(this.minHeight * width / this.minWidth);
27799         }
27800         
27801         this.thumbEl.setStyle({
27802             width : width + 'px',
27803             height : height + 'px'
27804         });
27805
27806         return;
27807             
27808     },
27809     
27810     setThumbBoxPosition : function()
27811     {
27812         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27813         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27814         
27815         this.thumbEl.setLeft(x);
27816         this.thumbEl.setTop(y);
27817         
27818     },
27819     
27820     baseRotateLevel : function()
27821     {
27822         this.baseRotate = 1;
27823         
27824         if(
27825                 typeof(this.exif) != 'undefined' &&
27826                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27827                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27828         ){
27829             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27830         }
27831         
27832         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27833         
27834     },
27835     
27836     baseScaleLevel : function()
27837     {
27838         var width, height;
27839         
27840         if(this.isDocument){
27841             
27842             if(this.baseRotate == 6 || this.baseRotate == 8){
27843             
27844                 height = this.thumbEl.getHeight();
27845                 this.baseScale = height / this.imageEl.OriginWidth;
27846
27847                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27848                     width = this.thumbEl.getWidth();
27849                     this.baseScale = width / this.imageEl.OriginHeight;
27850                 }
27851
27852                 return;
27853             }
27854
27855             height = this.thumbEl.getHeight();
27856             this.baseScale = height / this.imageEl.OriginHeight;
27857
27858             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27859                 width = this.thumbEl.getWidth();
27860                 this.baseScale = width / this.imageEl.OriginWidth;
27861             }
27862
27863             return;
27864         }
27865         
27866         if(this.baseRotate == 6 || this.baseRotate == 8){
27867             
27868             width = this.thumbEl.getHeight();
27869             this.baseScale = width / this.imageEl.OriginHeight;
27870             
27871             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27872                 height = this.thumbEl.getWidth();
27873                 this.baseScale = height / this.imageEl.OriginHeight;
27874             }
27875             
27876             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27877                 height = this.thumbEl.getWidth();
27878                 this.baseScale = height / this.imageEl.OriginHeight;
27879                 
27880                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27881                     width = this.thumbEl.getHeight();
27882                     this.baseScale = width / this.imageEl.OriginWidth;
27883                 }
27884             }
27885             
27886             return;
27887         }
27888         
27889         width = this.thumbEl.getWidth();
27890         this.baseScale = width / this.imageEl.OriginWidth;
27891         
27892         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27893             height = this.thumbEl.getHeight();
27894             this.baseScale = height / this.imageEl.OriginHeight;
27895         }
27896         
27897         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27898             
27899             height = this.thumbEl.getHeight();
27900             this.baseScale = height / this.imageEl.OriginHeight;
27901             
27902             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27903                 width = this.thumbEl.getWidth();
27904                 this.baseScale = width / this.imageEl.OriginWidth;
27905             }
27906             
27907         }
27908         
27909         return;
27910     },
27911     
27912     getScaleLevel : function()
27913     {
27914         return this.baseScale * Math.pow(1.1, this.scale);
27915     },
27916     
27917     onTouchStart : function(e)
27918     {
27919         if(!this.canvasLoaded){
27920             this.beforeSelectFile(e);
27921             return;
27922         }
27923         
27924         var touches = e.browserEvent.touches;
27925         
27926         if(!touches){
27927             return;
27928         }
27929         
27930         if(touches.length == 1){
27931             this.onMouseDown(e);
27932             return;
27933         }
27934         
27935         if(touches.length != 2){
27936             return;
27937         }
27938         
27939         var coords = [];
27940         
27941         for(var i = 0, finger; finger = touches[i]; i++){
27942             coords.push(finger.pageX, finger.pageY);
27943         }
27944         
27945         var x = Math.pow(coords[0] - coords[2], 2);
27946         var y = Math.pow(coords[1] - coords[3], 2);
27947         
27948         this.startDistance = Math.sqrt(x + y);
27949         
27950         this.startScale = this.scale;
27951         
27952         this.pinching = true;
27953         this.dragable = false;
27954         
27955     },
27956     
27957     onTouchMove : function(e)
27958     {
27959         if(!this.pinching && !this.dragable){
27960             return;
27961         }
27962         
27963         var touches = e.browserEvent.touches;
27964         
27965         if(!touches){
27966             return;
27967         }
27968         
27969         if(this.dragable){
27970             this.onMouseMove(e);
27971             return;
27972         }
27973         
27974         var coords = [];
27975         
27976         for(var i = 0, finger; finger = touches[i]; i++){
27977             coords.push(finger.pageX, finger.pageY);
27978         }
27979         
27980         var x = Math.pow(coords[0] - coords[2], 2);
27981         var y = Math.pow(coords[1] - coords[3], 2);
27982         
27983         this.endDistance = Math.sqrt(x + y);
27984         
27985         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
27986         
27987         if(!this.zoomable()){
27988             this.scale = this.startScale;
27989             return;
27990         }
27991         
27992         this.draw();
27993         
27994     },
27995     
27996     onTouchEnd : function(e)
27997     {
27998         this.pinching = false;
27999         this.dragable = false;
28000         
28001     },
28002     
28003     process : function(file, crop)
28004     {
28005         if(this.loadMask){
28006             this.maskEl.mask(this.loadingText);
28007         }
28008         
28009         this.xhr = new XMLHttpRequest();
28010         
28011         file.xhr = this.xhr;
28012
28013         this.xhr.open(this.method, this.url, true);
28014         
28015         var headers = {
28016             "Accept": "application/json",
28017             "Cache-Control": "no-cache",
28018             "X-Requested-With": "XMLHttpRequest"
28019         };
28020         
28021         for (var headerName in headers) {
28022             var headerValue = headers[headerName];
28023             if (headerValue) {
28024                 this.xhr.setRequestHeader(headerName, headerValue);
28025             }
28026         }
28027         
28028         var _this = this;
28029         
28030         this.xhr.onload = function()
28031         {
28032             _this.xhrOnLoad(_this.xhr);
28033         }
28034         
28035         this.xhr.onerror = function()
28036         {
28037             _this.xhrOnError(_this.xhr);
28038         }
28039         
28040         var formData = new FormData();
28041
28042         formData.append('returnHTML', 'NO');
28043         
28044         if(crop){
28045             formData.append('crop', crop);
28046         }
28047         
28048         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28049             formData.append(this.paramName, file, file.name);
28050         }
28051         
28052         if(typeof(file.filename) != 'undefined'){
28053             formData.append('filename', file.filename);
28054         }
28055         
28056         if(typeof(file.mimetype) != 'undefined'){
28057             formData.append('mimetype', file.mimetype);
28058         }
28059         
28060         if(this.fireEvent('arrange', this, formData) != false){
28061             this.xhr.send(formData);
28062         };
28063     },
28064     
28065     xhrOnLoad : function(xhr)
28066     {
28067         if(this.loadMask){
28068             this.maskEl.unmask();
28069         }
28070         
28071         if (xhr.readyState !== 4) {
28072             this.fireEvent('exception', this, xhr);
28073             return;
28074         }
28075
28076         var response = Roo.decode(xhr.responseText);
28077         
28078         if(!response.success){
28079             this.fireEvent('exception', this, xhr);
28080             return;
28081         }
28082         
28083         var response = Roo.decode(xhr.responseText);
28084         
28085         this.fireEvent('upload', this, response);
28086         
28087     },
28088     
28089     xhrOnError : function()
28090     {
28091         if(this.loadMask){
28092             this.maskEl.unmask();
28093         }
28094         
28095         Roo.log('xhr on error');
28096         
28097         var response = Roo.decode(xhr.responseText);
28098           
28099         Roo.log(response);
28100         
28101     },
28102     
28103     prepare : function(file)
28104     {   
28105         if(this.loadMask){
28106             this.maskEl.mask(this.loadingText);
28107         }
28108         
28109         this.file = false;
28110         this.exif = {};
28111         
28112         if(typeof(file) === 'string'){
28113             this.loadCanvas(file);
28114             return;
28115         }
28116         
28117         if(!file || !this.urlAPI){
28118             return;
28119         }
28120         
28121         this.file = file;
28122         this.cropType = file.type;
28123         
28124         var _this = this;
28125         
28126         if(this.fireEvent('prepare', this, this.file) != false){
28127             
28128             var reader = new FileReader();
28129             
28130             reader.onload = function (e) {
28131                 if (e.target.error) {
28132                     Roo.log(e.target.error);
28133                     return;
28134                 }
28135                 
28136                 var buffer = e.target.result,
28137                     dataView = new DataView(buffer),
28138                     offset = 2,
28139                     maxOffset = dataView.byteLength - 4,
28140                     markerBytes,
28141                     markerLength;
28142                 
28143                 if (dataView.getUint16(0) === 0xffd8) {
28144                     while (offset < maxOffset) {
28145                         markerBytes = dataView.getUint16(offset);
28146                         
28147                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28148                             markerLength = dataView.getUint16(offset + 2) + 2;
28149                             if (offset + markerLength > dataView.byteLength) {
28150                                 Roo.log('Invalid meta data: Invalid segment size.');
28151                                 break;
28152                             }
28153                             
28154                             if(markerBytes == 0xffe1){
28155                                 _this.parseExifData(
28156                                     dataView,
28157                                     offset,
28158                                     markerLength
28159                                 );
28160                             }
28161                             
28162                             offset += markerLength;
28163                             
28164                             continue;
28165                         }
28166                         
28167                         break;
28168                     }
28169                     
28170                 }
28171                 
28172                 var url = _this.urlAPI.createObjectURL(_this.file);
28173                 
28174                 _this.loadCanvas(url);
28175                 
28176                 return;
28177             }
28178             
28179             reader.readAsArrayBuffer(this.file);
28180             
28181         }
28182         
28183     },
28184     
28185     parseExifData : function(dataView, offset, length)
28186     {
28187         var tiffOffset = offset + 10,
28188             littleEndian,
28189             dirOffset;
28190     
28191         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28192             // No Exif data, might be XMP data instead
28193             return;
28194         }
28195         
28196         // Check for the ASCII code for "Exif" (0x45786966):
28197         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28198             // No Exif data, might be XMP data instead
28199             return;
28200         }
28201         if (tiffOffset + 8 > dataView.byteLength) {
28202             Roo.log('Invalid Exif data: Invalid segment size.');
28203             return;
28204         }
28205         // Check for the two null bytes:
28206         if (dataView.getUint16(offset + 8) !== 0x0000) {
28207             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28208             return;
28209         }
28210         // Check the byte alignment:
28211         switch (dataView.getUint16(tiffOffset)) {
28212         case 0x4949:
28213             littleEndian = true;
28214             break;
28215         case 0x4D4D:
28216             littleEndian = false;
28217             break;
28218         default:
28219             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28220             return;
28221         }
28222         // Check for the TIFF tag marker (0x002A):
28223         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28224             Roo.log('Invalid Exif data: Missing TIFF marker.');
28225             return;
28226         }
28227         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28228         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28229         
28230         this.parseExifTags(
28231             dataView,
28232             tiffOffset,
28233             tiffOffset + dirOffset,
28234             littleEndian
28235         );
28236     },
28237     
28238     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28239     {
28240         var tagsNumber,
28241             dirEndOffset,
28242             i;
28243         if (dirOffset + 6 > dataView.byteLength) {
28244             Roo.log('Invalid Exif data: Invalid directory offset.');
28245             return;
28246         }
28247         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28248         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28249         if (dirEndOffset + 4 > dataView.byteLength) {
28250             Roo.log('Invalid Exif data: Invalid directory size.');
28251             return;
28252         }
28253         for (i = 0; i < tagsNumber; i += 1) {
28254             this.parseExifTag(
28255                 dataView,
28256                 tiffOffset,
28257                 dirOffset + 2 + 12 * i, // tag offset
28258                 littleEndian
28259             );
28260         }
28261         // Return the offset to the next directory:
28262         return dataView.getUint32(dirEndOffset, littleEndian);
28263     },
28264     
28265     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28266     {
28267         var tag = dataView.getUint16(offset, littleEndian);
28268         
28269         this.exif[tag] = this.getExifValue(
28270             dataView,
28271             tiffOffset,
28272             offset,
28273             dataView.getUint16(offset + 2, littleEndian), // tag type
28274             dataView.getUint32(offset + 4, littleEndian), // tag length
28275             littleEndian
28276         );
28277     },
28278     
28279     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28280     {
28281         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28282             tagSize,
28283             dataOffset,
28284             values,
28285             i,
28286             str,
28287             c;
28288     
28289         if (!tagType) {
28290             Roo.log('Invalid Exif data: Invalid tag type.');
28291             return;
28292         }
28293         
28294         tagSize = tagType.size * length;
28295         // Determine if the value is contained in the dataOffset bytes,
28296         // or if the value at the dataOffset is a pointer to the actual data:
28297         dataOffset = tagSize > 4 ?
28298                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28299         if (dataOffset + tagSize > dataView.byteLength) {
28300             Roo.log('Invalid Exif data: Invalid data offset.');
28301             return;
28302         }
28303         if (length === 1) {
28304             return tagType.getValue(dataView, dataOffset, littleEndian);
28305         }
28306         values = [];
28307         for (i = 0; i < length; i += 1) {
28308             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28309         }
28310         
28311         if (tagType.ascii) {
28312             str = '';
28313             // Concatenate the chars:
28314             for (i = 0; i < values.length; i += 1) {
28315                 c = values[i];
28316                 // Ignore the terminating NULL byte(s):
28317                 if (c === '\u0000') {
28318                     break;
28319                 }
28320                 str += c;
28321             }
28322             return str;
28323         }
28324         return values;
28325     }
28326     
28327 });
28328
28329 Roo.apply(Roo.bootstrap.UploadCropbox, {
28330     tags : {
28331         'Orientation': 0x0112
28332     },
28333     
28334     Orientation: {
28335             1: 0, //'top-left',
28336 //            2: 'top-right',
28337             3: 180, //'bottom-right',
28338 //            4: 'bottom-left',
28339 //            5: 'left-top',
28340             6: 90, //'right-top',
28341 //            7: 'right-bottom',
28342             8: 270 //'left-bottom'
28343     },
28344     
28345     exifTagTypes : {
28346         // byte, 8-bit unsigned int:
28347         1: {
28348             getValue: function (dataView, dataOffset) {
28349                 return dataView.getUint8(dataOffset);
28350             },
28351             size: 1
28352         },
28353         // ascii, 8-bit byte:
28354         2: {
28355             getValue: function (dataView, dataOffset) {
28356                 return String.fromCharCode(dataView.getUint8(dataOffset));
28357             },
28358             size: 1,
28359             ascii: true
28360         },
28361         // short, 16 bit int:
28362         3: {
28363             getValue: function (dataView, dataOffset, littleEndian) {
28364                 return dataView.getUint16(dataOffset, littleEndian);
28365             },
28366             size: 2
28367         },
28368         // long, 32 bit int:
28369         4: {
28370             getValue: function (dataView, dataOffset, littleEndian) {
28371                 return dataView.getUint32(dataOffset, littleEndian);
28372             },
28373             size: 4
28374         },
28375         // rational = two long values, first is numerator, second is denominator:
28376         5: {
28377             getValue: function (dataView, dataOffset, littleEndian) {
28378                 return dataView.getUint32(dataOffset, littleEndian) /
28379                     dataView.getUint32(dataOffset + 4, littleEndian);
28380             },
28381             size: 8
28382         },
28383         // slong, 32 bit signed int:
28384         9: {
28385             getValue: function (dataView, dataOffset, littleEndian) {
28386                 return dataView.getInt32(dataOffset, littleEndian);
28387             },
28388             size: 4
28389         },
28390         // srational, two slongs, first is numerator, second is denominator:
28391         10: {
28392             getValue: function (dataView, dataOffset, littleEndian) {
28393                 return dataView.getInt32(dataOffset, littleEndian) /
28394                     dataView.getInt32(dataOffset + 4, littleEndian);
28395             },
28396             size: 8
28397         }
28398     },
28399     
28400     footer : {
28401         STANDARD : [
28402             {
28403                 tag : 'div',
28404                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28405                 action : 'rotate-left',
28406                 cn : [
28407                     {
28408                         tag : 'button',
28409                         cls : 'btn btn-default',
28410                         html : '<i class="fa fa-undo"></i>'
28411                     }
28412                 ]
28413             },
28414             {
28415                 tag : 'div',
28416                 cls : 'btn-group roo-upload-cropbox-picture',
28417                 action : 'picture',
28418                 cn : [
28419                     {
28420                         tag : 'button',
28421                         cls : 'btn btn-default',
28422                         html : '<i class="fa fa-picture-o"></i>'
28423                     }
28424                 ]
28425             },
28426             {
28427                 tag : 'div',
28428                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28429                 action : 'rotate-right',
28430                 cn : [
28431                     {
28432                         tag : 'button',
28433                         cls : 'btn btn-default',
28434                         html : '<i class="fa fa-repeat"></i>'
28435                     }
28436                 ]
28437             }
28438         ],
28439         DOCUMENT : [
28440             {
28441                 tag : 'div',
28442                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28443                 action : 'rotate-left',
28444                 cn : [
28445                     {
28446                         tag : 'button',
28447                         cls : 'btn btn-default',
28448                         html : '<i class="fa fa-undo"></i>'
28449                     }
28450                 ]
28451             },
28452             {
28453                 tag : 'div',
28454                 cls : 'btn-group roo-upload-cropbox-download',
28455                 action : 'download',
28456                 cn : [
28457                     {
28458                         tag : 'button',
28459                         cls : 'btn btn-default',
28460                         html : '<i class="fa fa-download"></i>'
28461                     }
28462                 ]
28463             },
28464             {
28465                 tag : 'div',
28466                 cls : 'btn-group roo-upload-cropbox-crop',
28467                 action : 'crop',
28468                 cn : [
28469                     {
28470                         tag : 'button',
28471                         cls : 'btn btn-default',
28472                         html : '<i class="fa fa-crop"></i>'
28473                     }
28474                 ]
28475             },
28476             {
28477                 tag : 'div',
28478                 cls : 'btn-group roo-upload-cropbox-trash',
28479                 action : 'trash',
28480                 cn : [
28481                     {
28482                         tag : 'button',
28483                         cls : 'btn btn-default',
28484                         html : '<i class="fa fa-trash"></i>'
28485                     }
28486                 ]
28487             },
28488             {
28489                 tag : 'div',
28490                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28491                 action : 'rotate-right',
28492                 cn : [
28493                     {
28494                         tag : 'button',
28495                         cls : 'btn btn-default',
28496                         html : '<i class="fa fa-repeat"></i>'
28497                     }
28498                 ]
28499             }
28500         ],
28501         ROTATOR : [
28502             {
28503                 tag : 'div',
28504                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28505                 action : 'rotate-left',
28506                 cn : [
28507                     {
28508                         tag : 'button',
28509                         cls : 'btn btn-default',
28510                         html : '<i class="fa fa-undo"></i>'
28511                     }
28512                 ]
28513             },
28514             {
28515                 tag : 'div',
28516                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28517                 action : 'rotate-right',
28518                 cn : [
28519                     {
28520                         tag : 'button',
28521                         cls : 'btn btn-default',
28522                         html : '<i class="fa fa-repeat"></i>'
28523                     }
28524                 ]
28525             }
28526         ]
28527     }
28528 });
28529
28530 /*
28531 * Licence: LGPL
28532 */
28533
28534 /**
28535  * @class Roo.bootstrap.DocumentManager
28536  * @extends Roo.bootstrap.Component
28537  * Bootstrap DocumentManager class
28538  * @cfg {String} paramName default 'imageUpload'
28539  * @cfg {String} toolTipName default 'filename'
28540  * @cfg {String} method default POST
28541  * @cfg {String} url action url
28542  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28543  * @cfg {Boolean} multiple multiple upload default true
28544  * @cfg {Number} thumbSize default 300
28545  * @cfg {String} fieldLabel
28546  * @cfg {Number} labelWidth default 4
28547  * @cfg {String} labelAlign (left|top) default left
28548  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28549 * @cfg {Number} labellg set the width of label (1-12)
28550  * @cfg {Number} labelmd set the width of label (1-12)
28551  * @cfg {Number} labelsm set the width of label (1-12)
28552  * @cfg {Number} labelxs set the width of label (1-12)
28553  * 
28554  * @constructor
28555  * Create a new DocumentManager
28556  * @param {Object} config The config object
28557  */
28558
28559 Roo.bootstrap.DocumentManager = function(config){
28560     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28561     
28562     this.files = [];
28563     this.delegates = [];
28564     
28565     this.addEvents({
28566         /**
28567          * @event initial
28568          * Fire when initial the DocumentManager
28569          * @param {Roo.bootstrap.DocumentManager} this
28570          */
28571         "initial" : true,
28572         /**
28573          * @event inspect
28574          * inspect selected file
28575          * @param {Roo.bootstrap.DocumentManager} this
28576          * @param {File} file
28577          */
28578         "inspect" : true,
28579         /**
28580          * @event exception
28581          * Fire when xhr load exception
28582          * @param {Roo.bootstrap.DocumentManager} this
28583          * @param {XMLHttpRequest} xhr
28584          */
28585         "exception" : true,
28586         /**
28587          * @event afterupload
28588          * Fire when xhr load exception
28589          * @param {Roo.bootstrap.DocumentManager} this
28590          * @param {XMLHttpRequest} xhr
28591          */
28592         "afterupload" : true,
28593         /**
28594          * @event prepare
28595          * prepare the form data
28596          * @param {Roo.bootstrap.DocumentManager} this
28597          * @param {Object} formData
28598          */
28599         "prepare" : true,
28600         /**
28601          * @event remove
28602          * Fire when remove the file
28603          * @param {Roo.bootstrap.DocumentManager} this
28604          * @param {Object} file
28605          */
28606         "remove" : true,
28607         /**
28608          * @event refresh
28609          * Fire after refresh the file
28610          * @param {Roo.bootstrap.DocumentManager} this
28611          */
28612         "refresh" : true,
28613         /**
28614          * @event click
28615          * Fire after click the image
28616          * @param {Roo.bootstrap.DocumentManager} this
28617          * @param {Object} file
28618          */
28619         "click" : true,
28620         /**
28621          * @event edit
28622          * Fire when upload a image and editable set to true
28623          * @param {Roo.bootstrap.DocumentManager} this
28624          * @param {Object} file
28625          */
28626         "edit" : true,
28627         /**
28628          * @event beforeselectfile
28629          * Fire before select file
28630          * @param {Roo.bootstrap.DocumentManager} this
28631          */
28632         "beforeselectfile" : true,
28633         /**
28634          * @event process
28635          * Fire before process file
28636          * @param {Roo.bootstrap.DocumentManager} this
28637          * @param {Object} file
28638          */
28639         "process" : true,
28640         /**
28641          * @event previewrendered
28642          * Fire when preview rendered
28643          * @param {Roo.bootstrap.DocumentManager} this
28644          * @param {Object} file
28645          */
28646         "previewrendered" : true
28647         
28648     });
28649 };
28650
28651 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28652     
28653     boxes : 0,
28654     inputName : '',
28655     thumbSize : 300,
28656     multiple : true,
28657     files : false,
28658     method : 'POST',
28659     url : '',
28660     paramName : 'imageUpload',
28661     toolTipName : 'filename',
28662     fieldLabel : '',
28663     labelWidth : 4,
28664     labelAlign : 'left',
28665     editable : true,
28666     delegates : false,
28667     xhr : false, 
28668     
28669     labellg : 0,
28670     labelmd : 0,
28671     labelsm : 0,
28672     labelxs : 0,
28673     
28674     getAutoCreate : function()
28675     {   
28676         var managerWidget = {
28677             tag : 'div',
28678             cls : 'roo-document-manager',
28679             cn : [
28680                 {
28681                     tag : 'input',
28682                     cls : 'roo-document-manager-selector',
28683                     type : 'file'
28684                 },
28685                 {
28686                     tag : 'div',
28687                     cls : 'roo-document-manager-uploader',
28688                     cn : [
28689                         {
28690                             tag : 'div',
28691                             cls : 'roo-document-manager-upload-btn',
28692                             html : '<i class="fa fa-plus"></i>'
28693                         }
28694                     ]
28695                     
28696                 }
28697             ]
28698         };
28699         
28700         var content = [
28701             {
28702                 tag : 'div',
28703                 cls : 'column col-md-12',
28704                 cn : managerWidget
28705             }
28706         ];
28707         
28708         if(this.fieldLabel.length){
28709             
28710             content = [
28711                 {
28712                     tag : 'div',
28713                     cls : 'column col-md-12',
28714                     html : this.fieldLabel
28715                 },
28716                 {
28717                     tag : 'div',
28718                     cls : 'column col-md-12',
28719                     cn : managerWidget
28720                 }
28721             ];
28722
28723             if(this.labelAlign == 'left'){
28724                 content = [
28725                     {
28726                         tag : 'div',
28727                         cls : 'column',
28728                         html : this.fieldLabel
28729                     },
28730                     {
28731                         tag : 'div',
28732                         cls : 'column',
28733                         cn : managerWidget
28734                     }
28735                 ];
28736                 
28737                 if(this.labelWidth > 12){
28738                     content[0].style = "width: " + this.labelWidth + 'px';
28739                 }
28740
28741                 if(this.labelWidth < 13 && this.labelmd == 0){
28742                     this.labelmd = this.labelWidth;
28743                 }
28744
28745                 if(this.labellg > 0){
28746                     content[0].cls += ' col-lg-' + this.labellg;
28747                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28748                 }
28749
28750                 if(this.labelmd > 0){
28751                     content[0].cls += ' col-md-' + this.labelmd;
28752                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28753                 }
28754
28755                 if(this.labelsm > 0){
28756                     content[0].cls += ' col-sm-' + this.labelsm;
28757                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28758                 }
28759
28760                 if(this.labelxs > 0){
28761                     content[0].cls += ' col-xs-' + this.labelxs;
28762                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28763                 }
28764                 
28765             }
28766         }
28767         
28768         var cfg = {
28769             tag : 'div',
28770             cls : 'row clearfix',
28771             cn : content
28772         };
28773         
28774         return cfg;
28775         
28776     },
28777     
28778     initEvents : function()
28779     {
28780         this.managerEl = this.el.select('.roo-document-manager', true).first();
28781         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28782         
28783         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28784         this.selectorEl.hide();
28785         
28786         if(this.multiple){
28787             this.selectorEl.attr('multiple', 'multiple');
28788         }
28789         
28790         this.selectorEl.on('change', this.onFileSelected, this);
28791         
28792         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28793         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28794         
28795         this.uploader.on('click', this.onUploaderClick, this);
28796         
28797         this.renderProgressDialog();
28798         
28799         var _this = this;
28800         
28801         window.addEventListener("resize", function() { _this.refresh(); } );
28802         
28803         this.fireEvent('initial', this);
28804     },
28805     
28806     renderProgressDialog : function()
28807     {
28808         var _this = this;
28809         
28810         this.progressDialog = new Roo.bootstrap.Modal({
28811             cls : 'roo-document-manager-progress-dialog',
28812             allow_close : false,
28813             title : '',
28814             buttons : [
28815                 {
28816                     name  :'cancel',
28817                     weight : 'danger',
28818                     html : 'Cancel'
28819                 }
28820             ], 
28821             listeners : { 
28822                 btnclick : function() {
28823                     _this.uploadCancel();
28824                     this.hide();
28825                 }
28826             }
28827         });
28828          
28829         this.progressDialog.render(Roo.get(document.body));
28830          
28831         this.progress = new Roo.bootstrap.Progress({
28832             cls : 'roo-document-manager-progress',
28833             active : true,
28834             striped : true
28835         });
28836         
28837         this.progress.render(this.progressDialog.getChildContainer());
28838         
28839         this.progressBar = new Roo.bootstrap.ProgressBar({
28840             cls : 'roo-document-manager-progress-bar',
28841             aria_valuenow : 0,
28842             aria_valuemin : 0,
28843             aria_valuemax : 12,
28844             panel : 'success'
28845         });
28846         
28847         this.progressBar.render(this.progress.getChildContainer());
28848     },
28849     
28850     onUploaderClick : function(e)
28851     {
28852         e.preventDefault();
28853      
28854         if(this.fireEvent('beforeselectfile', this) != false){
28855             this.selectorEl.dom.click();
28856         }
28857         
28858     },
28859     
28860     onFileSelected : function(e)
28861     {
28862         e.preventDefault();
28863         
28864         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28865             return;
28866         }
28867         
28868         Roo.each(this.selectorEl.dom.files, function(file){
28869             if(this.fireEvent('inspect', this, file) != false){
28870                 this.files.push(file);
28871             }
28872         }, this);
28873         
28874         this.queue();
28875         
28876     },
28877     
28878     queue : function()
28879     {
28880         this.selectorEl.dom.value = '';
28881         
28882         if(!this.files || !this.files.length){
28883             return;
28884         }
28885         
28886         if(this.boxes > 0 && this.files.length > this.boxes){
28887             this.files = this.files.slice(0, this.boxes);
28888         }
28889         
28890         this.uploader.show();
28891         
28892         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28893             this.uploader.hide();
28894         }
28895         
28896         var _this = this;
28897         
28898         var files = [];
28899         
28900         var docs = [];
28901         
28902         Roo.each(this.files, function(file){
28903             
28904             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28905                 var f = this.renderPreview(file);
28906                 files.push(f);
28907                 return;
28908             }
28909             
28910             if(file.type.indexOf('image') != -1){
28911                 this.delegates.push(
28912                     (function(){
28913                         _this.process(file);
28914                     }).createDelegate(this)
28915                 );
28916         
28917                 return;
28918             }
28919             
28920             docs.push(
28921                 (function(){
28922                     _this.process(file);
28923                 }).createDelegate(this)
28924             );
28925             
28926         }, this);
28927         
28928         this.files = files;
28929         
28930         this.delegates = this.delegates.concat(docs);
28931         
28932         if(!this.delegates.length){
28933             this.refresh();
28934             return;
28935         }
28936         
28937         this.progressBar.aria_valuemax = this.delegates.length;
28938         
28939         this.arrange();
28940         
28941         return;
28942     },
28943     
28944     arrange : function()
28945     {
28946         if(!this.delegates.length){
28947             this.progressDialog.hide();
28948             this.refresh();
28949             return;
28950         }
28951         
28952         var delegate = this.delegates.shift();
28953         
28954         this.progressDialog.show();
28955         
28956         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
28957         
28958         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
28959         
28960         delegate();
28961     },
28962     
28963     refresh : function()
28964     {
28965         this.uploader.show();
28966         
28967         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28968             this.uploader.hide();
28969         }
28970         
28971         Roo.isTouch ? this.closable(false) : this.closable(true);
28972         
28973         this.fireEvent('refresh', this);
28974     },
28975     
28976     onRemove : function(e, el, o)
28977     {
28978         e.preventDefault();
28979         
28980         this.fireEvent('remove', this, o);
28981         
28982     },
28983     
28984     remove : function(o)
28985     {
28986         var files = [];
28987         
28988         Roo.each(this.files, function(file){
28989             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
28990                 files.push(file);
28991                 return;
28992             }
28993
28994             o.target.remove();
28995
28996         }, this);
28997         
28998         this.files = files;
28999         
29000         this.refresh();
29001     },
29002     
29003     clear : function()
29004     {
29005         Roo.each(this.files, function(file){
29006             if(!file.target){
29007                 return;
29008             }
29009             
29010             file.target.remove();
29011
29012         }, this);
29013         
29014         this.files = [];
29015         
29016         this.refresh();
29017     },
29018     
29019     onClick : function(e, el, o)
29020     {
29021         e.preventDefault();
29022         
29023         this.fireEvent('click', this, o);
29024         
29025     },
29026     
29027     closable : function(closable)
29028     {
29029         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29030             
29031             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29032             
29033             if(closable){
29034                 el.show();
29035                 return;
29036             }
29037             
29038             el.hide();
29039             
29040         }, this);
29041     },
29042     
29043     xhrOnLoad : function(xhr)
29044     {
29045         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29046             el.remove();
29047         }, this);
29048         
29049         if (xhr.readyState !== 4) {
29050             this.arrange();
29051             this.fireEvent('exception', this, xhr);
29052             return;
29053         }
29054
29055         var response = Roo.decode(xhr.responseText);
29056         
29057         if(!response.success){
29058             this.arrange();
29059             this.fireEvent('exception', this, xhr);
29060             return;
29061         }
29062         
29063         var file = this.renderPreview(response.data);
29064         
29065         this.files.push(file);
29066         
29067         this.arrange();
29068         
29069         this.fireEvent('afterupload', this, xhr);
29070         
29071     },
29072     
29073     xhrOnError : function(xhr)
29074     {
29075         Roo.log('xhr on error');
29076         
29077         var response = Roo.decode(xhr.responseText);
29078           
29079         Roo.log(response);
29080         
29081         this.arrange();
29082     },
29083     
29084     process : function(file)
29085     {
29086         if(this.fireEvent('process', this, file) !== false){
29087             if(this.editable && file.type.indexOf('image') != -1){
29088                 this.fireEvent('edit', this, file);
29089                 return;
29090             }
29091
29092             this.uploadStart(file, false);
29093
29094             return;
29095         }
29096         
29097     },
29098     
29099     uploadStart : function(file, crop)
29100     {
29101         this.xhr = new XMLHttpRequest();
29102         
29103         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29104             this.arrange();
29105             return;
29106         }
29107         
29108         file.xhr = this.xhr;
29109             
29110         this.managerEl.createChild({
29111             tag : 'div',
29112             cls : 'roo-document-manager-loading',
29113             cn : [
29114                 {
29115                     tag : 'div',
29116                     tooltip : file.name,
29117                     cls : 'roo-document-manager-thumb',
29118                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29119                 }
29120             ]
29121
29122         });
29123
29124         this.xhr.open(this.method, this.url, true);
29125         
29126         var headers = {
29127             "Accept": "application/json",
29128             "Cache-Control": "no-cache",
29129             "X-Requested-With": "XMLHttpRequest"
29130         };
29131         
29132         for (var headerName in headers) {
29133             var headerValue = headers[headerName];
29134             if (headerValue) {
29135                 this.xhr.setRequestHeader(headerName, headerValue);
29136             }
29137         }
29138         
29139         var _this = this;
29140         
29141         this.xhr.onload = function()
29142         {
29143             _this.xhrOnLoad(_this.xhr);
29144         }
29145         
29146         this.xhr.onerror = function()
29147         {
29148             _this.xhrOnError(_this.xhr);
29149         }
29150         
29151         var formData = new FormData();
29152
29153         formData.append('returnHTML', 'NO');
29154         
29155         if(crop){
29156             formData.append('crop', crop);
29157         }
29158         
29159         formData.append(this.paramName, file, file.name);
29160         
29161         var options = {
29162             file : file, 
29163             manually : false
29164         };
29165         
29166         if(this.fireEvent('prepare', this, formData, options) != false){
29167             
29168             if(options.manually){
29169                 return;
29170             }
29171             
29172             this.xhr.send(formData);
29173             return;
29174         };
29175         
29176         this.uploadCancel();
29177     },
29178     
29179     uploadCancel : function()
29180     {
29181         if (this.xhr) {
29182             this.xhr.abort();
29183         }
29184         
29185         this.delegates = [];
29186         
29187         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29188             el.remove();
29189         }, this);
29190         
29191         this.arrange();
29192     },
29193     
29194     renderPreview : function(file)
29195     {
29196         if(typeof(file.target) != 'undefined' && file.target){
29197             return file;
29198         }
29199         
29200         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29201         
29202         var previewEl = this.managerEl.createChild({
29203             tag : 'div',
29204             cls : 'roo-document-manager-preview',
29205             cn : [
29206                 {
29207                     tag : 'div',
29208                     tooltip : file[this.toolTipName],
29209                     cls : 'roo-document-manager-thumb',
29210                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29211                 },
29212                 {
29213                     tag : 'button',
29214                     cls : 'close',
29215                     html : '<i class="fa fa-times-circle"></i>'
29216                 }
29217             ]
29218         });
29219
29220         var close = previewEl.select('button.close', true).first();
29221
29222         close.on('click', this.onRemove, this, file);
29223
29224         file.target = previewEl;
29225
29226         var image = previewEl.select('img', true).first();
29227         
29228         var _this = this;
29229         
29230         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29231         
29232         image.on('click', this.onClick, this, file);
29233         
29234         this.fireEvent('previewrendered', this, file);
29235         
29236         return file;
29237         
29238     },
29239     
29240     onPreviewLoad : function(file, image)
29241     {
29242         if(typeof(file.target) == 'undefined' || !file.target){
29243             return;
29244         }
29245         
29246         var width = image.dom.naturalWidth || image.dom.width;
29247         var height = image.dom.naturalHeight || image.dom.height;
29248         
29249         if(width > height){
29250             file.target.addClass('wide');
29251             return;
29252         }
29253         
29254         file.target.addClass('tall');
29255         return;
29256         
29257     },
29258     
29259     uploadFromSource : function(file, crop)
29260     {
29261         this.xhr = new XMLHttpRequest();
29262         
29263         this.managerEl.createChild({
29264             tag : 'div',
29265             cls : 'roo-document-manager-loading',
29266             cn : [
29267                 {
29268                     tag : 'div',
29269                     tooltip : file.name,
29270                     cls : 'roo-document-manager-thumb',
29271                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29272                 }
29273             ]
29274
29275         });
29276
29277         this.xhr.open(this.method, this.url, true);
29278         
29279         var headers = {
29280             "Accept": "application/json",
29281             "Cache-Control": "no-cache",
29282             "X-Requested-With": "XMLHttpRequest"
29283         };
29284         
29285         for (var headerName in headers) {
29286             var headerValue = headers[headerName];
29287             if (headerValue) {
29288                 this.xhr.setRequestHeader(headerName, headerValue);
29289             }
29290         }
29291         
29292         var _this = this;
29293         
29294         this.xhr.onload = function()
29295         {
29296             _this.xhrOnLoad(_this.xhr);
29297         }
29298         
29299         this.xhr.onerror = function()
29300         {
29301             _this.xhrOnError(_this.xhr);
29302         }
29303         
29304         var formData = new FormData();
29305
29306         formData.append('returnHTML', 'NO');
29307         
29308         formData.append('crop', crop);
29309         
29310         if(typeof(file.filename) != 'undefined'){
29311             formData.append('filename', file.filename);
29312         }
29313         
29314         if(typeof(file.mimetype) != 'undefined'){
29315             formData.append('mimetype', file.mimetype);
29316         }
29317         
29318         Roo.log(formData);
29319         
29320         if(this.fireEvent('prepare', this, formData) != false){
29321             this.xhr.send(formData);
29322         };
29323     }
29324 });
29325
29326 /*
29327 * Licence: LGPL
29328 */
29329
29330 /**
29331  * @class Roo.bootstrap.DocumentViewer
29332  * @extends Roo.bootstrap.Component
29333  * Bootstrap DocumentViewer class
29334  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29335  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29336  * 
29337  * @constructor
29338  * Create a new DocumentViewer
29339  * @param {Object} config The config object
29340  */
29341
29342 Roo.bootstrap.DocumentViewer = function(config){
29343     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29344     
29345     this.addEvents({
29346         /**
29347          * @event initial
29348          * Fire after initEvent
29349          * @param {Roo.bootstrap.DocumentViewer} this
29350          */
29351         "initial" : true,
29352         /**
29353          * @event click
29354          * Fire after click
29355          * @param {Roo.bootstrap.DocumentViewer} this
29356          */
29357         "click" : true,
29358         /**
29359          * @event download
29360          * Fire after download button
29361          * @param {Roo.bootstrap.DocumentViewer} this
29362          */
29363         "download" : true,
29364         /**
29365          * @event trash
29366          * Fire after trash button
29367          * @param {Roo.bootstrap.DocumentViewer} this
29368          */
29369         "trash" : true
29370         
29371     });
29372 };
29373
29374 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29375     
29376     showDownload : true,
29377     
29378     showTrash : true,
29379     
29380     getAutoCreate : function()
29381     {
29382         var cfg = {
29383             tag : 'div',
29384             cls : 'roo-document-viewer',
29385             cn : [
29386                 {
29387                     tag : 'div',
29388                     cls : 'roo-document-viewer-body',
29389                     cn : [
29390                         {
29391                             tag : 'div',
29392                             cls : 'roo-document-viewer-thumb',
29393                             cn : [
29394                                 {
29395                                     tag : 'img',
29396                                     cls : 'roo-document-viewer-image'
29397                                 }
29398                             ]
29399                         }
29400                     ]
29401                 },
29402                 {
29403                     tag : 'div',
29404                     cls : 'roo-document-viewer-footer',
29405                     cn : {
29406                         tag : 'div',
29407                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29408                         cn : [
29409                             {
29410                                 tag : 'div',
29411                                 cls : 'btn-group roo-document-viewer-download',
29412                                 cn : [
29413                                     {
29414                                         tag : 'button',
29415                                         cls : 'btn btn-default',
29416                                         html : '<i class="fa fa-download"></i>'
29417                                     }
29418                                 ]
29419                             },
29420                             {
29421                                 tag : 'div',
29422                                 cls : 'btn-group roo-document-viewer-trash',
29423                                 cn : [
29424                                     {
29425                                         tag : 'button',
29426                                         cls : 'btn btn-default',
29427                                         html : '<i class="fa fa-trash"></i>'
29428                                     }
29429                                 ]
29430                             }
29431                         ]
29432                     }
29433                 }
29434             ]
29435         };
29436         
29437         return cfg;
29438     },
29439     
29440     initEvents : function()
29441     {
29442         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29443         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29444         
29445         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29446         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29447         
29448         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29449         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29450         
29451         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29452         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29453         
29454         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29455         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29456         
29457         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29458         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29459         
29460         this.bodyEl.on('click', this.onClick, this);
29461         this.downloadBtn.on('click', this.onDownload, this);
29462         this.trashBtn.on('click', this.onTrash, this);
29463         
29464         this.downloadBtn.hide();
29465         this.trashBtn.hide();
29466         
29467         if(this.showDownload){
29468             this.downloadBtn.show();
29469         }
29470         
29471         if(this.showTrash){
29472             this.trashBtn.show();
29473         }
29474         
29475         if(!this.showDownload && !this.showTrash) {
29476             this.footerEl.hide();
29477         }
29478         
29479     },
29480     
29481     initial : function()
29482     {
29483         this.fireEvent('initial', this);
29484         
29485     },
29486     
29487     onClick : function(e)
29488     {
29489         e.preventDefault();
29490         
29491         this.fireEvent('click', this);
29492     },
29493     
29494     onDownload : function(e)
29495     {
29496         e.preventDefault();
29497         
29498         this.fireEvent('download', this);
29499     },
29500     
29501     onTrash : function(e)
29502     {
29503         e.preventDefault();
29504         
29505         this.fireEvent('trash', this);
29506     }
29507     
29508 });
29509 /*
29510  * - LGPL
29511  *
29512  * nav progress bar
29513  * 
29514  */
29515
29516 /**
29517  * @class Roo.bootstrap.NavProgressBar
29518  * @extends Roo.bootstrap.Component
29519  * Bootstrap NavProgressBar class
29520  * 
29521  * @constructor
29522  * Create a new nav progress bar
29523  * @param {Object} config The config object
29524  */
29525
29526 Roo.bootstrap.NavProgressBar = function(config){
29527     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29528
29529     this.bullets = this.bullets || [];
29530    
29531 //    Roo.bootstrap.NavProgressBar.register(this);
29532      this.addEvents({
29533         /**
29534              * @event changed
29535              * Fires when the active item changes
29536              * @param {Roo.bootstrap.NavProgressBar} this
29537              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29538              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29539          */
29540         'changed': true
29541      });
29542     
29543 };
29544
29545 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29546     
29547     bullets : [],
29548     barItems : [],
29549     
29550     getAutoCreate : function()
29551     {
29552         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29553         
29554         cfg = {
29555             tag : 'div',
29556             cls : 'roo-navigation-bar-group',
29557             cn : [
29558                 {
29559                     tag : 'div',
29560                     cls : 'roo-navigation-top-bar'
29561                 },
29562                 {
29563                     tag : 'div',
29564                     cls : 'roo-navigation-bullets-bar',
29565                     cn : [
29566                         {
29567                             tag : 'ul',
29568                             cls : 'roo-navigation-bar'
29569                         }
29570                     ]
29571                 },
29572                 
29573                 {
29574                     tag : 'div',
29575                     cls : 'roo-navigation-bottom-bar'
29576                 }
29577             ]
29578             
29579         };
29580         
29581         return cfg;
29582         
29583     },
29584     
29585     initEvents: function() 
29586     {
29587         
29588     },
29589     
29590     onRender : function(ct, position) 
29591     {
29592         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29593         
29594         if(this.bullets.length){
29595             Roo.each(this.bullets, function(b){
29596                this.addItem(b);
29597             }, this);
29598         }
29599         
29600         this.format();
29601         
29602     },
29603     
29604     addItem : function(cfg)
29605     {
29606         var item = new Roo.bootstrap.NavProgressItem(cfg);
29607         
29608         item.parentId = this.id;
29609         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29610         
29611         if(cfg.html){
29612             var top = new Roo.bootstrap.Element({
29613                 tag : 'div',
29614                 cls : 'roo-navigation-bar-text'
29615             });
29616             
29617             var bottom = new Roo.bootstrap.Element({
29618                 tag : 'div',
29619                 cls : 'roo-navigation-bar-text'
29620             });
29621             
29622             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29623             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29624             
29625             var topText = new Roo.bootstrap.Element({
29626                 tag : 'span',
29627                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29628             });
29629             
29630             var bottomText = new Roo.bootstrap.Element({
29631                 tag : 'span',
29632                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29633             });
29634             
29635             topText.onRender(top.el, null);
29636             bottomText.onRender(bottom.el, null);
29637             
29638             item.topEl = top;
29639             item.bottomEl = bottom;
29640         }
29641         
29642         this.barItems.push(item);
29643         
29644         return item;
29645     },
29646     
29647     getActive : function()
29648     {
29649         var active = false;
29650         
29651         Roo.each(this.barItems, function(v){
29652             
29653             if (!v.isActive()) {
29654                 return;
29655             }
29656             
29657             active = v;
29658             return false;
29659             
29660         });
29661         
29662         return active;
29663     },
29664     
29665     setActiveItem : function(item)
29666     {
29667         var prev = false;
29668         
29669         Roo.each(this.barItems, function(v){
29670             if (v.rid == item.rid) {
29671                 return ;
29672             }
29673             
29674             if (v.isActive()) {
29675                 v.setActive(false);
29676                 prev = v;
29677             }
29678         });
29679
29680         item.setActive(true);
29681         
29682         this.fireEvent('changed', this, item, prev);
29683     },
29684     
29685     getBarItem: function(rid)
29686     {
29687         var ret = false;
29688         
29689         Roo.each(this.barItems, function(e) {
29690             if (e.rid != rid) {
29691                 return;
29692             }
29693             
29694             ret =  e;
29695             return false;
29696         });
29697         
29698         return ret;
29699     },
29700     
29701     indexOfItem : function(item)
29702     {
29703         var index = false;
29704         
29705         Roo.each(this.barItems, function(v, i){
29706             
29707             if (v.rid != item.rid) {
29708                 return;
29709             }
29710             
29711             index = i;
29712             return false
29713         });
29714         
29715         return index;
29716     },
29717     
29718     setActiveNext : function()
29719     {
29720         var i = this.indexOfItem(this.getActive());
29721         
29722         if (i > this.barItems.length) {
29723             return;
29724         }
29725         
29726         this.setActiveItem(this.barItems[i+1]);
29727     },
29728     
29729     setActivePrev : function()
29730     {
29731         var i = this.indexOfItem(this.getActive());
29732         
29733         if (i  < 1) {
29734             return;
29735         }
29736         
29737         this.setActiveItem(this.barItems[i-1]);
29738     },
29739     
29740     format : function()
29741     {
29742         if(!this.barItems.length){
29743             return;
29744         }
29745      
29746         var width = 100 / this.barItems.length;
29747         
29748         Roo.each(this.barItems, function(i){
29749             i.el.setStyle('width', width + '%');
29750             i.topEl.el.setStyle('width', width + '%');
29751             i.bottomEl.el.setStyle('width', width + '%');
29752         }, this);
29753         
29754     }
29755     
29756 });
29757 /*
29758  * - LGPL
29759  *
29760  * Nav Progress Item
29761  * 
29762  */
29763
29764 /**
29765  * @class Roo.bootstrap.NavProgressItem
29766  * @extends Roo.bootstrap.Component
29767  * Bootstrap NavProgressItem class
29768  * @cfg {String} rid the reference id
29769  * @cfg {Boolean} active (true|false) Is item active default false
29770  * @cfg {Boolean} disabled (true|false) Is item active default false
29771  * @cfg {String} html
29772  * @cfg {String} position (top|bottom) text position default bottom
29773  * @cfg {String} icon show icon instead of number
29774  * 
29775  * @constructor
29776  * Create a new NavProgressItem
29777  * @param {Object} config The config object
29778  */
29779 Roo.bootstrap.NavProgressItem = function(config){
29780     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29781     this.addEvents({
29782         // raw events
29783         /**
29784          * @event click
29785          * The raw click event for the entire grid.
29786          * @param {Roo.bootstrap.NavProgressItem} this
29787          * @param {Roo.EventObject} e
29788          */
29789         "click" : true
29790     });
29791    
29792 };
29793
29794 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29795     
29796     rid : '',
29797     active : false,
29798     disabled : false,
29799     html : '',
29800     position : 'bottom',
29801     icon : false,
29802     
29803     getAutoCreate : function()
29804     {
29805         var iconCls = 'roo-navigation-bar-item-icon';
29806         
29807         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29808         
29809         var cfg = {
29810             tag: 'li',
29811             cls: 'roo-navigation-bar-item',
29812             cn : [
29813                 {
29814                     tag : 'i',
29815                     cls : iconCls
29816                 }
29817             ]
29818         };
29819         
29820         if(this.active){
29821             cfg.cls += ' active';
29822         }
29823         if(this.disabled){
29824             cfg.cls += ' disabled';
29825         }
29826         
29827         return cfg;
29828     },
29829     
29830     disable : function()
29831     {
29832         this.setDisabled(true);
29833     },
29834     
29835     enable : function()
29836     {
29837         this.setDisabled(false);
29838     },
29839     
29840     initEvents: function() 
29841     {
29842         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29843         
29844         this.iconEl.on('click', this.onClick, this);
29845     },
29846     
29847     onClick : function(e)
29848     {
29849         e.preventDefault();
29850         
29851         if(this.disabled){
29852             return;
29853         }
29854         
29855         if(this.fireEvent('click', this, e) === false){
29856             return;
29857         };
29858         
29859         this.parent().setActiveItem(this);
29860     },
29861     
29862     isActive: function () 
29863     {
29864         return this.active;
29865     },
29866     
29867     setActive : function(state)
29868     {
29869         if(this.active == state){
29870             return;
29871         }
29872         
29873         this.active = state;
29874         
29875         if (state) {
29876             this.el.addClass('active');
29877             return;
29878         }
29879         
29880         this.el.removeClass('active');
29881         
29882         return;
29883     },
29884     
29885     setDisabled : function(state)
29886     {
29887         if(this.disabled == state){
29888             return;
29889         }
29890         
29891         this.disabled = state;
29892         
29893         if (state) {
29894             this.el.addClass('disabled');
29895             return;
29896         }
29897         
29898         this.el.removeClass('disabled');
29899     },
29900     
29901     tooltipEl : function()
29902     {
29903         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29904     }
29905 });
29906  
29907
29908  /*
29909  * - LGPL
29910  *
29911  * FieldLabel
29912  * 
29913  */
29914
29915 /**
29916  * @class Roo.bootstrap.FieldLabel
29917  * @extends Roo.bootstrap.Component
29918  * Bootstrap FieldLabel class
29919  * @cfg {String} html contents of the element
29920  * @cfg {String} tag tag of the element default label
29921  * @cfg {String} cls class of the element
29922  * @cfg {String} target label target 
29923  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29924  * @cfg {String} invalidClass default "text-warning"
29925  * @cfg {String} validClass default "text-success"
29926  * @cfg {String} iconTooltip default "This field is required"
29927  * @cfg {String} indicatorpos (left|right) default left
29928  * 
29929  * @constructor
29930  * Create a new FieldLabel
29931  * @param {Object} config The config object
29932  */
29933
29934 Roo.bootstrap.FieldLabel = function(config){
29935     Roo.bootstrap.Element.superclass.constructor.call(this, config);
29936     
29937     this.addEvents({
29938             /**
29939              * @event invalid
29940              * Fires after the field has been marked as invalid.
29941              * @param {Roo.form.FieldLabel} this
29942              * @param {String} msg The validation message
29943              */
29944             invalid : true,
29945             /**
29946              * @event valid
29947              * Fires after the field has been validated with no errors.
29948              * @param {Roo.form.FieldLabel} this
29949              */
29950             valid : true
29951         });
29952 };
29953
29954 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
29955     
29956     tag: 'label',
29957     cls: '',
29958     html: '',
29959     target: '',
29960     allowBlank : true,
29961     invalidClass : 'has-warning',
29962     validClass : 'has-success',
29963     iconTooltip : 'This field is required',
29964     indicatorpos : 'left',
29965     
29966     getAutoCreate : function(){
29967         
29968         var cfg = {
29969             tag : this.tag,
29970             cls : 'roo-bootstrap-field-label ' + this.cls,
29971             for : this.target,
29972             cn : [
29973                 {
29974                     tag : 'i',
29975                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
29976                     tooltip : this.iconTooltip
29977                 },
29978                 {
29979                     tag : 'span',
29980                     html : this.html
29981                 }
29982             ] 
29983         };
29984         
29985         if(this.indicatorpos == 'right'){
29986             var cfg = {
29987                 tag : this.tag,
29988                 cls : 'roo-bootstrap-field-label ' + this.cls,
29989                 for : this.target,
29990                 cn : [
29991                     {
29992                         tag : 'span',
29993                         html : this.html
29994                     },
29995                     {
29996                         tag : 'i',
29997                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
29998                         tooltip : this.iconTooltip
29999                     }
30000                 ] 
30001             };
30002         }
30003         
30004         return cfg;
30005     },
30006     
30007     initEvents: function() 
30008     {
30009         Roo.bootstrap.Element.superclass.initEvents.call(this);
30010         
30011         this.indicator = this.indicatorEl();
30012         
30013         if(this.indicator){
30014             this.indicator.removeClass('visible');
30015             this.indicator.addClass('invisible');
30016         }
30017         
30018         Roo.bootstrap.FieldLabel.register(this);
30019     },
30020     
30021     indicatorEl : function()
30022     {
30023         var indicator = this.el.select('i.roo-required-indicator',true).first();
30024         
30025         if(!indicator){
30026             return false;
30027         }
30028         
30029         return indicator;
30030         
30031     },
30032     
30033     /**
30034      * Mark this field as valid
30035      */
30036     markValid : function()
30037     {
30038         if(this.indicator){
30039             this.indicator.removeClass('visible');
30040             this.indicator.addClass('invisible');
30041         }
30042         
30043         this.el.removeClass(this.invalidClass);
30044         
30045         this.el.addClass(this.validClass);
30046         
30047         this.fireEvent('valid', this);
30048     },
30049     
30050     /**
30051      * Mark this field as invalid
30052      * @param {String} msg The validation message
30053      */
30054     markInvalid : function(msg)
30055     {
30056         if(this.indicator){
30057             this.indicator.removeClass('invisible');
30058             this.indicator.addClass('visible');
30059         }
30060         
30061         this.el.removeClass(this.validClass);
30062         
30063         this.el.addClass(this.invalidClass);
30064         
30065         this.fireEvent('invalid', this, msg);
30066     }
30067     
30068    
30069 });
30070
30071 Roo.apply(Roo.bootstrap.FieldLabel, {
30072     
30073     groups: {},
30074     
30075      /**
30076     * register a FieldLabel Group
30077     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30078     */
30079     register : function(label)
30080     {
30081         if(this.groups.hasOwnProperty(label.target)){
30082             return;
30083         }
30084      
30085         this.groups[label.target] = label;
30086         
30087     },
30088     /**
30089     * fetch a FieldLabel Group based on the target
30090     * @param {string} target
30091     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30092     */
30093     get: function(target) {
30094         if (typeof(this.groups[target]) == 'undefined') {
30095             return false;
30096         }
30097         
30098         return this.groups[target] ;
30099     }
30100 });
30101
30102  
30103
30104  /*
30105  * - LGPL
30106  *
30107  * page DateSplitField.
30108  * 
30109  */
30110
30111
30112 /**
30113  * @class Roo.bootstrap.DateSplitField
30114  * @extends Roo.bootstrap.Component
30115  * Bootstrap DateSplitField class
30116  * @cfg {string} fieldLabel - the label associated
30117  * @cfg {Number} labelWidth set the width of label (0-12)
30118  * @cfg {String} labelAlign (top|left)
30119  * @cfg {Boolean} dayAllowBlank (true|false) default false
30120  * @cfg {Boolean} monthAllowBlank (true|false) default false
30121  * @cfg {Boolean} yearAllowBlank (true|false) default false
30122  * @cfg {string} dayPlaceholder 
30123  * @cfg {string} monthPlaceholder
30124  * @cfg {string} yearPlaceholder
30125  * @cfg {string} dayFormat default 'd'
30126  * @cfg {string} monthFormat default 'm'
30127  * @cfg {string} yearFormat default 'Y'
30128  * @cfg {Number} labellg set the width of label (1-12)
30129  * @cfg {Number} labelmd set the width of label (1-12)
30130  * @cfg {Number} labelsm set the width of label (1-12)
30131  * @cfg {Number} labelxs set the width of label (1-12)
30132
30133  *     
30134  * @constructor
30135  * Create a new DateSplitField
30136  * @param {Object} config The config object
30137  */
30138
30139 Roo.bootstrap.DateSplitField = function(config){
30140     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30141     
30142     this.addEvents({
30143         // raw events
30144          /**
30145          * @event years
30146          * getting the data of years
30147          * @param {Roo.bootstrap.DateSplitField} this
30148          * @param {Object} years
30149          */
30150         "years" : true,
30151         /**
30152          * @event days
30153          * getting the data of days
30154          * @param {Roo.bootstrap.DateSplitField} this
30155          * @param {Object} days
30156          */
30157         "days" : true,
30158         /**
30159          * @event invalid
30160          * Fires after the field has been marked as invalid.
30161          * @param {Roo.form.Field} this
30162          * @param {String} msg The validation message
30163          */
30164         invalid : true,
30165        /**
30166          * @event valid
30167          * Fires after the field has been validated with no errors.
30168          * @param {Roo.form.Field} this
30169          */
30170         valid : true
30171     });
30172 };
30173
30174 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30175     
30176     fieldLabel : '',
30177     labelAlign : 'top',
30178     labelWidth : 3,
30179     dayAllowBlank : false,
30180     monthAllowBlank : false,
30181     yearAllowBlank : false,
30182     dayPlaceholder : '',
30183     monthPlaceholder : '',
30184     yearPlaceholder : '',
30185     dayFormat : 'd',
30186     monthFormat : 'm',
30187     yearFormat : 'Y',
30188     isFormField : true,
30189     labellg : 0,
30190     labelmd : 0,
30191     labelsm : 0,
30192     labelxs : 0,
30193     
30194     getAutoCreate : function()
30195     {
30196         var cfg = {
30197             tag : 'div',
30198             cls : 'row roo-date-split-field-group',
30199             cn : [
30200                 {
30201                     tag : 'input',
30202                     type : 'hidden',
30203                     cls : 'form-hidden-field roo-date-split-field-group-value',
30204                     name : this.name
30205                 }
30206             ]
30207         };
30208         
30209         var labelCls = 'col-md-12';
30210         var contentCls = 'col-md-4';
30211         
30212         if(this.fieldLabel){
30213             
30214             var label = {
30215                 tag : 'div',
30216                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30217                 cn : [
30218                     {
30219                         tag : 'label',
30220                         html : this.fieldLabel
30221                     }
30222                 ]
30223             };
30224             
30225             if(this.labelAlign == 'left'){
30226             
30227                 if(this.labelWidth > 12){
30228                     label.style = "width: " + this.labelWidth + 'px';
30229                 }
30230
30231                 if(this.labelWidth < 13 && this.labelmd == 0){
30232                     this.labelmd = this.labelWidth;
30233                 }
30234
30235                 if(this.labellg > 0){
30236                     labelCls = ' col-lg-' + this.labellg;
30237                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30238                 }
30239
30240                 if(this.labelmd > 0){
30241                     labelCls = ' col-md-' + this.labelmd;
30242                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30243                 }
30244
30245                 if(this.labelsm > 0){
30246                     labelCls = ' col-sm-' + this.labelsm;
30247                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30248                 }
30249
30250                 if(this.labelxs > 0){
30251                     labelCls = ' col-xs-' + this.labelxs;
30252                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30253                 }
30254             }
30255             
30256             label.cls += ' ' + labelCls;
30257             
30258             cfg.cn.push(label);
30259         }
30260         
30261         Roo.each(['day', 'month', 'year'], function(t){
30262             cfg.cn.push({
30263                 tag : 'div',
30264                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30265             });
30266         }, this);
30267         
30268         return cfg;
30269     },
30270     
30271     inputEl: function ()
30272     {
30273         return this.el.select('.roo-date-split-field-group-value', true).first();
30274     },
30275     
30276     onRender : function(ct, position) 
30277     {
30278         var _this = this;
30279         
30280         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30281         
30282         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30283         
30284         this.dayField = new Roo.bootstrap.ComboBox({
30285             allowBlank : this.dayAllowBlank,
30286             alwaysQuery : true,
30287             displayField : 'value',
30288             editable : false,
30289             fieldLabel : '',
30290             forceSelection : true,
30291             mode : 'local',
30292             placeholder : this.dayPlaceholder,
30293             selectOnFocus : true,
30294             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30295             triggerAction : 'all',
30296             typeAhead : true,
30297             valueField : 'value',
30298             store : new Roo.data.SimpleStore({
30299                 data : (function() {    
30300                     var days = [];
30301                     _this.fireEvent('days', _this, days);
30302                     return days;
30303                 })(),
30304                 fields : [ 'value' ]
30305             }),
30306             listeners : {
30307                 select : function (_self, record, index)
30308                 {
30309                     _this.setValue(_this.getValue());
30310                 }
30311             }
30312         });
30313
30314         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30315         
30316         this.monthField = new Roo.bootstrap.MonthField({
30317             after : '<i class=\"fa fa-calendar\"></i>',
30318             allowBlank : this.monthAllowBlank,
30319             placeholder : this.monthPlaceholder,
30320             readOnly : true,
30321             listeners : {
30322                 render : function (_self)
30323                 {
30324                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30325                         e.preventDefault();
30326                         _self.focus();
30327                     });
30328                 },
30329                 select : function (_self, oldvalue, newvalue)
30330                 {
30331                     _this.setValue(_this.getValue());
30332                 }
30333             }
30334         });
30335         
30336         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30337         
30338         this.yearField = new Roo.bootstrap.ComboBox({
30339             allowBlank : this.yearAllowBlank,
30340             alwaysQuery : true,
30341             displayField : 'value',
30342             editable : false,
30343             fieldLabel : '',
30344             forceSelection : true,
30345             mode : 'local',
30346             placeholder : this.yearPlaceholder,
30347             selectOnFocus : true,
30348             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30349             triggerAction : 'all',
30350             typeAhead : true,
30351             valueField : 'value',
30352             store : new Roo.data.SimpleStore({
30353                 data : (function() {
30354                     var years = [];
30355                     _this.fireEvent('years', _this, years);
30356                     return years;
30357                 })(),
30358                 fields : [ 'value' ]
30359             }),
30360             listeners : {
30361                 select : function (_self, record, index)
30362                 {
30363                     _this.setValue(_this.getValue());
30364                 }
30365             }
30366         });
30367
30368         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30369     },
30370     
30371     setValue : function(v, format)
30372     {
30373         this.inputEl.dom.value = v;
30374         
30375         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30376         
30377         var d = Date.parseDate(v, f);
30378         
30379         if(!d){
30380             this.validate();
30381             return;
30382         }
30383         
30384         this.setDay(d.format(this.dayFormat));
30385         this.setMonth(d.format(this.monthFormat));
30386         this.setYear(d.format(this.yearFormat));
30387         
30388         this.validate();
30389         
30390         return;
30391     },
30392     
30393     setDay : function(v)
30394     {
30395         this.dayField.setValue(v);
30396         this.inputEl.dom.value = this.getValue();
30397         this.validate();
30398         return;
30399     },
30400     
30401     setMonth : function(v)
30402     {
30403         this.monthField.setValue(v, true);
30404         this.inputEl.dom.value = this.getValue();
30405         this.validate();
30406         return;
30407     },
30408     
30409     setYear : function(v)
30410     {
30411         this.yearField.setValue(v);
30412         this.inputEl.dom.value = this.getValue();
30413         this.validate();
30414         return;
30415     },
30416     
30417     getDay : function()
30418     {
30419         return this.dayField.getValue();
30420     },
30421     
30422     getMonth : function()
30423     {
30424         return this.monthField.getValue();
30425     },
30426     
30427     getYear : function()
30428     {
30429         return this.yearField.getValue();
30430     },
30431     
30432     getValue : function()
30433     {
30434         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30435         
30436         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30437         
30438         return date;
30439     },
30440     
30441     reset : function()
30442     {
30443         this.setDay('');
30444         this.setMonth('');
30445         this.setYear('');
30446         this.inputEl.dom.value = '';
30447         this.validate();
30448         return;
30449     },
30450     
30451     validate : function()
30452     {
30453         var d = this.dayField.validate();
30454         var m = this.monthField.validate();
30455         var y = this.yearField.validate();
30456         
30457         var valid = true;
30458         
30459         if(
30460                 (!this.dayAllowBlank && !d) ||
30461                 (!this.monthAllowBlank && !m) ||
30462                 (!this.yearAllowBlank && !y)
30463         ){
30464             valid = false;
30465         }
30466         
30467         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30468             return valid;
30469         }
30470         
30471         if(valid){
30472             this.markValid();
30473             return valid;
30474         }
30475         
30476         this.markInvalid();
30477         
30478         return valid;
30479     },
30480     
30481     markValid : function()
30482     {
30483         
30484         var label = this.el.select('label', true).first();
30485         var icon = this.el.select('i.fa-star', true).first();
30486
30487         if(label && icon){
30488             icon.remove();
30489         }
30490         
30491         this.fireEvent('valid', this);
30492     },
30493     
30494      /**
30495      * Mark this field as invalid
30496      * @param {String} msg The validation message
30497      */
30498     markInvalid : function(msg)
30499     {
30500         
30501         var label = this.el.select('label', true).first();
30502         var icon = this.el.select('i.fa-star', true).first();
30503
30504         if(label && !icon){
30505             this.el.select('.roo-date-split-field-label', true).createChild({
30506                 tag : 'i',
30507                 cls : 'text-danger fa fa-lg fa-star',
30508                 tooltip : 'This field is required',
30509                 style : 'margin-right:5px;'
30510             }, label, true);
30511         }
30512         
30513         this.fireEvent('invalid', this, msg);
30514     },
30515     
30516     clearInvalid : function()
30517     {
30518         var label = this.el.select('label', true).first();
30519         var icon = this.el.select('i.fa-star', true).first();
30520
30521         if(label && icon){
30522             icon.remove();
30523         }
30524         
30525         this.fireEvent('valid', this);
30526     },
30527     
30528     getName: function()
30529     {
30530         return this.name;
30531     }
30532     
30533 });
30534
30535  /**
30536  *
30537  * This is based on 
30538  * http://masonry.desandro.com
30539  *
30540  * The idea is to render all the bricks based on vertical width...
30541  *
30542  * The original code extends 'outlayer' - we might need to use that....
30543  * 
30544  */
30545
30546
30547 /**
30548  * @class Roo.bootstrap.LayoutMasonry
30549  * @extends Roo.bootstrap.Component
30550  * Bootstrap Layout Masonry class
30551  * 
30552  * @constructor
30553  * Create a new Element
30554  * @param {Object} config The config object
30555  */
30556
30557 Roo.bootstrap.LayoutMasonry = function(config){
30558     
30559     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30560     
30561     this.bricks = [];
30562     
30563     Roo.bootstrap.LayoutMasonry.register(this);
30564     
30565     this.addEvents({
30566         // raw events
30567         /**
30568          * @event layout
30569          * Fire after layout the items
30570          * @param {Roo.bootstrap.LayoutMasonry} this
30571          * @param {Roo.EventObject} e
30572          */
30573         "layout" : true
30574     });
30575     
30576 };
30577
30578 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30579     
30580     /**
30581      * @cfg {Boolean} isLayoutInstant = no animation?
30582      */   
30583     isLayoutInstant : false, // needed?
30584    
30585     /**
30586      * @cfg {Number} boxWidth  width of the columns
30587      */   
30588     boxWidth : 450,
30589     
30590       /**
30591      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30592      */   
30593     boxHeight : 0,
30594     
30595     /**
30596      * @cfg {Number} padWidth padding below box..
30597      */   
30598     padWidth : 10, 
30599     
30600     /**
30601      * @cfg {Number} gutter gutter width..
30602      */   
30603     gutter : 10,
30604     
30605      /**
30606      * @cfg {Number} maxCols maximum number of columns
30607      */   
30608     
30609     maxCols: 0,
30610     
30611     /**
30612      * @cfg {Boolean} isAutoInitial defalut true
30613      */   
30614     isAutoInitial : true, 
30615     
30616     containerWidth: 0,
30617     
30618     /**
30619      * @cfg {Boolean} isHorizontal defalut false
30620      */   
30621     isHorizontal : false, 
30622
30623     currentSize : null,
30624     
30625     tag: 'div',
30626     
30627     cls: '',
30628     
30629     bricks: null, //CompositeElement
30630     
30631     cols : 1,
30632     
30633     _isLayoutInited : false,
30634     
30635 //    isAlternative : false, // only use for vertical layout...
30636     
30637     /**
30638      * @cfg {Number} alternativePadWidth padding below box..
30639      */   
30640     alternativePadWidth : 50,
30641     
30642     selectedBrick : [],
30643     
30644     getAutoCreate : function(){
30645         
30646         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30647         
30648         var cfg = {
30649             tag: this.tag,
30650             cls: 'blog-masonary-wrapper ' + this.cls,
30651             cn : {
30652                 cls : 'mas-boxes masonary'
30653             }
30654         };
30655         
30656         return cfg;
30657     },
30658     
30659     getChildContainer: function( )
30660     {
30661         if (this.boxesEl) {
30662             return this.boxesEl;
30663         }
30664         
30665         this.boxesEl = this.el.select('.mas-boxes').first();
30666         
30667         return this.boxesEl;
30668     },
30669     
30670     
30671     initEvents : function()
30672     {
30673         var _this = this;
30674         
30675         if(this.isAutoInitial){
30676             Roo.log('hook children rendered');
30677             this.on('childrenrendered', function() {
30678                 Roo.log('children rendered');
30679                 _this.initial();
30680             } ,this);
30681         }
30682     },
30683     
30684     initial : function()
30685     {
30686         this.selectedBrick = [];
30687         
30688         this.currentSize = this.el.getBox(true);
30689         
30690         Roo.EventManager.onWindowResize(this.resize, this); 
30691
30692         if(!this.isAutoInitial){
30693             this.layout();
30694             return;
30695         }
30696         
30697         this.layout();
30698         
30699         return;
30700         //this.layout.defer(500,this);
30701         
30702     },
30703     
30704     resize : function()
30705     {
30706         var cs = this.el.getBox(true);
30707         
30708         if (
30709                 this.currentSize.width == cs.width && 
30710                 this.currentSize.x == cs.x && 
30711                 this.currentSize.height == cs.height && 
30712                 this.currentSize.y == cs.y 
30713         ) {
30714             Roo.log("no change in with or X or Y");
30715             return;
30716         }
30717         
30718         this.currentSize = cs;
30719         
30720         this.layout();
30721         
30722     },
30723     
30724     layout : function()
30725     {   
30726         this._resetLayout();
30727         
30728         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30729         
30730         this.layoutItems( isInstant );
30731       
30732         this._isLayoutInited = true;
30733         
30734         this.fireEvent('layout', this);
30735         
30736     },
30737     
30738     _resetLayout : function()
30739     {
30740         if(this.isHorizontal){
30741             this.horizontalMeasureColumns();
30742             return;
30743         }
30744         
30745         this.verticalMeasureColumns();
30746         
30747     },
30748     
30749     verticalMeasureColumns : function()
30750     {
30751         this.getContainerWidth();
30752         
30753 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30754 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30755 //            return;
30756 //        }
30757         
30758         var boxWidth = this.boxWidth + this.padWidth;
30759         
30760         if(this.containerWidth < this.boxWidth){
30761             boxWidth = this.containerWidth
30762         }
30763         
30764         var containerWidth = this.containerWidth;
30765         
30766         var cols = Math.floor(containerWidth / boxWidth);
30767         
30768         this.cols = Math.max( cols, 1 );
30769         
30770         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30771         
30772         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30773         
30774         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30775         
30776         this.colWidth = boxWidth + avail - this.padWidth;
30777         
30778         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30779         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30780     },
30781     
30782     horizontalMeasureColumns : function()
30783     {
30784         this.getContainerWidth();
30785         
30786         var boxWidth = this.boxWidth;
30787         
30788         if(this.containerWidth < boxWidth){
30789             boxWidth = this.containerWidth;
30790         }
30791         
30792         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30793         
30794         this.el.setHeight(boxWidth);
30795         
30796     },
30797     
30798     getContainerWidth : function()
30799     {
30800         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30801     },
30802     
30803     layoutItems : function( isInstant )
30804     {
30805         Roo.log(this.bricks);
30806         
30807         var items = Roo.apply([], this.bricks);
30808         
30809         if(this.isHorizontal){
30810             this._horizontalLayoutItems( items , isInstant );
30811             return;
30812         }
30813         
30814 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30815 //            this._verticalAlternativeLayoutItems( items , isInstant );
30816 //            return;
30817 //        }
30818         
30819         this._verticalLayoutItems( items , isInstant );
30820         
30821     },
30822     
30823     _verticalLayoutItems : function ( items , isInstant)
30824     {
30825         if ( !items || !items.length ) {
30826             return;
30827         }
30828         
30829         var standard = [
30830             ['xs', 'xs', 'xs', 'tall'],
30831             ['xs', 'xs', 'tall'],
30832             ['xs', 'xs', 'sm'],
30833             ['xs', 'xs', 'xs'],
30834             ['xs', 'tall'],
30835             ['xs', 'sm'],
30836             ['xs', 'xs'],
30837             ['xs'],
30838             
30839             ['sm', 'xs', 'xs'],
30840             ['sm', 'xs'],
30841             ['sm'],
30842             
30843             ['tall', 'xs', 'xs', 'xs'],
30844             ['tall', 'xs', 'xs'],
30845             ['tall', 'xs'],
30846             ['tall']
30847             
30848         ];
30849         
30850         var queue = [];
30851         
30852         var boxes = [];
30853         
30854         var box = [];
30855         
30856         Roo.each(items, function(item, k){
30857             
30858             switch (item.size) {
30859                 // these layouts take up a full box,
30860                 case 'md' :
30861                 case 'md-left' :
30862                 case 'md-right' :
30863                 case 'wide' :
30864                     
30865                     if(box.length){
30866                         boxes.push(box);
30867                         box = [];
30868                     }
30869                     
30870                     boxes.push([item]);
30871                     
30872                     break;
30873                     
30874                 case 'xs' :
30875                 case 'sm' :
30876                 case 'tall' :
30877                     
30878                     box.push(item);
30879                     
30880                     break;
30881                 default :
30882                     break;
30883                     
30884             }
30885             
30886         }, this);
30887         
30888         if(box.length){
30889             boxes.push(box);
30890             box = [];
30891         }
30892         
30893         var filterPattern = function(box, length)
30894         {
30895             if(!box.length){
30896                 return;
30897             }
30898             
30899             var match = false;
30900             
30901             var pattern = box.slice(0, length);
30902             
30903             var format = [];
30904             
30905             Roo.each(pattern, function(i){
30906                 format.push(i.size);
30907             }, this);
30908             
30909             Roo.each(standard, function(s){
30910                 
30911                 if(String(s) != String(format)){
30912                     return;
30913                 }
30914                 
30915                 match = true;
30916                 return false;
30917                 
30918             }, this);
30919             
30920             if(!match && length == 1){
30921                 return;
30922             }
30923             
30924             if(!match){
30925                 filterPattern(box, length - 1);
30926                 return;
30927             }
30928                 
30929             queue.push(pattern);
30930
30931             box = box.slice(length, box.length);
30932
30933             filterPattern(box, 4);
30934
30935             return;
30936             
30937         }
30938         
30939         Roo.each(boxes, function(box, k){
30940             
30941             if(!box.length){
30942                 return;
30943             }
30944             
30945             if(box.length == 1){
30946                 queue.push(box);
30947                 return;
30948             }
30949             
30950             filterPattern(box, 4);
30951             
30952         }, this);
30953         
30954         this._processVerticalLayoutQueue( queue, isInstant );
30955         
30956     },
30957     
30958 //    _verticalAlternativeLayoutItems : function( items , isInstant )
30959 //    {
30960 //        if ( !items || !items.length ) {
30961 //            return;
30962 //        }
30963 //
30964 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
30965 //        
30966 //    },
30967     
30968     _horizontalLayoutItems : function ( items , isInstant)
30969     {
30970         if ( !items || !items.length || items.length < 3) {
30971             return;
30972         }
30973         
30974         items.reverse();
30975         
30976         var eItems = items.slice(0, 3);
30977         
30978         items = items.slice(3, items.length);
30979         
30980         var standard = [
30981             ['xs', 'xs', 'xs', 'wide'],
30982             ['xs', 'xs', 'wide'],
30983             ['xs', 'xs', 'sm'],
30984             ['xs', 'xs', 'xs'],
30985             ['xs', 'wide'],
30986             ['xs', 'sm'],
30987             ['xs', 'xs'],
30988             ['xs'],
30989             
30990             ['sm', 'xs', 'xs'],
30991             ['sm', 'xs'],
30992             ['sm'],
30993             
30994             ['wide', 'xs', 'xs', 'xs'],
30995             ['wide', 'xs', 'xs'],
30996             ['wide', 'xs'],
30997             ['wide'],
30998             
30999             ['wide-thin']
31000         ];
31001         
31002         var queue = [];
31003         
31004         var boxes = [];
31005         
31006         var box = [];
31007         
31008         Roo.each(items, function(item, k){
31009             
31010             switch (item.size) {
31011                 case 'md' :
31012                 case 'md-left' :
31013                 case 'md-right' :
31014                 case 'tall' :
31015                     
31016                     if(box.length){
31017                         boxes.push(box);
31018                         box = [];
31019                     }
31020                     
31021                     boxes.push([item]);
31022                     
31023                     break;
31024                     
31025                 case 'xs' :
31026                 case 'sm' :
31027                 case 'wide' :
31028                 case 'wide-thin' :
31029                     
31030                     box.push(item);
31031                     
31032                     break;
31033                 default :
31034                     break;
31035                     
31036             }
31037             
31038         }, this);
31039         
31040         if(box.length){
31041             boxes.push(box);
31042             box = [];
31043         }
31044         
31045         var filterPattern = function(box, length)
31046         {
31047             if(!box.length){
31048                 return;
31049             }
31050             
31051             var match = false;
31052             
31053             var pattern = box.slice(0, length);
31054             
31055             var format = [];
31056             
31057             Roo.each(pattern, function(i){
31058                 format.push(i.size);
31059             }, this);
31060             
31061             Roo.each(standard, function(s){
31062                 
31063                 if(String(s) != String(format)){
31064                     return;
31065                 }
31066                 
31067                 match = true;
31068                 return false;
31069                 
31070             }, this);
31071             
31072             if(!match && length == 1){
31073                 return;
31074             }
31075             
31076             if(!match){
31077                 filterPattern(box, length - 1);
31078                 return;
31079             }
31080                 
31081             queue.push(pattern);
31082
31083             box = box.slice(length, box.length);
31084
31085             filterPattern(box, 4);
31086
31087             return;
31088             
31089         }
31090         
31091         Roo.each(boxes, function(box, k){
31092             
31093             if(!box.length){
31094                 return;
31095             }
31096             
31097             if(box.length == 1){
31098                 queue.push(box);
31099                 return;
31100             }
31101             
31102             filterPattern(box, 4);
31103             
31104         }, this);
31105         
31106         
31107         var prune = [];
31108         
31109         var pos = this.el.getBox(true);
31110         
31111         var minX = pos.x;
31112         
31113         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31114         
31115         var hit_end = false;
31116         
31117         Roo.each(queue, function(box){
31118             
31119             if(hit_end){
31120                 
31121                 Roo.each(box, function(b){
31122                 
31123                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31124                     b.el.hide();
31125
31126                 }, this);
31127
31128                 return;
31129             }
31130             
31131             var mx = 0;
31132             
31133             Roo.each(box, function(b){
31134                 
31135                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31136                 b.el.show();
31137
31138                 mx = Math.max(mx, b.x);
31139                 
31140             }, this);
31141             
31142             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31143             
31144             if(maxX < minX){
31145                 
31146                 Roo.each(box, function(b){
31147                 
31148                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31149                     b.el.hide();
31150                     
31151                 }, this);
31152                 
31153                 hit_end = true;
31154                 
31155                 return;
31156             }
31157             
31158             prune.push(box);
31159             
31160         }, this);
31161         
31162         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31163     },
31164     
31165     /** Sets position of item in DOM
31166     * @param {Element} item
31167     * @param {Number} x - horizontal position
31168     * @param {Number} y - vertical position
31169     * @param {Boolean} isInstant - disables transitions
31170     */
31171     _processVerticalLayoutQueue : function( queue, isInstant )
31172     {
31173         var pos = this.el.getBox(true);
31174         var x = pos.x;
31175         var y = pos.y;
31176         var maxY = [];
31177         
31178         for (var i = 0; i < this.cols; i++){
31179             maxY[i] = pos.y;
31180         }
31181         
31182         Roo.each(queue, function(box, k){
31183             
31184             var col = k % this.cols;
31185             
31186             Roo.each(box, function(b,kk){
31187                 
31188                 b.el.position('absolute');
31189                 
31190                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31191                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31192                 
31193                 if(b.size == 'md-left' || b.size == 'md-right'){
31194                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31195                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31196                 }
31197                 
31198                 b.el.setWidth(width);
31199                 b.el.setHeight(height);
31200                 // iframe?
31201                 b.el.select('iframe',true).setSize(width,height);
31202                 
31203             }, this);
31204             
31205             for (var i = 0; i < this.cols; i++){
31206                 
31207                 if(maxY[i] < maxY[col]){
31208                     col = i;
31209                     continue;
31210                 }
31211                 
31212                 col = Math.min(col, i);
31213                 
31214             }
31215             
31216             x = pos.x + col * (this.colWidth + this.padWidth);
31217             
31218             y = maxY[col];
31219             
31220             var positions = [];
31221             
31222             switch (box.length){
31223                 case 1 :
31224                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31225                     break;
31226                 case 2 :
31227                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31228                     break;
31229                 case 3 :
31230                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31231                     break;
31232                 case 4 :
31233                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31234                     break;
31235                 default :
31236                     break;
31237             }
31238             
31239             Roo.each(box, function(b,kk){
31240                 
31241                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31242                 
31243                 var sz = b.el.getSize();
31244                 
31245                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31246                 
31247             }, this);
31248             
31249         }, this);
31250         
31251         var mY = 0;
31252         
31253         for (var i = 0; i < this.cols; i++){
31254             mY = Math.max(mY, maxY[i]);
31255         }
31256         
31257         this.el.setHeight(mY - pos.y);
31258         
31259     },
31260     
31261 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31262 //    {
31263 //        var pos = this.el.getBox(true);
31264 //        var x = pos.x;
31265 //        var y = pos.y;
31266 //        var maxX = pos.right;
31267 //        
31268 //        var maxHeight = 0;
31269 //        
31270 //        Roo.each(items, function(item, k){
31271 //            
31272 //            var c = k % 2;
31273 //            
31274 //            item.el.position('absolute');
31275 //                
31276 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31277 //
31278 //            item.el.setWidth(width);
31279 //
31280 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31281 //
31282 //            item.el.setHeight(height);
31283 //            
31284 //            if(c == 0){
31285 //                item.el.setXY([x, y], isInstant ? false : true);
31286 //            } else {
31287 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31288 //            }
31289 //            
31290 //            y = y + height + this.alternativePadWidth;
31291 //            
31292 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31293 //            
31294 //        }, this);
31295 //        
31296 //        this.el.setHeight(maxHeight);
31297 //        
31298 //    },
31299     
31300     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31301     {
31302         var pos = this.el.getBox(true);
31303         
31304         var minX = pos.x;
31305         var minY = pos.y;
31306         
31307         var maxX = pos.right;
31308         
31309         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31310         
31311         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31312         
31313         Roo.each(queue, function(box, k){
31314             
31315             Roo.each(box, function(b, kk){
31316                 
31317                 b.el.position('absolute');
31318                 
31319                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31320                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31321                 
31322                 if(b.size == 'md-left' || b.size == 'md-right'){
31323                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31324                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31325                 }
31326                 
31327                 b.el.setWidth(width);
31328                 b.el.setHeight(height);
31329                 
31330             }, this);
31331             
31332             if(!box.length){
31333                 return;
31334             }
31335             
31336             var positions = [];
31337             
31338             switch (box.length){
31339                 case 1 :
31340                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31341                     break;
31342                 case 2 :
31343                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31344                     break;
31345                 case 3 :
31346                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31347                     break;
31348                 case 4 :
31349                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31350                     break;
31351                 default :
31352                     break;
31353             }
31354             
31355             Roo.each(box, function(b,kk){
31356                 
31357                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31358                 
31359                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31360                 
31361             }, this);
31362             
31363         }, this);
31364         
31365     },
31366     
31367     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31368     {
31369         Roo.each(eItems, function(b,k){
31370             
31371             b.size = (k == 0) ? 'sm' : 'xs';
31372             b.x = (k == 0) ? 2 : 1;
31373             b.y = (k == 0) ? 2 : 1;
31374             
31375             b.el.position('absolute');
31376             
31377             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31378                 
31379             b.el.setWidth(width);
31380             
31381             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31382             
31383             b.el.setHeight(height);
31384             
31385         }, this);
31386
31387         var positions = [];
31388         
31389         positions.push({
31390             x : maxX - this.unitWidth * 2 - this.gutter,
31391             y : minY
31392         });
31393         
31394         positions.push({
31395             x : maxX - this.unitWidth,
31396             y : minY + (this.unitWidth + this.gutter) * 2
31397         });
31398         
31399         positions.push({
31400             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31401             y : minY
31402         });
31403         
31404         Roo.each(eItems, function(b,k){
31405             
31406             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31407
31408         }, this);
31409         
31410     },
31411     
31412     getVerticalOneBoxColPositions : function(x, y, box)
31413     {
31414         var pos = [];
31415         
31416         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31417         
31418         if(box[0].size == 'md-left'){
31419             rand = 0;
31420         }
31421         
31422         if(box[0].size == 'md-right'){
31423             rand = 1;
31424         }
31425         
31426         pos.push({
31427             x : x + (this.unitWidth + this.gutter) * rand,
31428             y : y
31429         });
31430         
31431         return pos;
31432     },
31433     
31434     getVerticalTwoBoxColPositions : function(x, y, box)
31435     {
31436         var pos = [];
31437         
31438         if(box[0].size == 'xs'){
31439             
31440             pos.push({
31441                 x : x,
31442                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31443             });
31444
31445             pos.push({
31446                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31447                 y : y
31448             });
31449             
31450             return pos;
31451             
31452         }
31453         
31454         pos.push({
31455             x : x,
31456             y : y
31457         });
31458
31459         pos.push({
31460             x : x + (this.unitWidth + this.gutter) * 2,
31461             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31462         });
31463         
31464         return pos;
31465         
31466     },
31467     
31468     getVerticalThreeBoxColPositions : function(x, y, box)
31469     {
31470         var pos = [];
31471         
31472         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31473             
31474             pos.push({
31475                 x : x,
31476                 y : y
31477             });
31478
31479             pos.push({
31480                 x : x + (this.unitWidth + this.gutter) * 1,
31481                 y : y
31482             });
31483             
31484             pos.push({
31485                 x : x + (this.unitWidth + this.gutter) * 2,
31486                 y : y
31487             });
31488             
31489             return pos;
31490             
31491         }
31492         
31493         if(box[0].size == 'xs' && box[1].size == 'xs'){
31494             
31495             pos.push({
31496                 x : x,
31497                 y : y
31498             });
31499
31500             pos.push({
31501                 x : x,
31502                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31503             });
31504             
31505             pos.push({
31506                 x : x + (this.unitWidth + this.gutter) * 1,
31507                 y : y
31508             });
31509             
31510             return pos;
31511             
31512         }
31513         
31514         pos.push({
31515             x : x,
31516             y : y
31517         });
31518
31519         pos.push({
31520             x : x + (this.unitWidth + this.gutter) * 2,
31521             y : y
31522         });
31523
31524         pos.push({
31525             x : x + (this.unitWidth + this.gutter) * 2,
31526             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31527         });
31528             
31529         return pos;
31530         
31531     },
31532     
31533     getVerticalFourBoxColPositions : function(x, y, box)
31534     {
31535         var pos = [];
31536         
31537         if(box[0].size == 'xs'){
31538             
31539             pos.push({
31540                 x : x,
31541                 y : y
31542             });
31543
31544             pos.push({
31545                 x : x,
31546                 y : y + (this.unitHeight + this.gutter) * 1
31547             });
31548             
31549             pos.push({
31550                 x : x,
31551                 y : y + (this.unitHeight + this.gutter) * 2
31552             });
31553             
31554             pos.push({
31555                 x : x + (this.unitWidth + this.gutter) * 1,
31556                 y : y
31557             });
31558             
31559             return pos;
31560             
31561         }
31562         
31563         pos.push({
31564             x : x,
31565             y : y
31566         });
31567
31568         pos.push({
31569             x : x + (this.unitWidth + this.gutter) * 2,
31570             y : y
31571         });
31572
31573         pos.push({
31574             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31575             y : y + (this.unitHeight + this.gutter) * 1
31576         });
31577
31578         pos.push({
31579             x : x + (this.unitWidth + this.gutter) * 2,
31580             y : y + (this.unitWidth + this.gutter) * 2
31581         });
31582
31583         return pos;
31584         
31585     },
31586     
31587     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31588     {
31589         var pos = [];
31590         
31591         if(box[0].size == 'md-left'){
31592             pos.push({
31593                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31594                 y : minY
31595             });
31596             
31597             return pos;
31598         }
31599         
31600         if(box[0].size == 'md-right'){
31601             pos.push({
31602                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31603                 y : minY + (this.unitWidth + this.gutter) * 1
31604             });
31605             
31606             return pos;
31607         }
31608         
31609         var rand = Math.floor(Math.random() * (4 - box[0].y));
31610         
31611         pos.push({
31612             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31613             y : minY + (this.unitWidth + this.gutter) * rand
31614         });
31615         
31616         return pos;
31617         
31618     },
31619     
31620     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31621     {
31622         var pos = [];
31623         
31624         if(box[0].size == 'xs'){
31625             
31626             pos.push({
31627                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31628                 y : minY
31629             });
31630
31631             pos.push({
31632                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31633                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31634             });
31635             
31636             return pos;
31637             
31638         }
31639         
31640         pos.push({
31641             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31642             y : minY
31643         });
31644
31645         pos.push({
31646             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31647             y : minY + (this.unitWidth + this.gutter) * 2
31648         });
31649         
31650         return pos;
31651         
31652     },
31653     
31654     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31655     {
31656         var pos = [];
31657         
31658         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31659             
31660             pos.push({
31661                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31662                 y : minY
31663             });
31664
31665             pos.push({
31666                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31667                 y : minY + (this.unitWidth + this.gutter) * 1
31668             });
31669             
31670             pos.push({
31671                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31672                 y : minY + (this.unitWidth + this.gutter) * 2
31673             });
31674             
31675             return pos;
31676             
31677         }
31678         
31679         if(box[0].size == 'xs' && box[1].size == 'xs'){
31680             
31681             pos.push({
31682                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31683                 y : minY
31684             });
31685
31686             pos.push({
31687                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31688                 y : minY
31689             });
31690             
31691             pos.push({
31692                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31693                 y : minY + (this.unitWidth + this.gutter) * 1
31694             });
31695             
31696             return pos;
31697             
31698         }
31699         
31700         pos.push({
31701             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31702             y : minY
31703         });
31704
31705         pos.push({
31706             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31707             y : minY + (this.unitWidth + this.gutter) * 2
31708         });
31709
31710         pos.push({
31711             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31712             y : minY + (this.unitWidth + this.gutter) * 2
31713         });
31714             
31715         return pos;
31716         
31717     },
31718     
31719     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31720     {
31721         var pos = [];
31722         
31723         if(box[0].size == 'xs'){
31724             
31725             pos.push({
31726                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31727                 y : minY
31728             });
31729
31730             pos.push({
31731                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31732                 y : minY
31733             });
31734             
31735             pos.push({
31736                 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),
31737                 y : minY
31738             });
31739             
31740             pos.push({
31741                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31742                 y : minY + (this.unitWidth + this.gutter) * 1
31743             });
31744             
31745             return pos;
31746             
31747         }
31748         
31749         pos.push({
31750             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31751             y : minY
31752         });
31753         
31754         pos.push({
31755             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31756             y : minY + (this.unitWidth + this.gutter) * 2
31757         });
31758         
31759         pos.push({
31760             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31761             y : minY + (this.unitWidth + this.gutter) * 2
31762         });
31763         
31764         pos.push({
31765             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),
31766             y : minY + (this.unitWidth + this.gutter) * 2
31767         });
31768
31769         return pos;
31770         
31771     },
31772     
31773     /**
31774     * remove a Masonry Brick
31775     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31776     */
31777     removeBrick : function(brick_id)
31778     {
31779         if (!brick_id) {
31780             return;
31781         }
31782         
31783         for (var i = 0; i<this.bricks.length; i++) {
31784             if (this.bricks[i].id == brick_id) {
31785                 this.bricks.splice(i,1);
31786                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31787                 this.initial();
31788             }
31789         }
31790     },
31791     
31792     /**
31793     * adds a Masonry Brick
31794     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31795     */
31796     addBrick : function(cfg)
31797     {
31798         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31799         //this.register(cn);
31800         cn.parentId = this.id;
31801         cn.onRender(this.el, null);
31802         return cn;
31803     },
31804     
31805     /**
31806     * register a Masonry Brick
31807     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31808     */
31809     
31810     register : function(brick)
31811     {
31812         this.bricks.push(brick);
31813         brick.masonryId = this.id;
31814     },
31815     
31816     /**
31817     * clear all the Masonry Brick
31818     */
31819     clearAll : function()
31820     {
31821         this.bricks = [];
31822         //this.getChildContainer().dom.innerHTML = "";
31823         this.el.dom.innerHTML = '';
31824     },
31825     
31826     getSelected : function()
31827     {
31828         if (!this.selectedBrick) {
31829             return false;
31830         }
31831         
31832         return this.selectedBrick;
31833     }
31834 });
31835
31836 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31837     
31838     groups: {},
31839      /**
31840     * register a Masonry Layout
31841     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31842     */
31843     
31844     register : function(layout)
31845     {
31846         this.groups[layout.id] = layout;
31847     },
31848     /**
31849     * fetch a  Masonry Layout based on the masonry layout ID
31850     * @param {string} the masonry layout to add
31851     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31852     */
31853     
31854     get: function(layout_id) {
31855         if (typeof(this.groups[layout_id]) == 'undefined') {
31856             return false;
31857         }
31858         return this.groups[layout_id] ;
31859     }
31860     
31861     
31862     
31863 });
31864
31865  
31866
31867  /**
31868  *
31869  * This is based on 
31870  * http://masonry.desandro.com
31871  *
31872  * The idea is to render all the bricks based on vertical width...
31873  *
31874  * The original code extends 'outlayer' - we might need to use that....
31875  * 
31876  */
31877
31878
31879 /**
31880  * @class Roo.bootstrap.LayoutMasonryAuto
31881  * @extends Roo.bootstrap.Component
31882  * Bootstrap Layout Masonry class
31883  * 
31884  * @constructor
31885  * Create a new Element
31886  * @param {Object} config The config object
31887  */
31888
31889 Roo.bootstrap.LayoutMasonryAuto = function(config){
31890     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31891 };
31892
31893 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
31894     
31895       /**
31896      * @cfg {Boolean} isFitWidth  - resize the width..
31897      */   
31898     isFitWidth : false,  // options..
31899     /**
31900      * @cfg {Boolean} isOriginLeft = left align?
31901      */   
31902     isOriginLeft : true,
31903     /**
31904      * @cfg {Boolean} isOriginTop = top align?
31905      */   
31906     isOriginTop : false,
31907     /**
31908      * @cfg {Boolean} isLayoutInstant = no animation?
31909      */   
31910     isLayoutInstant : false, // needed?
31911     /**
31912      * @cfg {Boolean} isResizingContainer = not sure if this is used..
31913      */   
31914     isResizingContainer : true,
31915     /**
31916      * @cfg {Number} columnWidth  width of the columns 
31917      */   
31918     
31919     columnWidth : 0,
31920     
31921     /**
31922      * @cfg {Number} maxCols maximum number of columns
31923      */   
31924     
31925     maxCols: 0,
31926     /**
31927      * @cfg {Number} padHeight padding below box..
31928      */   
31929     
31930     padHeight : 10, 
31931     
31932     /**
31933      * @cfg {Boolean} isAutoInitial defalut true
31934      */   
31935     
31936     isAutoInitial : true, 
31937     
31938     // private?
31939     gutter : 0,
31940     
31941     containerWidth: 0,
31942     initialColumnWidth : 0,
31943     currentSize : null,
31944     
31945     colYs : null, // array.
31946     maxY : 0,
31947     padWidth: 10,
31948     
31949     
31950     tag: 'div',
31951     cls: '',
31952     bricks: null, //CompositeElement
31953     cols : 0, // array?
31954     // element : null, // wrapped now this.el
31955     _isLayoutInited : null, 
31956     
31957     
31958     getAutoCreate : function(){
31959         
31960         var cfg = {
31961             tag: this.tag,
31962             cls: 'blog-masonary-wrapper ' + this.cls,
31963             cn : {
31964                 cls : 'mas-boxes masonary'
31965             }
31966         };
31967         
31968         return cfg;
31969     },
31970     
31971     getChildContainer: function( )
31972     {
31973         if (this.boxesEl) {
31974             return this.boxesEl;
31975         }
31976         
31977         this.boxesEl = this.el.select('.mas-boxes').first();
31978         
31979         return this.boxesEl;
31980     },
31981     
31982     
31983     initEvents : function()
31984     {
31985         var _this = this;
31986         
31987         if(this.isAutoInitial){
31988             Roo.log('hook children rendered');
31989             this.on('childrenrendered', function() {
31990                 Roo.log('children rendered');
31991                 _this.initial();
31992             } ,this);
31993         }
31994         
31995     },
31996     
31997     initial : function()
31998     {
31999         this.reloadItems();
32000
32001         this.currentSize = this.el.getBox(true);
32002
32003         /// was window resize... - let's see if this works..
32004         Roo.EventManager.onWindowResize(this.resize, this); 
32005
32006         if(!this.isAutoInitial){
32007             this.layout();
32008             return;
32009         }
32010         
32011         this.layout.defer(500,this);
32012     },
32013     
32014     reloadItems: function()
32015     {
32016         this.bricks = this.el.select('.masonry-brick', true);
32017         
32018         this.bricks.each(function(b) {
32019             //Roo.log(b.getSize());
32020             if (!b.attr('originalwidth')) {
32021                 b.attr('originalwidth',  b.getSize().width);
32022             }
32023             
32024         });
32025         
32026         Roo.log(this.bricks.elements.length);
32027     },
32028     
32029     resize : function()
32030     {
32031         Roo.log('resize');
32032         var cs = this.el.getBox(true);
32033         
32034         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32035             Roo.log("no change in with or X");
32036             return;
32037         }
32038         this.currentSize = cs;
32039         this.layout();
32040     },
32041     
32042     layout : function()
32043     {
32044          Roo.log('layout');
32045         this._resetLayout();
32046         //this._manageStamps();
32047       
32048         // don't animate first layout
32049         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32050         this.layoutItems( isInstant );
32051       
32052         // flag for initalized
32053         this._isLayoutInited = true;
32054     },
32055     
32056     layoutItems : function( isInstant )
32057     {
32058         //var items = this._getItemsForLayout( this.items );
32059         // original code supports filtering layout items.. we just ignore it..
32060         
32061         this._layoutItems( this.bricks , isInstant );
32062       
32063         this._postLayout();
32064     },
32065     _layoutItems : function ( items , isInstant)
32066     {
32067        //this.fireEvent( 'layout', this, items );
32068     
32069
32070         if ( !items || !items.elements.length ) {
32071           // no items, emit event with empty array
32072             return;
32073         }
32074
32075         var queue = [];
32076         items.each(function(item) {
32077             Roo.log("layout item");
32078             Roo.log(item);
32079             // get x/y object from method
32080             var position = this._getItemLayoutPosition( item );
32081             // enqueue
32082             position.item = item;
32083             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32084             queue.push( position );
32085         }, this);
32086       
32087         this._processLayoutQueue( queue );
32088     },
32089     /** Sets position of item in DOM
32090     * @param {Element} item
32091     * @param {Number} x - horizontal position
32092     * @param {Number} y - vertical position
32093     * @param {Boolean} isInstant - disables transitions
32094     */
32095     _processLayoutQueue : function( queue )
32096     {
32097         for ( var i=0, len = queue.length; i < len; i++ ) {
32098             var obj = queue[i];
32099             obj.item.position('absolute');
32100             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32101         }
32102     },
32103       
32104     
32105     /**
32106     * Any logic you want to do after each layout,
32107     * i.e. size the container
32108     */
32109     _postLayout : function()
32110     {
32111         this.resizeContainer();
32112     },
32113     
32114     resizeContainer : function()
32115     {
32116         if ( !this.isResizingContainer ) {
32117             return;
32118         }
32119         var size = this._getContainerSize();
32120         if ( size ) {
32121             this.el.setSize(size.width,size.height);
32122             this.boxesEl.setSize(size.width,size.height);
32123         }
32124     },
32125     
32126     
32127     
32128     _resetLayout : function()
32129     {
32130         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32131         this.colWidth = this.el.getWidth();
32132         //this.gutter = this.el.getWidth(); 
32133         
32134         this.measureColumns();
32135
32136         // reset column Y
32137         var i = this.cols;
32138         this.colYs = [];
32139         while (i--) {
32140             this.colYs.push( 0 );
32141         }
32142     
32143         this.maxY = 0;
32144     },
32145
32146     measureColumns : function()
32147     {
32148         this.getContainerWidth();
32149       // if columnWidth is 0, default to outerWidth of first item
32150         if ( !this.columnWidth ) {
32151             var firstItem = this.bricks.first();
32152             Roo.log(firstItem);
32153             this.columnWidth  = this.containerWidth;
32154             if (firstItem && firstItem.attr('originalwidth') ) {
32155                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32156             }
32157             // columnWidth fall back to item of first element
32158             Roo.log("set column width?");
32159                         this.initialColumnWidth = this.columnWidth  ;
32160
32161             // if first elem has no width, default to size of container
32162             
32163         }
32164         
32165         
32166         if (this.initialColumnWidth) {
32167             this.columnWidth = this.initialColumnWidth;
32168         }
32169         
32170         
32171             
32172         // column width is fixed at the top - however if container width get's smaller we should
32173         // reduce it...
32174         
32175         // this bit calcs how man columns..
32176             
32177         var columnWidth = this.columnWidth += this.gutter;
32178       
32179         // calculate columns
32180         var containerWidth = this.containerWidth + this.gutter;
32181         
32182         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32183         // fix rounding errors, typically with gutters
32184         var excess = columnWidth - containerWidth % columnWidth;
32185         
32186         
32187         // if overshoot is less than a pixel, round up, otherwise floor it
32188         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32189         cols = Math[ mathMethod ]( cols );
32190         this.cols = Math.max( cols, 1 );
32191         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32192         
32193          // padding positioning..
32194         var totalColWidth = this.cols * this.columnWidth;
32195         var padavail = this.containerWidth - totalColWidth;
32196         // so for 2 columns - we need 3 'pads'
32197         
32198         var padNeeded = (1+this.cols) * this.padWidth;
32199         
32200         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32201         
32202         this.columnWidth += padExtra
32203         //this.padWidth = Math.floor(padavail /  ( this.cols));
32204         
32205         // adjust colum width so that padding is fixed??
32206         
32207         // we have 3 columns ... total = width * 3
32208         // we have X left over... that should be used by 
32209         
32210         //if (this.expandC) {
32211             
32212         //}
32213         
32214         
32215         
32216     },
32217     
32218     getContainerWidth : function()
32219     {
32220        /* // container is parent if fit width
32221         var container = this.isFitWidth ? this.element.parentNode : this.element;
32222         // check that this.size and size are there
32223         // IE8 triggers resize on body size change, so they might not be
32224         
32225         var size = getSize( container );  //FIXME
32226         this.containerWidth = size && size.innerWidth; //FIXME
32227         */
32228          
32229         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32230         
32231     },
32232     
32233     _getItemLayoutPosition : function( item )  // what is item?
32234     {
32235         // we resize the item to our columnWidth..
32236       
32237         item.setWidth(this.columnWidth);
32238         item.autoBoxAdjust  = false;
32239         
32240         var sz = item.getSize();
32241  
32242         // how many columns does this brick span
32243         var remainder = this.containerWidth % this.columnWidth;
32244         
32245         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32246         // round if off by 1 pixel, otherwise use ceil
32247         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32248         colSpan = Math.min( colSpan, this.cols );
32249         
32250         // normally this should be '1' as we dont' currently allow multi width columns..
32251         
32252         var colGroup = this._getColGroup( colSpan );
32253         // get the minimum Y value from the columns
32254         var minimumY = Math.min.apply( Math, colGroup );
32255         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32256         
32257         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32258          
32259         // position the brick
32260         var position = {
32261             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32262             y: this.currentSize.y + minimumY + this.padHeight
32263         };
32264         
32265         Roo.log(position);
32266         // apply setHeight to necessary columns
32267         var setHeight = minimumY + sz.height + this.padHeight;
32268         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32269         
32270         var setSpan = this.cols + 1 - colGroup.length;
32271         for ( var i = 0; i < setSpan; i++ ) {
32272           this.colYs[ shortColIndex + i ] = setHeight ;
32273         }
32274       
32275         return position;
32276     },
32277     
32278     /**
32279      * @param {Number} colSpan - number of columns the element spans
32280      * @returns {Array} colGroup
32281      */
32282     _getColGroup : function( colSpan )
32283     {
32284         if ( colSpan < 2 ) {
32285           // if brick spans only one column, use all the column Ys
32286           return this.colYs;
32287         }
32288       
32289         var colGroup = [];
32290         // how many different places could this brick fit horizontally
32291         var groupCount = this.cols + 1 - colSpan;
32292         // for each group potential horizontal position
32293         for ( var i = 0; i < groupCount; i++ ) {
32294           // make an array of colY values for that one group
32295           var groupColYs = this.colYs.slice( i, i + colSpan );
32296           // and get the max value of the array
32297           colGroup[i] = Math.max.apply( Math, groupColYs );
32298         }
32299         return colGroup;
32300     },
32301     /*
32302     _manageStamp : function( stamp )
32303     {
32304         var stampSize =  stamp.getSize();
32305         var offset = stamp.getBox();
32306         // get the columns that this stamp affects
32307         var firstX = this.isOriginLeft ? offset.x : offset.right;
32308         var lastX = firstX + stampSize.width;
32309         var firstCol = Math.floor( firstX / this.columnWidth );
32310         firstCol = Math.max( 0, firstCol );
32311         
32312         var lastCol = Math.floor( lastX / this.columnWidth );
32313         // lastCol should not go over if multiple of columnWidth #425
32314         lastCol -= lastX % this.columnWidth ? 0 : 1;
32315         lastCol = Math.min( this.cols - 1, lastCol );
32316         
32317         // set colYs to bottom of the stamp
32318         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32319             stampSize.height;
32320             
32321         for ( var i = firstCol; i <= lastCol; i++ ) {
32322           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32323         }
32324     },
32325     */
32326     
32327     _getContainerSize : function()
32328     {
32329         this.maxY = Math.max.apply( Math, this.colYs );
32330         var size = {
32331             height: this.maxY
32332         };
32333       
32334         if ( this.isFitWidth ) {
32335             size.width = this._getContainerFitWidth();
32336         }
32337       
32338         return size;
32339     },
32340     
32341     _getContainerFitWidth : function()
32342     {
32343         var unusedCols = 0;
32344         // count unused columns
32345         var i = this.cols;
32346         while ( --i ) {
32347           if ( this.colYs[i] !== 0 ) {
32348             break;
32349           }
32350           unusedCols++;
32351         }
32352         // fit container to columns that have been used
32353         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32354     },
32355     
32356     needsResizeLayout : function()
32357     {
32358         var previousWidth = this.containerWidth;
32359         this.getContainerWidth();
32360         return previousWidth !== this.containerWidth;
32361     }
32362  
32363 });
32364
32365  
32366
32367  /*
32368  * - LGPL
32369  *
32370  * element
32371  * 
32372  */
32373
32374 /**
32375  * @class Roo.bootstrap.MasonryBrick
32376  * @extends Roo.bootstrap.Component
32377  * Bootstrap MasonryBrick class
32378  * 
32379  * @constructor
32380  * Create a new MasonryBrick
32381  * @param {Object} config The config object
32382  */
32383
32384 Roo.bootstrap.MasonryBrick = function(config){
32385     
32386     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32387     
32388     Roo.bootstrap.MasonryBrick.register(this);
32389     
32390     this.addEvents({
32391         // raw events
32392         /**
32393          * @event click
32394          * When a MasonryBrick is clcik
32395          * @param {Roo.bootstrap.MasonryBrick} this
32396          * @param {Roo.EventObject} e
32397          */
32398         "click" : true
32399     });
32400 };
32401
32402 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32403     
32404     /**
32405      * @cfg {String} title
32406      */   
32407     title : '',
32408     /**
32409      * @cfg {String} html
32410      */   
32411     html : '',
32412     /**
32413      * @cfg {String} bgimage
32414      */   
32415     bgimage : '',
32416     /**
32417      * @cfg {String} videourl
32418      */   
32419     videourl : '',
32420     /**
32421      * @cfg {String} cls
32422      */   
32423     cls : '',
32424     /**
32425      * @cfg {String} href
32426      */   
32427     href : '',
32428     /**
32429      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32430      */   
32431     size : 'xs',
32432     
32433     /**
32434      * @cfg {String} placetitle (center|bottom)
32435      */   
32436     placetitle : '',
32437     
32438     /**
32439      * @cfg {Boolean} isFitContainer defalut true
32440      */   
32441     isFitContainer : true, 
32442     
32443     /**
32444      * @cfg {Boolean} preventDefault defalut false
32445      */   
32446     preventDefault : false, 
32447     
32448     /**
32449      * @cfg {Boolean} inverse defalut false
32450      */   
32451     maskInverse : false, 
32452     
32453     getAutoCreate : function()
32454     {
32455         if(!this.isFitContainer){
32456             return this.getSplitAutoCreate();
32457         }
32458         
32459         var cls = 'masonry-brick masonry-brick-full';
32460         
32461         if(this.href.length){
32462             cls += ' masonry-brick-link';
32463         }
32464         
32465         if(this.bgimage.length){
32466             cls += ' masonry-brick-image';
32467         }
32468         
32469         if(this.maskInverse){
32470             cls += ' mask-inverse';
32471         }
32472         
32473         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32474             cls += ' enable-mask';
32475         }
32476         
32477         if(this.size){
32478             cls += ' masonry-' + this.size + '-brick';
32479         }
32480         
32481         if(this.placetitle.length){
32482             
32483             switch (this.placetitle) {
32484                 case 'center' :
32485                     cls += ' masonry-center-title';
32486                     break;
32487                 case 'bottom' :
32488                     cls += ' masonry-bottom-title';
32489                     break;
32490                 default:
32491                     break;
32492             }
32493             
32494         } else {
32495             if(!this.html.length && !this.bgimage.length){
32496                 cls += ' masonry-center-title';
32497             }
32498
32499             if(!this.html.length && this.bgimage.length){
32500                 cls += ' masonry-bottom-title';
32501             }
32502         }
32503         
32504         if(this.cls){
32505             cls += ' ' + this.cls;
32506         }
32507         
32508         var cfg = {
32509             tag: (this.href.length) ? 'a' : 'div',
32510             cls: cls,
32511             cn: [
32512                 {
32513                     tag: 'div',
32514                     cls: 'masonry-brick-mask'
32515                 },
32516                 {
32517                     tag: 'div',
32518                     cls: 'masonry-brick-paragraph',
32519                     cn: []
32520                 }
32521             ]
32522         };
32523         
32524         if(this.href.length){
32525             cfg.href = this.href;
32526         }
32527         
32528         var cn = cfg.cn[1].cn;
32529         
32530         if(this.title.length){
32531             cn.push({
32532                 tag: 'h4',
32533                 cls: 'masonry-brick-title',
32534                 html: this.title
32535             });
32536         }
32537         
32538         if(this.html.length){
32539             cn.push({
32540                 tag: 'p',
32541                 cls: 'masonry-brick-text',
32542                 html: this.html
32543             });
32544         }
32545         
32546         if (!this.title.length && !this.html.length) {
32547             cfg.cn[1].cls += ' hide';
32548         }
32549         
32550         if(this.bgimage.length){
32551             cfg.cn.push({
32552                 tag: 'img',
32553                 cls: 'masonry-brick-image-view',
32554                 src: this.bgimage
32555             });
32556         }
32557         
32558         if(this.videourl.length){
32559             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32560             // youtube support only?
32561             cfg.cn.push({
32562                 tag: 'iframe',
32563                 cls: 'masonry-brick-image-view',
32564                 src: vurl,
32565                 frameborder : 0,
32566                 allowfullscreen : true
32567             });
32568         }
32569         
32570         return cfg;
32571         
32572     },
32573     
32574     getSplitAutoCreate : function()
32575     {
32576         var cls = 'masonry-brick masonry-brick-split';
32577         
32578         if(this.href.length){
32579             cls += ' masonry-brick-link';
32580         }
32581         
32582         if(this.bgimage.length){
32583             cls += ' masonry-brick-image';
32584         }
32585         
32586         if(this.size){
32587             cls += ' masonry-' + this.size + '-brick';
32588         }
32589         
32590         switch (this.placetitle) {
32591             case 'center' :
32592                 cls += ' masonry-center-title';
32593                 break;
32594             case 'bottom' :
32595                 cls += ' masonry-bottom-title';
32596                 break;
32597             default:
32598                 if(!this.bgimage.length){
32599                     cls += ' masonry-center-title';
32600                 }
32601
32602                 if(this.bgimage.length){
32603                     cls += ' masonry-bottom-title';
32604                 }
32605                 break;
32606         }
32607         
32608         if(this.cls){
32609             cls += ' ' + this.cls;
32610         }
32611         
32612         var cfg = {
32613             tag: (this.href.length) ? 'a' : 'div',
32614             cls: cls,
32615             cn: [
32616                 {
32617                     tag: 'div',
32618                     cls: 'masonry-brick-split-head',
32619                     cn: [
32620                         {
32621                             tag: 'div',
32622                             cls: 'masonry-brick-paragraph',
32623                             cn: []
32624                         }
32625                     ]
32626                 },
32627                 {
32628                     tag: 'div',
32629                     cls: 'masonry-brick-split-body',
32630                     cn: []
32631                 }
32632             ]
32633         };
32634         
32635         if(this.href.length){
32636             cfg.href = this.href;
32637         }
32638         
32639         if(this.title.length){
32640             cfg.cn[0].cn[0].cn.push({
32641                 tag: 'h4',
32642                 cls: 'masonry-brick-title',
32643                 html: this.title
32644             });
32645         }
32646         
32647         if(this.html.length){
32648             cfg.cn[1].cn.push({
32649                 tag: 'p',
32650                 cls: 'masonry-brick-text',
32651                 html: this.html
32652             });
32653         }
32654
32655         if(this.bgimage.length){
32656             cfg.cn[0].cn.push({
32657                 tag: 'img',
32658                 cls: 'masonry-brick-image-view',
32659                 src: this.bgimage
32660             });
32661         }
32662         
32663         if(this.videourl.length){
32664             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32665             // youtube support only?
32666             cfg.cn[0].cn.cn.push({
32667                 tag: 'iframe',
32668                 cls: 'masonry-brick-image-view',
32669                 src: vurl,
32670                 frameborder : 0,
32671                 allowfullscreen : true
32672             });
32673         }
32674         
32675         return cfg;
32676     },
32677     
32678     initEvents: function() 
32679     {
32680         switch (this.size) {
32681             case 'xs' :
32682                 this.x = 1;
32683                 this.y = 1;
32684                 break;
32685             case 'sm' :
32686                 this.x = 2;
32687                 this.y = 2;
32688                 break;
32689             case 'md' :
32690             case 'md-left' :
32691             case 'md-right' :
32692                 this.x = 3;
32693                 this.y = 3;
32694                 break;
32695             case 'tall' :
32696                 this.x = 2;
32697                 this.y = 3;
32698                 break;
32699             case 'wide' :
32700                 this.x = 3;
32701                 this.y = 2;
32702                 break;
32703             case 'wide-thin' :
32704                 this.x = 3;
32705                 this.y = 1;
32706                 break;
32707                         
32708             default :
32709                 break;
32710         }
32711         
32712         if(Roo.isTouch){
32713             this.el.on('touchstart', this.onTouchStart, this);
32714             this.el.on('touchmove', this.onTouchMove, this);
32715             this.el.on('touchend', this.onTouchEnd, this);
32716             this.el.on('contextmenu', this.onContextMenu, this);
32717         } else {
32718             this.el.on('mouseenter'  ,this.enter, this);
32719             this.el.on('mouseleave', this.leave, this);
32720             this.el.on('click', this.onClick, this);
32721         }
32722         
32723         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32724             this.parent().bricks.push(this);   
32725         }
32726         
32727     },
32728     
32729     onClick: function(e, el)
32730     {
32731         var time = this.endTimer - this.startTimer;
32732         // Roo.log(e.preventDefault());
32733         if(Roo.isTouch){
32734             if(time > 1000){
32735                 e.preventDefault();
32736                 return;
32737             }
32738         }
32739         
32740         if(!this.preventDefault){
32741             return;
32742         }
32743         
32744         e.preventDefault();
32745         
32746         if (this.activcClass != '') {
32747             this.selectBrick();
32748         }
32749         
32750         this.fireEvent('click', this);
32751     },
32752     
32753     enter: function(e, el)
32754     {
32755         e.preventDefault();
32756         
32757         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32758             return;
32759         }
32760         
32761         if(this.bgimage.length && this.html.length){
32762             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32763         }
32764     },
32765     
32766     leave: function(e, el)
32767     {
32768         e.preventDefault();
32769         
32770         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32771             return;
32772         }
32773         
32774         if(this.bgimage.length && this.html.length){
32775             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32776         }
32777     },
32778     
32779     onTouchStart: function(e, el)
32780     {
32781 //        e.preventDefault();
32782         
32783         this.touchmoved = false;
32784         
32785         if(!this.isFitContainer){
32786             return;
32787         }
32788         
32789         if(!this.bgimage.length || !this.html.length){
32790             return;
32791         }
32792         
32793         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32794         
32795         this.timer = new Date().getTime();
32796         
32797     },
32798     
32799     onTouchMove: function(e, el)
32800     {
32801         this.touchmoved = true;
32802     },
32803     
32804     onContextMenu : function(e,el)
32805     {
32806         e.preventDefault();
32807         e.stopPropagation();
32808         return false;
32809     },
32810     
32811     onTouchEnd: function(e, el)
32812     {
32813 //        e.preventDefault();
32814         
32815         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32816         
32817             this.leave(e,el);
32818             
32819             return;
32820         }
32821         
32822         if(!this.bgimage.length || !this.html.length){
32823             
32824             if(this.href.length){
32825                 window.location.href = this.href;
32826             }
32827             
32828             return;
32829         }
32830         
32831         if(!this.isFitContainer){
32832             return;
32833         }
32834         
32835         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32836         
32837         window.location.href = this.href;
32838     },
32839     
32840     //selection on single brick only
32841     selectBrick : function() {
32842         
32843         if (!this.parentId) {
32844             return;
32845         }
32846         
32847         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32848         var index = m.selectedBrick.indexOf(this.id);
32849         
32850         if ( index > -1) {
32851             m.selectedBrick.splice(index,1);
32852             this.el.removeClass(this.activeClass);
32853             return;
32854         }
32855         
32856         for(var i = 0; i < m.selectedBrick.length; i++) {
32857             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32858             b.el.removeClass(b.activeClass);
32859         }
32860         
32861         m.selectedBrick = [];
32862         
32863         m.selectedBrick.push(this.id);
32864         this.el.addClass(this.activeClass);
32865         return;
32866     }
32867     
32868 });
32869
32870 Roo.apply(Roo.bootstrap.MasonryBrick, {
32871     
32872     //groups: {},
32873     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32874      /**
32875     * register a Masonry Brick
32876     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32877     */
32878     
32879     register : function(brick)
32880     {
32881         //this.groups[brick.id] = brick;
32882         this.groups.add(brick.id, brick);
32883     },
32884     /**
32885     * fetch a  masonry brick based on the masonry brick ID
32886     * @param {string} the masonry brick to add
32887     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32888     */
32889     
32890     get: function(brick_id) 
32891     {
32892         // if (typeof(this.groups[brick_id]) == 'undefined') {
32893         //     return false;
32894         // }
32895         // return this.groups[brick_id] ;
32896         
32897         if(this.groups.key(brick_id)) {
32898             return this.groups.key(brick_id);
32899         }
32900         
32901         return false;
32902     }
32903     
32904     
32905     
32906 });
32907
32908  /*
32909  * - LGPL
32910  *
32911  * element
32912  * 
32913  */
32914
32915 /**
32916  * @class Roo.bootstrap.Brick
32917  * @extends Roo.bootstrap.Component
32918  * Bootstrap Brick class
32919  * 
32920  * @constructor
32921  * Create a new Brick
32922  * @param {Object} config The config object
32923  */
32924
32925 Roo.bootstrap.Brick = function(config){
32926     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32927     
32928     this.addEvents({
32929         // raw events
32930         /**
32931          * @event click
32932          * When a Brick is click
32933          * @param {Roo.bootstrap.Brick} this
32934          * @param {Roo.EventObject} e
32935          */
32936         "click" : true
32937     });
32938 };
32939
32940 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
32941     
32942     /**
32943      * @cfg {String} title
32944      */   
32945     title : '',
32946     /**
32947      * @cfg {String} html
32948      */   
32949     html : '',
32950     /**
32951      * @cfg {String} bgimage
32952      */   
32953     bgimage : '',
32954     /**
32955      * @cfg {String} cls
32956      */   
32957     cls : '',
32958     /**
32959      * @cfg {String} href
32960      */   
32961     href : '',
32962     /**
32963      * @cfg {String} video
32964      */   
32965     video : '',
32966     /**
32967      * @cfg {Boolean} square
32968      */   
32969     square : true,
32970     
32971     getAutoCreate : function()
32972     {
32973         var cls = 'roo-brick';
32974         
32975         if(this.href.length){
32976             cls += ' roo-brick-link';
32977         }
32978         
32979         if(this.bgimage.length){
32980             cls += ' roo-brick-image';
32981         }
32982         
32983         if(!this.html.length && !this.bgimage.length){
32984             cls += ' roo-brick-center-title';
32985         }
32986         
32987         if(!this.html.length && this.bgimage.length){
32988             cls += ' roo-brick-bottom-title';
32989         }
32990         
32991         if(this.cls){
32992             cls += ' ' + this.cls;
32993         }
32994         
32995         var cfg = {
32996             tag: (this.href.length) ? 'a' : 'div',
32997             cls: cls,
32998             cn: [
32999                 {
33000                     tag: 'div',
33001                     cls: 'roo-brick-paragraph',
33002                     cn: []
33003                 }
33004             ]
33005         };
33006         
33007         if(this.href.length){
33008             cfg.href = this.href;
33009         }
33010         
33011         var cn = cfg.cn[0].cn;
33012         
33013         if(this.title.length){
33014             cn.push({
33015                 tag: 'h4',
33016                 cls: 'roo-brick-title',
33017                 html: this.title
33018             });
33019         }
33020         
33021         if(this.html.length){
33022             cn.push({
33023                 tag: 'p',
33024                 cls: 'roo-brick-text',
33025                 html: this.html
33026             });
33027         } else {
33028             cn.cls += ' hide';
33029         }
33030         
33031         if(this.bgimage.length){
33032             cfg.cn.push({
33033                 tag: 'img',
33034                 cls: 'roo-brick-image-view',
33035                 src: this.bgimage
33036             });
33037         }
33038         
33039         return cfg;
33040     },
33041     
33042     initEvents: function() 
33043     {
33044         if(this.title.length || this.html.length){
33045             this.el.on('mouseenter'  ,this.enter, this);
33046             this.el.on('mouseleave', this.leave, this);
33047         }
33048         
33049         Roo.EventManager.onWindowResize(this.resize, this); 
33050         
33051         if(this.bgimage.length){
33052             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33053             this.imageEl.on('load', this.onImageLoad, this);
33054             return;
33055         }
33056         
33057         this.resize();
33058     },
33059     
33060     onImageLoad : function()
33061     {
33062         this.resize();
33063     },
33064     
33065     resize : function()
33066     {
33067         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33068         
33069         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33070         
33071         if(this.bgimage.length){
33072             var image = this.el.select('.roo-brick-image-view', true).first();
33073             
33074             image.setWidth(paragraph.getWidth());
33075             
33076             if(this.square){
33077                 image.setHeight(paragraph.getWidth());
33078             }
33079             
33080             this.el.setHeight(image.getHeight());
33081             paragraph.setHeight(image.getHeight());
33082             
33083         }
33084         
33085     },
33086     
33087     enter: function(e, el)
33088     {
33089         e.preventDefault();
33090         
33091         if(this.bgimage.length){
33092             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33093             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33094         }
33095     },
33096     
33097     leave: function(e, el)
33098     {
33099         e.preventDefault();
33100         
33101         if(this.bgimage.length){
33102             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33103             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33104         }
33105     }
33106     
33107 });
33108
33109  
33110
33111  /*
33112  * - LGPL
33113  *
33114  * Number field 
33115  */
33116
33117 /**
33118  * @class Roo.bootstrap.NumberField
33119  * @extends Roo.bootstrap.Input
33120  * Bootstrap NumberField class
33121  * 
33122  * 
33123  * 
33124  * 
33125  * @constructor
33126  * Create a new NumberField
33127  * @param {Object} config The config object
33128  */
33129
33130 Roo.bootstrap.NumberField = function(config){
33131     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33132 };
33133
33134 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33135     
33136     /**
33137      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33138      */
33139     allowDecimals : true,
33140     /**
33141      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33142      */
33143     decimalSeparator : ".",
33144     /**
33145      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33146      */
33147     decimalPrecision : 2,
33148     /**
33149      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33150      */
33151     allowNegative : true,
33152     
33153     /**
33154      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33155      */
33156     allowZero: true,
33157     /**
33158      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33159      */
33160     minValue : Number.NEGATIVE_INFINITY,
33161     /**
33162      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33163      */
33164     maxValue : Number.MAX_VALUE,
33165     /**
33166      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33167      */
33168     minText : "The minimum value for this field is {0}",
33169     /**
33170      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33171      */
33172     maxText : "The maximum value for this field is {0}",
33173     /**
33174      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33175      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33176      */
33177     nanText : "{0} is not a valid number",
33178     /**
33179      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
33180      */
33181     castInt : true,
33182     /**
33183      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33184      */
33185     thousandsDelimiter : false,
33186     /**
33187      * @cfg {String} valueAlign alignment of value
33188      */
33189     valueAlign : "left",
33190
33191     getAutoCreate : function()
33192     {
33193         var hiddenInput = {
33194             tag: 'input',
33195             type: 'hidden',
33196             id: Roo.id(),
33197             cls: 'hidden-number-input'
33198         };
33199         
33200         if (this.name) {
33201             hiddenInput.name = this.name;
33202         }
33203         
33204         this.name = '';
33205         
33206         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33207         
33208         this.name = hiddenInput.name;
33209         
33210         if(cfg.cn.length > 0) {
33211             cfg.cn.push(hiddenInput);
33212         }
33213         
33214         return cfg;
33215     },
33216
33217     // private
33218     initEvents : function()
33219     {   
33220         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33221         
33222         var allowed = "0123456789";
33223         
33224         if(this.allowDecimals){
33225             allowed += this.decimalSeparator;
33226         }
33227         
33228         if(this.allowNegative){
33229             allowed += "-";
33230         }
33231         
33232         if(this.thousandsDelimiter) {
33233             allowed += ",";
33234         }
33235         
33236         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33237         
33238         var keyPress = function(e){
33239             
33240             var k = e.getKey();
33241             
33242             var c = e.getCharCode();
33243             
33244             if(
33245                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33246                     allowed.indexOf(String.fromCharCode(c)) === -1
33247             ){
33248                 e.stopEvent();
33249                 return;
33250             }
33251             
33252             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33253                 return;
33254             }
33255             
33256             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33257                 e.stopEvent();
33258             }
33259         };
33260         
33261         this.el.on("keypress", keyPress, this);
33262     },
33263     
33264     validateValue : function(value)
33265     {
33266         
33267         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33268             return false;
33269         }
33270         
33271         var num = this.parseValue(value);
33272         
33273         if(isNaN(num)){
33274             this.markInvalid(String.format(this.nanText, value));
33275             return false;
33276         }
33277         
33278         if(num < this.minValue){
33279             this.markInvalid(String.format(this.minText, this.minValue));
33280             return false;
33281         }
33282         
33283         if(num > this.maxValue){
33284             this.markInvalid(String.format(this.maxText, this.maxValue));
33285             return false;
33286         }
33287         
33288         return true;
33289     },
33290
33291     getValue : function()
33292     {
33293         var v = this.hiddenEl().getValue();
33294         
33295         return this.fixPrecision(this.parseValue(v));
33296     },
33297
33298     parseValue : function(value)
33299     {
33300         if(this.thousandsDelimiter) {
33301             value += "";
33302             r = new RegExp(",", "g");
33303             value = value.replace(r, "");
33304         }
33305         
33306         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33307         return isNaN(value) ? '' : value;
33308     },
33309
33310     fixPrecision : function(value)
33311     {
33312         if(this.thousandsDelimiter) {
33313             value += "";
33314             r = new RegExp(",", "g");
33315             value = value.replace(r, "");
33316         }
33317         
33318         var nan = isNaN(value);
33319         
33320         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33321             return nan ? '' : value;
33322         }
33323         return parseFloat(value).toFixed(this.decimalPrecision);
33324     },
33325
33326     setValue : function(v)
33327     {
33328         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33329         
33330         this.value = v;
33331         
33332         if(this.rendered){
33333             
33334             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33335             
33336             this.inputEl().dom.value = (v == '') ? '' :
33337                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33338             
33339             if(!this.allowZero && v === '0') {
33340                 this.hiddenEl().dom.value = '';
33341                 this.inputEl().dom.value = '';
33342             }
33343             
33344             this.validate();
33345         }
33346     },
33347
33348     decimalPrecisionFcn : function(v)
33349     {
33350         return Math.floor(v);
33351     },
33352
33353     beforeBlur : function()
33354     {
33355         if(!this.castInt){
33356             return;
33357         }
33358         
33359         var v = this.parseValue(this.getRawValue());
33360         
33361         if(v || v === 0){
33362             this.setValue(v);
33363         }
33364     },
33365     
33366     hiddenEl : function()
33367     {
33368         return this.el.select('input.hidden-number-input',true).first();
33369     }
33370     
33371 });
33372
33373  
33374
33375 /*
33376 * Licence: LGPL
33377 */
33378
33379 /**
33380  * @class Roo.bootstrap.DocumentSlider
33381  * @extends Roo.bootstrap.Component
33382  * Bootstrap DocumentSlider class
33383  * 
33384  * @constructor
33385  * Create a new DocumentViewer
33386  * @param {Object} config The config object
33387  */
33388
33389 Roo.bootstrap.DocumentSlider = function(config){
33390     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33391     
33392     this.files = [];
33393     
33394     this.addEvents({
33395         /**
33396          * @event initial
33397          * Fire after initEvent
33398          * @param {Roo.bootstrap.DocumentSlider} this
33399          */
33400         "initial" : true,
33401         /**
33402          * @event update
33403          * Fire after update
33404          * @param {Roo.bootstrap.DocumentSlider} this
33405          */
33406         "update" : true,
33407         /**
33408          * @event click
33409          * Fire after click
33410          * @param {Roo.bootstrap.DocumentSlider} this
33411          */
33412         "click" : true
33413     });
33414 };
33415
33416 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33417     
33418     files : false,
33419     
33420     indicator : 0,
33421     
33422     getAutoCreate : function()
33423     {
33424         var cfg = {
33425             tag : 'div',
33426             cls : 'roo-document-slider',
33427             cn : [
33428                 {
33429                     tag : 'div',
33430                     cls : 'roo-document-slider-header',
33431                     cn : [
33432                         {
33433                             tag : 'div',
33434                             cls : 'roo-document-slider-header-title'
33435                         }
33436                     ]
33437                 },
33438                 {
33439                     tag : 'div',
33440                     cls : 'roo-document-slider-body',
33441                     cn : [
33442                         {
33443                             tag : 'div',
33444                             cls : 'roo-document-slider-prev',
33445                             cn : [
33446                                 {
33447                                     tag : 'i',
33448                                     cls : 'fa fa-chevron-left'
33449                                 }
33450                             ]
33451                         },
33452                         {
33453                             tag : 'div',
33454                             cls : 'roo-document-slider-thumb',
33455                             cn : [
33456                                 {
33457                                     tag : 'img',
33458                                     cls : 'roo-document-slider-image'
33459                                 }
33460                             ]
33461                         },
33462                         {
33463                             tag : 'div',
33464                             cls : 'roo-document-slider-next',
33465                             cn : [
33466                                 {
33467                                     tag : 'i',
33468                                     cls : 'fa fa-chevron-right'
33469                                 }
33470                             ]
33471                         }
33472                     ]
33473                 }
33474             ]
33475         };
33476         
33477         return cfg;
33478     },
33479     
33480     initEvents : function()
33481     {
33482         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33483         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33484         
33485         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33486         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33487         
33488         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33489         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33490         
33491         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33492         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33493         
33494         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33495         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33496         
33497         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33498         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33499         
33500         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33501         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33502         
33503         this.thumbEl.on('click', this.onClick, this);
33504         
33505         this.prevIndicator.on('click', this.prev, this);
33506         
33507         this.nextIndicator.on('click', this.next, this);
33508         
33509     },
33510     
33511     initial : function()
33512     {
33513         if(this.files.length){
33514             this.indicator = 1;
33515             this.update()
33516         }
33517         
33518         this.fireEvent('initial', this);
33519     },
33520     
33521     update : function()
33522     {
33523         this.imageEl.attr('src', this.files[this.indicator - 1]);
33524         
33525         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33526         
33527         this.prevIndicator.show();
33528         
33529         if(this.indicator == 1){
33530             this.prevIndicator.hide();
33531         }
33532         
33533         this.nextIndicator.show();
33534         
33535         if(this.indicator == this.files.length){
33536             this.nextIndicator.hide();
33537         }
33538         
33539         this.thumbEl.scrollTo('top');
33540         
33541         this.fireEvent('update', this);
33542     },
33543     
33544     onClick : function(e)
33545     {
33546         e.preventDefault();
33547         
33548         this.fireEvent('click', this);
33549     },
33550     
33551     prev : function(e)
33552     {
33553         e.preventDefault();
33554         
33555         this.indicator = Math.max(1, this.indicator - 1);
33556         
33557         this.update();
33558     },
33559     
33560     next : function(e)
33561     {
33562         e.preventDefault();
33563         
33564         this.indicator = Math.min(this.files.length, this.indicator + 1);
33565         
33566         this.update();
33567     }
33568 });
33569 /*
33570  * - LGPL
33571  *
33572  * RadioSet
33573  *
33574  *
33575  */
33576
33577 /**
33578  * @class Roo.bootstrap.RadioSet
33579  * @extends Roo.bootstrap.Input
33580  * Bootstrap RadioSet class
33581  * @cfg {String} indicatorpos (left|right) default left
33582  * @cfg {Boolean} inline (true|false) inline the element (default true)
33583  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33584  * @constructor
33585  * Create a new RadioSet
33586  * @param {Object} config The config object
33587  */
33588
33589 Roo.bootstrap.RadioSet = function(config){
33590     
33591     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33592     
33593     this.radioes = [];
33594     
33595     Roo.bootstrap.RadioSet.register(this);
33596     
33597     this.addEvents({
33598         /**
33599         * @event check
33600         * Fires when the element is checked or unchecked.
33601         * @param {Roo.bootstrap.RadioSet} this This radio
33602         * @param {Roo.bootstrap.Radio} item The checked item
33603         */
33604        check : true,
33605        /**
33606         * @event click
33607         * Fires when the element is click.
33608         * @param {Roo.bootstrap.RadioSet} this This radio set
33609         * @param {Roo.bootstrap.Radio} item The checked item
33610         * @param {Roo.EventObject} e The event object
33611         */
33612        click : true
33613     });
33614     
33615 };
33616
33617 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33618
33619     radioes : false,
33620     
33621     inline : true,
33622     
33623     weight : '',
33624     
33625     indicatorpos : 'left',
33626     
33627     getAutoCreate : function()
33628     {
33629         var label = {
33630             tag : 'label',
33631             cls : 'roo-radio-set-label',
33632             cn : [
33633                 {
33634                     tag : 'span',
33635                     html : this.fieldLabel
33636                 }
33637             ]
33638         };
33639         
33640         if(this.indicatorpos == 'left'){
33641             label.cn.unshift({
33642                 tag : 'i',
33643                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33644                 tooltip : 'This field is required'
33645             });
33646         } else {
33647             label.cn.push({
33648                 tag : 'i',
33649                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33650                 tooltip : 'This field is required'
33651             });
33652         }
33653         
33654         var items = {
33655             tag : 'div',
33656             cls : 'roo-radio-set-items'
33657         };
33658         
33659         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33660         
33661         if (align === 'left' && this.fieldLabel.length) {
33662             
33663             items = {
33664                 cls : "roo-radio-set-right", 
33665                 cn: [
33666                     items
33667                 ]
33668             };
33669             
33670             if(this.labelWidth > 12){
33671                 label.style = "width: " + this.labelWidth + 'px';
33672             }
33673             
33674             if(this.labelWidth < 13 && this.labelmd == 0){
33675                 this.labelmd = this.labelWidth;
33676             }
33677             
33678             if(this.labellg > 0){
33679                 label.cls += ' col-lg-' + this.labellg;
33680                 items.cls += ' col-lg-' + (12 - this.labellg);
33681             }
33682             
33683             if(this.labelmd > 0){
33684                 label.cls += ' col-md-' + this.labelmd;
33685                 items.cls += ' col-md-' + (12 - this.labelmd);
33686             }
33687             
33688             if(this.labelsm > 0){
33689                 label.cls += ' col-sm-' + this.labelsm;
33690                 items.cls += ' col-sm-' + (12 - this.labelsm);
33691             }
33692             
33693             if(this.labelxs > 0){
33694                 label.cls += ' col-xs-' + this.labelxs;
33695                 items.cls += ' col-xs-' + (12 - this.labelxs);
33696             }
33697         }
33698         
33699         var cfg = {
33700             tag : 'div',
33701             cls : 'roo-radio-set',
33702             cn : [
33703                 {
33704                     tag : 'input',
33705                     cls : 'roo-radio-set-input',
33706                     type : 'hidden',
33707                     name : this.name,
33708                     value : this.value ? this.value :  ''
33709                 },
33710                 label,
33711                 items
33712             ]
33713         };
33714         
33715         if(this.weight.length){
33716             cfg.cls += ' roo-radio-' + this.weight;
33717         }
33718         
33719         if(this.inline) {
33720             cfg.cls += ' roo-radio-set-inline';
33721         }
33722         
33723         var settings=this;
33724         ['xs','sm','md','lg'].map(function(size){
33725             if (settings[size]) {
33726                 cfg.cls += ' col-' + size + '-' + settings[size];
33727             }
33728         });
33729         
33730         return cfg;
33731         
33732     },
33733
33734     initEvents : function()
33735     {
33736         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33737         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33738         
33739         if(!this.fieldLabel.length){
33740             this.labelEl.hide();
33741         }
33742         
33743         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33744         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33745         
33746         this.indicatorEl().addClass('invisible');
33747         
33748         this.originalValue = this.getValue();
33749         
33750     },
33751     
33752     inputEl: function ()
33753     {
33754         return this.el.select('.roo-radio-set-input', true).first();
33755     },
33756     
33757     getChildContainer : function()
33758     {
33759         return this.itemsEl;
33760     },
33761     
33762     register : function(item)
33763     {
33764         this.radioes.push(item);
33765         
33766     },
33767     
33768     validate : function()
33769     {   
33770         if(this.getEl().hasClass('hidden')){
33771             return true;
33772         }
33773         
33774         var valid = false;
33775         
33776         Roo.each(this.radioes, function(i){
33777             if(!i.checked){
33778                 return;
33779             }
33780             
33781             valid = true;
33782             return false;
33783         });
33784         
33785         if(this.allowBlank) {
33786             return true;
33787         }
33788         
33789         if(this.disabled || valid){
33790             this.markValid();
33791             return true;
33792         }
33793         
33794         this.markInvalid();
33795         return false;
33796         
33797     },
33798     
33799     markValid : function()
33800     {
33801         if(this.labelEl.isVisible(true)){
33802             this.indicatorEl().removeClass('visible');
33803             this.indicatorEl().addClass('invisible');
33804         }
33805         
33806         this.el.removeClass([this.invalidClass, this.validClass]);
33807         this.el.addClass(this.validClass);
33808         
33809         this.fireEvent('valid', this);
33810     },
33811     
33812     markInvalid : function(msg)
33813     {
33814         if(this.allowBlank || this.disabled){
33815             return;
33816         }
33817         
33818         if(this.labelEl.isVisible(true)){
33819             this.indicatorEl().removeClass('invisible');
33820             this.indicatorEl().addClass('visible');
33821         }
33822         
33823         this.el.removeClass([this.invalidClass, this.validClass]);
33824         this.el.addClass(this.invalidClass);
33825         
33826         this.fireEvent('invalid', this, msg);
33827         
33828     },
33829     
33830     setValue : function(v, suppressEvent)
33831     {   
33832         if(this.value === v){
33833             return;
33834         }
33835         
33836         this.value = v;
33837         
33838         if(this.rendered){
33839             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33840         }
33841         
33842         Roo.each(this.radioes, function(i){
33843             i.checked = false;
33844             i.el.removeClass('checked');
33845         });
33846         
33847         Roo.each(this.radioes, function(i){
33848             
33849             if(i.value === v || i.value.toString() === v.toString()){
33850                 i.checked = true;
33851                 i.el.addClass('checked');
33852                 
33853                 if(suppressEvent !== true){
33854                     this.fireEvent('check', this, i);
33855                 }
33856                 
33857                 return false;
33858             }
33859             
33860         }, this);
33861         
33862         this.validate();
33863     },
33864     
33865     clearInvalid : function(){
33866         
33867         if(!this.el || this.preventMark){
33868             return;
33869         }
33870         
33871         this.el.removeClass([this.invalidClass]);
33872         
33873         this.fireEvent('valid', this);
33874     }
33875     
33876 });
33877
33878 Roo.apply(Roo.bootstrap.RadioSet, {
33879     
33880     groups: {},
33881     
33882     register : function(set)
33883     {
33884         this.groups[set.name] = set;
33885     },
33886     
33887     get: function(name) 
33888     {
33889         if (typeof(this.groups[name]) == 'undefined') {
33890             return false;
33891         }
33892         
33893         return this.groups[name] ;
33894     }
33895     
33896 });
33897 /*
33898  * Based on:
33899  * Ext JS Library 1.1.1
33900  * Copyright(c) 2006-2007, Ext JS, LLC.
33901  *
33902  * Originally Released Under LGPL - original licence link has changed is not relivant.
33903  *
33904  * Fork - LGPL
33905  * <script type="text/javascript">
33906  */
33907
33908
33909 /**
33910  * @class Roo.bootstrap.SplitBar
33911  * @extends Roo.util.Observable
33912  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33913  * <br><br>
33914  * Usage:
33915  * <pre><code>
33916 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33917                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33918 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33919 split.minSize = 100;
33920 split.maxSize = 600;
33921 split.animate = true;
33922 split.on('moved', splitterMoved);
33923 </code></pre>
33924  * @constructor
33925  * Create a new SplitBar
33926  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
33927  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
33928  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33929  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
33930                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33931                         position of the SplitBar).
33932  */
33933 Roo.bootstrap.SplitBar = function(cfg){
33934     
33935     /** @private */
33936     
33937     //{
33938     //  dragElement : elm
33939     //  resizingElement: el,
33940         // optional..
33941     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
33942     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
33943         // existingProxy ???
33944     //}
33945     
33946     this.el = Roo.get(cfg.dragElement, true);
33947     this.el.dom.unselectable = "on";
33948     /** @private */
33949     this.resizingEl = Roo.get(cfg.resizingElement, true);
33950
33951     /**
33952      * @private
33953      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33954      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
33955      * @type Number
33956      */
33957     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
33958     
33959     /**
33960      * The minimum size of the resizing element. (Defaults to 0)
33961      * @type Number
33962      */
33963     this.minSize = 0;
33964     
33965     /**
33966      * The maximum size of the resizing element. (Defaults to 2000)
33967      * @type Number
33968      */
33969     this.maxSize = 2000;
33970     
33971     /**
33972      * Whether to animate the transition to the new size
33973      * @type Boolean
33974      */
33975     this.animate = false;
33976     
33977     /**
33978      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
33979      * @type Boolean
33980      */
33981     this.useShim = false;
33982     
33983     /** @private */
33984     this.shim = null;
33985     
33986     if(!cfg.existingProxy){
33987         /** @private */
33988         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
33989     }else{
33990         this.proxy = Roo.get(cfg.existingProxy).dom;
33991     }
33992     /** @private */
33993     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
33994     
33995     /** @private */
33996     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
33997     
33998     /** @private */
33999     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34000     
34001     /** @private */
34002     this.dragSpecs = {};
34003     
34004     /**
34005      * @private The adapter to use to positon and resize elements
34006      */
34007     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34008     this.adapter.init(this);
34009     
34010     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34011         /** @private */
34012         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34013         this.el.addClass("roo-splitbar-h");
34014     }else{
34015         /** @private */
34016         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34017         this.el.addClass("roo-splitbar-v");
34018     }
34019     
34020     this.addEvents({
34021         /**
34022          * @event resize
34023          * Fires when the splitter is moved (alias for {@link #event-moved})
34024          * @param {Roo.bootstrap.SplitBar} this
34025          * @param {Number} newSize the new width or height
34026          */
34027         "resize" : true,
34028         /**
34029          * @event moved
34030          * Fires when the splitter is moved
34031          * @param {Roo.bootstrap.SplitBar} this
34032          * @param {Number} newSize the new width or height
34033          */
34034         "moved" : true,
34035         /**
34036          * @event beforeresize
34037          * Fires before the splitter is dragged
34038          * @param {Roo.bootstrap.SplitBar} this
34039          */
34040         "beforeresize" : true,
34041
34042         "beforeapply" : true
34043     });
34044
34045     Roo.util.Observable.call(this);
34046 };
34047
34048 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34049     onStartProxyDrag : function(x, y){
34050         this.fireEvent("beforeresize", this);
34051         if(!this.overlay){
34052             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34053             o.unselectable();
34054             o.enableDisplayMode("block");
34055             // all splitbars share the same overlay
34056             Roo.bootstrap.SplitBar.prototype.overlay = o;
34057         }
34058         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34059         this.overlay.show();
34060         Roo.get(this.proxy).setDisplayed("block");
34061         var size = this.adapter.getElementSize(this);
34062         this.activeMinSize = this.getMinimumSize();;
34063         this.activeMaxSize = this.getMaximumSize();;
34064         var c1 = size - this.activeMinSize;
34065         var c2 = Math.max(this.activeMaxSize - size, 0);
34066         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34067             this.dd.resetConstraints();
34068             this.dd.setXConstraint(
34069                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34070                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34071             );
34072             this.dd.setYConstraint(0, 0);
34073         }else{
34074             this.dd.resetConstraints();
34075             this.dd.setXConstraint(0, 0);
34076             this.dd.setYConstraint(
34077                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34078                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34079             );
34080          }
34081         this.dragSpecs.startSize = size;
34082         this.dragSpecs.startPoint = [x, y];
34083         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34084     },
34085     
34086     /** 
34087      * @private Called after the drag operation by the DDProxy
34088      */
34089     onEndProxyDrag : function(e){
34090         Roo.get(this.proxy).setDisplayed(false);
34091         var endPoint = Roo.lib.Event.getXY(e);
34092         if(this.overlay){
34093             this.overlay.hide();
34094         }
34095         var newSize;
34096         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34097             newSize = this.dragSpecs.startSize + 
34098                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34099                     endPoint[0] - this.dragSpecs.startPoint[0] :
34100                     this.dragSpecs.startPoint[0] - endPoint[0]
34101                 );
34102         }else{
34103             newSize = this.dragSpecs.startSize + 
34104                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34105                     endPoint[1] - this.dragSpecs.startPoint[1] :
34106                     this.dragSpecs.startPoint[1] - endPoint[1]
34107                 );
34108         }
34109         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34110         if(newSize != this.dragSpecs.startSize){
34111             if(this.fireEvent('beforeapply', this, newSize) !== false){
34112                 this.adapter.setElementSize(this, newSize);
34113                 this.fireEvent("moved", this, newSize);
34114                 this.fireEvent("resize", this, newSize);
34115             }
34116         }
34117     },
34118     
34119     /**
34120      * Get the adapter this SplitBar uses
34121      * @return The adapter object
34122      */
34123     getAdapter : function(){
34124         return this.adapter;
34125     },
34126     
34127     /**
34128      * Set the adapter this SplitBar uses
34129      * @param {Object} adapter A SplitBar adapter object
34130      */
34131     setAdapter : function(adapter){
34132         this.adapter = adapter;
34133         this.adapter.init(this);
34134     },
34135     
34136     /**
34137      * Gets the minimum size for the resizing element
34138      * @return {Number} The minimum size
34139      */
34140     getMinimumSize : function(){
34141         return this.minSize;
34142     },
34143     
34144     /**
34145      * Sets the minimum size for the resizing element
34146      * @param {Number} minSize The minimum size
34147      */
34148     setMinimumSize : function(minSize){
34149         this.minSize = minSize;
34150     },
34151     
34152     /**
34153      * Gets the maximum size for the resizing element
34154      * @return {Number} The maximum size
34155      */
34156     getMaximumSize : function(){
34157         return this.maxSize;
34158     },
34159     
34160     /**
34161      * Sets the maximum size for the resizing element
34162      * @param {Number} maxSize The maximum size
34163      */
34164     setMaximumSize : function(maxSize){
34165         this.maxSize = maxSize;
34166     },
34167     
34168     /**
34169      * Sets the initialize size for the resizing element
34170      * @param {Number} size The initial size
34171      */
34172     setCurrentSize : function(size){
34173         var oldAnimate = this.animate;
34174         this.animate = false;
34175         this.adapter.setElementSize(this, size);
34176         this.animate = oldAnimate;
34177     },
34178     
34179     /**
34180      * Destroy this splitbar. 
34181      * @param {Boolean} removeEl True to remove the element
34182      */
34183     destroy : function(removeEl){
34184         if(this.shim){
34185             this.shim.remove();
34186         }
34187         this.dd.unreg();
34188         this.proxy.parentNode.removeChild(this.proxy);
34189         if(removeEl){
34190             this.el.remove();
34191         }
34192     }
34193 });
34194
34195 /**
34196  * @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.
34197  */
34198 Roo.bootstrap.SplitBar.createProxy = function(dir){
34199     var proxy = new Roo.Element(document.createElement("div"));
34200     proxy.unselectable();
34201     var cls = 'roo-splitbar-proxy';
34202     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34203     document.body.appendChild(proxy.dom);
34204     return proxy.dom;
34205 };
34206
34207 /** 
34208  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34209  * Default Adapter. It assumes the splitter and resizing element are not positioned
34210  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34211  */
34212 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34213 };
34214
34215 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34216     // do nothing for now
34217     init : function(s){
34218     
34219     },
34220     /**
34221      * Called before drag operations to get the current size of the resizing element. 
34222      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34223      */
34224      getElementSize : function(s){
34225         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34226             return s.resizingEl.getWidth();
34227         }else{
34228             return s.resizingEl.getHeight();
34229         }
34230     },
34231     
34232     /**
34233      * Called after drag operations to set the size of the resizing element.
34234      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34235      * @param {Number} newSize The new size to set
34236      * @param {Function} onComplete A function to be invoked when resizing is complete
34237      */
34238     setElementSize : function(s, newSize, onComplete){
34239         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34240             if(!s.animate){
34241                 s.resizingEl.setWidth(newSize);
34242                 if(onComplete){
34243                     onComplete(s, newSize);
34244                 }
34245             }else{
34246                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34247             }
34248         }else{
34249             
34250             if(!s.animate){
34251                 s.resizingEl.setHeight(newSize);
34252                 if(onComplete){
34253                     onComplete(s, newSize);
34254                 }
34255             }else{
34256                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34257             }
34258         }
34259     }
34260 };
34261
34262 /** 
34263  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34264  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34265  * Adapter that  moves the splitter element to align with the resized sizing element. 
34266  * Used with an absolute positioned SplitBar.
34267  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34268  * document.body, make sure you assign an id to the body element.
34269  */
34270 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34271     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34272     this.container = Roo.get(container);
34273 };
34274
34275 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34276     init : function(s){
34277         this.basic.init(s);
34278     },
34279     
34280     getElementSize : function(s){
34281         return this.basic.getElementSize(s);
34282     },
34283     
34284     setElementSize : function(s, newSize, onComplete){
34285         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34286     },
34287     
34288     moveSplitter : function(s){
34289         var yes = Roo.bootstrap.SplitBar;
34290         switch(s.placement){
34291             case yes.LEFT:
34292                 s.el.setX(s.resizingEl.getRight());
34293                 break;
34294             case yes.RIGHT:
34295                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34296                 break;
34297             case yes.TOP:
34298                 s.el.setY(s.resizingEl.getBottom());
34299                 break;
34300             case yes.BOTTOM:
34301                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34302                 break;
34303         }
34304     }
34305 };
34306
34307 /**
34308  * Orientation constant - Create a vertical SplitBar
34309  * @static
34310  * @type Number
34311  */
34312 Roo.bootstrap.SplitBar.VERTICAL = 1;
34313
34314 /**
34315  * Orientation constant - Create a horizontal SplitBar
34316  * @static
34317  * @type Number
34318  */
34319 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34320
34321 /**
34322  * Placement constant - The resizing element is to the left of the splitter element
34323  * @static
34324  * @type Number
34325  */
34326 Roo.bootstrap.SplitBar.LEFT = 1;
34327
34328 /**
34329  * Placement constant - The resizing element is to the right of the splitter element
34330  * @static
34331  * @type Number
34332  */
34333 Roo.bootstrap.SplitBar.RIGHT = 2;
34334
34335 /**
34336  * Placement constant - The resizing element is positioned above the splitter element
34337  * @static
34338  * @type Number
34339  */
34340 Roo.bootstrap.SplitBar.TOP = 3;
34341
34342 /**
34343  * Placement constant - The resizing element is positioned under splitter element
34344  * @static
34345  * @type Number
34346  */
34347 Roo.bootstrap.SplitBar.BOTTOM = 4;
34348 Roo.namespace("Roo.bootstrap.layout");/*
34349  * Based on:
34350  * Ext JS Library 1.1.1
34351  * Copyright(c) 2006-2007, Ext JS, LLC.
34352  *
34353  * Originally Released Under LGPL - original licence link has changed is not relivant.
34354  *
34355  * Fork - LGPL
34356  * <script type="text/javascript">
34357  */
34358
34359 /**
34360  * @class Roo.bootstrap.layout.Manager
34361  * @extends Roo.bootstrap.Component
34362  * Base class for layout managers.
34363  */
34364 Roo.bootstrap.layout.Manager = function(config)
34365 {
34366     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34367
34368
34369
34370
34371
34372     /** false to disable window resize monitoring @type Boolean */
34373     this.monitorWindowResize = true;
34374     this.regions = {};
34375     this.addEvents({
34376         /**
34377          * @event layout
34378          * Fires when a layout is performed.
34379          * @param {Roo.LayoutManager} this
34380          */
34381         "layout" : true,
34382         /**
34383          * @event regionresized
34384          * Fires when the user resizes a region.
34385          * @param {Roo.LayoutRegion} region The resized region
34386          * @param {Number} newSize The new size (width for east/west, height for north/south)
34387          */
34388         "regionresized" : true,
34389         /**
34390          * @event regioncollapsed
34391          * Fires when a region is collapsed.
34392          * @param {Roo.LayoutRegion} region The collapsed region
34393          */
34394         "regioncollapsed" : true,
34395         /**
34396          * @event regionexpanded
34397          * Fires when a region is expanded.
34398          * @param {Roo.LayoutRegion} region The expanded region
34399          */
34400         "regionexpanded" : true
34401     });
34402     this.updating = false;
34403
34404     if (config.el) {
34405         this.el = Roo.get(config.el);
34406         this.initEvents();
34407     }
34408
34409 };
34410
34411 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34412
34413
34414     regions : null,
34415
34416     monitorWindowResize : true,
34417
34418
34419     updating : false,
34420
34421
34422     onRender : function(ct, position)
34423     {
34424         if(!this.el){
34425             this.el = Roo.get(ct);
34426             this.initEvents();
34427         }
34428         //this.fireEvent('render',this);
34429     },
34430
34431
34432     initEvents: function()
34433     {
34434
34435
34436         // ie scrollbar fix
34437         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34438             document.body.scroll = "no";
34439         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34440             this.el.position('relative');
34441         }
34442         this.id = this.el.id;
34443         this.el.addClass("roo-layout-container");
34444         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34445         if(this.el.dom != document.body ) {
34446             this.el.on('resize', this.layout,this);
34447             this.el.on('show', this.layout,this);
34448         }
34449
34450     },
34451
34452     /**
34453      * Returns true if this layout is currently being updated
34454      * @return {Boolean}
34455      */
34456     isUpdating : function(){
34457         return this.updating;
34458     },
34459
34460     /**
34461      * Suspend the LayoutManager from doing auto-layouts while
34462      * making multiple add or remove calls
34463      */
34464     beginUpdate : function(){
34465         this.updating = true;
34466     },
34467
34468     /**
34469      * Restore auto-layouts and optionally disable the manager from performing a layout
34470      * @param {Boolean} noLayout true to disable a layout update
34471      */
34472     endUpdate : function(noLayout){
34473         this.updating = false;
34474         if(!noLayout){
34475             this.layout();
34476         }
34477     },
34478
34479     layout: function(){
34480         // abstract...
34481     },
34482
34483     onRegionResized : function(region, newSize){
34484         this.fireEvent("regionresized", region, newSize);
34485         this.layout();
34486     },
34487
34488     onRegionCollapsed : function(region){
34489         this.fireEvent("regioncollapsed", region);
34490     },
34491
34492     onRegionExpanded : function(region){
34493         this.fireEvent("regionexpanded", region);
34494     },
34495
34496     /**
34497      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34498      * performs box-model adjustments.
34499      * @return {Object} The size as an object {width: (the width), height: (the height)}
34500      */
34501     getViewSize : function()
34502     {
34503         var size;
34504         if(this.el.dom != document.body){
34505             size = this.el.getSize();
34506         }else{
34507             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34508         }
34509         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34510         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34511         return size;
34512     },
34513
34514     /**
34515      * Returns the Element this layout is bound to.
34516      * @return {Roo.Element}
34517      */
34518     getEl : function(){
34519         return this.el;
34520     },
34521
34522     /**
34523      * Returns the specified region.
34524      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34525      * @return {Roo.LayoutRegion}
34526      */
34527     getRegion : function(target){
34528         return this.regions[target.toLowerCase()];
34529     },
34530
34531     onWindowResize : function(){
34532         if(this.monitorWindowResize){
34533             this.layout();
34534         }
34535     }
34536 });
34537 /*
34538  * Based on:
34539  * Ext JS Library 1.1.1
34540  * Copyright(c) 2006-2007, Ext JS, LLC.
34541  *
34542  * Originally Released Under LGPL - original licence link has changed is not relivant.
34543  *
34544  * Fork - LGPL
34545  * <script type="text/javascript">
34546  */
34547 /**
34548  * @class Roo.bootstrap.layout.Border
34549  * @extends Roo.bootstrap.layout.Manager
34550  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34551  * please see: examples/bootstrap/nested.html<br><br>
34552  
34553 <b>The container the layout is rendered into can be either the body element or any other element.
34554 If it is not the body element, the container needs to either be an absolute positioned element,
34555 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34556 the container size if it is not the body element.</b>
34557
34558 * @constructor
34559 * Create a new Border
34560 * @param {Object} config Configuration options
34561  */
34562 Roo.bootstrap.layout.Border = function(config){
34563     config = config || {};
34564     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34565     
34566     
34567     
34568     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34569         if(config[region]){
34570             config[region].region = region;
34571             this.addRegion(config[region]);
34572         }
34573     },this);
34574     
34575 };
34576
34577 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34578
34579 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34580     /**
34581      * Creates and adds a new region if it doesn't already exist.
34582      * @param {String} target The target region key (north, south, east, west or center).
34583      * @param {Object} config The regions config object
34584      * @return {BorderLayoutRegion} The new region
34585      */
34586     addRegion : function(config)
34587     {
34588         if(!this.regions[config.region]){
34589             var r = this.factory(config);
34590             this.bindRegion(r);
34591         }
34592         return this.regions[config.region];
34593     },
34594
34595     // private (kinda)
34596     bindRegion : function(r){
34597         this.regions[r.config.region] = r;
34598         
34599         r.on("visibilitychange",    this.layout, this);
34600         r.on("paneladded",          this.layout, this);
34601         r.on("panelremoved",        this.layout, this);
34602         r.on("invalidated",         this.layout, this);
34603         r.on("resized",             this.onRegionResized, this);
34604         r.on("collapsed",           this.onRegionCollapsed, this);
34605         r.on("expanded",            this.onRegionExpanded, this);
34606     },
34607
34608     /**
34609      * Performs a layout update.
34610      */
34611     layout : function()
34612     {
34613         if(this.updating) {
34614             return;
34615         }
34616         
34617         // render all the rebions if they have not been done alreayd?
34618         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34619             if(this.regions[region] && !this.regions[region].bodyEl){
34620                 this.regions[region].onRender(this.el)
34621             }
34622         },this);
34623         
34624         var size = this.getViewSize();
34625         var w = size.width;
34626         var h = size.height;
34627         var centerW = w;
34628         var centerH = h;
34629         var centerY = 0;
34630         var centerX = 0;
34631         //var x = 0, y = 0;
34632
34633         var rs = this.regions;
34634         var north = rs["north"];
34635         var south = rs["south"]; 
34636         var west = rs["west"];
34637         var east = rs["east"];
34638         var center = rs["center"];
34639         //if(this.hideOnLayout){ // not supported anymore
34640             //c.el.setStyle("display", "none");
34641         //}
34642         if(north && north.isVisible()){
34643             var b = north.getBox();
34644             var m = north.getMargins();
34645             b.width = w - (m.left+m.right);
34646             b.x = m.left;
34647             b.y = m.top;
34648             centerY = b.height + b.y + m.bottom;
34649             centerH -= centerY;
34650             north.updateBox(this.safeBox(b));
34651         }
34652         if(south && south.isVisible()){
34653             var b = south.getBox();
34654             var m = south.getMargins();
34655             b.width = w - (m.left+m.right);
34656             b.x = m.left;
34657             var totalHeight = (b.height + m.top + m.bottom);
34658             b.y = h - totalHeight + m.top;
34659             centerH -= totalHeight;
34660             south.updateBox(this.safeBox(b));
34661         }
34662         if(west && west.isVisible()){
34663             var b = west.getBox();
34664             var m = west.getMargins();
34665             b.height = centerH - (m.top+m.bottom);
34666             b.x = m.left;
34667             b.y = centerY + m.top;
34668             var totalWidth = (b.width + m.left + m.right);
34669             centerX += totalWidth;
34670             centerW -= totalWidth;
34671             west.updateBox(this.safeBox(b));
34672         }
34673         if(east && east.isVisible()){
34674             var b = east.getBox();
34675             var m = east.getMargins();
34676             b.height = centerH - (m.top+m.bottom);
34677             var totalWidth = (b.width + m.left + m.right);
34678             b.x = w - totalWidth + m.left;
34679             b.y = centerY + m.top;
34680             centerW -= totalWidth;
34681             east.updateBox(this.safeBox(b));
34682         }
34683         if(center){
34684             var m = center.getMargins();
34685             var centerBox = {
34686                 x: centerX + m.left,
34687                 y: centerY + m.top,
34688                 width: centerW - (m.left+m.right),
34689                 height: centerH - (m.top+m.bottom)
34690             };
34691             //if(this.hideOnLayout){
34692                 //center.el.setStyle("display", "block");
34693             //}
34694             center.updateBox(this.safeBox(centerBox));
34695         }
34696         this.el.repaint();
34697         this.fireEvent("layout", this);
34698     },
34699
34700     // private
34701     safeBox : function(box){
34702         box.width = Math.max(0, box.width);
34703         box.height = Math.max(0, box.height);
34704         return box;
34705     },
34706
34707     /**
34708      * Adds a ContentPanel (or subclass) to this layout.
34709      * @param {String} target The target region key (north, south, east, west or center).
34710      * @param {Roo.ContentPanel} panel The panel to add
34711      * @return {Roo.ContentPanel} The added panel
34712      */
34713     add : function(target, panel){
34714          
34715         target = target.toLowerCase();
34716         return this.regions[target].add(panel);
34717     },
34718
34719     /**
34720      * Remove a ContentPanel (or subclass) to this layout.
34721      * @param {String} target The target region key (north, south, east, west or center).
34722      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34723      * @return {Roo.ContentPanel} The removed panel
34724      */
34725     remove : function(target, panel){
34726         target = target.toLowerCase();
34727         return this.regions[target].remove(panel);
34728     },
34729
34730     /**
34731      * Searches all regions for a panel with the specified id
34732      * @param {String} panelId
34733      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34734      */
34735     findPanel : function(panelId){
34736         var rs = this.regions;
34737         for(var target in rs){
34738             if(typeof rs[target] != "function"){
34739                 var p = rs[target].getPanel(panelId);
34740                 if(p){
34741                     return p;
34742                 }
34743             }
34744         }
34745         return null;
34746     },
34747
34748     /**
34749      * Searches all regions for a panel with the specified id and activates (shows) it.
34750      * @param {String/ContentPanel} panelId The panels id or the panel itself
34751      * @return {Roo.ContentPanel} The shown panel or null
34752      */
34753     showPanel : function(panelId) {
34754       var rs = this.regions;
34755       for(var target in rs){
34756          var r = rs[target];
34757          if(typeof r != "function"){
34758             if(r.hasPanel(panelId)){
34759                return r.showPanel(panelId);
34760             }
34761          }
34762       }
34763       return null;
34764    },
34765
34766    /**
34767      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34768      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34769      */
34770    /*
34771     restoreState : function(provider){
34772         if(!provider){
34773             provider = Roo.state.Manager;
34774         }
34775         var sm = new Roo.LayoutStateManager();
34776         sm.init(this, provider);
34777     },
34778 */
34779  
34780  
34781     /**
34782      * Adds a xtype elements to the layout.
34783      * <pre><code>
34784
34785 layout.addxtype({
34786        xtype : 'ContentPanel',
34787        region: 'west',
34788        items: [ .... ]
34789    }
34790 );
34791
34792 layout.addxtype({
34793         xtype : 'NestedLayoutPanel',
34794         region: 'west',
34795         layout: {
34796            center: { },
34797            west: { }   
34798         },
34799         items : [ ... list of content panels or nested layout panels.. ]
34800    }
34801 );
34802 </code></pre>
34803      * @param {Object} cfg Xtype definition of item to add.
34804      */
34805     addxtype : function(cfg)
34806     {
34807         // basically accepts a pannel...
34808         // can accept a layout region..!?!?
34809         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34810         
34811         
34812         // theory?  children can only be panels??
34813         
34814         //if (!cfg.xtype.match(/Panel$/)) {
34815         //    return false;
34816         //}
34817         var ret = false;
34818         
34819         if (typeof(cfg.region) == 'undefined') {
34820             Roo.log("Failed to add Panel, region was not set");
34821             Roo.log(cfg);
34822             return false;
34823         }
34824         var region = cfg.region;
34825         delete cfg.region;
34826         
34827           
34828         var xitems = [];
34829         if (cfg.items) {
34830             xitems = cfg.items;
34831             delete cfg.items;
34832         }
34833         var nb = false;
34834         
34835         switch(cfg.xtype) 
34836         {
34837             case 'Content':  // ContentPanel (el, cfg)
34838             case 'Scroll':  // ContentPanel (el, cfg)
34839             case 'View': 
34840                 cfg.autoCreate = true;
34841                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34842                 //} else {
34843                 //    var el = this.el.createChild();
34844                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34845                 //}
34846                 
34847                 this.add(region, ret);
34848                 break;
34849             
34850             /*
34851             case 'TreePanel': // our new panel!
34852                 cfg.el = this.el.createChild();
34853                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34854                 this.add(region, ret);
34855                 break;
34856             */
34857             
34858             case 'Nest': 
34859                 // create a new Layout (which is  a Border Layout...
34860                 
34861                 var clayout = cfg.layout;
34862                 clayout.el  = this.el.createChild();
34863                 clayout.items   = clayout.items  || [];
34864                 
34865                 delete cfg.layout;
34866                 
34867                 // replace this exitems with the clayout ones..
34868                 xitems = clayout.items;
34869                  
34870                 // force background off if it's in center...
34871                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34872                     cfg.background = false;
34873                 }
34874                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
34875                 
34876                 
34877                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34878                 //console.log('adding nested layout panel '  + cfg.toSource());
34879                 this.add(region, ret);
34880                 nb = {}; /// find first...
34881                 break;
34882             
34883             case 'Grid':
34884                 
34885                 // needs grid and region
34886                 
34887                 //var el = this.getRegion(region).el.createChild();
34888                 /*
34889                  *var el = this.el.createChild();
34890                 // create the grid first...
34891                 cfg.grid.container = el;
34892                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34893                 */
34894                 
34895                 if (region == 'center' && this.active ) {
34896                     cfg.background = false;
34897                 }
34898                 
34899                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34900                 
34901                 this.add(region, ret);
34902                 /*
34903                 if (cfg.background) {
34904                     // render grid on panel activation (if panel background)
34905                     ret.on('activate', function(gp) {
34906                         if (!gp.grid.rendered) {
34907                     //        gp.grid.render(el);
34908                         }
34909                     });
34910                 } else {
34911                   //  cfg.grid.render(el);
34912                 }
34913                 */
34914                 break;
34915            
34916            
34917             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34918                 // it was the old xcomponent building that caused this before.
34919                 // espeically if border is the top element in the tree.
34920                 ret = this;
34921                 break; 
34922                 
34923                     
34924                 
34925                 
34926                 
34927             default:
34928                 /*
34929                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34930                     
34931                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34932                     this.add(region, ret);
34933                 } else {
34934                 */
34935                     Roo.log(cfg);
34936                     throw "Can not add '" + cfg.xtype + "' to Border";
34937                     return null;
34938              
34939                                 
34940              
34941         }
34942         this.beginUpdate();
34943         // add children..
34944         var region = '';
34945         var abn = {};
34946         Roo.each(xitems, function(i)  {
34947             region = nb && i.region ? i.region : false;
34948             
34949             var add = ret.addxtype(i);
34950            
34951             if (region) {
34952                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
34953                 if (!i.background) {
34954                     abn[region] = nb[region] ;
34955                 }
34956             }
34957             
34958         });
34959         this.endUpdate();
34960
34961         // make the last non-background panel active..
34962         //if (nb) { Roo.log(abn); }
34963         if (nb) {
34964             
34965             for(var r in abn) {
34966                 region = this.getRegion(r);
34967                 if (region) {
34968                     // tried using nb[r], but it does not work..
34969                      
34970                     region.showPanel(abn[r]);
34971                    
34972                 }
34973             }
34974         }
34975         return ret;
34976         
34977     },
34978     
34979     
34980 // private
34981     factory : function(cfg)
34982     {
34983         
34984         var validRegions = Roo.bootstrap.layout.Border.regions;
34985
34986         var target = cfg.region;
34987         cfg.mgr = this;
34988         
34989         var r = Roo.bootstrap.layout;
34990         Roo.log(target);
34991         switch(target){
34992             case "north":
34993                 return new r.North(cfg);
34994             case "south":
34995                 return new r.South(cfg);
34996             case "east":
34997                 return new r.East(cfg);
34998             case "west":
34999                 return new r.West(cfg);
35000             case "center":
35001                 return new r.Center(cfg);
35002         }
35003         throw 'Layout region "'+target+'" not supported.';
35004     }
35005     
35006     
35007 });
35008  /*
35009  * Based on:
35010  * Ext JS Library 1.1.1
35011  * Copyright(c) 2006-2007, Ext JS, LLC.
35012  *
35013  * Originally Released Under LGPL - original licence link has changed is not relivant.
35014  *
35015  * Fork - LGPL
35016  * <script type="text/javascript">
35017  */
35018  
35019 /**
35020  * @class Roo.bootstrap.layout.Basic
35021  * @extends Roo.util.Observable
35022  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35023  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35024  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35025  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35026  * @cfg {string}   region  the region that it inhabits..
35027  * @cfg {bool}   skipConfig skip config?
35028  * 
35029
35030  */
35031 Roo.bootstrap.layout.Basic = function(config){
35032     
35033     this.mgr = config.mgr;
35034     
35035     this.position = config.region;
35036     
35037     var skipConfig = config.skipConfig;
35038     
35039     this.events = {
35040         /**
35041          * @scope Roo.BasicLayoutRegion
35042          */
35043         
35044         /**
35045          * @event beforeremove
35046          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35047          * @param {Roo.LayoutRegion} this
35048          * @param {Roo.ContentPanel} panel The panel
35049          * @param {Object} e The cancel event object
35050          */
35051         "beforeremove" : true,
35052         /**
35053          * @event invalidated
35054          * Fires when the layout for this region is changed.
35055          * @param {Roo.LayoutRegion} this
35056          */
35057         "invalidated" : true,
35058         /**
35059          * @event visibilitychange
35060          * Fires when this region is shown or hidden 
35061          * @param {Roo.LayoutRegion} this
35062          * @param {Boolean} visibility true or false
35063          */
35064         "visibilitychange" : true,
35065         /**
35066          * @event paneladded
35067          * Fires when a panel is added. 
35068          * @param {Roo.LayoutRegion} this
35069          * @param {Roo.ContentPanel} panel The panel
35070          */
35071         "paneladded" : true,
35072         /**
35073          * @event panelremoved
35074          * Fires when a panel is removed. 
35075          * @param {Roo.LayoutRegion} this
35076          * @param {Roo.ContentPanel} panel The panel
35077          */
35078         "panelremoved" : true,
35079         /**
35080          * @event beforecollapse
35081          * Fires when this region before collapse.
35082          * @param {Roo.LayoutRegion} this
35083          */
35084         "beforecollapse" : true,
35085         /**
35086          * @event collapsed
35087          * Fires when this region is collapsed.
35088          * @param {Roo.LayoutRegion} this
35089          */
35090         "collapsed" : true,
35091         /**
35092          * @event expanded
35093          * Fires when this region is expanded.
35094          * @param {Roo.LayoutRegion} this
35095          */
35096         "expanded" : true,
35097         /**
35098          * @event slideshow
35099          * Fires when this region is slid into view.
35100          * @param {Roo.LayoutRegion} this
35101          */
35102         "slideshow" : true,
35103         /**
35104          * @event slidehide
35105          * Fires when this region slides out of view. 
35106          * @param {Roo.LayoutRegion} this
35107          */
35108         "slidehide" : true,
35109         /**
35110          * @event panelactivated
35111          * Fires when a panel is activated. 
35112          * @param {Roo.LayoutRegion} this
35113          * @param {Roo.ContentPanel} panel The activated panel
35114          */
35115         "panelactivated" : true,
35116         /**
35117          * @event resized
35118          * Fires when the user resizes this region. 
35119          * @param {Roo.LayoutRegion} this
35120          * @param {Number} newSize The new size (width for east/west, height for north/south)
35121          */
35122         "resized" : true
35123     };
35124     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35125     this.panels = new Roo.util.MixedCollection();
35126     this.panels.getKey = this.getPanelId.createDelegate(this);
35127     this.box = null;
35128     this.activePanel = null;
35129     // ensure listeners are added...
35130     
35131     if (config.listeners || config.events) {
35132         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35133             listeners : config.listeners || {},
35134             events : config.events || {}
35135         });
35136     }
35137     
35138     if(skipConfig !== true){
35139         this.applyConfig(config);
35140     }
35141 };
35142
35143 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35144 {
35145     getPanelId : function(p){
35146         return p.getId();
35147     },
35148     
35149     applyConfig : function(config){
35150         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35151         this.config = config;
35152         
35153     },
35154     
35155     /**
35156      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35157      * the width, for horizontal (north, south) the height.
35158      * @param {Number} newSize The new width or height
35159      */
35160     resizeTo : function(newSize){
35161         var el = this.el ? this.el :
35162                  (this.activePanel ? this.activePanel.getEl() : null);
35163         if(el){
35164             switch(this.position){
35165                 case "east":
35166                 case "west":
35167                     el.setWidth(newSize);
35168                     this.fireEvent("resized", this, newSize);
35169                 break;
35170                 case "north":
35171                 case "south":
35172                     el.setHeight(newSize);
35173                     this.fireEvent("resized", this, newSize);
35174                 break;                
35175             }
35176         }
35177     },
35178     
35179     getBox : function(){
35180         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35181     },
35182     
35183     getMargins : function(){
35184         return this.margins;
35185     },
35186     
35187     updateBox : function(box){
35188         this.box = box;
35189         var el = this.activePanel.getEl();
35190         el.dom.style.left = box.x + "px";
35191         el.dom.style.top = box.y + "px";
35192         this.activePanel.setSize(box.width, box.height);
35193     },
35194     
35195     /**
35196      * Returns the container element for this region.
35197      * @return {Roo.Element}
35198      */
35199     getEl : function(){
35200         return this.activePanel;
35201     },
35202     
35203     /**
35204      * Returns true if this region is currently visible.
35205      * @return {Boolean}
35206      */
35207     isVisible : function(){
35208         return this.activePanel ? true : false;
35209     },
35210     
35211     setActivePanel : function(panel){
35212         panel = this.getPanel(panel);
35213         if(this.activePanel && this.activePanel != panel){
35214             this.activePanel.setActiveState(false);
35215             this.activePanel.getEl().setLeftTop(-10000,-10000);
35216         }
35217         this.activePanel = panel;
35218         panel.setActiveState(true);
35219         if(this.box){
35220             panel.setSize(this.box.width, this.box.height);
35221         }
35222         this.fireEvent("panelactivated", this, panel);
35223         this.fireEvent("invalidated");
35224     },
35225     
35226     /**
35227      * Show the specified panel.
35228      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35229      * @return {Roo.ContentPanel} The shown panel or null
35230      */
35231     showPanel : function(panel){
35232         panel = this.getPanel(panel);
35233         if(panel){
35234             this.setActivePanel(panel);
35235         }
35236         return panel;
35237     },
35238     
35239     /**
35240      * Get the active panel for this region.
35241      * @return {Roo.ContentPanel} The active panel or null
35242      */
35243     getActivePanel : function(){
35244         return this.activePanel;
35245     },
35246     
35247     /**
35248      * Add the passed ContentPanel(s)
35249      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35250      * @return {Roo.ContentPanel} The panel added (if only one was added)
35251      */
35252     add : function(panel){
35253         if(arguments.length > 1){
35254             for(var i = 0, len = arguments.length; i < len; i++) {
35255                 this.add(arguments[i]);
35256             }
35257             return null;
35258         }
35259         if(this.hasPanel(panel)){
35260             this.showPanel(panel);
35261             return panel;
35262         }
35263         var el = panel.getEl();
35264         if(el.dom.parentNode != this.mgr.el.dom){
35265             this.mgr.el.dom.appendChild(el.dom);
35266         }
35267         if(panel.setRegion){
35268             panel.setRegion(this);
35269         }
35270         this.panels.add(panel);
35271         el.setStyle("position", "absolute");
35272         if(!panel.background){
35273             this.setActivePanel(panel);
35274             if(this.config.initialSize && this.panels.getCount()==1){
35275                 this.resizeTo(this.config.initialSize);
35276             }
35277         }
35278         this.fireEvent("paneladded", this, panel);
35279         return panel;
35280     },
35281     
35282     /**
35283      * Returns true if the panel is in this region.
35284      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35285      * @return {Boolean}
35286      */
35287     hasPanel : function(panel){
35288         if(typeof panel == "object"){ // must be panel obj
35289             panel = panel.getId();
35290         }
35291         return this.getPanel(panel) ? true : false;
35292     },
35293     
35294     /**
35295      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35296      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35297      * @param {Boolean} preservePanel Overrides the config preservePanel option
35298      * @return {Roo.ContentPanel} The panel that was removed
35299      */
35300     remove : function(panel, preservePanel){
35301         panel = this.getPanel(panel);
35302         if(!panel){
35303             return null;
35304         }
35305         var e = {};
35306         this.fireEvent("beforeremove", this, panel, e);
35307         if(e.cancel === true){
35308             return null;
35309         }
35310         var panelId = panel.getId();
35311         this.panels.removeKey(panelId);
35312         return panel;
35313     },
35314     
35315     /**
35316      * Returns the panel specified or null if it's not in this region.
35317      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35318      * @return {Roo.ContentPanel}
35319      */
35320     getPanel : function(id){
35321         if(typeof id == "object"){ // must be panel obj
35322             return id;
35323         }
35324         return this.panels.get(id);
35325     },
35326     
35327     /**
35328      * Returns this regions position (north/south/east/west/center).
35329      * @return {String} 
35330      */
35331     getPosition: function(){
35332         return this.position;    
35333     }
35334 });/*
35335  * Based on:
35336  * Ext JS Library 1.1.1
35337  * Copyright(c) 2006-2007, Ext JS, LLC.
35338  *
35339  * Originally Released Under LGPL - original licence link has changed is not relivant.
35340  *
35341  * Fork - LGPL
35342  * <script type="text/javascript">
35343  */
35344  
35345 /**
35346  * @class Roo.bootstrap.layout.Region
35347  * @extends Roo.bootstrap.layout.Basic
35348  * This class represents a region in a layout manager.
35349  
35350  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35351  * @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})
35352  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35353  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35354  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35355  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35356  * @cfg {String}    title           The title for the region (overrides panel titles)
35357  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35358  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35359  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35360  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35361  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35362  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35363  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35364  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35365  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35366  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35367
35368  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35369  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35370  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35371  * @cfg {Number}    width           For East/West panels
35372  * @cfg {Number}    height          For North/South panels
35373  * @cfg {Boolean}   split           To show the splitter
35374  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35375  * 
35376  * @cfg {string}   cls             Extra CSS classes to add to region
35377  * 
35378  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35379  * @cfg {string}   region  the region that it inhabits..
35380  *
35381
35382  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35383  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35384
35385  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35386  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35387  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35388  */
35389 Roo.bootstrap.layout.Region = function(config)
35390 {
35391     this.applyConfig(config);
35392
35393     var mgr = config.mgr;
35394     var pos = config.region;
35395     config.skipConfig = true;
35396     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35397     
35398     if (mgr.el) {
35399         this.onRender(mgr.el);   
35400     }
35401      
35402     this.visible = true;
35403     this.collapsed = false;
35404     this.unrendered_panels = [];
35405 };
35406
35407 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35408
35409     position: '', // set by wrapper (eg. north/south etc..)
35410     unrendered_panels : null,  // unrendered panels.
35411     createBody : function(){
35412         /** This region's body element 
35413         * @type Roo.Element */
35414         this.bodyEl = this.el.createChild({
35415                 tag: "div",
35416                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35417         });
35418     },
35419
35420     onRender: function(ctr, pos)
35421     {
35422         var dh = Roo.DomHelper;
35423         /** This region's container element 
35424         * @type Roo.Element */
35425         this.el = dh.append(ctr.dom, {
35426                 tag: "div",
35427                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35428             }, true);
35429         /** This region's title element 
35430         * @type Roo.Element */
35431     
35432         this.titleEl = dh.append(this.el.dom,
35433             {
35434                     tag: "div",
35435                     unselectable: "on",
35436                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35437                     children:[
35438                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35439                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35440                     ]}, true);
35441         
35442         this.titleEl.enableDisplayMode();
35443         /** This region's title text element 
35444         * @type HTMLElement */
35445         this.titleTextEl = this.titleEl.dom.firstChild;
35446         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35447         /*
35448         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35449         this.closeBtn.enableDisplayMode();
35450         this.closeBtn.on("click", this.closeClicked, this);
35451         this.closeBtn.hide();
35452     */
35453         this.createBody(this.config);
35454         if(this.config.hideWhenEmpty){
35455             this.hide();
35456             this.on("paneladded", this.validateVisibility, this);
35457             this.on("panelremoved", this.validateVisibility, this);
35458         }
35459         if(this.autoScroll){
35460             this.bodyEl.setStyle("overflow", "auto");
35461         }else{
35462             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35463         }
35464         //if(c.titlebar !== false){
35465             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35466                 this.titleEl.hide();
35467             }else{
35468                 this.titleEl.show();
35469                 if(this.config.title){
35470                     this.titleTextEl.innerHTML = this.config.title;
35471                 }
35472             }
35473         //}
35474         if(this.config.collapsed){
35475             this.collapse(true);
35476         }
35477         if(this.config.hidden){
35478             this.hide();
35479         }
35480         
35481         if (this.unrendered_panels && this.unrendered_panels.length) {
35482             for (var i =0;i< this.unrendered_panels.length; i++) {
35483                 this.add(this.unrendered_panels[i]);
35484             }
35485             this.unrendered_panels = null;
35486             
35487         }
35488         
35489     },
35490     
35491     applyConfig : function(c)
35492     {
35493         /*
35494          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35495             var dh = Roo.DomHelper;
35496             if(c.titlebar !== false){
35497                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35498                 this.collapseBtn.on("click", this.collapse, this);
35499                 this.collapseBtn.enableDisplayMode();
35500                 /*
35501                 if(c.showPin === true || this.showPin){
35502                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35503                     this.stickBtn.enableDisplayMode();
35504                     this.stickBtn.on("click", this.expand, this);
35505                     this.stickBtn.hide();
35506                 }
35507                 
35508             }
35509             */
35510             /** This region's collapsed element
35511             * @type Roo.Element */
35512             /*
35513              *
35514             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35515                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35516             ]}, true);
35517             
35518             if(c.floatable !== false){
35519                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35520                this.collapsedEl.on("click", this.collapseClick, this);
35521             }
35522
35523             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35524                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35525                    id: "message", unselectable: "on", style:{"float":"left"}});
35526                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35527              }
35528             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35529             this.expandBtn.on("click", this.expand, this);
35530             
35531         }
35532         
35533         if(this.collapseBtn){
35534             this.collapseBtn.setVisible(c.collapsible == true);
35535         }
35536         
35537         this.cmargins = c.cmargins || this.cmargins ||
35538                          (this.position == "west" || this.position == "east" ?
35539                              {top: 0, left: 2, right:2, bottom: 0} :
35540                              {top: 2, left: 0, right:0, bottom: 2});
35541         */
35542         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35543         
35544         
35545         this.bottomTabs = c.tabPosition != "top";
35546         
35547         this.autoScroll = c.autoScroll || false;
35548         
35549         
35550        
35551         
35552         this.duration = c.duration || .30;
35553         this.slideDuration = c.slideDuration || .45;
35554         this.config = c;
35555        
35556     },
35557     /**
35558      * Returns true if this region is currently visible.
35559      * @return {Boolean}
35560      */
35561     isVisible : function(){
35562         return this.visible;
35563     },
35564
35565     /**
35566      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35567      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35568      */
35569     //setCollapsedTitle : function(title){
35570     //    title = title || "&#160;";
35571      //   if(this.collapsedTitleTextEl){
35572       //      this.collapsedTitleTextEl.innerHTML = title;
35573        // }
35574     //},
35575
35576     getBox : function(){
35577         var b;
35578       //  if(!this.collapsed){
35579             b = this.el.getBox(false, true);
35580        // }else{
35581           //  b = this.collapsedEl.getBox(false, true);
35582         //}
35583         return b;
35584     },
35585
35586     getMargins : function(){
35587         return this.margins;
35588         //return this.collapsed ? this.cmargins : this.margins;
35589     },
35590 /*
35591     highlight : function(){
35592         this.el.addClass("x-layout-panel-dragover");
35593     },
35594
35595     unhighlight : function(){
35596         this.el.removeClass("x-layout-panel-dragover");
35597     },
35598 */
35599     updateBox : function(box)
35600     {
35601         if (!this.bodyEl) {
35602             return; // not rendered yet..
35603         }
35604         
35605         this.box = box;
35606         if(!this.collapsed){
35607             this.el.dom.style.left = box.x + "px";
35608             this.el.dom.style.top = box.y + "px";
35609             this.updateBody(box.width, box.height);
35610         }else{
35611             this.collapsedEl.dom.style.left = box.x + "px";
35612             this.collapsedEl.dom.style.top = box.y + "px";
35613             this.collapsedEl.setSize(box.width, box.height);
35614         }
35615         if(this.tabs){
35616             this.tabs.autoSizeTabs();
35617         }
35618     },
35619
35620     updateBody : function(w, h)
35621     {
35622         if(w !== null){
35623             this.el.setWidth(w);
35624             w -= this.el.getBorderWidth("rl");
35625             if(this.config.adjustments){
35626                 w += this.config.adjustments[0];
35627             }
35628         }
35629         if(h !== null && h > 0){
35630             this.el.setHeight(h);
35631             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35632             h -= this.el.getBorderWidth("tb");
35633             if(this.config.adjustments){
35634                 h += this.config.adjustments[1];
35635             }
35636             this.bodyEl.setHeight(h);
35637             if(this.tabs){
35638                 h = this.tabs.syncHeight(h);
35639             }
35640         }
35641         if(this.panelSize){
35642             w = w !== null ? w : this.panelSize.width;
35643             h = h !== null ? h : this.panelSize.height;
35644         }
35645         if(this.activePanel){
35646             var el = this.activePanel.getEl();
35647             w = w !== null ? w : el.getWidth();
35648             h = h !== null ? h : el.getHeight();
35649             this.panelSize = {width: w, height: h};
35650             this.activePanel.setSize(w, h);
35651         }
35652         if(Roo.isIE && this.tabs){
35653             this.tabs.el.repaint();
35654         }
35655     },
35656
35657     /**
35658      * Returns the container element for this region.
35659      * @return {Roo.Element}
35660      */
35661     getEl : function(){
35662         return this.el;
35663     },
35664
35665     /**
35666      * Hides this region.
35667      */
35668     hide : function(){
35669         //if(!this.collapsed){
35670             this.el.dom.style.left = "-2000px";
35671             this.el.hide();
35672         //}else{
35673          //   this.collapsedEl.dom.style.left = "-2000px";
35674          //   this.collapsedEl.hide();
35675        // }
35676         this.visible = false;
35677         this.fireEvent("visibilitychange", this, false);
35678     },
35679
35680     /**
35681      * Shows this region if it was previously hidden.
35682      */
35683     show : function(){
35684         //if(!this.collapsed){
35685             this.el.show();
35686         //}else{
35687         //    this.collapsedEl.show();
35688        // }
35689         this.visible = true;
35690         this.fireEvent("visibilitychange", this, true);
35691     },
35692 /*
35693     closeClicked : function(){
35694         if(this.activePanel){
35695             this.remove(this.activePanel);
35696         }
35697     },
35698
35699     collapseClick : function(e){
35700         if(this.isSlid){
35701            e.stopPropagation();
35702            this.slideIn();
35703         }else{
35704            e.stopPropagation();
35705            this.slideOut();
35706         }
35707     },
35708 */
35709     /**
35710      * Collapses this region.
35711      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35712      */
35713     /*
35714     collapse : function(skipAnim, skipCheck = false){
35715         if(this.collapsed) {
35716             return;
35717         }
35718         
35719         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35720             
35721             this.collapsed = true;
35722             if(this.split){
35723                 this.split.el.hide();
35724             }
35725             if(this.config.animate && skipAnim !== true){
35726                 this.fireEvent("invalidated", this);
35727                 this.animateCollapse();
35728             }else{
35729                 this.el.setLocation(-20000,-20000);
35730                 this.el.hide();
35731                 this.collapsedEl.show();
35732                 this.fireEvent("collapsed", this);
35733                 this.fireEvent("invalidated", this);
35734             }
35735         }
35736         
35737     },
35738 */
35739     animateCollapse : function(){
35740         // overridden
35741     },
35742
35743     /**
35744      * Expands this region if it was previously collapsed.
35745      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35746      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35747      */
35748     /*
35749     expand : function(e, skipAnim){
35750         if(e) {
35751             e.stopPropagation();
35752         }
35753         if(!this.collapsed || this.el.hasActiveFx()) {
35754             return;
35755         }
35756         if(this.isSlid){
35757             this.afterSlideIn();
35758             skipAnim = true;
35759         }
35760         this.collapsed = false;
35761         if(this.config.animate && skipAnim !== true){
35762             this.animateExpand();
35763         }else{
35764             this.el.show();
35765             if(this.split){
35766                 this.split.el.show();
35767             }
35768             this.collapsedEl.setLocation(-2000,-2000);
35769             this.collapsedEl.hide();
35770             this.fireEvent("invalidated", this);
35771             this.fireEvent("expanded", this);
35772         }
35773     },
35774 */
35775     animateExpand : function(){
35776         // overridden
35777     },
35778
35779     initTabs : function()
35780     {
35781         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35782         
35783         var ts = new Roo.bootstrap.panel.Tabs({
35784                 el: this.bodyEl.dom,
35785                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35786                 disableTooltips: this.config.disableTabTips,
35787                 toolbar : this.config.toolbar
35788             });
35789         
35790         if(this.config.hideTabs){
35791             ts.stripWrap.setDisplayed(false);
35792         }
35793         this.tabs = ts;
35794         ts.resizeTabs = this.config.resizeTabs === true;
35795         ts.minTabWidth = this.config.minTabWidth || 40;
35796         ts.maxTabWidth = this.config.maxTabWidth || 250;
35797         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35798         ts.monitorResize = false;
35799         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35800         ts.bodyEl.addClass('roo-layout-tabs-body');
35801         this.panels.each(this.initPanelAsTab, this);
35802     },
35803
35804     initPanelAsTab : function(panel){
35805         var ti = this.tabs.addTab(
35806             panel.getEl().id,
35807             panel.getTitle(),
35808             null,
35809             this.config.closeOnTab && panel.isClosable(),
35810             panel.tpl
35811         );
35812         if(panel.tabTip !== undefined){
35813             ti.setTooltip(panel.tabTip);
35814         }
35815         ti.on("activate", function(){
35816               this.setActivePanel(panel);
35817         }, this);
35818         
35819         if(this.config.closeOnTab){
35820             ti.on("beforeclose", function(t, e){
35821                 e.cancel = true;
35822                 this.remove(panel);
35823             }, this);
35824         }
35825         
35826         panel.tabItem = ti;
35827         
35828         return ti;
35829     },
35830
35831     updatePanelTitle : function(panel, title)
35832     {
35833         if(this.activePanel == panel){
35834             this.updateTitle(title);
35835         }
35836         if(this.tabs){
35837             var ti = this.tabs.getTab(panel.getEl().id);
35838             ti.setText(title);
35839             if(panel.tabTip !== undefined){
35840                 ti.setTooltip(panel.tabTip);
35841             }
35842         }
35843     },
35844
35845     updateTitle : function(title){
35846         if(this.titleTextEl && !this.config.title){
35847             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35848         }
35849     },
35850
35851     setActivePanel : function(panel)
35852     {
35853         panel = this.getPanel(panel);
35854         if(this.activePanel && this.activePanel != panel){
35855             if(this.activePanel.setActiveState(false) === false){
35856                 return;
35857             }
35858         }
35859         this.activePanel = panel;
35860         panel.setActiveState(true);
35861         if(this.panelSize){
35862             panel.setSize(this.panelSize.width, this.panelSize.height);
35863         }
35864         if(this.closeBtn){
35865             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35866         }
35867         this.updateTitle(panel.getTitle());
35868         if(this.tabs){
35869             this.fireEvent("invalidated", this);
35870         }
35871         this.fireEvent("panelactivated", this, panel);
35872     },
35873
35874     /**
35875      * Shows the specified panel.
35876      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35877      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35878      */
35879     showPanel : function(panel)
35880     {
35881         panel = this.getPanel(panel);
35882         if(panel){
35883             if(this.tabs){
35884                 var tab = this.tabs.getTab(panel.getEl().id);
35885                 if(tab.isHidden()){
35886                     this.tabs.unhideTab(tab.id);
35887                 }
35888                 tab.activate();
35889             }else{
35890                 this.setActivePanel(panel);
35891             }
35892         }
35893         return panel;
35894     },
35895
35896     /**
35897      * Get the active panel for this region.
35898      * @return {Roo.ContentPanel} The active panel or null
35899      */
35900     getActivePanel : function(){
35901         return this.activePanel;
35902     },
35903
35904     validateVisibility : function(){
35905         if(this.panels.getCount() < 1){
35906             this.updateTitle("&#160;");
35907             this.closeBtn.hide();
35908             this.hide();
35909         }else{
35910             if(!this.isVisible()){
35911                 this.show();
35912             }
35913         }
35914     },
35915
35916     /**
35917      * Adds the passed ContentPanel(s) to this region.
35918      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35919      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35920      */
35921     add : function(panel)
35922     {
35923         if(arguments.length > 1){
35924             for(var i = 0, len = arguments.length; i < len; i++) {
35925                 this.add(arguments[i]);
35926             }
35927             return null;
35928         }
35929         
35930         // if we have not been rendered yet, then we can not really do much of this..
35931         if (!this.bodyEl) {
35932             this.unrendered_panels.push(panel);
35933             return panel;
35934         }
35935         
35936         
35937         
35938         
35939         if(this.hasPanel(panel)){
35940             this.showPanel(panel);
35941             return panel;
35942         }
35943         panel.setRegion(this);
35944         this.panels.add(panel);
35945        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35946             // sinle panel - no tab...?? would it not be better to render it with the tabs,
35947             // and hide them... ???
35948             this.bodyEl.dom.appendChild(panel.getEl().dom);
35949             if(panel.background !== true){
35950                 this.setActivePanel(panel);
35951             }
35952             this.fireEvent("paneladded", this, panel);
35953             return panel;
35954         }
35955         */
35956         if(!this.tabs){
35957             this.initTabs();
35958         }else{
35959             this.initPanelAsTab(panel);
35960         }
35961         
35962         
35963         if(panel.background !== true){
35964             this.tabs.activate(panel.getEl().id);
35965         }
35966         this.fireEvent("paneladded", this, panel);
35967         return panel;
35968     },
35969
35970     /**
35971      * Hides the tab for the specified panel.
35972      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35973      */
35974     hidePanel : function(panel){
35975         if(this.tabs && (panel = this.getPanel(panel))){
35976             this.tabs.hideTab(panel.getEl().id);
35977         }
35978     },
35979
35980     /**
35981      * Unhides the tab for a previously hidden panel.
35982      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35983      */
35984     unhidePanel : function(panel){
35985         if(this.tabs && (panel = this.getPanel(panel))){
35986             this.tabs.unhideTab(panel.getEl().id);
35987         }
35988     },
35989
35990     clearPanels : function(){
35991         while(this.panels.getCount() > 0){
35992              this.remove(this.panels.first());
35993         }
35994     },
35995
35996     /**
35997      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35998      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35999      * @param {Boolean} preservePanel Overrides the config preservePanel option
36000      * @return {Roo.ContentPanel} The panel that was removed
36001      */
36002     remove : function(panel, preservePanel)
36003     {
36004         panel = this.getPanel(panel);
36005         if(!panel){
36006             return null;
36007         }
36008         var e = {};
36009         this.fireEvent("beforeremove", this, panel, e);
36010         if(e.cancel === true){
36011             return null;
36012         }
36013         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36014         var panelId = panel.getId();
36015         this.panels.removeKey(panelId);
36016         if(preservePanel){
36017             document.body.appendChild(panel.getEl().dom);
36018         }
36019         if(this.tabs){
36020             this.tabs.removeTab(panel.getEl().id);
36021         }else if (!preservePanel){
36022             this.bodyEl.dom.removeChild(panel.getEl().dom);
36023         }
36024         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36025             var p = this.panels.first();
36026             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36027             tempEl.appendChild(p.getEl().dom);
36028             this.bodyEl.update("");
36029             this.bodyEl.dom.appendChild(p.getEl().dom);
36030             tempEl = null;
36031             this.updateTitle(p.getTitle());
36032             this.tabs = null;
36033             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36034             this.setActivePanel(p);
36035         }
36036         panel.setRegion(null);
36037         if(this.activePanel == panel){
36038             this.activePanel = null;
36039         }
36040         if(this.config.autoDestroy !== false && preservePanel !== true){
36041             try{panel.destroy();}catch(e){}
36042         }
36043         this.fireEvent("panelremoved", this, panel);
36044         return panel;
36045     },
36046
36047     /**
36048      * Returns the TabPanel component used by this region
36049      * @return {Roo.TabPanel}
36050      */
36051     getTabs : function(){
36052         return this.tabs;
36053     },
36054
36055     createTool : function(parentEl, className){
36056         var btn = Roo.DomHelper.append(parentEl, {
36057             tag: "div",
36058             cls: "x-layout-tools-button",
36059             children: [ {
36060                 tag: "div",
36061                 cls: "roo-layout-tools-button-inner " + className,
36062                 html: "&#160;"
36063             }]
36064         }, true);
36065         btn.addClassOnOver("roo-layout-tools-button-over");
36066         return btn;
36067     }
36068 });/*
36069  * Based on:
36070  * Ext JS Library 1.1.1
36071  * Copyright(c) 2006-2007, Ext JS, LLC.
36072  *
36073  * Originally Released Under LGPL - original licence link has changed is not relivant.
36074  *
36075  * Fork - LGPL
36076  * <script type="text/javascript">
36077  */
36078  
36079
36080
36081 /**
36082  * @class Roo.SplitLayoutRegion
36083  * @extends Roo.LayoutRegion
36084  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36085  */
36086 Roo.bootstrap.layout.Split = function(config){
36087     this.cursor = config.cursor;
36088     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36089 };
36090
36091 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36092 {
36093     splitTip : "Drag to resize.",
36094     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36095     useSplitTips : false,
36096
36097     applyConfig : function(config){
36098         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36099     },
36100     
36101     onRender : function(ctr,pos) {
36102         
36103         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36104         if(!this.config.split){
36105             return;
36106         }
36107         if(!this.split){
36108             
36109             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36110                             tag: "div",
36111                             id: this.el.id + "-split",
36112                             cls: "roo-layout-split roo-layout-split-"+this.position,
36113                             html: "&#160;"
36114             });
36115             /** The SplitBar for this region 
36116             * @type Roo.SplitBar */
36117             // does not exist yet...
36118             Roo.log([this.position, this.orientation]);
36119             
36120             this.split = new Roo.bootstrap.SplitBar({
36121                 dragElement : splitEl,
36122                 resizingElement: this.el,
36123                 orientation : this.orientation
36124             });
36125             
36126             this.split.on("moved", this.onSplitMove, this);
36127             this.split.useShim = this.config.useShim === true;
36128             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36129             if(this.useSplitTips){
36130                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36131             }
36132             //if(config.collapsible){
36133             //    this.split.el.on("dblclick", this.collapse,  this);
36134             //}
36135         }
36136         if(typeof this.config.minSize != "undefined"){
36137             this.split.minSize = this.config.minSize;
36138         }
36139         if(typeof this.config.maxSize != "undefined"){
36140             this.split.maxSize = this.config.maxSize;
36141         }
36142         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36143             this.hideSplitter();
36144         }
36145         
36146     },
36147
36148     getHMaxSize : function(){
36149          var cmax = this.config.maxSize || 10000;
36150          var center = this.mgr.getRegion("center");
36151          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36152     },
36153
36154     getVMaxSize : function(){
36155          var cmax = this.config.maxSize || 10000;
36156          var center = this.mgr.getRegion("center");
36157          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36158     },
36159
36160     onSplitMove : function(split, newSize){
36161         this.fireEvent("resized", this, newSize);
36162     },
36163     
36164     /** 
36165      * Returns the {@link Roo.SplitBar} for this region.
36166      * @return {Roo.SplitBar}
36167      */
36168     getSplitBar : function(){
36169         return this.split;
36170     },
36171     
36172     hide : function(){
36173         this.hideSplitter();
36174         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36175     },
36176
36177     hideSplitter : function(){
36178         if(this.split){
36179             this.split.el.setLocation(-2000,-2000);
36180             this.split.el.hide();
36181         }
36182     },
36183
36184     show : function(){
36185         if(this.split){
36186             this.split.el.show();
36187         }
36188         Roo.bootstrap.layout.Split.superclass.show.call(this);
36189     },
36190     
36191     beforeSlide: function(){
36192         if(Roo.isGecko){// firefox overflow auto bug workaround
36193             this.bodyEl.clip();
36194             if(this.tabs) {
36195                 this.tabs.bodyEl.clip();
36196             }
36197             if(this.activePanel){
36198                 this.activePanel.getEl().clip();
36199                 
36200                 if(this.activePanel.beforeSlide){
36201                     this.activePanel.beforeSlide();
36202                 }
36203             }
36204         }
36205     },
36206     
36207     afterSlide : function(){
36208         if(Roo.isGecko){// firefox overflow auto bug workaround
36209             this.bodyEl.unclip();
36210             if(this.tabs) {
36211                 this.tabs.bodyEl.unclip();
36212             }
36213             if(this.activePanel){
36214                 this.activePanel.getEl().unclip();
36215                 if(this.activePanel.afterSlide){
36216                     this.activePanel.afterSlide();
36217                 }
36218             }
36219         }
36220     },
36221
36222     initAutoHide : function(){
36223         if(this.autoHide !== false){
36224             if(!this.autoHideHd){
36225                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36226                 this.autoHideHd = {
36227                     "mouseout": function(e){
36228                         if(!e.within(this.el, true)){
36229                             st.delay(500);
36230                         }
36231                     },
36232                     "mouseover" : function(e){
36233                         st.cancel();
36234                     },
36235                     scope : this
36236                 };
36237             }
36238             this.el.on(this.autoHideHd);
36239         }
36240     },
36241
36242     clearAutoHide : function(){
36243         if(this.autoHide !== false){
36244             this.el.un("mouseout", this.autoHideHd.mouseout);
36245             this.el.un("mouseover", this.autoHideHd.mouseover);
36246         }
36247     },
36248
36249     clearMonitor : function(){
36250         Roo.get(document).un("click", this.slideInIf, this);
36251     },
36252
36253     // these names are backwards but not changed for compat
36254     slideOut : function(){
36255         if(this.isSlid || this.el.hasActiveFx()){
36256             return;
36257         }
36258         this.isSlid = true;
36259         if(this.collapseBtn){
36260             this.collapseBtn.hide();
36261         }
36262         this.closeBtnState = this.closeBtn.getStyle('display');
36263         this.closeBtn.hide();
36264         if(this.stickBtn){
36265             this.stickBtn.show();
36266         }
36267         this.el.show();
36268         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36269         this.beforeSlide();
36270         this.el.setStyle("z-index", 10001);
36271         this.el.slideIn(this.getSlideAnchor(), {
36272             callback: function(){
36273                 this.afterSlide();
36274                 this.initAutoHide();
36275                 Roo.get(document).on("click", this.slideInIf, this);
36276                 this.fireEvent("slideshow", this);
36277             },
36278             scope: this,
36279             block: true
36280         });
36281     },
36282
36283     afterSlideIn : function(){
36284         this.clearAutoHide();
36285         this.isSlid = false;
36286         this.clearMonitor();
36287         this.el.setStyle("z-index", "");
36288         if(this.collapseBtn){
36289             this.collapseBtn.show();
36290         }
36291         this.closeBtn.setStyle('display', this.closeBtnState);
36292         if(this.stickBtn){
36293             this.stickBtn.hide();
36294         }
36295         this.fireEvent("slidehide", this);
36296     },
36297
36298     slideIn : function(cb){
36299         if(!this.isSlid || this.el.hasActiveFx()){
36300             Roo.callback(cb);
36301             return;
36302         }
36303         this.isSlid = false;
36304         this.beforeSlide();
36305         this.el.slideOut(this.getSlideAnchor(), {
36306             callback: function(){
36307                 this.el.setLeftTop(-10000, -10000);
36308                 this.afterSlide();
36309                 this.afterSlideIn();
36310                 Roo.callback(cb);
36311             },
36312             scope: this,
36313             block: true
36314         });
36315     },
36316     
36317     slideInIf : function(e){
36318         if(!e.within(this.el)){
36319             this.slideIn();
36320         }
36321     },
36322
36323     animateCollapse : function(){
36324         this.beforeSlide();
36325         this.el.setStyle("z-index", 20000);
36326         var anchor = this.getSlideAnchor();
36327         this.el.slideOut(anchor, {
36328             callback : function(){
36329                 this.el.setStyle("z-index", "");
36330                 this.collapsedEl.slideIn(anchor, {duration:.3});
36331                 this.afterSlide();
36332                 this.el.setLocation(-10000,-10000);
36333                 this.el.hide();
36334                 this.fireEvent("collapsed", this);
36335             },
36336             scope: this,
36337             block: true
36338         });
36339     },
36340
36341     animateExpand : function(){
36342         this.beforeSlide();
36343         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36344         this.el.setStyle("z-index", 20000);
36345         this.collapsedEl.hide({
36346             duration:.1
36347         });
36348         this.el.slideIn(this.getSlideAnchor(), {
36349             callback : function(){
36350                 this.el.setStyle("z-index", "");
36351                 this.afterSlide();
36352                 if(this.split){
36353                     this.split.el.show();
36354                 }
36355                 this.fireEvent("invalidated", this);
36356                 this.fireEvent("expanded", this);
36357             },
36358             scope: this,
36359             block: true
36360         });
36361     },
36362
36363     anchors : {
36364         "west" : "left",
36365         "east" : "right",
36366         "north" : "top",
36367         "south" : "bottom"
36368     },
36369
36370     sanchors : {
36371         "west" : "l",
36372         "east" : "r",
36373         "north" : "t",
36374         "south" : "b"
36375     },
36376
36377     canchors : {
36378         "west" : "tl-tr",
36379         "east" : "tr-tl",
36380         "north" : "tl-bl",
36381         "south" : "bl-tl"
36382     },
36383
36384     getAnchor : function(){
36385         return this.anchors[this.position];
36386     },
36387
36388     getCollapseAnchor : function(){
36389         return this.canchors[this.position];
36390     },
36391
36392     getSlideAnchor : function(){
36393         return this.sanchors[this.position];
36394     },
36395
36396     getAlignAdj : function(){
36397         var cm = this.cmargins;
36398         switch(this.position){
36399             case "west":
36400                 return [0, 0];
36401             break;
36402             case "east":
36403                 return [0, 0];
36404             break;
36405             case "north":
36406                 return [0, 0];
36407             break;
36408             case "south":
36409                 return [0, 0];
36410             break;
36411         }
36412     },
36413
36414     getExpandAdj : function(){
36415         var c = this.collapsedEl, cm = this.cmargins;
36416         switch(this.position){
36417             case "west":
36418                 return [-(cm.right+c.getWidth()+cm.left), 0];
36419             break;
36420             case "east":
36421                 return [cm.right+c.getWidth()+cm.left, 0];
36422             break;
36423             case "north":
36424                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36425             break;
36426             case "south":
36427                 return [0, cm.top+cm.bottom+c.getHeight()];
36428             break;
36429         }
36430     }
36431 });/*
36432  * Based on:
36433  * Ext JS Library 1.1.1
36434  * Copyright(c) 2006-2007, Ext JS, LLC.
36435  *
36436  * Originally Released Under LGPL - original licence link has changed is not relivant.
36437  *
36438  * Fork - LGPL
36439  * <script type="text/javascript">
36440  */
36441 /*
36442  * These classes are private internal classes
36443  */
36444 Roo.bootstrap.layout.Center = function(config){
36445     config.region = "center";
36446     Roo.bootstrap.layout.Region.call(this, config);
36447     this.visible = true;
36448     this.minWidth = config.minWidth || 20;
36449     this.minHeight = config.minHeight || 20;
36450 };
36451
36452 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36453     hide : function(){
36454         // center panel can't be hidden
36455     },
36456     
36457     show : function(){
36458         // center panel can't be hidden
36459     },
36460     
36461     getMinWidth: function(){
36462         return this.minWidth;
36463     },
36464     
36465     getMinHeight: function(){
36466         return this.minHeight;
36467     }
36468 });
36469
36470
36471
36472
36473  
36474
36475
36476
36477
36478
36479 Roo.bootstrap.layout.North = function(config)
36480 {
36481     config.region = 'north';
36482     config.cursor = 'n-resize';
36483     
36484     Roo.bootstrap.layout.Split.call(this, config);
36485     
36486     
36487     if(this.split){
36488         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36489         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36490         this.split.el.addClass("roo-layout-split-v");
36491     }
36492     var size = config.initialSize || config.height;
36493     if(typeof size != "undefined"){
36494         this.el.setHeight(size);
36495     }
36496 };
36497 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36498 {
36499     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36500     
36501     
36502     
36503     getBox : function(){
36504         if(this.collapsed){
36505             return this.collapsedEl.getBox();
36506         }
36507         var box = this.el.getBox();
36508         if(this.split){
36509             box.height += this.split.el.getHeight();
36510         }
36511         return box;
36512     },
36513     
36514     updateBox : function(box){
36515         if(this.split && !this.collapsed){
36516             box.height -= this.split.el.getHeight();
36517             this.split.el.setLeft(box.x);
36518             this.split.el.setTop(box.y+box.height);
36519             this.split.el.setWidth(box.width);
36520         }
36521         if(this.collapsed){
36522             this.updateBody(box.width, null);
36523         }
36524         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36525     }
36526 });
36527
36528
36529
36530
36531
36532 Roo.bootstrap.layout.South = function(config){
36533     config.region = 'south';
36534     config.cursor = 's-resize';
36535     Roo.bootstrap.layout.Split.call(this, config);
36536     if(this.split){
36537         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36538         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36539         this.split.el.addClass("roo-layout-split-v");
36540     }
36541     var size = config.initialSize || config.height;
36542     if(typeof size != "undefined"){
36543         this.el.setHeight(size);
36544     }
36545 };
36546
36547 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36548     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36549     getBox : function(){
36550         if(this.collapsed){
36551             return this.collapsedEl.getBox();
36552         }
36553         var box = this.el.getBox();
36554         if(this.split){
36555             var sh = this.split.el.getHeight();
36556             box.height += sh;
36557             box.y -= sh;
36558         }
36559         return box;
36560     },
36561     
36562     updateBox : function(box){
36563         if(this.split && !this.collapsed){
36564             var sh = this.split.el.getHeight();
36565             box.height -= sh;
36566             box.y += sh;
36567             this.split.el.setLeft(box.x);
36568             this.split.el.setTop(box.y-sh);
36569             this.split.el.setWidth(box.width);
36570         }
36571         if(this.collapsed){
36572             this.updateBody(box.width, null);
36573         }
36574         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36575     }
36576 });
36577
36578 Roo.bootstrap.layout.East = function(config){
36579     config.region = "east";
36580     config.cursor = "e-resize";
36581     Roo.bootstrap.layout.Split.call(this, config);
36582     if(this.split){
36583         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36584         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36585         this.split.el.addClass("roo-layout-split-h");
36586     }
36587     var size = config.initialSize || config.width;
36588     if(typeof size != "undefined"){
36589         this.el.setWidth(size);
36590     }
36591 };
36592 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36593     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36594     getBox : function(){
36595         if(this.collapsed){
36596             return this.collapsedEl.getBox();
36597         }
36598         var box = this.el.getBox();
36599         if(this.split){
36600             var sw = this.split.el.getWidth();
36601             box.width += sw;
36602             box.x -= sw;
36603         }
36604         return box;
36605     },
36606
36607     updateBox : function(box){
36608         if(this.split && !this.collapsed){
36609             var sw = this.split.el.getWidth();
36610             box.width -= sw;
36611             this.split.el.setLeft(box.x);
36612             this.split.el.setTop(box.y);
36613             this.split.el.setHeight(box.height);
36614             box.x += sw;
36615         }
36616         if(this.collapsed){
36617             this.updateBody(null, box.height);
36618         }
36619         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36620     }
36621 });
36622
36623 Roo.bootstrap.layout.West = function(config){
36624     config.region = "west";
36625     config.cursor = "w-resize";
36626     
36627     Roo.bootstrap.layout.Split.call(this, config);
36628     if(this.split){
36629         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36630         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36631         this.split.el.addClass("roo-layout-split-h");
36632     }
36633     
36634 };
36635 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36636     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36637     
36638     onRender: function(ctr, pos)
36639     {
36640         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36641         var size = this.config.initialSize || this.config.width;
36642         if(typeof size != "undefined"){
36643             this.el.setWidth(size);
36644         }
36645     },
36646     
36647     getBox : function(){
36648         if(this.collapsed){
36649             return this.collapsedEl.getBox();
36650         }
36651         var box = this.el.getBox();
36652         if(this.split){
36653             box.width += this.split.el.getWidth();
36654         }
36655         return box;
36656     },
36657     
36658     updateBox : function(box){
36659         if(this.split && !this.collapsed){
36660             var sw = this.split.el.getWidth();
36661             box.width -= sw;
36662             this.split.el.setLeft(box.x+box.width);
36663             this.split.el.setTop(box.y);
36664             this.split.el.setHeight(box.height);
36665         }
36666         if(this.collapsed){
36667             this.updateBody(null, box.height);
36668         }
36669         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36670     }
36671 });
36672 Roo.namespace("Roo.bootstrap.panel");/*
36673  * Based on:
36674  * Ext JS Library 1.1.1
36675  * Copyright(c) 2006-2007, Ext JS, LLC.
36676  *
36677  * Originally Released Under LGPL - original licence link has changed is not relivant.
36678  *
36679  * Fork - LGPL
36680  * <script type="text/javascript">
36681  */
36682 /**
36683  * @class Roo.ContentPanel
36684  * @extends Roo.util.Observable
36685  * A basic ContentPanel element.
36686  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36687  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36688  * @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
36689  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36690  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36691  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36692  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36693  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36694  * @cfg {String} title          The title for this panel
36695  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36696  * @cfg {String} url            Calls {@link #setUrl} with this value
36697  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36698  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36699  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36700  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36701  * @cfg {Boolean} badges render the badges
36702
36703  * @constructor
36704  * Create a new ContentPanel.
36705  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36706  * @param {String/Object} config A string to set only the title or a config object
36707  * @param {String} content (optional) Set the HTML content for this panel
36708  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36709  */
36710 Roo.bootstrap.panel.Content = function( config){
36711     
36712     this.tpl = config.tpl || false;
36713     
36714     var el = config.el;
36715     var content = config.content;
36716
36717     if(config.autoCreate){ // xtype is available if this is called from factory
36718         el = Roo.id();
36719     }
36720     this.el = Roo.get(el);
36721     if(!this.el && config && config.autoCreate){
36722         if(typeof config.autoCreate == "object"){
36723             if(!config.autoCreate.id){
36724                 config.autoCreate.id = config.id||el;
36725             }
36726             this.el = Roo.DomHelper.append(document.body,
36727                         config.autoCreate, true);
36728         }else{
36729             var elcfg =  {   tag: "div",
36730                             cls: "roo-layout-inactive-content",
36731                             id: config.id||el
36732                             };
36733             if (config.html) {
36734                 elcfg.html = config.html;
36735                 
36736             }
36737                         
36738             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36739         }
36740     } 
36741     this.closable = false;
36742     this.loaded = false;
36743     this.active = false;
36744    
36745       
36746     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36747         
36748         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36749         
36750         this.wrapEl = this.el; //this.el.wrap();
36751         var ti = [];
36752         if (config.toolbar.items) {
36753             ti = config.toolbar.items ;
36754             delete config.toolbar.items ;
36755         }
36756         
36757         var nitems = [];
36758         this.toolbar.render(this.wrapEl, 'before');
36759         for(var i =0;i < ti.length;i++) {
36760           //  Roo.log(['add child', items[i]]);
36761             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36762         }
36763         this.toolbar.items = nitems;
36764         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36765         delete config.toolbar;
36766         
36767     }
36768     /*
36769     // xtype created footer. - not sure if will work as we normally have to render first..
36770     if (this.footer && !this.footer.el && this.footer.xtype) {
36771         if (!this.wrapEl) {
36772             this.wrapEl = this.el.wrap();
36773         }
36774     
36775         this.footer.container = this.wrapEl.createChild();
36776          
36777         this.footer = Roo.factory(this.footer, Roo);
36778         
36779     }
36780     */
36781     
36782      if(typeof config == "string"){
36783         this.title = config;
36784     }else{
36785         Roo.apply(this, config);
36786     }
36787     
36788     if(this.resizeEl){
36789         this.resizeEl = Roo.get(this.resizeEl, true);
36790     }else{
36791         this.resizeEl = this.el;
36792     }
36793     // handle view.xtype
36794     
36795  
36796     
36797     
36798     this.addEvents({
36799         /**
36800          * @event activate
36801          * Fires when this panel is activated. 
36802          * @param {Roo.ContentPanel} this
36803          */
36804         "activate" : true,
36805         /**
36806          * @event deactivate
36807          * Fires when this panel is activated. 
36808          * @param {Roo.ContentPanel} this
36809          */
36810         "deactivate" : true,
36811
36812         /**
36813          * @event resize
36814          * Fires when this panel is resized if fitToFrame is true.
36815          * @param {Roo.ContentPanel} this
36816          * @param {Number} width The width after any component adjustments
36817          * @param {Number} height The height after any component adjustments
36818          */
36819         "resize" : true,
36820         
36821          /**
36822          * @event render
36823          * Fires when this tab is created
36824          * @param {Roo.ContentPanel} this
36825          */
36826         "render" : true
36827         
36828         
36829         
36830     });
36831     
36832
36833     
36834     
36835     if(this.autoScroll){
36836         this.resizeEl.setStyle("overflow", "auto");
36837     } else {
36838         // fix randome scrolling
36839         //this.el.on('scroll', function() {
36840         //    Roo.log('fix random scolling');
36841         //    this.scrollTo('top',0); 
36842         //});
36843     }
36844     content = content || this.content;
36845     if(content){
36846         this.setContent(content);
36847     }
36848     if(config && config.url){
36849         this.setUrl(this.url, this.params, this.loadOnce);
36850     }
36851     
36852     
36853     
36854     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36855     
36856     if (this.view && typeof(this.view.xtype) != 'undefined') {
36857         this.view.el = this.el.appendChild(document.createElement("div"));
36858         this.view = Roo.factory(this.view); 
36859         this.view.render  &&  this.view.render(false, '');  
36860     }
36861     
36862     
36863     this.fireEvent('render', this);
36864 };
36865
36866 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36867     
36868     tabTip : '',
36869     
36870     setRegion : function(region){
36871         this.region = region;
36872         this.setActiveClass(region && !this.background);
36873     },
36874     
36875     
36876     setActiveClass: function(state)
36877     {
36878         if(state){
36879            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36880            this.el.setStyle('position','relative');
36881         }else{
36882            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36883            this.el.setStyle('position', 'absolute');
36884         } 
36885     },
36886     
36887     /**
36888      * Returns the toolbar for this Panel if one was configured. 
36889      * @return {Roo.Toolbar} 
36890      */
36891     getToolbar : function(){
36892         return this.toolbar;
36893     },
36894     
36895     setActiveState : function(active)
36896     {
36897         this.active = active;
36898         this.setActiveClass(active);
36899         if(!active){
36900             if(this.fireEvent("deactivate", this) === false){
36901                 return false;
36902             }
36903             return true;
36904         }
36905         this.fireEvent("activate", this);
36906         return true;
36907     },
36908     /**
36909      * Updates this panel's element
36910      * @param {String} content The new content
36911      * @param {Boolean} loadScripts (optional) true to look for and process scripts
36912     */
36913     setContent : function(content, loadScripts){
36914         this.el.update(content, loadScripts);
36915     },
36916
36917     ignoreResize : function(w, h){
36918         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36919             return true;
36920         }else{
36921             this.lastSize = {width: w, height: h};
36922             return false;
36923         }
36924     },
36925     /**
36926      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36927      * @return {Roo.UpdateManager} The UpdateManager
36928      */
36929     getUpdateManager : function(){
36930         return this.el.getUpdateManager();
36931     },
36932      /**
36933      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36934      * @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:
36935 <pre><code>
36936 panel.load({
36937     url: "your-url.php",
36938     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36939     callback: yourFunction,
36940     scope: yourObject, //(optional scope)
36941     discardUrl: false,
36942     nocache: false,
36943     text: "Loading...",
36944     timeout: 30,
36945     scripts: false
36946 });
36947 </code></pre>
36948      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
36949      * 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.
36950      * @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}
36951      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
36952      * @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.
36953      * @return {Roo.ContentPanel} this
36954      */
36955     load : function(){
36956         var um = this.el.getUpdateManager();
36957         um.update.apply(um, arguments);
36958         return this;
36959     },
36960
36961
36962     /**
36963      * 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.
36964      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
36965      * @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)
36966      * @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)
36967      * @return {Roo.UpdateManager} The UpdateManager
36968      */
36969     setUrl : function(url, params, loadOnce){
36970         if(this.refreshDelegate){
36971             this.removeListener("activate", this.refreshDelegate);
36972         }
36973         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36974         this.on("activate", this.refreshDelegate);
36975         return this.el.getUpdateManager();
36976     },
36977     
36978     _handleRefresh : function(url, params, loadOnce){
36979         if(!loadOnce || !this.loaded){
36980             var updater = this.el.getUpdateManager();
36981             updater.update(url, params, this._setLoaded.createDelegate(this));
36982         }
36983     },
36984     
36985     _setLoaded : function(){
36986         this.loaded = true;
36987     }, 
36988     
36989     /**
36990      * Returns this panel's id
36991      * @return {String} 
36992      */
36993     getId : function(){
36994         return this.el.id;
36995     },
36996     
36997     /** 
36998      * Returns this panel's element - used by regiosn to add.
36999      * @return {Roo.Element} 
37000      */
37001     getEl : function(){
37002         return this.wrapEl || this.el;
37003     },
37004     
37005    
37006     
37007     adjustForComponents : function(width, height)
37008     {
37009         //Roo.log('adjustForComponents ');
37010         if(this.resizeEl != this.el){
37011             width -= this.el.getFrameWidth('lr');
37012             height -= this.el.getFrameWidth('tb');
37013         }
37014         if(this.toolbar){
37015             var te = this.toolbar.getEl();
37016             te.setWidth(width);
37017             height -= te.getHeight();
37018         }
37019         if(this.footer){
37020             var te = this.footer.getEl();
37021             te.setWidth(width);
37022             height -= te.getHeight();
37023         }
37024         
37025         
37026         if(this.adjustments){
37027             width += this.adjustments[0];
37028             height += this.adjustments[1];
37029         }
37030         return {"width": width, "height": height};
37031     },
37032     
37033     setSize : function(width, height){
37034         if(this.fitToFrame && !this.ignoreResize(width, height)){
37035             if(this.fitContainer && this.resizeEl != this.el){
37036                 this.el.setSize(width, height);
37037             }
37038             var size = this.adjustForComponents(width, height);
37039             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37040             this.fireEvent('resize', this, size.width, size.height);
37041         }
37042     },
37043     
37044     /**
37045      * Returns this panel's title
37046      * @return {String} 
37047      */
37048     getTitle : function(){
37049         
37050         if (typeof(this.title) != 'object') {
37051             return this.title;
37052         }
37053         
37054         var t = '';
37055         for (var k in this.title) {
37056             if (!this.title.hasOwnProperty(k)) {
37057                 continue;
37058             }
37059             
37060             if (k.indexOf('-') >= 0) {
37061                 var s = k.split('-');
37062                 for (var i = 0; i<s.length; i++) {
37063                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37064                 }
37065             } else {
37066                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37067             }
37068         }
37069         return t;
37070     },
37071     
37072     /**
37073      * Set this panel's title
37074      * @param {String} title
37075      */
37076     setTitle : function(title){
37077         this.title = title;
37078         if(this.region){
37079             this.region.updatePanelTitle(this, title);
37080         }
37081     },
37082     
37083     /**
37084      * Returns true is this panel was configured to be closable
37085      * @return {Boolean} 
37086      */
37087     isClosable : function(){
37088         return this.closable;
37089     },
37090     
37091     beforeSlide : function(){
37092         this.el.clip();
37093         this.resizeEl.clip();
37094     },
37095     
37096     afterSlide : function(){
37097         this.el.unclip();
37098         this.resizeEl.unclip();
37099     },
37100     
37101     /**
37102      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37103      *   Will fail silently if the {@link #setUrl} method has not been called.
37104      *   This does not activate the panel, just updates its content.
37105      */
37106     refresh : function(){
37107         if(this.refreshDelegate){
37108            this.loaded = false;
37109            this.refreshDelegate();
37110         }
37111     },
37112     
37113     /**
37114      * Destroys this panel
37115      */
37116     destroy : function(){
37117         this.el.removeAllListeners();
37118         var tempEl = document.createElement("span");
37119         tempEl.appendChild(this.el.dom);
37120         tempEl.innerHTML = "";
37121         this.el.remove();
37122         this.el = null;
37123     },
37124     
37125     /**
37126      * form - if the content panel contains a form - this is a reference to it.
37127      * @type {Roo.form.Form}
37128      */
37129     form : false,
37130     /**
37131      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37132      *    This contains a reference to it.
37133      * @type {Roo.View}
37134      */
37135     view : false,
37136     
37137       /**
37138      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37139      * <pre><code>
37140
37141 layout.addxtype({
37142        xtype : 'Form',
37143        items: [ .... ]
37144    }
37145 );
37146
37147 </code></pre>
37148      * @param {Object} cfg Xtype definition of item to add.
37149      */
37150     
37151     
37152     getChildContainer: function () {
37153         return this.getEl();
37154     }
37155     
37156     
37157     /*
37158         var  ret = new Roo.factory(cfg);
37159         return ret;
37160         
37161         
37162         // add form..
37163         if (cfg.xtype.match(/^Form$/)) {
37164             
37165             var el;
37166             //if (this.footer) {
37167             //    el = this.footer.container.insertSibling(false, 'before');
37168             //} else {
37169                 el = this.el.createChild();
37170             //}
37171
37172             this.form = new  Roo.form.Form(cfg);
37173             
37174             
37175             if ( this.form.allItems.length) {
37176                 this.form.render(el.dom);
37177             }
37178             return this.form;
37179         }
37180         // should only have one of theses..
37181         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37182             // views.. should not be just added - used named prop 'view''
37183             
37184             cfg.el = this.el.appendChild(document.createElement("div"));
37185             // factory?
37186             
37187             var ret = new Roo.factory(cfg);
37188              
37189              ret.render && ret.render(false, ''); // render blank..
37190             this.view = ret;
37191             return ret;
37192         }
37193         return false;
37194     }
37195     \*/
37196 });
37197  
37198 /**
37199  * @class Roo.bootstrap.panel.Grid
37200  * @extends Roo.bootstrap.panel.Content
37201  * @constructor
37202  * Create a new GridPanel.
37203  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37204  * @param {Object} config A the config object
37205   
37206  */
37207
37208
37209
37210 Roo.bootstrap.panel.Grid = function(config)
37211 {
37212     
37213       
37214     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37215         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37216
37217     config.el = this.wrapper;
37218     //this.el = this.wrapper;
37219     
37220       if (config.container) {
37221         // ctor'ed from a Border/panel.grid
37222         
37223         
37224         this.wrapper.setStyle("overflow", "hidden");
37225         this.wrapper.addClass('roo-grid-container');
37226
37227     }
37228     
37229     
37230     if(config.toolbar){
37231         var tool_el = this.wrapper.createChild();    
37232         this.toolbar = Roo.factory(config.toolbar);
37233         var ti = [];
37234         if (config.toolbar.items) {
37235             ti = config.toolbar.items ;
37236             delete config.toolbar.items ;
37237         }
37238         
37239         var nitems = [];
37240         this.toolbar.render(tool_el);
37241         for(var i =0;i < ti.length;i++) {
37242           //  Roo.log(['add child', items[i]]);
37243             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37244         }
37245         this.toolbar.items = nitems;
37246         
37247         delete config.toolbar;
37248     }
37249     
37250     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37251     config.grid.scrollBody = true;;
37252     config.grid.monitorWindowResize = false; // turn off autosizing
37253     config.grid.autoHeight = false;
37254     config.grid.autoWidth = false;
37255     
37256     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37257     
37258     if (config.background) {
37259         // render grid on panel activation (if panel background)
37260         this.on('activate', function(gp) {
37261             if (!gp.grid.rendered) {
37262                 gp.grid.render(this.wrapper);
37263                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37264             }
37265         });
37266             
37267     } else {
37268         this.grid.render(this.wrapper);
37269         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37270
37271     }
37272     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37273     // ??? needed ??? config.el = this.wrapper;
37274     
37275     
37276     
37277   
37278     // xtype created footer. - not sure if will work as we normally have to render first..
37279     if (this.footer && !this.footer.el && this.footer.xtype) {
37280         
37281         var ctr = this.grid.getView().getFooterPanel(true);
37282         this.footer.dataSource = this.grid.dataSource;
37283         this.footer = Roo.factory(this.footer, Roo);
37284         this.footer.render(ctr);
37285         
37286     }
37287     
37288     
37289     
37290     
37291      
37292 };
37293
37294 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37295     getId : function(){
37296         return this.grid.id;
37297     },
37298     
37299     /**
37300      * Returns the grid for this panel
37301      * @return {Roo.bootstrap.Table} 
37302      */
37303     getGrid : function(){
37304         return this.grid;    
37305     },
37306     
37307     setSize : function(width, height){
37308         if(!this.ignoreResize(width, height)){
37309             var grid = this.grid;
37310             var size = this.adjustForComponents(width, height);
37311             var gridel = grid.getGridEl();
37312             gridel.setSize(size.width, size.height);
37313             /*
37314             var thd = grid.getGridEl().select('thead',true).first();
37315             var tbd = grid.getGridEl().select('tbody', true).first();
37316             if (tbd) {
37317                 tbd.setSize(width, height - thd.getHeight());
37318             }
37319             */
37320             grid.autoSize();
37321         }
37322     },
37323      
37324     
37325     
37326     beforeSlide : function(){
37327         this.grid.getView().scroller.clip();
37328     },
37329     
37330     afterSlide : function(){
37331         this.grid.getView().scroller.unclip();
37332     },
37333     
37334     destroy : function(){
37335         this.grid.destroy();
37336         delete this.grid;
37337         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37338     }
37339 });
37340
37341 /**
37342  * @class Roo.bootstrap.panel.Nest
37343  * @extends Roo.bootstrap.panel.Content
37344  * @constructor
37345  * Create a new Panel, that can contain a layout.Border.
37346  * 
37347  * 
37348  * @param {Roo.BorderLayout} layout The layout for this panel
37349  * @param {String/Object} config A string to set only the title or a config object
37350  */
37351 Roo.bootstrap.panel.Nest = function(config)
37352 {
37353     // construct with only one argument..
37354     /* FIXME - implement nicer consturctors
37355     if (layout.layout) {
37356         config = layout;
37357         layout = config.layout;
37358         delete config.layout;
37359     }
37360     if (layout.xtype && !layout.getEl) {
37361         // then layout needs constructing..
37362         layout = Roo.factory(layout, Roo);
37363     }
37364     */
37365     
37366     config.el =  config.layout.getEl();
37367     
37368     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37369     
37370     config.layout.monitorWindowResize = false; // turn off autosizing
37371     this.layout = config.layout;
37372     this.layout.getEl().addClass("roo-layout-nested-layout");
37373     
37374     
37375     
37376     
37377 };
37378
37379 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37380
37381     setSize : function(width, height){
37382         if(!this.ignoreResize(width, height)){
37383             var size = this.adjustForComponents(width, height);
37384             var el = this.layout.getEl();
37385             if (size.height < 1) {
37386                 el.setWidth(size.width);   
37387             } else {
37388                 el.setSize(size.width, size.height);
37389             }
37390             var touch = el.dom.offsetWidth;
37391             this.layout.layout();
37392             // ie requires a double layout on the first pass
37393             if(Roo.isIE && !this.initialized){
37394                 this.initialized = true;
37395                 this.layout.layout();
37396             }
37397         }
37398     },
37399     
37400     // activate all subpanels if not currently active..
37401     
37402     setActiveState : function(active){
37403         this.active = active;
37404         this.setActiveClass(active);
37405         
37406         if(!active){
37407             this.fireEvent("deactivate", this);
37408             return;
37409         }
37410         
37411         this.fireEvent("activate", this);
37412         // not sure if this should happen before or after..
37413         if (!this.layout) {
37414             return; // should not happen..
37415         }
37416         var reg = false;
37417         for (var r in this.layout.regions) {
37418             reg = this.layout.getRegion(r);
37419             if (reg.getActivePanel()) {
37420                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37421                 reg.setActivePanel(reg.getActivePanel());
37422                 continue;
37423             }
37424             if (!reg.panels.length) {
37425                 continue;
37426             }
37427             reg.showPanel(reg.getPanel(0));
37428         }
37429         
37430         
37431         
37432         
37433     },
37434     
37435     /**
37436      * Returns the nested BorderLayout for this panel
37437      * @return {Roo.BorderLayout} 
37438      */
37439     getLayout : function(){
37440         return this.layout;
37441     },
37442     
37443      /**
37444      * Adds a xtype elements to the layout of the nested panel
37445      * <pre><code>
37446
37447 panel.addxtype({
37448        xtype : 'ContentPanel',
37449        region: 'west',
37450        items: [ .... ]
37451    }
37452 );
37453
37454 panel.addxtype({
37455         xtype : 'NestedLayoutPanel',
37456         region: 'west',
37457         layout: {
37458            center: { },
37459            west: { }   
37460         },
37461         items : [ ... list of content panels or nested layout panels.. ]
37462    }
37463 );
37464 </code></pre>
37465      * @param {Object} cfg Xtype definition of item to add.
37466      */
37467     addxtype : function(cfg) {
37468         return this.layout.addxtype(cfg);
37469     
37470     }
37471 });        /*
37472  * Based on:
37473  * Ext JS Library 1.1.1
37474  * Copyright(c) 2006-2007, Ext JS, LLC.
37475  *
37476  * Originally Released Under LGPL - original licence link has changed is not relivant.
37477  *
37478  * Fork - LGPL
37479  * <script type="text/javascript">
37480  */
37481 /**
37482  * @class Roo.TabPanel
37483  * @extends Roo.util.Observable
37484  * A lightweight tab container.
37485  * <br><br>
37486  * Usage:
37487  * <pre><code>
37488 // basic tabs 1, built from existing content
37489 var tabs = new Roo.TabPanel("tabs1");
37490 tabs.addTab("script", "View Script");
37491 tabs.addTab("markup", "View Markup");
37492 tabs.activate("script");
37493
37494 // more advanced tabs, built from javascript
37495 var jtabs = new Roo.TabPanel("jtabs");
37496 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37497
37498 // set up the UpdateManager
37499 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37500 var updater = tab2.getUpdateManager();
37501 updater.setDefaultUrl("ajax1.htm");
37502 tab2.on('activate', updater.refresh, updater, true);
37503
37504 // Use setUrl for Ajax loading
37505 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37506 tab3.setUrl("ajax2.htm", null, true);
37507
37508 // Disabled tab
37509 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37510 tab4.disable();
37511
37512 jtabs.activate("jtabs-1");
37513  * </code></pre>
37514  * @constructor
37515  * Create a new TabPanel.
37516  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37517  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37518  */
37519 Roo.bootstrap.panel.Tabs = function(config){
37520     /**
37521     * The container element for this TabPanel.
37522     * @type Roo.Element
37523     */
37524     this.el = Roo.get(config.el);
37525     delete config.el;
37526     if(config){
37527         if(typeof config == "boolean"){
37528             this.tabPosition = config ? "bottom" : "top";
37529         }else{
37530             Roo.apply(this, config);
37531         }
37532     }
37533     
37534     if(this.tabPosition == "bottom"){
37535         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37536         this.el.addClass("roo-tabs-bottom");
37537     }
37538     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37539     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37540     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37541     if(Roo.isIE){
37542         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37543     }
37544     if(this.tabPosition != "bottom"){
37545         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37546          * @type Roo.Element
37547          */
37548         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37549         this.el.addClass("roo-tabs-top");
37550     }
37551     this.items = [];
37552
37553     this.bodyEl.setStyle("position", "relative");
37554
37555     this.active = null;
37556     this.activateDelegate = this.activate.createDelegate(this);
37557
37558     this.addEvents({
37559         /**
37560          * @event tabchange
37561          * Fires when the active tab changes
37562          * @param {Roo.TabPanel} this
37563          * @param {Roo.TabPanelItem} activePanel The new active tab
37564          */
37565         "tabchange": true,
37566         /**
37567          * @event beforetabchange
37568          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37569          * @param {Roo.TabPanel} this
37570          * @param {Object} e Set cancel to true on this object to cancel the tab change
37571          * @param {Roo.TabPanelItem} tab The tab being changed to
37572          */
37573         "beforetabchange" : true
37574     });
37575
37576     Roo.EventManager.onWindowResize(this.onResize, this);
37577     this.cpad = this.el.getPadding("lr");
37578     this.hiddenCount = 0;
37579
37580
37581     // toolbar on the tabbar support...
37582     if (this.toolbar) {
37583         alert("no toolbar support yet");
37584         this.toolbar  = false;
37585         /*
37586         var tcfg = this.toolbar;
37587         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37588         this.toolbar = new Roo.Toolbar(tcfg);
37589         if (Roo.isSafari) {
37590             var tbl = tcfg.container.child('table', true);
37591             tbl.setAttribute('width', '100%');
37592         }
37593         */
37594         
37595     }
37596    
37597
37598
37599     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37600 };
37601
37602 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37603     /*
37604      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37605      */
37606     tabPosition : "top",
37607     /*
37608      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37609      */
37610     currentTabWidth : 0,
37611     /*
37612      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37613      */
37614     minTabWidth : 40,
37615     /*
37616      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37617      */
37618     maxTabWidth : 250,
37619     /*
37620      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37621      */
37622     preferredTabWidth : 175,
37623     /*
37624      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37625      */
37626     resizeTabs : false,
37627     /*
37628      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37629      */
37630     monitorResize : true,
37631     /*
37632      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37633      */
37634     toolbar : false,
37635
37636     /**
37637      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37638      * @param {String} id The id of the div to use <b>or create</b>
37639      * @param {String} text The text for the tab
37640      * @param {String} content (optional) Content to put in the TabPanelItem body
37641      * @param {Boolean} closable (optional) True to create a close icon on the tab
37642      * @return {Roo.TabPanelItem} The created TabPanelItem
37643      */
37644     addTab : function(id, text, content, closable, tpl)
37645     {
37646         var item = new Roo.bootstrap.panel.TabItem({
37647             panel: this,
37648             id : id,
37649             text : text,
37650             closable : closable,
37651             tpl : tpl
37652         });
37653         this.addTabItem(item);
37654         if(content){
37655             item.setContent(content);
37656         }
37657         return item;
37658     },
37659
37660     /**
37661      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37662      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37663      * @return {Roo.TabPanelItem}
37664      */
37665     getTab : function(id){
37666         return this.items[id];
37667     },
37668
37669     /**
37670      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37671      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37672      */
37673     hideTab : function(id){
37674         var t = this.items[id];
37675         if(!t.isHidden()){
37676            t.setHidden(true);
37677            this.hiddenCount++;
37678            this.autoSizeTabs();
37679         }
37680     },
37681
37682     /**
37683      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37684      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37685      */
37686     unhideTab : function(id){
37687         var t = this.items[id];
37688         if(t.isHidden()){
37689            t.setHidden(false);
37690            this.hiddenCount--;
37691            this.autoSizeTabs();
37692         }
37693     },
37694
37695     /**
37696      * Adds an existing {@link Roo.TabPanelItem}.
37697      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37698      */
37699     addTabItem : function(item){
37700         this.items[item.id] = item;
37701         this.items.push(item);
37702       //  if(this.resizeTabs){
37703     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37704   //         this.autoSizeTabs();
37705 //        }else{
37706 //            item.autoSize();
37707        // }
37708     },
37709
37710     /**
37711      * Removes a {@link Roo.TabPanelItem}.
37712      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37713      */
37714     removeTab : function(id){
37715         var items = this.items;
37716         var tab = items[id];
37717         if(!tab) { return; }
37718         var index = items.indexOf(tab);
37719         if(this.active == tab && items.length > 1){
37720             var newTab = this.getNextAvailable(index);
37721             if(newTab) {
37722                 newTab.activate();
37723             }
37724         }
37725         this.stripEl.dom.removeChild(tab.pnode.dom);
37726         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37727             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37728         }
37729         items.splice(index, 1);
37730         delete this.items[tab.id];
37731         tab.fireEvent("close", tab);
37732         tab.purgeListeners();
37733         this.autoSizeTabs();
37734     },
37735
37736     getNextAvailable : function(start){
37737         var items = this.items;
37738         var index = start;
37739         // look for a next tab that will slide over to
37740         // replace the one being removed
37741         while(index < items.length){
37742             var item = items[++index];
37743             if(item && !item.isHidden()){
37744                 return item;
37745             }
37746         }
37747         // if one isn't found select the previous tab (on the left)
37748         index = start;
37749         while(index >= 0){
37750             var item = items[--index];
37751             if(item && !item.isHidden()){
37752                 return item;
37753             }
37754         }
37755         return null;
37756     },
37757
37758     /**
37759      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37760      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37761      */
37762     disableTab : function(id){
37763         var tab = this.items[id];
37764         if(tab && this.active != tab){
37765             tab.disable();
37766         }
37767     },
37768
37769     /**
37770      * Enables a {@link Roo.TabPanelItem} that is disabled.
37771      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37772      */
37773     enableTab : function(id){
37774         var tab = this.items[id];
37775         tab.enable();
37776     },
37777
37778     /**
37779      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37780      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37781      * @return {Roo.TabPanelItem} The TabPanelItem.
37782      */
37783     activate : function(id){
37784         var tab = this.items[id];
37785         if(!tab){
37786             return null;
37787         }
37788         if(tab == this.active || tab.disabled){
37789             return tab;
37790         }
37791         var e = {};
37792         this.fireEvent("beforetabchange", this, e, tab);
37793         if(e.cancel !== true && !tab.disabled){
37794             if(this.active){
37795                 this.active.hide();
37796             }
37797             this.active = this.items[id];
37798             this.active.show();
37799             this.fireEvent("tabchange", this, this.active);
37800         }
37801         return tab;
37802     },
37803
37804     /**
37805      * Gets the active {@link Roo.TabPanelItem}.
37806      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37807      */
37808     getActiveTab : function(){
37809         return this.active;
37810     },
37811
37812     /**
37813      * Updates the tab body element to fit the height of the container element
37814      * for overflow scrolling
37815      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37816      */
37817     syncHeight : function(targetHeight){
37818         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37819         var bm = this.bodyEl.getMargins();
37820         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37821         this.bodyEl.setHeight(newHeight);
37822         return newHeight;
37823     },
37824
37825     onResize : function(){
37826         if(this.monitorResize){
37827             this.autoSizeTabs();
37828         }
37829     },
37830
37831     /**
37832      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37833      */
37834     beginUpdate : function(){
37835         this.updating = true;
37836     },
37837
37838     /**
37839      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37840      */
37841     endUpdate : function(){
37842         this.updating = false;
37843         this.autoSizeTabs();
37844     },
37845
37846     /**
37847      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37848      */
37849     autoSizeTabs : function(){
37850         var count = this.items.length;
37851         var vcount = count - this.hiddenCount;
37852         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37853             return;
37854         }
37855         var w = Math.max(this.el.getWidth() - this.cpad, 10);
37856         var availWidth = Math.floor(w / vcount);
37857         var b = this.stripBody;
37858         if(b.getWidth() > w){
37859             var tabs = this.items;
37860             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37861             if(availWidth < this.minTabWidth){
37862                 /*if(!this.sleft){    // incomplete scrolling code
37863                     this.createScrollButtons();
37864                 }
37865                 this.showScroll();
37866                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37867             }
37868         }else{
37869             if(this.currentTabWidth < this.preferredTabWidth){
37870                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37871             }
37872         }
37873     },
37874
37875     /**
37876      * Returns the number of tabs in this TabPanel.
37877      * @return {Number}
37878      */
37879      getCount : function(){
37880          return this.items.length;
37881      },
37882
37883     /**
37884      * Resizes all the tabs to the passed width
37885      * @param {Number} The new width
37886      */
37887     setTabWidth : function(width){
37888         this.currentTabWidth = width;
37889         for(var i = 0, len = this.items.length; i < len; i++) {
37890                 if(!this.items[i].isHidden()) {
37891                 this.items[i].setWidth(width);
37892             }
37893         }
37894     },
37895
37896     /**
37897      * Destroys this TabPanel
37898      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37899      */
37900     destroy : function(removeEl){
37901         Roo.EventManager.removeResizeListener(this.onResize, this);
37902         for(var i = 0, len = this.items.length; i < len; i++){
37903             this.items[i].purgeListeners();
37904         }
37905         if(removeEl === true){
37906             this.el.update("");
37907             this.el.remove();
37908         }
37909     },
37910     
37911     createStrip : function(container)
37912     {
37913         var strip = document.createElement("nav");
37914         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37915         container.appendChild(strip);
37916         return strip;
37917     },
37918     
37919     createStripList : function(strip)
37920     {
37921         // div wrapper for retard IE
37922         // returns the "tr" element.
37923         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37924         //'<div class="x-tabs-strip-wrap">'+
37925           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37926           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37927         return strip.firstChild; //.firstChild.firstChild.firstChild;
37928     },
37929     createBody : function(container)
37930     {
37931         var body = document.createElement("div");
37932         Roo.id(body, "tab-body");
37933         //Roo.fly(body).addClass("x-tabs-body");
37934         Roo.fly(body).addClass("tab-content");
37935         container.appendChild(body);
37936         return body;
37937     },
37938     createItemBody :function(bodyEl, id){
37939         var body = Roo.getDom(id);
37940         if(!body){
37941             body = document.createElement("div");
37942             body.id = id;
37943         }
37944         //Roo.fly(body).addClass("x-tabs-item-body");
37945         Roo.fly(body).addClass("tab-pane");
37946          bodyEl.insertBefore(body, bodyEl.firstChild);
37947         return body;
37948     },
37949     /** @private */
37950     createStripElements :  function(stripEl, text, closable, tpl)
37951     {
37952         var td = document.createElement("li"); // was td..
37953         
37954         
37955         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
37956         
37957         
37958         stripEl.appendChild(td);
37959         /*if(closable){
37960             td.className = "x-tabs-closable";
37961             if(!this.closeTpl){
37962                 this.closeTpl = new Roo.Template(
37963                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37964                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
37965                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
37966                 );
37967             }
37968             var el = this.closeTpl.overwrite(td, {"text": text});
37969             var close = el.getElementsByTagName("div")[0];
37970             var inner = el.getElementsByTagName("em")[0];
37971             return {"el": el, "close": close, "inner": inner};
37972         } else {
37973         */
37974         // not sure what this is..
37975 //            if(!this.tabTpl){
37976                 //this.tabTpl = new Roo.Template(
37977                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37978                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
37979                 //);
37980 //                this.tabTpl = new Roo.Template(
37981 //                   '<a href="#">' +
37982 //                   '<span unselectable="on"' +
37983 //                            (this.disableTooltips ? '' : ' title="{text}"') +
37984 //                            ' >{text}</span></a>'
37985 //                );
37986 //                
37987 //            }
37988
37989
37990             var template = tpl || this.tabTpl || false;
37991             
37992             if(!template){
37993                 
37994                 template = new Roo.Template(
37995                    '<a href="#">' +
37996                    '<span unselectable="on"' +
37997                             (this.disableTooltips ? '' : ' title="{text}"') +
37998                             ' >{text}</span></a>'
37999                 );
38000             }
38001             
38002             switch (typeof(template)) {
38003                 case 'object' :
38004                     break;
38005                 case 'string' :
38006                     template = new Roo.Template(template);
38007                     break;
38008                 default :
38009                     break;
38010             }
38011             
38012             var el = template.overwrite(td, {"text": text});
38013             
38014             var inner = el.getElementsByTagName("span")[0];
38015             
38016             return {"el": el, "inner": inner};
38017             
38018     }
38019         
38020     
38021 });
38022
38023 /**
38024  * @class Roo.TabPanelItem
38025  * @extends Roo.util.Observable
38026  * Represents an individual item (tab plus body) in a TabPanel.
38027  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38028  * @param {String} id The id of this TabPanelItem
38029  * @param {String} text The text for the tab of this TabPanelItem
38030  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38031  */
38032 Roo.bootstrap.panel.TabItem = function(config){
38033     /**
38034      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38035      * @type Roo.TabPanel
38036      */
38037     this.tabPanel = config.panel;
38038     /**
38039      * The id for this TabPanelItem
38040      * @type String
38041      */
38042     this.id = config.id;
38043     /** @private */
38044     this.disabled = false;
38045     /** @private */
38046     this.text = config.text;
38047     /** @private */
38048     this.loaded = false;
38049     this.closable = config.closable;
38050
38051     /**
38052      * The body element for this TabPanelItem.
38053      * @type Roo.Element
38054      */
38055     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38056     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38057     this.bodyEl.setStyle("display", "block");
38058     this.bodyEl.setStyle("zoom", "1");
38059     //this.hideAction();
38060
38061     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38062     /** @private */
38063     this.el = Roo.get(els.el);
38064     this.inner = Roo.get(els.inner, true);
38065     this.textEl = Roo.get(this.el.dom.firstChild, true);
38066     this.pnode = Roo.get(els.el.parentNode, true);
38067 //    this.el.on("mousedown", this.onTabMouseDown, this);
38068     this.el.on("click", this.onTabClick, this);
38069     /** @private */
38070     if(config.closable){
38071         var c = Roo.get(els.close, true);
38072         c.dom.title = this.closeText;
38073         c.addClassOnOver("close-over");
38074         c.on("click", this.closeClick, this);
38075      }
38076
38077     this.addEvents({
38078          /**
38079          * @event activate
38080          * Fires when this tab becomes the active tab.
38081          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38082          * @param {Roo.TabPanelItem} this
38083          */
38084         "activate": true,
38085         /**
38086          * @event beforeclose
38087          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38088          * @param {Roo.TabPanelItem} this
38089          * @param {Object} e Set cancel to true on this object to cancel the close.
38090          */
38091         "beforeclose": true,
38092         /**
38093          * @event close
38094          * Fires when this tab is closed.
38095          * @param {Roo.TabPanelItem} this
38096          */
38097          "close": true,
38098         /**
38099          * @event deactivate
38100          * Fires when this tab is no longer the active tab.
38101          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38102          * @param {Roo.TabPanelItem} this
38103          */
38104          "deactivate" : true
38105     });
38106     this.hidden = false;
38107
38108     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38109 };
38110
38111 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38112            {
38113     purgeListeners : function(){
38114        Roo.util.Observable.prototype.purgeListeners.call(this);
38115        this.el.removeAllListeners();
38116     },
38117     /**
38118      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38119      */
38120     show : function(){
38121         this.pnode.addClass("active");
38122         this.showAction();
38123         if(Roo.isOpera){
38124             this.tabPanel.stripWrap.repaint();
38125         }
38126         this.fireEvent("activate", this.tabPanel, this);
38127     },
38128
38129     /**
38130      * Returns true if this tab is the active tab.
38131      * @return {Boolean}
38132      */
38133     isActive : function(){
38134         return this.tabPanel.getActiveTab() == this;
38135     },
38136
38137     /**
38138      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38139      */
38140     hide : function(){
38141         this.pnode.removeClass("active");
38142         this.hideAction();
38143         this.fireEvent("deactivate", this.tabPanel, this);
38144     },
38145
38146     hideAction : function(){
38147         this.bodyEl.hide();
38148         this.bodyEl.setStyle("position", "absolute");
38149         this.bodyEl.setLeft("-20000px");
38150         this.bodyEl.setTop("-20000px");
38151     },
38152
38153     showAction : function(){
38154         this.bodyEl.setStyle("position", "relative");
38155         this.bodyEl.setTop("");
38156         this.bodyEl.setLeft("");
38157         this.bodyEl.show();
38158     },
38159
38160     /**
38161      * Set the tooltip for the tab.
38162      * @param {String} tooltip The tab's tooltip
38163      */
38164     setTooltip : function(text){
38165         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38166             this.textEl.dom.qtip = text;
38167             this.textEl.dom.removeAttribute('title');
38168         }else{
38169             this.textEl.dom.title = text;
38170         }
38171     },
38172
38173     onTabClick : function(e){
38174         e.preventDefault();
38175         this.tabPanel.activate(this.id);
38176     },
38177
38178     onTabMouseDown : function(e){
38179         e.preventDefault();
38180         this.tabPanel.activate(this.id);
38181     },
38182 /*
38183     getWidth : function(){
38184         return this.inner.getWidth();
38185     },
38186
38187     setWidth : function(width){
38188         var iwidth = width - this.pnode.getPadding("lr");
38189         this.inner.setWidth(iwidth);
38190         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38191         this.pnode.setWidth(width);
38192     },
38193 */
38194     /**
38195      * Show or hide the tab
38196      * @param {Boolean} hidden True to hide or false to show.
38197      */
38198     setHidden : function(hidden){
38199         this.hidden = hidden;
38200         this.pnode.setStyle("display", hidden ? "none" : "");
38201     },
38202
38203     /**
38204      * Returns true if this tab is "hidden"
38205      * @return {Boolean}
38206      */
38207     isHidden : function(){
38208         return this.hidden;
38209     },
38210
38211     /**
38212      * Returns the text for this tab
38213      * @return {String}
38214      */
38215     getText : function(){
38216         return this.text;
38217     },
38218     /*
38219     autoSize : function(){
38220         //this.el.beginMeasure();
38221         this.textEl.setWidth(1);
38222         /*
38223          *  #2804 [new] Tabs in Roojs
38224          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38225          */
38226         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38227         //this.el.endMeasure();
38228     //},
38229
38230     /**
38231      * Sets the text for the tab (Note: this also sets the tooltip text)
38232      * @param {String} text The tab's text and tooltip
38233      */
38234     setText : function(text){
38235         this.text = text;
38236         this.textEl.update(text);
38237         this.setTooltip(text);
38238         //if(!this.tabPanel.resizeTabs){
38239         //    this.autoSize();
38240         //}
38241     },
38242     /**
38243      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38244      */
38245     activate : function(){
38246         this.tabPanel.activate(this.id);
38247     },
38248
38249     /**
38250      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38251      */
38252     disable : function(){
38253         if(this.tabPanel.active != this){
38254             this.disabled = true;
38255             this.pnode.addClass("disabled");
38256         }
38257     },
38258
38259     /**
38260      * Enables this TabPanelItem if it was previously disabled.
38261      */
38262     enable : function(){
38263         this.disabled = false;
38264         this.pnode.removeClass("disabled");
38265     },
38266
38267     /**
38268      * Sets the content for this TabPanelItem.
38269      * @param {String} content The content
38270      * @param {Boolean} loadScripts true to look for and load scripts
38271      */
38272     setContent : function(content, loadScripts){
38273         this.bodyEl.update(content, loadScripts);
38274     },
38275
38276     /**
38277      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38278      * @return {Roo.UpdateManager} The UpdateManager
38279      */
38280     getUpdateManager : function(){
38281         return this.bodyEl.getUpdateManager();
38282     },
38283
38284     /**
38285      * Set a URL to be used to load the content for this TabPanelItem.
38286      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38287      * @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)
38288      * @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)
38289      * @return {Roo.UpdateManager} The UpdateManager
38290      */
38291     setUrl : function(url, params, loadOnce){
38292         if(this.refreshDelegate){
38293             this.un('activate', this.refreshDelegate);
38294         }
38295         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38296         this.on("activate", this.refreshDelegate);
38297         return this.bodyEl.getUpdateManager();
38298     },
38299
38300     /** @private */
38301     _handleRefresh : function(url, params, loadOnce){
38302         if(!loadOnce || !this.loaded){
38303             var updater = this.bodyEl.getUpdateManager();
38304             updater.update(url, params, this._setLoaded.createDelegate(this));
38305         }
38306     },
38307
38308     /**
38309      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38310      *   Will fail silently if the setUrl method has not been called.
38311      *   This does not activate the panel, just updates its content.
38312      */
38313     refresh : function(){
38314         if(this.refreshDelegate){
38315            this.loaded = false;
38316            this.refreshDelegate();
38317         }
38318     },
38319
38320     /** @private */
38321     _setLoaded : function(){
38322         this.loaded = true;
38323     },
38324
38325     /** @private */
38326     closeClick : function(e){
38327         var o = {};
38328         e.stopEvent();
38329         this.fireEvent("beforeclose", this, o);
38330         if(o.cancel !== true){
38331             this.tabPanel.removeTab(this.id);
38332         }
38333     },
38334     /**
38335      * The text displayed in the tooltip for the close icon.
38336      * @type String
38337      */
38338     closeText : "Close this tab"
38339 });
38340 /**
38341 *    This script refer to:
38342 *    Title: International Telephone Input
38343 *    Author: Jack O'Connor
38344 *    Code version:  v12.1.12
38345 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38346 **/
38347
38348 Roo.bootstrap.PhoneInputData = function() {
38349     var d = [
38350       [
38351         "Afghanistan (‫افغانستان‬‎)",
38352         "af",
38353         "93"
38354       ],
38355       [
38356         "Albania (Shqipëri)",
38357         "al",
38358         "355"
38359       ],
38360       [
38361         "Algeria (‫الجزائر‬‎)",
38362         "dz",
38363         "213"
38364       ],
38365       [
38366         "American Samoa",
38367         "as",
38368         "1684"
38369       ],
38370       [
38371         "Andorra",
38372         "ad",
38373         "376"
38374       ],
38375       [
38376         "Angola",
38377         "ao",
38378         "244"
38379       ],
38380       [
38381         "Anguilla",
38382         "ai",
38383         "1264"
38384       ],
38385       [
38386         "Antigua and Barbuda",
38387         "ag",
38388         "1268"
38389       ],
38390       [
38391         "Argentina",
38392         "ar",
38393         "54"
38394       ],
38395       [
38396         "Armenia (Հայաստան)",
38397         "am",
38398         "374"
38399       ],
38400       [
38401         "Aruba",
38402         "aw",
38403         "297"
38404       ],
38405       [
38406         "Australia",
38407         "au",
38408         "61",
38409         0
38410       ],
38411       [
38412         "Austria (Österreich)",
38413         "at",
38414         "43"
38415       ],
38416       [
38417         "Azerbaijan (Azərbaycan)",
38418         "az",
38419         "994"
38420       ],
38421       [
38422         "Bahamas",
38423         "bs",
38424         "1242"
38425       ],
38426       [
38427         "Bahrain (‫البحرين‬‎)",
38428         "bh",
38429         "973"
38430       ],
38431       [
38432         "Bangladesh (বাংলাদেশ)",
38433         "bd",
38434         "880"
38435       ],
38436       [
38437         "Barbados",
38438         "bb",
38439         "1246"
38440       ],
38441       [
38442         "Belarus (Беларусь)",
38443         "by",
38444         "375"
38445       ],
38446       [
38447         "Belgium (België)",
38448         "be",
38449         "32"
38450       ],
38451       [
38452         "Belize",
38453         "bz",
38454         "501"
38455       ],
38456       [
38457         "Benin (Bénin)",
38458         "bj",
38459         "229"
38460       ],
38461       [
38462         "Bermuda",
38463         "bm",
38464         "1441"
38465       ],
38466       [
38467         "Bhutan (འབྲུག)",
38468         "bt",
38469         "975"
38470       ],
38471       [
38472         "Bolivia",
38473         "bo",
38474         "591"
38475       ],
38476       [
38477         "Bosnia and Herzegovina (Босна и Херцеговина)",
38478         "ba",
38479         "387"
38480       ],
38481       [
38482         "Botswana",
38483         "bw",
38484         "267"
38485       ],
38486       [
38487         "Brazil (Brasil)",
38488         "br",
38489         "55"
38490       ],
38491       [
38492         "British Indian Ocean Territory",
38493         "io",
38494         "246"
38495       ],
38496       [
38497         "British Virgin Islands",
38498         "vg",
38499         "1284"
38500       ],
38501       [
38502         "Brunei",
38503         "bn",
38504         "673"
38505       ],
38506       [
38507         "Bulgaria (България)",
38508         "bg",
38509         "359"
38510       ],
38511       [
38512         "Burkina Faso",
38513         "bf",
38514         "226"
38515       ],
38516       [
38517         "Burundi (Uburundi)",
38518         "bi",
38519         "257"
38520       ],
38521       [
38522         "Cambodia (កម្ពុជា)",
38523         "kh",
38524         "855"
38525       ],
38526       [
38527         "Cameroon (Cameroun)",
38528         "cm",
38529         "237"
38530       ],
38531       [
38532         "Canada",
38533         "ca",
38534         "1",
38535         1,
38536         ["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"]
38537       ],
38538       [
38539         "Cape Verde (Kabu Verdi)",
38540         "cv",
38541         "238"
38542       ],
38543       [
38544         "Caribbean Netherlands",
38545         "bq",
38546         "599",
38547         1
38548       ],
38549       [
38550         "Cayman Islands",
38551         "ky",
38552         "1345"
38553       ],
38554       [
38555         "Central African Republic (République centrafricaine)",
38556         "cf",
38557         "236"
38558       ],
38559       [
38560         "Chad (Tchad)",
38561         "td",
38562         "235"
38563       ],
38564       [
38565         "Chile",
38566         "cl",
38567         "56"
38568       ],
38569       [
38570         "China (中国)",
38571         "cn",
38572         "86"
38573       ],
38574       [
38575         "Christmas Island",
38576         "cx",
38577         "61",
38578         2
38579       ],
38580       [
38581         "Cocos (Keeling) Islands",
38582         "cc",
38583         "61",
38584         1
38585       ],
38586       [
38587         "Colombia",
38588         "co",
38589         "57"
38590       ],
38591       [
38592         "Comoros (‫جزر القمر‬‎)",
38593         "km",
38594         "269"
38595       ],
38596       [
38597         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38598         "cd",
38599         "243"
38600       ],
38601       [
38602         "Congo (Republic) (Congo-Brazzaville)",
38603         "cg",
38604         "242"
38605       ],
38606       [
38607         "Cook Islands",
38608         "ck",
38609         "682"
38610       ],
38611       [
38612         "Costa Rica",
38613         "cr",
38614         "506"
38615       ],
38616       [
38617         "Côte d’Ivoire",
38618         "ci",
38619         "225"
38620       ],
38621       [
38622         "Croatia (Hrvatska)",
38623         "hr",
38624         "385"
38625       ],
38626       [
38627         "Cuba",
38628         "cu",
38629         "53"
38630       ],
38631       [
38632         "Curaçao",
38633         "cw",
38634         "599",
38635         0
38636       ],
38637       [
38638         "Cyprus (Κύπρος)",
38639         "cy",
38640         "357"
38641       ],
38642       [
38643         "Czech Republic (Česká republika)",
38644         "cz",
38645         "420"
38646       ],
38647       [
38648         "Denmark (Danmark)",
38649         "dk",
38650         "45"
38651       ],
38652       [
38653         "Djibouti",
38654         "dj",
38655         "253"
38656       ],
38657       [
38658         "Dominica",
38659         "dm",
38660         "1767"
38661       ],
38662       [
38663         "Dominican Republic (República Dominicana)",
38664         "do",
38665         "1",
38666         2,
38667         ["809", "829", "849"]
38668       ],
38669       [
38670         "Ecuador",
38671         "ec",
38672         "593"
38673       ],
38674       [
38675         "Egypt (‫مصر‬‎)",
38676         "eg",
38677         "20"
38678       ],
38679       [
38680         "El Salvador",
38681         "sv",
38682         "503"
38683       ],
38684       [
38685         "Equatorial Guinea (Guinea Ecuatorial)",
38686         "gq",
38687         "240"
38688       ],
38689       [
38690         "Eritrea",
38691         "er",
38692         "291"
38693       ],
38694       [
38695         "Estonia (Eesti)",
38696         "ee",
38697         "372"
38698       ],
38699       [
38700         "Ethiopia",
38701         "et",
38702         "251"
38703       ],
38704       [
38705         "Falkland Islands (Islas Malvinas)",
38706         "fk",
38707         "500"
38708       ],
38709       [
38710         "Faroe Islands (Føroyar)",
38711         "fo",
38712         "298"
38713       ],
38714       [
38715         "Fiji",
38716         "fj",
38717         "679"
38718       ],
38719       [
38720         "Finland (Suomi)",
38721         "fi",
38722         "358",
38723         0
38724       ],
38725       [
38726         "France",
38727         "fr",
38728         "33"
38729       ],
38730       [
38731         "French Guiana (Guyane française)",
38732         "gf",
38733         "594"
38734       ],
38735       [
38736         "French Polynesia (Polynésie française)",
38737         "pf",
38738         "689"
38739       ],
38740       [
38741         "Gabon",
38742         "ga",
38743         "241"
38744       ],
38745       [
38746         "Gambia",
38747         "gm",
38748         "220"
38749       ],
38750       [
38751         "Georgia (საქართველო)",
38752         "ge",
38753         "995"
38754       ],
38755       [
38756         "Germany (Deutschland)",
38757         "de",
38758         "49"
38759       ],
38760       [
38761         "Ghana (Gaana)",
38762         "gh",
38763         "233"
38764       ],
38765       [
38766         "Gibraltar",
38767         "gi",
38768         "350"
38769       ],
38770       [
38771         "Greece (Ελλάδα)",
38772         "gr",
38773         "30"
38774       ],
38775       [
38776         "Greenland (Kalaallit Nunaat)",
38777         "gl",
38778         "299"
38779       ],
38780       [
38781         "Grenada",
38782         "gd",
38783         "1473"
38784       ],
38785       [
38786         "Guadeloupe",
38787         "gp",
38788         "590",
38789         0
38790       ],
38791       [
38792         "Guam",
38793         "gu",
38794         "1671"
38795       ],
38796       [
38797         "Guatemala",
38798         "gt",
38799         "502"
38800       ],
38801       [
38802         "Guernsey",
38803         "gg",
38804         "44",
38805         1
38806       ],
38807       [
38808         "Guinea (Guinée)",
38809         "gn",
38810         "224"
38811       ],
38812       [
38813         "Guinea-Bissau (Guiné Bissau)",
38814         "gw",
38815         "245"
38816       ],
38817       [
38818         "Guyana",
38819         "gy",
38820         "592"
38821       ],
38822       [
38823         "Haiti",
38824         "ht",
38825         "509"
38826       ],
38827       [
38828         "Honduras",
38829         "hn",
38830         "504"
38831       ],
38832       [
38833         "Hong Kong (香港)",
38834         "hk",
38835         "852"
38836       ],
38837       [
38838         "Hungary (Magyarország)",
38839         "hu",
38840         "36"
38841       ],
38842       [
38843         "Iceland (Ísland)",
38844         "is",
38845         "354"
38846       ],
38847       [
38848         "India (भारत)",
38849         "in",
38850         "91"
38851       ],
38852       [
38853         "Indonesia",
38854         "id",
38855         "62"
38856       ],
38857       [
38858         "Iran (‫ایران‬‎)",
38859         "ir",
38860         "98"
38861       ],
38862       [
38863         "Iraq (‫العراق‬‎)",
38864         "iq",
38865         "964"
38866       ],
38867       [
38868         "Ireland",
38869         "ie",
38870         "353"
38871       ],
38872       [
38873         "Isle of Man",
38874         "im",
38875         "44",
38876         2
38877       ],
38878       [
38879         "Israel (‫ישראל‬‎)",
38880         "il",
38881         "972"
38882       ],
38883       [
38884         "Italy (Italia)",
38885         "it",
38886         "39",
38887         0
38888       ],
38889       [
38890         "Jamaica",
38891         "jm",
38892         "1876"
38893       ],
38894       [
38895         "Japan (日本)",
38896         "jp",
38897         "81"
38898       ],
38899       [
38900         "Jersey",
38901         "je",
38902         "44",
38903         3
38904       ],
38905       [
38906         "Jordan (‫الأردن‬‎)",
38907         "jo",
38908         "962"
38909       ],
38910       [
38911         "Kazakhstan (Казахстан)",
38912         "kz",
38913         "7",
38914         1
38915       ],
38916       [
38917         "Kenya",
38918         "ke",
38919         "254"
38920       ],
38921       [
38922         "Kiribati",
38923         "ki",
38924         "686"
38925       ],
38926       [
38927         "Kosovo",
38928         "xk",
38929         "383"
38930       ],
38931       [
38932         "Kuwait (‫الكويت‬‎)",
38933         "kw",
38934         "965"
38935       ],
38936       [
38937         "Kyrgyzstan (Кыргызстан)",
38938         "kg",
38939         "996"
38940       ],
38941       [
38942         "Laos (ລາວ)",
38943         "la",
38944         "856"
38945       ],
38946       [
38947         "Latvia (Latvija)",
38948         "lv",
38949         "371"
38950       ],
38951       [
38952         "Lebanon (‫لبنان‬‎)",
38953         "lb",
38954         "961"
38955       ],
38956       [
38957         "Lesotho",
38958         "ls",
38959         "266"
38960       ],
38961       [
38962         "Liberia",
38963         "lr",
38964         "231"
38965       ],
38966       [
38967         "Libya (‫ليبيا‬‎)",
38968         "ly",
38969         "218"
38970       ],
38971       [
38972         "Liechtenstein",
38973         "li",
38974         "423"
38975       ],
38976       [
38977         "Lithuania (Lietuva)",
38978         "lt",
38979         "370"
38980       ],
38981       [
38982         "Luxembourg",
38983         "lu",
38984         "352"
38985       ],
38986       [
38987         "Macau (澳門)",
38988         "mo",
38989         "853"
38990       ],
38991       [
38992         "Macedonia (FYROM) (Македонија)",
38993         "mk",
38994         "389"
38995       ],
38996       [
38997         "Madagascar (Madagasikara)",
38998         "mg",
38999         "261"
39000       ],
39001       [
39002         "Malawi",
39003         "mw",
39004         "265"
39005       ],
39006       [
39007         "Malaysia",
39008         "my",
39009         "60"
39010       ],
39011       [
39012         "Maldives",
39013         "mv",
39014         "960"
39015       ],
39016       [
39017         "Mali",
39018         "ml",
39019         "223"
39020       ],
39021       [
39022         "Malta",
39023         "mt",
39024         "356"
39025       ],
39026       [
39027         "Marshall Islands",
39028         "mh",
39029         "692"
39030       ],
39031       [
39032         "Martinique",
39033         "mq",
39034         "596"
39035       ],
39036       [
39037         "Mauritania (‫موريتانيا‬‎)",
39038         "mr",
39039         "222"
39040       ],
39041       [
39042         "Mauritius (Moris)",
39043         "mu",
39044         "230"
39045       ],
39046       [
39047         "Mayotte",
39048         "yt",
39049         "262",
39050         1
39051       ],
39052       [
39053         "Mexico (México)",
39054         "mx",
39055         "52"
39056       ],
39057       [
39058         "Micronesia",
39059         "fm",
39060         "691"
39061       ],
39062       [
39063         "Moldova (Republica Moldova)",
39064         "md",
39065         "373"
39066       ],
39067       [
39068         "Monaco",
39069         "mc",
39070         "377"
39071       ],
39072       [
39073         "Mongolia (Монгол)",
39074         "mn",
39075         "976"
39076       ],
39077       [
39078         "Montenegro (Crna Gora)",
39079         "me",
39080         "382"
39081       ],
39082       [
39083         "Montserrat",
39084         "ms",
39085         "1664"
39086       ],
39087       [
39088         "Morocco (‫المغرب‬‎)",
39089         "ma",
39090         "212",
39091         0
39092       ],
39093       [
39094         "Mozambique (Moçambique)",
39095         "mz",
39096         "258"
39097       ],
39098       [
39099         "Myanmar (Burma) (မြန်မာ)",
39100         "mm",
39101         "95"
39102       ],
39103       [
39104         "Namibia (Namibië)",
39105         "na",
39106         "264"
39107       ],
39108       [
39109         "Nauru",
39110         "nr",
39111         "674"
39112       ],
39113       [
39114         "Nepal (नेपाल)",
39115         "np",
39116         "977"
39117       ],
39118       [
39119         "Netherlands (Nederland)",
39120         "nl",
39121         "31"
39122       ],
39123       [
39124         "New Caledonia (Nouvelle-Calédonie)",
39125         "nc",
39126         "687"
39127       ],
39128       [
39129         "New Zealand",
39130         "nz",
39131         "64"
39132       ],
39133       [
39134         "Nicaragua",
39135         "ni",
39136         "505"
39137       ],
39138       [
39139         "Niger (Nijar)",
39140         "ne",
39141         "227"
39142       ],
39143       [
39144         "Nigeria",
39145         "ng",
39146         "234"
39147       ],
39148       [
39149         "Niue",
39150         "nu",
39151         "683"
39152       ],
39153       [
39154         "Norfolk Island",
39155         "nf",
39156         "672"
39157       ],
39158       [
39159         "North Korea (조선 민주주의 인민 공화국)",
39160         "kp",
39161         "850"
39162       ],
39163       [
39164         "Northern Mariana Islands",
39165         "mp",
39166         "1670"
39167       ],
39168       [
39169         "Norway (Norge)",
39170         "no",
39171         "47",
39172         0
39173       ],
39174       [
39175         "Oman (‫عُمان‬‎)",
39176         "om",
39177         "968"
39178       ],
39179       [
39180         "Pakistan (‫پاکستان‬‎)",
39181         "pk",
39182         "92"
39183       ],
39184       [
39185         "Palau",
39186         "pw",
39187         "680"
39188       ],
39189       [
39190         "Palestine (‫فلسطين‬‎)",
39191         "ps",
39192         "970"
39193       ],
39194       [
39195         "Panama (Panamá)",
39196         "pa",
39197         "507"
39198       ],
39199       [
39200         "Papua New Guinea",
39201         "pg",
39202         "675"
39203       ],
39204       [
39205         "Paraguay",
39206         "py",
39207         "595"
39208       ],
39209       [
39210         "Peru (Perú)",
39211         "pe",
39212         "51"
39213       ],
39214       [
39215         "Philippines",
39216         "ph",
39217         "63"
39218       ],
39219       [
39220         "Poland (Polska)",
39221         "pl",
39222         "48"
39223       ],
39224       [
39225         "Portugal",
39226         "pt",
39227         "351"
39228       ],
39229       [
39230         "Puerto Rico",
39231         "pr",
39232         "1",
39233         3,
39234         ["787", "939"]
39235       ],
39236       [
39237         "Qatar (‫قطر‬‎)",
39238         "qa",
39239         "974"
39240       ],
39241       [
39242         "Réunion (La Réunion)",
39243         "re",
39244         "262",
39245         0
39246       ],
39247       [
39248         "Romania (România)",
39249         "ro",
39250         "40"
39251       ],
39252       [
39253         "Russia (Россия)",
39254         "ru",
39255         "7",
39256         0
39257       ],
39258       [
39259         "Rwanda",
39260         "rw",
39261         "250"
39262       ],
39263       [
39264         "Saint Barthélemy",
39265         "bl",
39266         "590",
39267         1
39268       ],
39269       [
39270         "Saint Helena",
39271         "sh",
39272         "290"
39273       ],
39274       [
39275         "Saint Kitts and Nevis",
39276         "kn",
39277         "1869"
39278       ],
39279       [
39280         "Saint Lucia",
39281         "lc",
39282         "1758"
39283       ],
39284       [
39285         "Saint Martin (Saint-Martin (partie française))",
39286         "mf",
39287         "590",
39288         2
39289       ],
39290       [
39291         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39292         "pm",
39293         "508"
39294       ],
39295       [
39296         "Saint Vincent and the Grenadines",
39297         "vc",
39298         "1784"
39299       ],
39300       [
39301         "Samoa",
39302         "ws",
39303         "685"
39304       ],
39305       [
39306         "San Marino",
39307         "sm",
39308         "378"
39309       ],
39310       [
39311         "São Tomé and Príncipe (São Tomé e Príncipe)",
39312         "st",
39313         "239"
39314       ],
39315       [
39316         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39317         "sa",
39318         "966"
39319       ],
39320       [
39321         "Senegal (Sénégal)",
39322         "sn",
39323         "221"
39324       ],
39325       [
39326         "Serbia (Србија)",
39327         "rs",
39328         "381"
39329       ],
39330       [
39331         "Seychelles",
39332         "sc",
39333         "248"
39334       ],
39335       [
39336         "Sierra Leone",
39337         "sl",
39338         "232"
39339       ],
39340       [
39341         "Singapore",
39342         "sg",
39343         "65"
39344       ],
39345       [
39346         "Sint Maarten",
39347         "sx",
39348         "1721"
39349       ],
39350       [
39351         "Slovakia (Slovensko)",
39352         "sk",
39353         "421"
39354       ],
39355       [
39356         "Slovenia (Slovenija)",
39357         "si",
39358         "386"
39359       ],
39360       [
39361         "Solomon Islands",
39362         "sb",
39363         "677"
39364       ],
39365       [
39366         "Somalia (Soomaaliya)",
39367         "so",
39368         "252"
39369       ],
39370       [
39371         "South Africa",
39372         "za",
39373         "27"
39374       ],
39375       [
39376         "South Korea (대한민국)",
39377         "kr",
39378         "82"
39379       ],
39380       [
39381         "South Sudan (‫جنوب السودان‬‎)",
39382         "ss",
39383         "211"
39384       ],
39385       [
39386         "Spain (España)",
39387         "es",
39388         "34"
39389       ],
39390       [
39391         "Sri Lanka (ශ්‍රී ලංකාව)",
39392         "lk",
39393         "94"
39394       ],
39395       [
39396         "Sudan (‫السودان‬‎)",
39397         "sd",
39398         "249"
39399       ],
39400       [
39401         "Suriname",
39402         "sr",
39403         "597"
39404       ],
39405       [
39406         "Svalbard and Jan Mayen",
39407         "sj",
39408         "47",
39409         1
39410       ],
39411       [
39412         "Swaziland",
39413         "sz",
39414         "268"
39415       ],
39416       [
39417         "Sweden (Sverige)",
39418         "se",
39419         "46"
39420       ],
39421       [
39422         "Switzerland (Schweiz)",
39423         "ch",
39424         "41"
39425       ],
39426       [
39427         "Syria (‫سوريا‬‎)",
39428         "sy",
39429         "963"
39430       ],
39431       [
39432         "Taiwan (台灣)",
39433         "tw",
39434         "886"
39435       ],
39436       [
39437         "Tajikistan",
39438         "tj",
39439         "992"
39440       ],
39441       [
39442         "Tanzania",
39443         "tz",
39444         "255"
39445       ],
39446       [
39447         "Thailand (ไทย)",
39448         "th",
39449         "66"
39450       ],
39451       [
39452         "Timor-Leste",
39453         "tl",
39454         "670"
39455       ],
39456       [
39457         "Togo",
39458         "tg",
39459         "228"
39460       ],
39461       [
39462         "Tokelau",
39463         "tk",
39464         "690"
39465       ],
39466       [
39467         "Tonga",
39468         "to",
39469         "676"
39470       ],
39471       [
39472         "Trinidad and Tobago",
39473         "tt",
39474         "1868"
39475       ],
39476       [
39477         "Tunisia (‫تونس‬‎)",
39478         "tn",
39479         "216"
39480       ],
39481       [
39482         "Turkey (Türkiye)",
39483         "tr",
39484         "90"
39485       ],
39486       [
39487         "Turkmenistan",
39488         "tm",
39489         "993"
39490       ],
39491       [
39492         "Turks and Caicos Islands",
39493         "tc",
39494         "1649"
39495       ],
39496       [
39497         "Tuvalu",
39498         "tv",
39499         "688"
39500       ],
39501       [
39502         "U.S. Virgin Islands",
39503         "vi",
39504         "1340"
39505       ],
39506       [
39507         "Uganda",
39508         "ug",
39509         "256"
39510       ],
39511       [
39512         "Ukraine (Україна)",
39513         "ua",
39514         "380"
39515       ],
39516       [
39517         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39518         "ae",
39519         "971"
39520       ],
39521       [
39522         "United Kingdom",
39523         "gb",
39524         "44",
39525         0
39526       ],
39527       [
39528         "United States",
39529         "us",
39530         "1",
39531         0
39532       ],
39533       [
39534         "Uruguay",
39535         "uy",
39536         "598"
39537       ],
39538       [
39539         "Uzbekistan (Oʻzbekiston)",
39540         "uz",
39541         "998"
39542       ],
39543       [
39544         "Vanuatu",
39545         "vu",
39546         "678"
39547       ],
39548       [
39549         "Vatican City (Città del Vaticano)",
39550         "va",
39551         "39",
39552         1
39553       ],
39554       [
39555         "Venezuela",
39556         "ve",
39557         "58"
39558       ],
39559       [
39560         "Vietnam (Việt Nam)",
39561         "vn",
39562         "84"
39563       ],
39564       [
39565         "Wallis and Futuna (Wallis-et-Futuna)",
39566         "wf",
39567         "681"
39568       ],
39569       [
39570         "Western Sahara (‫الصحراء الغربية‬‎)",
39571         "eh",
39572         "212",
39573         1
39574       ],
39575       [
39576         "Yemen (‫اليمن‬‎)",
39577         "ye",
39578         "967"
39579       ],
39580       [
39581         "Zambia",
39582         "zm",
39583         "260"
39584       ],
39585       [
39586         "Zimbabwe",
39587         "zw",
39588         "263"
39589       ],
39590       [
39591         "Åland Islands",
39592         "ax",
39593         "358",
39594         1
39595       ]
39596   ];
39597   
39598   return d;
39599 }/**
39600 *    This script refer to:
39601 *    Title: International Telephone Input
39602 *    Author: Jack O'Connor
39603 *    Code version:  v12.1.12
39604 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39605 **/
39606
39607 /**
39608  * @class Roo.bootstrap.PhoneInput
39609  * @extends Roo.bootstrap.TriggerField
39610  * An input with International dial-code selection
39611  
39612  * @cfg {String} defaultDialCode default '+852'
39613  * @cfg {Array} preferedCountries default []
39614   
39615  * @constructor
39616  * Create a new PhoneInput.
39617  * @param {Object} config Configuration options
39618  */
39619
39620 Roo.bootstrap.PhoneInput = function(config) {
39621     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39622 };
39623
39624 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39625         
39626         listWidth: undefined,
39627         
39628         selectedClass: 'active',
39629         
39630         invalidClass : "has-warning",
39631         
39632         validClass: 'has-success',
39633         
39634         allowed: '0123456789',
39635         
39636         /**
39637          * @cfg {String} defaultDialCode The default dial code when initializing the input
39638          */
39639         defaultDialCode: '+852',
39640         
39641         /**
39642          * @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
39643          */
39644         preferedCountries: false,
39645         
39646         getAutoCreate : function()
39647         {
39648             var data = Roo.bootstrap.PhoneInputData();
39649             var align = this.labelAlign || this.parentLabelAlign();
39650             var id = Roo.id();
39651             
39652             this.allCountries = [];
39653             this.dialCodeMapping = [];
39654             
39655             for (var i = 0; i < data.length; i++) {
39656               var c = data[i];
39657               this.allCountries[i] = {
39658                 name: c[0],
39659                 iso2: c[1],
39660                 dialCode: c[2],
39661                 priority: c[3] || 0,
39662                 areaCodes: c[4] || null
39663               };
39664               this.dialCodeMapping[c[2]] = {
39665                   name: c[0],
39666                   iso2: c[1],
39667                   priority: c[3] || 0,
39668                   areaCodes: c[4] || null
39669               };
39670             }
39671             
39672             var cfg = {
39673                 cls: 'form-group',
39674                 cn: []
39675             };
39676             
39677             var input =  {
39678                 tag: 'input',
39679                 id : id,
39680                 cls : 'form-control tel-input',
39681                 autocomplete: 'new-password'
39682             };
39683             
39684             var hiddenInput = {
39685                 tag: 'input',
39686                 type: 'hidden',
39687                 cls: 'hidden-tel-input'
39688             };
39689             
39690             if (this.name) {
39691                 hiddenInput.name = this.name;
39692             }
39693             
39694             if (this.disabled) {
39695                 input.disabled = true;
39696             }
39697             
39698             var flag_container = {
39699                 tag: 'div',
39700                 cls: 'flag-box',
39701                 cn: [
39702                     {
39703                         tag: 'div',
39704                         cls: 'flag'
39705                     },
39706                     {
39707                         tag: 'div',
39708                         cls: 'caret'
39709                     }
39710                 ]
39711             };
39712             
39713             var box = {
39714                 tag: 'div',
39715                 cls: this.hasFeedback ? 'has-feedback' : '',
39716                 cn: [
39717                     hiddenInput,
39718                     input,
39719                     {
39720                         tag: 'input',
39721                         cls: 'dial-code-holder',
39722                         disabled: true
39723                     }
39724                 ]
39725             };
39726             
39727             var container = {
39728                 cls: 'roo-select2-container input-group',
39729                 cn: [
39730                     flag_container,
39731                     box
39732                 ]
39733             };
39734             
39735             if (this.fieldLabel.length) {
39736                 var indicator = {
39737                     tag: 'i',
39738                     tooltip: 'This field is required'
39739                 };
39740                 
39741                 var label = {
39742                     tag: 'label',
39743                     'for':  id,
39744                     cls: 'control-label',
39745                     cn: []
39746                 };
39747                 
39748                 var label_text = {
39749                     tag: 'span',
39750                     html: this.fieldLabel
39751                 };
39752                 
39753                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39754                 label.cn = [
39755                     indicator,
39756                     label_text
39757                 ];
39758                 
39759                 if(this.indicatorpos == 'right') {
39760                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39761                     label.cn = [
39762                         label_text,
39763                         indicator
39764                     ];
39765                 }
39766                 
39767                 if(align == 'left') {
39768                     container = {
39769                         tag: 'div',
39770                         cn: [
39771                             container
39772                         ]
39773                     };
39774                     
39775                     if(this.labelWidth > 12){
39776                         label.style = "width: " + this.labelWidth + 'px';
39777                     }
39778                     if(this.labelWidth < 13 && this.labelmd == 0){
39779                         this.labelmd = this.labelWidth;
39780                     }
39781                     if(this.labellg > 0){
39782                         label.cls += ' col-lg-' + this.labellg;
39783                         input.cls += ' col-lg-' + (12 - this.labellg);
39784                     }
39785                     if(this.labelmd > 0){
39786                         label.cls += ' col-md-' + this.labelmd;
39787                         container.cls += ' col-md-' + (12 - this.labelmd);
39788                     }
39789                     if(this.labelsm > 0){
39790                         label.cls += ' col-sm-' + this.labelsm;
39791                         container.cls += ' col-sm-' + (12 - this.labelsm);
39792                     }
39793                     if(this.labelxs > 0){
39794                         label.cls += ' col-xs-' + this.labelxs;
39795                         container.cls += ' col-xs-' + (12 - this.labelxs);
39796                     }
39797                 }
39798             }
39799             
39800             cfg.cn = [
39801                 label,
39802                 container
39803             ];
39804             
39805             var settings = this;
39806             
39807             ['xs','sm','md','lg'].map(function(size){
39808                 if (settings[size]) {
39809                     cfg.cls += ' col-' + size + '-' + settings[size];
39810                 }
39811             });
39812             
39813             this.store = new Roo.data.Store({
39814                 proxy : new Roo.data.MemoryProxy({}),
39815                 reader : new Roo.data.JsonReader({
39816                     fields : [
39817                         {
39818                             'name' : 'name',
39819                             'type' : 'string'
39820                         },
39821                         {
39822                             'name' : 'iso2',
39823                             'type' : 'string'
39824                         },
39825                         {
39826                             'name' : 'dialCode',
39827                             'type' : 'string'
39828                         },
39829                         {
39830                             'name' : 'priority',
39831                             'type' : 'string'
39832                         },
39833                         {
39834                             'name' : 'areaCodes',
39835                             'type' : 'string'
39836                         }
39837                     ]
39838                 })
39839             });
39840             
39841             if(!this.preferedCountries) {
39842                 this.preferedCountries = [
39843                     'hk',
39844                     'gb',
39845                     'us'
39846                 ];
39847             }
39848             
39849             var p = this.preferedCountries.reverse();
39850             
39851             if(p) {
39852                 for (var i = 0; i < p.length; i++) {
39853                     for (var j = 0; j < this.allCountries.length; j++) {
39854                         if(this.allCountries[j].iso2 == p[i]) {
39855                             var t = this.allCountries[j];
39856                             this.allCountries.splice(j,1);
39857                             this.allCountries.unshift(t);
39858                         }
39859                     } 
39860                 }
39861             }
39862             
39863             this.store.proxy.data = {
39864                 success: true,
39865                 data: this.allCountries
39866             };
39867             
39868             return cfg;
39869         },
39870         
39871         initEvents : function()
39872         {
39873             this.createList();
39874             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39875             
39876             this.indicator = this.indicatorEl();
39877             this.flag = this.flagEl();
39878             this.dialCodeHolder = this.dialCodeHolderEl();
39879             
39880             this.trigger = this.el.select('div.flag-box',true).first();
39881             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39882             
39883             var _this = this;
39884             
39885             (function(){
39886                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39887                 _this.list.setWidth(lw);
39888             }).defer(100);
39889             
39890             this.list.on('mouseover', this.onViewOver, this);
39891             this.list.on('mousemove', this.onViewMove, this);
39892             this.inputEl().on("keyup", this.onKeyUp, this);
39893             
39894             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
39895
39896             this.view = new Roo.View(this.list, this.tpl, {
39897                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
39898             });
39899             
39900             this.view.on('click', this.onViewClick, this);
39901             this.setValue(this.defaultDialCode);
39902         },
39903         
39904         onTriggerClick : function(e)
39905         {
39906             Roo.log('trigger click');
39907             if(this.disabled){
39908                 return;
39909             }
39910             
39911             if(this.isExpanded()){
39912                 this.collapse();
39913                 this.hasFocus = false;
39914             }else {
39915                 this.store.load({});
39916                 this.hasFocus = true;
39917                 this.expand();
39918             }
39919         },
39920         
39921         isExpanded : function()
39922         {
39923             return this.list.isVisible();
39924         },
39925         
39926         collapse : function()
39927         {
39928             if(!this.isExpanded()){
39929                 return;
39930             }
39931             this.list.hide();
39932             Roo.get(document).un('mousedown', this.collapseIf, this);
39933             Roo.get(document).un('mousewheel', this.collapseIf, this);
39934             this.fireEvent('collapse', this);
39935             this.validate();
39936         },
39937         
39938         expand : function()
39939         {
39940             Roo.log('expand');
39941
39942             if(this.isExpanded() || !this.hasFocus){
39943                 return;
39944             }
39945             
39946             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
39947             this.list.setWidth(lw);
39948             
39949             this.list.show();
39950             this.restrictHeight();
39951             
39952             Roo.get(document).on('mousedown', this.collapseIf, this);
39953             Roo.get(document).on('mousewheel', this.collapseIf, this);
39954             
39955             this.fireEvent('expand', this);
39956         },
39957         
39958         restrictHeight : function()
39959         {
39960             this.list.alignTo(this.inputEl(), this.listAlign);
39961             this.list.alignTo(this.inputEl(), this.listAlign);
39962         },
39963         
39964         onViewOver : function(e, t)
39965         {
39966             if(this.inKeyMode){
39967                 return;
39968             }
39969             var item = this.view.findItemFromChild(t);
39970             
39971             if(item){
39972                 var index = this.view.indexOf(item);
39973                 this.select(index, false);
39974             }
39975         },
39976
39977         // private
39978         onViewClick : function(view, doFocus, el, e)
39979         {
39980             var index = this.view.getSelectedIndexes()[0];
39981             
39982             var r = this.store.getAt(index);
39983             
39984             if(r){
39985                 this.onSelect(r, index);
39986             }
39987             if(doFocus !== false && !this.blockFocus){
39988                 this.inputEl().focus();
39989             }
39990         },
39991         
39992         onViewMove : function(e, t)
39993         {
39994             this.inKeyMode = false;
39995         },
39996         
39997         select : function(index, scrollIntoView)
39998         {
39999             this.selectedIndex = index;
40000             this.view.select(index);
40001             if(scrollIntoView !== false){
40002                 var el = this.view.getNode(index);
40003                 if(el){
40004                     this.list.scrollChildIntoView(el, false);
40005                 }
40006             }
40007         },
40008         
40009         createList : function()
40010         {
40011             this.list = Roo.get(document.body).createChild({
40012                 tag: 'ul',
40013                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40014                 style: 'display:none'
40015             });
40016             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
40017         },
40018         
40019         collapseIf : function(e)
40020         {
40021             var in_combo  = e.within(this.el);
40022             var in_list =  e.within(this.list);
40023             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40024             
40025             if (in_combo || in_list || is_list) {
40026                 return;
40027             }
40028             this.collapse();
40029         },
40030         
40031         onSelect : function(record, index)
40032         {
40033             if(this.fireEvent('beforeselect', this, record, index) !== false){
40034                 
40035                 this.setFlagClass(record.data.iso2);
40036                 this.setDialCode(record.data.dialCode);
40037                 this.hasFocus = false;
40038                 this.collapse();
40039                 this.fireEvent('select', this, record, index);
40040             }
40041         },
40042         
40043         flagEl : function()
40044         {
40045             var flag = this.el.select('div.flag',true).first();
40046             if(!flag){
40047                 return false;
40048             }
40049             return flag;
40050         },
40051         
40052         dialCodeHolderEl : function()
40053         {
40054             var d = this.el.select('input.dial-code-holder',true).first();
40055             if(!d){
40056                 return false;
40057             }
40058             return d;
40059         },
40060         
40061         setDialCode : function(v)
40062         {
40063             this.dialCodeHolder.dom.value = '+'+v;
40064         },
40065         
40066         setFlagClass : function(n)
40067         {
40068             this.flag.dom.className = 'flag '+n;
40069         },
40070         
40071         getValue : function()
40072         {
40073             var v = this.inputEl().getValue();
40074             if(this.dialCodeHolder) {
40075                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40076             }
40077             return v;
40078         },
40079         
40080         setValue : function(v)
40081         {
40082             var d = this.getDialCode(v);
40083             
40084             //invalid dial code
40085             if(v.length == 0 || !d || d.length == 0) {
40086                 if(this.rendered){
40087                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40088                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40089                 }
40090                 return;
40091             }
40092             
40093             //valid dial code
40094             this.setFlagClass(this.dialCodeMapping[d].iso2);
40095             this.setDialCode(d);
40096             this.inputEl().dom.value = v.replace('+'+d,'');
40097             this.hiddenEl().dom.value = this.getValue();
40098             
40099             this.validate();
40100         },
40101         
40102         getDialCode : function(v = '')
40103         {
40104             if (v.length == 0) {
40105                 return this.dialCodeHolder.dom.value;
40106             }
40107             
40108             var dialCode = "";
40109             if (v.charAt(0) != "+") {
40110                 return false;
40111             }
40112             var numericChars = "";
40113             for (var i = 1; i < v.length; i++) {
40114               var c = v.charAt(i);
40115               if (!isNaN(c)) {
40116                 numericChars += c;
40117                 if (this.dialCodeMapping[numericChars]) {
40118                   dialCode = v.substr(1, i);
40119                 }
40120                 if (numericChars.length == 4) {
40121                   break;
40122                 }
40123               }
40124             }
40125             return dialCode;
40126         },
40127         
40128         reset : function()
40129         {
40130             this.setValue(this.defaultDialCode);
40131             this.validate();
40132         },
40133         
40134         hiddenEl : function()
40135         {
40136             return this.el.select('input.hidden-tel-input',true).first();
40137         },
40138         
40139         onKeyUp : function(e){
40140             
40141             var k = e.getKey();
40142             var c = e.getCharCode();
40143             
40144             if(
40145                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40146                     this.allowed.indexOf(String.fromCharCode(c)) === -1
40147             ){
40148                 e.stopEvent();
40149             }
40150             
40151             // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40152             //     return;
40153             // }
40154             if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40155                 e.stopEvent();
40156             }
40157             
40158             this.setValue(this.getValue());
40159         }
40160         
40161 });
40162 /**
40163  * @class Roo.bootstrap.MoneyField
40164  * @extends Roo.bootstrap.ComboBox
40165  * Bootstrap MoneyField class
40166  * 
40167  * @constructor
40168  * Create a new MoneyField.
40169  * @param {Object} config Configuration options
40170  */
40171
40172 Roo.bootstrap.MoneyField = function(config) {
40173     
40174     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40175     
40176 };
40177
40178 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40179     
40180     /**
40181      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40182      */
40183     allowDecimals : true,
40184     /**
40185      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40186      */
40187     decimalSeparator : ".",
40188     /**
40189      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40190      */
40191     decimalPrecision : 0,
40192     /**
40193      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40194      */
40195     allowNegative : true,
40196     /**
40197      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40198      */
40199     minValue : Number.NEGATIVE_INFINITY,
40200     /**
40201      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40202      */
40203     maxValue : Number.MAX_VALUE,
40204     /**
40205      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40206      */
40207     minText : "The minimum value for this field is {0}",
40208     /**
40209      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40210      */
40211     maxText : "The maximum value for this field is {0}",
40212     /**
40213      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40214      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40215      */
40216     nanText : "{0} is not a valid number",
40217     /**
40218      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40219      */
40220     castInt : true,
40221     /**
40222      * @cfg {String} defaults currency of the MoneyField
40223      * value should be in lkey
40224      */
40225     defaultCurrency : false,
40226     /**
40227      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40228      */
40229     thousandsDelimiter : false,
40230     
40231     
40232     inputlg : 9,
40233     inputmd : 9,
40234     inputsm : 9,
40235     inputxs : 6,
40236     
40237     store : false,
40238     
40239     getAutoCreate : function()
40240     {
40241         var align = this.labelAlign || this.parentLabelAlign();
40242         
40243         var id = Roo.id();
40244
40245         var cfg = {
40246             cls: 'form-group',
40247             cn: []
40248         };
40249
40250         var input =  {
40251             tag: 'input',
40252             id : id,
40253             cls : 'form-control roo-money-amount-input',
40254             autocomplete: 'new-password'
40255         };
40256         
40257         var hiddenInput = {
40258             tag: 'input',
40259             type: 'hidden',
40260             id: Roo.id(),
40261             cls: 'hidden-number-input'
40262         };
40263         
40264         if (this.name) {
40265             hiddenInput.name = this.name;
40266         }
40267
40268         if (this.disabled) {
40269             input.disabled = true;
40270         }
40271
40272         var clg = 12 - this.inputlg;
40273         var cmd = 12 - this.inputmd;
40274         var csm = 12 - this.inputsm;
40275         var cxs = 12 - this.inputxs;
40276         
40277         var container = {
40278             tag : 'div',
40279             cls : 'row roo-money-field',
40280             cn : [
40281                 {
40282                     tag : 'div',
40283                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40284                     cn : [
40285                         {
40286                             tag : 'div',
40287                             cls: 'roo-select2-container input-group',
40288                             cn: [
40289                                 {
40290                                     tag : 'input',
40291                                     cls : 'form-control roo-money-currency-input',
40292                                     autocomplete: 'new-password',
40293                                     readOnly : 1,
40294                                     name : this.currencyName
40295                                 },
40296                                 {
40297                                     tag :'span',
40298                                     cls : 'input-group-addon',
40299                                     cn : [
40300                                         {
40301                                             tag: 'span',
40302                                             cls: 'caret'
40303                                         }
40304                                     ]
40305                                 }
40306                             ]
40307                         }
40308                     ]
40309                 },
40310                 {
40311                     tag : 'div',
40312                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40313                     cn : [
40314                         {
40315                             tag: 'div',
40316                             cls: this.hasFeedback ? 'has-feedback' : '',
40317                             cn: [
40318                                 input
40319                             ]
40320                         }
40321                     ]
40322                 }
40323             ]
40324             
40325         };
40326         
40327         if (this.fieldLabel.length) {
40328             var indicator = {
40329                 tag: 'i',
40330                 tooltip: 'This field is required'
40331             };
40332
40333             var label = {
40334                 tag: 'label',
40335                 'for':  id,
40336                 cls: 'control-label',
40337                 cn: []
40338             };
40339
40340             var label_text = {
40341                 tag: 'span',
40342                 html: this.fieldLabel
40343             };
40344
40345             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40346             label.cn = [
40347                 indicator,
40348                 label_text
40349             ];
40350
40351             if(this.indicatorpos == 'right') {
40352                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40353                 label.cn = [
40354                     label_text,
40355                     indicator
40356                 ];
40357             }
40358
40359             if(align == 'left') {
40360                 container = {
40361                     tag: 'div',
40362                     cn: [
40363                         container
40364                     ]
40365                 };
40366
40367                 if(this.labelWidth > 12){
40368                     label.style = "width: " + this.labelWidth + 'px';
40369                 }
40370                 if(this.labelWidth < 13 && this.labelmd == 0){
40371                     this.labelmd = this.labelWidth;
40372                 }
40373                 if(this.labellg > 0){
40374                     label.cls += ' col-lg-' + this.labellg;
40375                     input.cls += ' col-lg-' + (12 - this.labellg);
40376                 }
40377                 if(this.labelmd > 0){
40378                     label.cls += ' col-md-' + this.labelmd;
40379                     container.cls += ' col-md-' + (12 - this.labelmd);
40380                 }
40381                 if(this.labelsm > 0){
40382                     label.cls += ' col-sm-' + this.labelsm;
40383                     container.cls += ' col-sm-' + (12 - this.labelsm);
40384                 }
40385                 if(this.labelxs > 0){
40386                     label.cls += ' col-xs-' + this.labelxs;
40387                     container.cls += ' col-xs-' + (12 - this.labelxs);
40388                 }
40389             }
40390         }
40391
40392         cfg.cn = [
40393             label,
40394             container,
40395             hiddenInput
40396         ];
40397         
40398         var settings = this;
40399
40400         ['xs','sm','md','lg'].map(function(size){
40401             if (settings[size]) {
40402                 cfg.cls += ' col-' + size + '-' + settings[size];
40403             }
40404         });
40405         
40406         return cfg;
40407     },
40408     
40409     initEvents : function()
40410     {
40411         this.indicator = this.indicatorEl();
40412         
40413         this.initCurrencyEvent();
40414         
40415         this.initNumberEvent();
40416     },
40417     
40418     initCurrencyEvent : function()
40419     {
40420         if (!this.store) {
40421             throw "can not find store for combo";
40422         }
40423         
40424         this.store = Roo.factory(this.store, Roo.data);
40425         this.store.parent = this;
40426         
40427         this.createList();
40428         
40429         this.triggerEl = this.el.select('.input-group-addon', true).first();
40430         
40431         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40432         
40433         var _this = this;
40434         
40435         (function(){
40436             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40437             _this.list.setWidth(lw);
40438         }).defer(100);
40439         
40440         this.list.on('mouseover', this.onViewOver, this);
40441         this.list.on('mousemove', this.onViewMove, this);
40442         this.list.on('scroll', this.onViewScroll, this);
40443         
40444         if(!this.tpl){
40445             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40446         }
40447         
40448         this.view = new Roo.View(this.list, this.tpl, {
40449             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40450         });
40451         
40452         this.view.on('click', this.onViewClick, this);
40453         
40454         this.store.on('beforeload', this.onBeforeLoad, this);
40455         this.store.on('load', this.onLoad, this);
40456         this.store.on('loadexception', this.onLoadException, this);
40457         
40458         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40459             "up" : function(e){
40460                 this.inKeyMode = true;
40461                 this.selectPrev();
40462             },
40463
40464             "down" : function(e){
40465                 if(!this.isExpanded()){
40466                     this.onTriggerClick();
40467                 }else{
40468                     this.inKeyMode = true;
40469                     this.selectNext();
40470                 }
40471             },
40472
40473             "enter" : function(e){
40474                 this.collapse();
40475                 
40476                 if(this.fireEvent("specialkey", this, e)){
40477                     this.onViewClick(false);
40478                 }
40479                 
40480                 return true;
40481             },
40482
40483             "esc" : function(e){
40484                 this.collapse();
40485             },
40486
40487             "tab" : function(e){
40488                 this.collapse();
40489                 
40490                 if(this.fireEvent("specialkey", this, e)){
40491                     this.onViewClick(false);
40492                 }
40493                 
40494                 return true;
40495             },
40496
40497             scope : this,
40498
40499             doRelay : function(foo, bar, hname){
40500                 if(hname == 'down' || this.scope.isExpanded()){
40501                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40502                 }
40503                 return true;
40504             },
40505
40506             forceKeyDown: true
40507         });
40508         
40509         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40510         
40511     },
40512     
40513     initNumberEvent : function(e)
40514     {
40515         this.inputEl().on("keydown" , this.fireKey,  this);
40516         this.inputEl().on("focus", this.onFocus,  this);
40517         this.inputEl().on("blur", this.onBlur,  this);
40518         
40519         this.inputEl().relayEvent('keyup', this);
40520         
40521         if(this.indicator){
40522             this.indicator.addClass('invisible');
40523         }
40524  
40525         this.originalValue = this.getValue();
40526         
40527         if(this.validationEvent == 'keyup'){
40528             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40529             this.inputEl().on('keyup', this.filterValidation, this);
40530         }
40531         else if(this.validationEvent !== false){
40532             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40533         }
40534         
40535         if(this.selectOnFocus){
40536             this.on("focus", this.preFocus, this);
40537             
40538         }
40539         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40540             this.inputEl().on("keypress", this.filterKeys, this);
40541         } else {
40542             this.inputEl().relayEvent('keypress', this);
40543         }
40544         
40545         var allowed = "0123456789";
40546         
40547         if(this.allowDecimals){
40548             allowed += this.decimalSeparator;
40549         }
40550         
40551         if(this.allowNegative){
40552             allowed += "-";
40553         }
40554         
40555         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40556         
40557         var keyPress = function(e){
40558             
40559             var k = e.getKey();
40560             
40561             var c = e.getCharCode();
40562             
40563             if(
40564                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40565                     allowed.indexOf(String.fromCharCode(c)) === -1
40566             ){
40567                 e.stopEvent();
40568                 return;
40569             }
40570             
40571             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40572                 return;
40573             }
40574             
40575             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40576                 e.stopEvent();
40577             }
40578         };
40579         
40580         this.inputEl().on("keypress", keyPress, this);
40581         
40582     },
40583     
40584     onTriggerClick : function(e)
40585     {   
40586         if(this.disabled){
40587             return;
40588         }
40589         
40590         this.page = 0;
40591         this.loadNext = false;
40592         
40593         if(this.isExpanded()){
40594             this.collapse();
40595             return;
40596         }
40597         
40598         this.hasFocus = true;
40599         
40600         if(this.triggerAction == 'all') {
40601             this.doQuery(this.allQuery, true);
40602             return;
40603         }
40604         
40605         this.doQuery(this.getRawValue());
40606     },
40607     
40608     getCurrency : function()
40609     {   
40610         var v = this.currencyEl().getValue();
40611         
40612         return v;
40613     },
40614     
40615     restrictHeight : function()
40616     {
40617         this.list.alignTo(this.currencyEl(), this.listAlign);
40618         this.list.alignTo(this.currencyEl(), this.listAlign);
40619     },
40620     
40621     onViewClick : function(view, doFocus, el, e)
40622     {
40623         var index = this.view.getSelectedIndexes()[0];
40624         
40625         var r = this.store.getAt(index);
40626         
40627         if(r){
40628             this.onSelect(r, index);
40629         }
40630     },
40631     
40632     onSelect : function(record, index){
40633         
40634         if(this.fireEvent('beforeselect', this, record, index) !== false){
40635         
40636             this.setFromCurrencyData(index > -1 ? record.data : false);
40637             
40638             this.collapse();
40639             
40640             this.fireEvent('select', this, record, index);
40641         }
40642     },
40643     
40644     setFromCurrencyData : function(o)
40645     {
40646         var currency = '';
40647         
40648         this.lastCurrency = o;
40649         
40650         if (this.currencyField) {
40651             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40652         } else {
40653             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40654         }
40655         
40656         this.lastSelectionText = currency;
40657         
40658         //setting default currency
40659         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40660             this.setCurrency(this.defaultCurrency);
40661             return;
40662         }
40663         
40664         this.setCurrency(currency);
40665     },
40666     
40667     setFromData : function(o)
40668     {
40669         var c = {};
40670         
40671         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40672         
40673         this.setFromCurrencyData(c);
40674         
40675         var value = '';
40676         
40677         if (this.name) {
40678             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40679         } else {
40680             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40681         }
40682         
40683         this.setValue(value);
40684         
40685     },
40686     
40687     setCurrency : function(v)
40688     {   
40689         this.currencyValue = v;
40690         
40691         if(this.rendered){
40692             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40693             this.validate();
40694         }
40695     },
40696     
40697     setValue : function(v)
40698     {
40699         v = this.fixPrecision(v);
40700         
40701         v = String(v).replace(".", this.decimalSeparator);
40702         
40703         this.value = v;
40704         
40705         if(this.rendered){
40706             
40707             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40708             
40709             this.inputEl().dom.value = Roo.util.Format.number(v, this.decimalPrecision, 
40710                 this.thousandsDelimiter || ','
40711             );
40712             
40713             if(this.allowBlank && !v) {
40714                 this.inputEl().dom.value = '';
40715             }
40716             
40717             this.validate();
40718         }
40719     },
40720     
40721     getRawValue : function()
40722     {
40723         var v = this.inputEl().getValue();
40724         
40725         return v;
40726     },
40727     
40728     getValue : function()
40729     {
40730         return this.fixPrecision(this.parseValue(this.getRawValue()));
40731     },
40732     
40733     parseValue : function(value)
40734     {
40735         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40736         return isNaN(value) ? '' : value;
40737     },
40738     
40739     fixPrecision : function(value)
40740     {
40741         var nan = isNaN(value);
40742         
40743         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40744             return nan ? '' : value;
40745         }
40746         
40747         return parseFloat(value).toFixed(this.decimalPrecision);
40748     },
40749     
40750     decimalPrecisionFcn : function(v)
40751     {
40752         return Math.floor(v);
40753     },
40754     
40755     validateValue : function(value)
40756     {
40757         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40758             return false;
40759         }
40760         
40761         var num = this.parseValue(value);
40762         
40763         if(isNaN(num)){
40764             this.markInvalid(String.format(this.nanText, value));
40765             return false;
40766         }
40767         
40768         if(num < this.minValue){
40769             this.markInvalid(String.format(this.minText, this.minValue));
40770             return false;
40771         }
40772         
40773         if(num > this.maxValue){
40774             this.markInvalid(String.format(this.maxText, this.maxValue));
40775             return false;
40776         }
40777         
40778         return true;
40779     },
40780     
40781     validate : function()
40782     {
40783         if(this.disabled || this.allowBlank){
40784             this.markValid();
40785             return true;
40786         }
40787         
40788         var currency = this.getCurrency();
40789         
40790         if(this.validateValue(this.getRawValue()) && currency.length){
40791             this.markValid();
40792             return true;
40793         }
40794         
40795         this.markInvalid();
40796         return false;
40797     },
40798     
40799     getName: function()
40800     {
40801         return this.name;
40802     },
40803     
40804     beforeBlur : function()
40805     {
40806         if(!this.castInt){
40807             return;
40808         }
40809         
40810         var v = this.parseValue(this.getRawValue());
40811         
40812         if(v || v == 0){
40813             this.setValue(v);
40814         }
40815     },
40816     
40817     onBlur : function()
40818     {
40819         this.beforeBlur();
40820         
40821         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40822             //this.el.removeClass(this.focusClass);
40823         }
40824         
40825         this.hasFocus = false;
40826         
40827         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40828             this.validate();
40829         }
40830         
40831         var v = this.getValue();
40832         
40833         if(String(v) !== String(this.startValue)){
40834             this.fireEvent('change', this, v, this.startValue);
40835         }
40836         
40837         this.fireEvent("blur", this);
40838     },
40839     
40840     inputEl : function()
40841     {
40842         return this.el.select('.roo-money-amount-input', true).first();
40843     },
40844     
40845     currencyEl : function()
40846     {
40847         return this.el.select('.roo-money-currency-input', true).first();
40848     },
40849     
40850     hiddenEl : function()
40851     {
40852         return this.el.select('input.hidden-number-input',true).first();
40853     }
40854     
40855 });