Partial Fix #5635 - chome extension for uploading clippings
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = (
7         function() {
8                 var ret=3;
9                 Roo.each(document.styleSheets[0], function(s) {
10                     if (s.href.match(/css-bootstrap4/)) {
11                         ret=4;
12                     }
13                 });
14         return ret;
15 })();/*
16  * - LGPL
17  *
18  * base class for bootstrap elements.
19  * 
20  */
21
22 Roo.bootstrap = Roo.bootstrap || {};
23 /**
24  * @class Roo.bootstrap.Component
25  * @extends Roo.Component
26  * Bootstrap Component base class
27  * @cfg {String} cls css class
28  * @cfg {String} style any extra css
29  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
30  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
31  * @cfg {string} dataId cutomer id
32  * @cfg {string} name Specifies name attribute
33  * @cfg {string} tooltip  Text for the tooltip
34  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
35  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
36  
37  * @constructor
38  * Do not use directly - it does not do anything..
39  * @param {Object} config The config object
40  */
41
42
43
44 Roo.bootstrap.Component = function(config){
45     Roo.bootstrap.Component.superclass.constructor.call(this, config);
46        
47     this.addEvents({
48         /**
49          * @event childrenrendered
50          * Fires when the children have been rendered..
51          * @param {Roo.bootstrap.Component} this
52          */
53         "childrenrendered" : true
54         
55         
56         
57     });
58     
59     
60 };
61
62 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
63     
64     
65     allowDomMove : false, // to stop relocations in parent onRender...
66     
67     cls : false,
68     
69     style : false,
70     
71     autoCreate : false,
72     
73     tooltip : null,
74     /**
75      * Initialize Events for the element
76      */
77     initEvents : function() { },
78     
79     xattr : false,
80     
81     parentId : false,
82     
83     can_build_overlaid : true,
84     
85     container_method : false,
86     
87     dataId : false,
88     
89     name : false,
90     
91     parent: function() {
92         // returns the parent component..
93         return Roo.ComponentMgr.get(this.parentId)
94         
95         
96     },
97     
98     // private
99     onRender : function(ct, position)
100     {
101        // Roo.log("Call onRender: " + this.xtype);
102         
103         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
104         
105         if(this.el){
106             if (this.el.attr('xtype')) {
107                 this.el.attr('xtypex', this.el.attr('xtype'));
108                 this.el.dom.removeAttribute('xtype');
109                 
110                 this.initEvents();
111             }
112             
113             return;
114         }
115         
116          
117         
118         var cfg = Roo.apply({},  this.getAutoCreate());
119         
120         cfg.id = this.id || Roo.id();
121         
122         // fill in the extra attributes 
123         if (this.xattr && typeof(this.xattr) =='object') {
124             for (var i in this.xattr) {
125                 cfg[i] = this.xattr[i];
126             }
127         }
128         
129         if(this.dataId){
130             cfg.dataId = this.dataId;
131         }
132         
133         if (this.cls) {
134             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
135         }
136         
137         if (this.style) { // fixme needs to support more complex style data.
138             cfg.style = this.style;
139         }
140         
141         if(this.name){
142             cfg.name = this.name;
143         }
144         
145         this.el = ct.createChild(cfg, position);
146         
147         if (this.tooltip) {
148             this.tooltipEl().attr('tooltip', this.tooltip);
149         }
150         
151         if(this.tabIndex !== undefined){
152             this.el.dom.setAttribute('tabIndex', this.tabIndex);
153         }
154         
155         this.initEvents();
156         
157     },
158     /**
159      * Fetch the element to add children to
160      * @return {Roo.Element} defaults to this.el
161      */
162     getChildContainer : function()
163     {
164         return this.el;
165     },
166     /**
167      * Fetch the element to display the tooltip on.
168      * @return {Roo.Element} defaults to this.el
169      */
170     tooltipEl : function()
171     {
172         return this.el;
173     },
174         
175     addxtype  : function(tree,cntr)
176     {
177         var cn = this;
178         
179         cn = Roo.factory(tree);
180         //Roo.log(['addxtype', cn]);
181            
182         cn.parentType = this.xtype; //??
183         cn.parentId = this.id;
184         
185         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
186         if (typeof(cn.container_method) == 'string') {
187             cntr = cn.container_method;
188         }
189         
190         
191         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
192         
193         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
194         
195         var build_from_html =  Roo.XComponent.build_from_html;
196           
197         var is_body  = (tree.xtype == 'Body') ;
198           
199         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
200           
201         var self_cntr_el = Roo.get(this[cntr](false));
202         
203         // do not try and build conditional elements 
204         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
205             return false;
206         }
207         
208         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
209             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
210                 return this.addxtypeChild(tree,cntr, is_body);
211             }
212             
213             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
214                 
215             if(echild){
216                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
217             }
218             
219             Roo.log('skipping render');
220             return cn;
221             
222         }
223         
224         var ret = false;
225         if (!build_from_html) {
226             return false;
227         }
228         
229         // this i think handles overlaying multiple children of the same type
230         // with the sam eelement.. - which might be buggy..
231         while (true) {
232             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
233             
234             if (!echild) {
235                 break;
236             }
237             
238             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
239                 break;
240             }
241             
242             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
243         }
244        
245         return ret;
246     },
247     
248     
249     addxtypeChild : function (tree, cntr, is_body)
250     {
251         Roo.debug && Roo.log('addxtypeChild:' + cntr);
252         var cn = this;
253         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
254         
255         
256         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
257                     (typeof(tree['flexy:foreach']) != 'undefined');
258           
259     
260         
261         skip_children = false;
262         // render the element if it's not BODY.
263         if (!is_body) {
264             
265             // if parent was disabled, then do not try and create the children..
266             if(!this[cntr](true)){
267                 tree.items = [];
268                 return tree;
269             }
270            
271             cn = Roo.factory(tree);
272            
273             cn.parentType = this.xtype; //??
274             cn.parentId = this.id;
275             
276             var build_from_html =  Roo.XComponent.build_from_html;
277             
278             
279             // does the container contain child eleemnts with 'xtype' attributes.
280             // that match this xtype..
281             // note - when we render we create these as well..
282             // so we should check to see if body has xtype set.
283             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
284                
285                 var self_cntr_el = Roo.get(this[cntr](false));
286                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
287                 if (echild) { 
288                     //Roo.log(Roo.XComponent.build_from_html);
289                     //Roo.log("got echild:");
290                     //Roo.log(echild);
291                 }
292                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
293                 // and are not displayed -this causes this to use up the wrong element when matching.
294                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
295                 
296                 
297                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
298                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
299                   
300                   
301                   
302                     cn.el = echild;
303                   //  Roo.log("GOT");
304                     //echild.dom.removeAttribute('xtype');
305                 } else {
306                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
307                     Roo.debug && Roo.log(self_cntr_el);
308                     Roo.debug && Roo.log(echild);
309                     Roo.debug && Roo.log(cn);
310                 }
311             }
312            
313             
314            
315             // if object has flexy:if - then it may or may not be rendered.
316             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
317                 // skip a flexy if element.
318                 Roo.debug && Roo.log('skipping render');
319                 Roo.debug && Roo.log(tree);
320                 if (!cn.el) {
321                     Roo.debug && Roo.log('skipping all children');
322                     skip_children = true;
323                 }
324                 
325              } else {
326                  
327                 // actually if flexy:foreach is found, we really want to create 
328                 // multiple copies here...
329                 //Roo.log('render');
330                 //Roo.log(this[cntr]());
331                 // some elements do not have render methods.. like the layouts...
332                 /*
333                 if(this[cntr](true) === false){
334                     cn.items = [];
335                     return cn;
336                 }
337                 */
338                 cn.render && cn.render(this[cntr](true));
339                 
340              }
341             // then add the element..
342         }
343          
344         // handle the kids..
345         
346         var nitems = [];
347         /*
348         if (typeof (tree.menu) != 'undefined') {
349             tree.menu.parentType = cn.xtype;
350             tree.menu.triggerEl = cn.el;
351             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
352             
353         }
354         */
355         if (!tree.items || !tree.items.length) {
356             cn.items = nitems;
357             //Roo.log(["no children", this]);
358             
359             return cn;
360         }
361          
362         var items = tree.items;
363         delete tree.items;
364         
365         //Roo.log(items.length);
366             // add the items..
367         if (!skip_children) {    
368             for(var i =0;i < items.length;i++) {
369               //  Roo.log(['add child', items[i]]);
370                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
371             }
372         }
373         
374         cn.items = nitems;
375         
376         //Roo.log("fire childrenrendered");
377         
378         cn.fireEvent('childrenrendered', this);
379         
380         return cn;
381     },
382     
383     /**
384      * Set the element that will be used to show or hide
385      */
386     setVisibilityEl : function(el)
387     {
388         this.visibilityEl = el;
389     },
390     
391      /**
392      * Get the element that will be used to show or hide
393      */
394     getVisibilityEl : function()
395     {
396         if (typeof(this.visibilityEl) == 'object') {
397             return this.visibilityEl;
398         }
399         
400         if (typeof(this.visibilityEl) == 'string') {
401             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
402         }
403         
404         return this.getEl();
405     },
406     
407     /**
408      * Show a component - removes 'hidden' class
409      */
410     show : function()
411     {
412         if(!this.getVisibilityEl()){
413             return;
414         }
415          
416         this.getVisibilityEl().removeClass(['hidden','d-none']);
417         
418         this.fireEvent('show', this);
419         
420         
421     },
422     /**
423      * Hide a component - adds 'hidden' class
424      */
425     hide: function()
426     {
427         if(!this.getVisibilityEl()){
428             return;
429         }
430         
431         this.getVisibilityEl().addClass(['hidden','d-none']);
432         
433         this.fireEvent('hide', this);
434         
435     }
436 });
437
438  /*
439  * - LGPL
440  *
441  * Body
442  *
443  */
444
445 /**
446  * @class Roo.bootstrap.Body
447  * @extends Roo.bootstrap.Component
448  * Bootstrap Body class
449  *
450  * @constructor
451  * Create a new body
452  * @param {Object} config The config object
453  */
454
455 Roo.bootstrap.Body = function(config){
456
457     config = config || {};
458
459     Roo.bootstrap.Body.superclass.constructor.call(this, config);
460     this.el = Roo.get(config.el ? config.el : document.body );
461     if (this.cls && this.cls.length) {
462         Roo.get(document.body).addClass(this.cls);
463     }
464 };
465
466 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
467
468     is_body : true,// just to make sure it's constructed?
469
470         autoCreate : {
471         cls: 'container'
472     },
473     onRender : function(ct, position)
474     {
475        /* Roo.log("Roo.bootstrap.Body - onRender");
476         if (this.cls && this.cls.length) {
477             Roo.get(document.body).addClass(this.cls);
478         }
479         // style??? xttr???
480         */
481     }
482
483
484
485
486 });
487 /*
488  * - LGPL
489  *
490  * button group
491  * 
492  */
493
494
495 /**
496  * @class Roo.bootstrap.ButtonGroup
497  * @extends Roo.bootstrap.Component
498  * Bootstrap ButtonGroup class
499  * @cfg {String} size lg | sm | xs (default empty normal)
500  * @cfg {String} align vertical | justified  (default none)
501  * @cfg {String} direction up | down (default down)
502  * @cfg {Boolean} toolbar false | true
503  * @cfg {Boolean} btn true | false
504  * 
505  * 
506  * @constructor
507  * Create a new Input
508  * @param {Object} config The config object
509  */
510
511 Roo.bootstrap.ButtonGroup = function(config){
512     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
513 };
514
515 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
516     
517     size: '',
518     align: '',
519     direction: '',
520     toolbar: false,
521     btn: true,
522
523     getAutoCreate : function(){
524         var cfg = {
525             cls: 'btn-group',
526             html : null
527         };
528         
529         cfg.html = this.html || cfg.html;
530         
531         if (this.toolbar) {
532             cfg = {
533                 cls: 'btn-toolbar',
534                 html: null
535             };
536             
537             return cfg;
538         }
539         
540         if (['vertical','justified'].indexOf(this.align)!==-1) {
541             cfg.cls = 'btn-group-' + this.align;
542             
543             if (this.align == 'justified') {
544                 console.log(this.items);
545             }
546         }
547         
548         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
549             cfg.cls += ' btn-group-' + this.size;
550         }
551         
552         if (this.direction == 'up') {
553             cfg.cls += ' dropup' ;
554         }
555         
556         return cfg;
557     },
558     /**
559      * Add a button to the group (similar to NavItem API.)
560      */
561     addItem : function(cfg)
562     {
563         var cn = new Roo.bootstrap.Button(cfg);
564         //this.register(cn);
565         cn.parentId = this.id;
566         cn.onRender(this.el, null);
567         return cn;
568     }
569    
570 });
571
572  /*
573  * - LGPL
574  *
575  * button
576  * 
577  */
578
579 /**
580  * @class Roo.bootstrap.Button
581  * @extends Roo.bootstrap.Component
582  * Bootstrap Button class
583  * @cfg {String} html The button content
584  * @cfg {String} weight (default | primary | secondary | success | info | warning | danger | link ) default
585  * @cfg {String} badge_weight (default | primary | secondary | success | info | warning | danger | link ) default (same as button)
586  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
587  * @cfg {String} size ( lg | sm | xs)
588  * @cfg {String} tag ( a | input | submit)
589  * @cfg {String} href empty or href
590  * @cfg {Boolean} disabled default false;
591  * @cfg {Boolean} isClose default false;
592  * @cfg {String} glyphicon depricated - use fa
593  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
594  * @cfg {String} badge text for badge
595  * @cfg {String} theme (default|glow)  
596  * @cfg {Boolean} inverse dark themed version
597  * @cfg {Boolean} toggle is it a slidy toggle button
598  * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
599  * @cfg {String} ontext text for on slidy toggle state
600  * @cfg {String} offtext text for off slidy toggle state
601  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
602  * @cfg {Boolean} removeClass remove the standard class..
603  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
604  * 
605  * @constructor
606  * Create a new button
607  * @param {Object} config The config object
608  */
609
610
611 Roo.bootstrap.Button = function(config){
612     Roo.bootstrap.Button.superclass.constructor.call(this, config);
613     this.weightClass = ["btn-default btn-outline-secondary", 
614                        "btn-primary", 
615                        "btn-success", 
616                        "btn-info", 
617                        "btn-warning",
618                        "btn-danger",
619                        "btn-link"
620                       ],  
621     this.addEvents({
622         // raw events
623         /**
624          * @event click
625          * When a butotn is pressed
626          * @param {Roo.bootstrap.Button} btn
627          * @param {Roo.EventObject} e
628          */
629         "click" : true,
630          /**
631          * @event toggle
632          * After the button has been toggles
633          * @param {Roo.bootstrap.Button} btn
634          * @param {Roo.EventObject} e
635          * @param {boolean} pressed (also available as button.pressed)
636          */
637         "toggle" : true
638     });
639 };
640
641 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
642     html: false,
643     active: false,
644     weight: '',
645     badge_weight: '',
646     outline : false,
647     size: '',
648     tag: 'button',
649     href: '',
650     disabled: false,
651     isClose: false,
652     glyphicon: '',
653     fa: '',
654     badge: '',
655     theme: 'default',
656     inverse: false,
657     
658     toggle: false,
659     ontext: 'ON',
660     offtext: 'OFF',
661     defaulton: true,
662     preventDefault: true,
663     removeClass: false,
664     name: false,
665     target: false,
666      
667     pressed : null,
668      
669     
670     getAutoCreate : function(){
671         
672         var cfg = {
673             tag : 'button',
674             cls : 'roo-button',
675             html: ''
676         };
677         
678         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
679             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
680             this.tag = 'button';
681         } else {
682             cfg.tag = this.tag;
683         }
684         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
685         
686         if (this.toggle == true) {
687             cfg={
688                 tag: 'div',
689                 cls: 'slider-frame roo-button',
690                 cn: [
691                     {
692                         tag: 'span',
693                         'data-on-text':'ON',
694                         'data-off-text':'OFF',
695                         cls: 'slider-button',
696                         html: this.offtext
697                     }
698                 ]
699             };
700             
701             if (['default', 'secondary' , 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
702                 cfg.cls += ' '+this.weight;
703             }
704             
705             return cfg;
706         }
707         
708         if (this.isClose) {
709             cfg.cls += ' close';
710             
711             cfg["aria-hidden"] = true;
712             
713             cfg.html = "&times;";
714             
715             return cfg;
716         }
717         
718          
719         if (this.theme==='default') {
720             cfg.cls = 'btn roo-button';
721             
722             //if (this.parentType != 'Navbar') {
723             this.weight = this.weight.length ?  this.weight : 'default';
724             //}
725             if (['default', 'primary', 'secondary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
726                 
727                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
728                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
729                 cfg.cls += ' btn-' + outline + weight;
730                 if (this.weight == 'default') {
731                     // BC
732                     cfg.cls += ' btn-' + this.weight;
733                 }
734             }
735         } else if (this.theme==='glow') {
736             
737             cfg.tag = 'a';
738             cfg.cls = 'btn-glow roo-button';
739             
740             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
741                 
742                 cfg.cls += ' ' + this.weight;
743             }
744         }
745    
746         
747         if (this.inverse) {
748             this.cls += ' inverse';
749         }
750         
751         
752         if (this.active || this.pressed === true) {
753             cfg.cls += ' active';
754         }
755         
756         if (this.disabled) {
757             cfg.disabled = 'disabled';
758         }
759         
760         if (this.items) {
761             Roo.log('changing to ul' );
762             cfg.tag = 'ul';
763             this.glyphicon = 'caret';
764             if (Roo.bootstrap.version == 4) {
765                 this.fa = 'caret-down';
766             }
767             
768         }
769         
770         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
771          
772         //gsRoo.log(this.parentType);
773         if (this.parentType === 'Navbar' && !this.parent().bar) {
774             Roo.log('changing to li?');
775             
776             cfg.tag = 'li';
777             
778             cfg.cls = '';
779             cfg.cn =  [{
780                 tag : 'a',
781                 cls : 'roo-button',
782                 html : this.html,
783                 href : this.href || '#'
784             }];
785             if (this.menu) {
786                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
787                 cfg.cls += ' dropdown';
788             }   
789             
790             delete cfg.html;
791             
792         }
793         
794        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
795         
796         if (this.glyphicon) {
797             cfg.html = ' ' + cfg.html;
798             
799             cfg.cn = [
800                 {
801                     tag: 'span',
802                     cls: 'glyphicon glyphicon-' + this.glyphicon
803                 }
804             ];
805         }
806         if (this.fa) {
807             cfg.html = ' ' + cfg.html;
808             
809             cfg.cn = [
810                 {
811                     tag: 'i',
812                     cls: 'fa fas fa-' + this.fa
813                 }
814             ];
815         }
816         
817         if (this.badge) {
818             cfg.html += ' ';
819             
820             cfg.tag = 'a';
821             
822 //            cfg.cls='btn roo-button';
823             
824             cfg.href=this.href;
825             
826             var value = cfg.html;
827             
828             if(this.glyphicon){
829                 value = {
830                     tag: 'span',
831                     cls: 'glyphicon glyphicon-' + this.glyphicon,
832                     html: this.html
833                 };
834             }
835             if(this.fa){
836                 value = {
837                     tag: 'i',
838                     cls: 'fa fas fa-' + this.fa,
839                     html: this.html
840                 };
841             }
842             
843             var bw = this.badge_weight.length ? this.badge_weight :
844                 (this.weight.length ? this.weight : 'secondary');
845             bw = bw == 'default' ? 'secondary' : bw;
846             
847             cfg.cn = [
848                 value,
849                 {
850                     tag: 'span',
851                     cls: 'badge badge-' + bw,
852                     html: this.badge
853                 }
854             ];
855             
856             cfg.html='';
857         }
858         
859         if (this.menu) {
860             cfg.cls += ' dropdown';
861             cfg.html = typeof(cfg.html) != 'undefined' ?
862                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
863         }
864         
865         if (cfg.tag !== 'a' && this.href !== '') {
866             throw "Tag must be a to set href.";
867         } else if (this.href.length > 0) {
868             cfg.href = this.href;
869         }
870         
871         if(this.removeClass){
872             cfg.cls = '';
873         }
874         
875         if(this.target){
876             cfg.target = this.target;
877         }
878         
879         return cfg;
880     },
881     initEvents: function() {
882        // Roo.log('init events?');
883 //        Roo.log(this.el.dom);
884         // add the menu...
885         
886         if (typeof (this.menu) != 'undefined') {
887             this.menu.parentType = this.xtype;
888             this.menu.triggerEl = this.el;
889             this.addxtype(Roo.apply({}, this.menu));
890         }
891
892
893        if (this.el.hasClass('roo-button')) {
894             this.el.on('click', this.onClick, this);
895        } else {
896             this.el.select('.roo-button').on('click', this.onClick, this);
897        }
898        
899        if(this.removeClass){
900            this.el.on('click', this.onClick, this);
901        }
902        
903        this.el.enableDisplayMode();
904         
905     },
906     onClick : function(e)
907     {
908         if (this.disabled) {
909             return;
910         }
911         
912         Roo.log('button on click ');
913         if(this.preventDefault){
914             e.preventDefault();
915         }
916         
917         if (this.pressed === true || this.pressed === false) {
918             this.toggleActive(e);
919         }
920         
921         
922         this.fireEvent('click', this, e);
923     },
924     
925     /**
926      * Enables this button
927      */
928     enable : function()
929     {
930         this.disabled = false;
931         this.el.removeClass('disabled');
932     },
933     
934     /**
935      * Disable this button
936      */
937     disable : function()
938     {
939         this.disabled = true;
940         this.el.addClass('disabled');
941     },
942      /**
943      * sets the active state on/off, 
944      * @param {Boolean} state (optional) Force a particular state
945      */
946     setActive : function(v) {
947         
948         this.el[v ? 'addClass' : 'removeClass']('active');
949         this.pressed = v;
950     },
951      /**
952      * toggles the current active state 
953      */
954     toggleActive : function(e)
955     {
956         this.setActive(!this.pressed);
957         this.fireEvent('toggle', this, e, !this.pressed);
958     },
959      /**
960      * get the current active state
961      * @return {boolean} true if it's active
962      */
963     isActive : function()
964     {
965         return this.el.hasClass('active');
966     },
967     /**
968      * set the text of the first selected button
969      */
970     setText : function(str)
971     {
972         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
973     },
974     /**
975      * get the text of the first selected button
976      */
977     getText : function()
978     {
979         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
980     },
981     
982     setWeight : function(str)
983     {
984         this.el.removeClass(this.weightClass);
985         this.weight = str;
986         var outline = this.outline ? 'outline-' : '';
987         if (str == 'default') {
988             this.el.addClass('btn-default btn-outline-secondary');        
989             return;
990         }
991         this.el.addClass('btn-' + outline + str);        
992     }
993     
994     
995 });
996
997  /*
998  * - LGPL
999  *
1000  * column
1001  * 
1002  */
1003
1004 /**
1005  * @class Roo.bootstrap.Column
1006  * @extends Roo.bootstrap.Component
1007  * Bootstrap Column class
1008  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1009  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1010  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1011  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1012  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1013  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1014  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1015  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1016  *
1017  * 
1018  * @cfg {Boolean} hidden (true|false) hide the element
1019  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1020  * @cfg {String} fa (ban|check|...) font awesome icon
1021  * @cfg {Number} fasize (1|2|....) font awsome size
1022
1023  * @cfg {String} icon (info-sign|check|...) glyphicon name
1024
1025  * @cfg {String} html content of column.
1026  * 
1027  * @constructor
1028  * Create a new Column
1029  * @param {Object} config The config object
1030  */
1031
1032 Roo.bootstrap.Column = function(config){
1033     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1034 };
1035
1036 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1037     
1038     xs: false,
1039     sm: false,
1040     md: false,
1041     lg: false,
1042     xsoff: false,
1043     smoff: false,
1044     mdoff: false,
1045     lgoff: false,
1046     html: '',
1047     offset: 0,
1048     alert: false,
1049     fa: false,
1050     icon : false,
1051     hidden : false,
1052     fasize : 1,
1053     
1054     getAutoCreate : function(){
1055         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1056         
1057         cfg = {
1058             tag: 'div',
1059             cls: 'column'
1060         };
1061         
1062         var settings=this;
1063         ['xs','sm','md','lg'].map(function(size){
1064             //Roo.log( size + ':' + settings[size]);
1065             
1066             if (settings[size+'off'] !== false) {
1067                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1068             }
1069             
1070             if (settings[size] === false) {
1071                 return;
1072             }
1073             
1074             if (!settings[size]) { // 0 = hidden
1075                 cfg.cls += ' hidden-' + size;
1076                 return;
1077             }
1078             cfg.cls += ' col-' + size + '-' + settings[size];
1079             
1080         });
1081         
1082         if (this.hidden) {
1083             cfg.cls += ' hidden';
1084         }
1085         
1086         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1087             cfg.cls +=' alert alert-' + this.alert;
1088         }
1089         
1090         
1091         if (this.html.length) {
1092             cfg.html = this.html;
1093         }
1094         if (this.fa) {
1095             var fasize = '';
1096             if (this.fasize > 1) {
1097                 fasize = ' fa-' + this.fasize + 'x';
1098             }
1099             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1100             
1101             
1102         }
1103         if (this.icon) {
1104             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1105         }
1106         
1107         return cfg;
1108     }
1109    
1110 });
1111
1112  
1113
1114  /*
1115  * - LGPL
1116  *
1117  * page container.
1118  * 
1119  */
1120
1121
1122 /**
1123  * @class Roo.bootstrap.Container
1124  * @extends Roo.bootstrap.Component
1125  * Bootstrap Container class
1126  * @cfg {Boolean} jumbotron is it a jumbotron element
1127  * @cfg {String} html content of element
1128  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1129  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1130  * @cfg {String} header content of header (for panel)
1131  * @cfg {String} footer content of footer (for panel)
1132  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1133  * @cfg {String} tag (header|aside|section) type of HTML tag.
1134  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1135  * @cfg {String} fa font awesome icon
1136  * @cfg {String} icon (info-sign|check|...) glyphicon name
1137  * @cfg {Boolean} hidden (true|false) hide the element
1138  * @cfg {Boolean} expandable (true|false) default false
1139  * @cfg {Boolean} expanded (true|false) default true
1140  * @cfg {String} rheader contet on the right of header
1141  * @cfg {Boolean} clickable (true|false) default false
1142
1143  *     
1144  * @constructor
1145  * Create a new Container
1146  * @param {Object} config The config object
1147  */
1148
1149 Roo.bootstrap.Container = function(config){
1150     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1151     
1152     this.addEvents({
1153         // raw events
1154          /**
1155          * @event expand
1156          * After the panel has been expand
1157          * 
1158          * @param {Roo.bootstrap.Container} this
1159          */
1160         "expand" : true,
1161         /**
1162          * @event collapse
1163          * After the panel has been collapsed
1164          * 
1165          * @param {Roo.bootstrap.Container} this
1166          */
1167         "collapse" : true,
1168         /**
1169          * @event click
1170          * When a element is chick
1171          * @param {Roo.bootstrap.Container} this
1172          * @param {Roo.EventObject} e
1173          */
1174         "click" : true
1175     });
1176 };
1177
1178 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1179     
1180     jumbotron : false,
1181     well: '',
1182     panel : '',
1183     header: '',
1184     footer : '',
1185     sticky: '',
1186     tag : false,
1187     alert : false,
1188     fa: false,
1189     icon : false,
1190     expandable : false,
1191     rheader : '',
1192     expanded : true,
1193     clickable: false,
1194   
1195      
1196     getChildContainer : function() {
1197         
1198         if(!this.el){
1199             return false;
1200         }
1201         
1202         if (this.panel.length) {
1203             return this.el.select('.panel-body',true).first();
1204         }
1205         
1206         return this.el;
1207     },
1208     
1209     
1210     getAutoCreate : function(){
1211         
1212         var cfg = {
1213             tag : this.tag || 'div',
1214             html : '',
1215             cls : ''
1216         };
1217         if (this.jumbotron) {
1218             cfg.cls = 'jumbotron';
1219         }
1220         
1221         
1222         
1223         // - this is applied by the parent..
1224         //if (this.cls) {
1225         //    cfg.cls = this.cls + '';
1226         //}
1227         
1228         if (this.sticky.length) {
1229             
1230             var bd = Roo.get(document.body);
1231             if (!bd.hasClass('bootstrap-sticky')) {
1232                 bd.addClass('bootstrap-sticky');
1233                 Roo.select('html',true).setStyle('height', '100%');
1234             }
1235              
1236             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1237         }
1238         
1239         
1240         if (this.well.length) {
1241             switch (this.well) {
1242                 case 'lg':
1243                 case 'sm':
1244                     cfg.cls +=' well well-' +this.well;
1245                     break;
1246                 default:
1247                     cfg.cls +=' well';
1248                     break;
1249             }
1250         }
1251         
1252         if (this.hidden) {
1253             cfg.cls += ' hidden';
1254         }
1255         
1256         
1257         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1258             cfg.cls +=' alert alert-' + this.alert;
1259         }
1260         
1261         var body = cfg;
1262         
1263         if (this.panel.length) {
1264             cfg.cls += ' panel panel-' + this.panel;
1265             cfg.cn = [];
1266             if (this.header.length) {
1267                 
1268                 var h = [];
1269                 
1270                 if(this.expandable){
1271                     
1272                     cfg.cls = cfg.cls + ' expandable';
1273                     
1274                     h.push({
1275                         tag: 'i',
1276                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1277                     });
1278                     
1279                 }
1280                 
1281                 h.push(
1282                     {
1283                         tag: 'span',
1284                         cls : 'panel-title',
1285                         html : (this.expandable ? '&nbsp;' : '') + this.header
1286                     },
1287                     {
1288                         tag: 'span',
1289                         cls: 'panel-header-right',
1290                         html: this.rheader
1291                     }
1292                 );
1293                 
1294                 cfg.cn.push({
1295                     cls : 'panel-heading',
1296                     style : this.expandable ? 'cursor: pointer' : '',
1297                     cn : h
1298                 });
1299                 
1300             }
1301             
1302             body = false;
1303             cfg.cn.push({
1304                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1305                 html : this.html
1306             });
1307             
1308             
1309             if (this.footer.length) {
1310                 cfg.cn.push({
1311                     cls : 'panel-footer',
1312                     html : this.footer
1313                     
1314                 });
1315             }
1316             
1317         }
1318         
1319         if (body) {
1320             body.html = this.html || cfg.html;
1321             // prefix with the icons..
1322             if (this.fa) {
1323                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1324             }
1325             if (this.icon) {
1326                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1327             }
1328             
1329             
1330         }
1331         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1332             cfg.cls =  'container';
1333         }
1334         
1335         return cfg;
1336     },
1337     
1338     initEvents: function() 
1339     {
1340         if(this.expandable){
1341             var headerEl = this.headerEl();
1342         
1343             if(headerEl){
1344                 headerEl.on('click', this.onToggleClick, this);
1345             }
1346         }
1347         
1348         if(this.clickable){
1349             this.el.on('click', this.onClick, this);
1350         }
1351         
1352     },
1353     
1354     onToggleClick : function()
1355     {
1356         var headerEl = this.headerEl();
1357         
1358         if(!headerEl){
1359             return;
1360         }
1361         
1362         if(this.expanded){
1363             this.collapse();
1364             return;
1365         }
1366         
1367         this.expand();
1368     },
1369     
1370     expand : function()
1371     {
1372         if(this.fireEvent('expand', this)) {
1373             
1374             this.expanded = true;
1375             
1376             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1377             
1378             this.el.select('.panel-body',true).first().removeClass('hide');
1379             
1380             var toggleEl = this.toggleEl();
1381
1382             if(!toggleEl){
1383                 return;
1384             }
1385
1386             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1387         }
1388         
1389     },
1390     
1391     collapse : function()
1392     {
1393         if(this.fireEvent('collapse', this)) {
1394             
1395             this.expanded = false;
1396             
1397             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1398             this.el.select('.panel-body',true).first().addClass('hide');
1399         
1400             var toggleEl = this.toggleEl();
1401
1402             if(!toggleEl){
1403                 return;
1404             }
1405
1406             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1407         }
1408     },
1409     
1410     toggleEl : function()
1411     {
1412         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1413             return;
1414         }
1415         
1416         return this.el.select('.panel-heading .fa',true).first();
1417     },
1418     
1419     headerEl : function()
1420     {
1421         if(!this.el || !this.panel.length || !this.header.length){
1422             return;
1423         }
1424         
1425         return this.el.select('.panel-heading',true).first()
1426     },
1427     
1428     bodyEl : function()
1429     {
1430         if(!this.el || !this.panel.length){
1431             return;
1432         }
1433         
1434         return this.el.select('.panel-body',true).first()
1435     },
1436     
1437     titleEl : function()
1438     {
1439         if(!this.el || !this.panel.length || !this.header.length){
1440             return;
1441         }
1442         
1443         return this.el.select('.panel-title',true).first();
1444     },
1445     
1446     setTitle : function(v)
1447     {
1448         var titleEl = this.titleEl();
1449         
1450         if(!titleEl){
1451             return;
1452         }
1453         
1454         titleEl.dom.innerHTML = v;
1455     },
1456     
1457     getTitle : function()
1458     {
1459         
1460         var titleEl = this.titleEl();
1461         
1462         if(!titleEl){
1463             return '';
1464         }
1465         
1466         return titleEl.dom.innerHTML;
1467     },
1468     
1469     setRightTitle : function(v)
1470     {
1471         var t = this.el.select('.panel-header-right',true).first();
1472         
1473         if(!t){
1474             return;
1475         }
1476         
1477         t.dom.innerHTML = v;
1478     },
1479     
1480     onClick : function(e)
1481     {
1482         e.preventDefault();
1483         
1484         this.fireEvent('click', this, e);
1485     }
1486 });
1487
1488  /*
1489  * - LGPL
1490  *
1491  * image
1492  * 
1493  */
1494
1495
1496 /**
1497  * @class Roo.bootstrap.Img
1498  * @extends Roo.bootstrap.Component
1499  * Bootstrap Img class
1500  * @cfg {Boolean} imgResponsive false | true
1501  * @cfg {String} border rounded | circle | thumbnail
1502  * @cfg {String} src image source
1503  * @cfg {String} alt image alternative text
1504  * @cfg {String} href a tag href
1505  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1506  * @cfg {String} xsUrl xs image source
1507  * @cfg {String} smUrl sm image source
1508  * @cfg {String} mdUrl md image source
1509  * @cfg {String} lgUrl lg image source
1510  * 
1511  * @constructor
1512  * Create a new Input
1513  * @param {Object} config The config object
1514  */
1515
1516 Roo.bootstrap.Img = function(config){
1517     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1518     
1519     this.addEvents({
1520         // img events
1521         /**
1522          * @event click
1523          * The img click event for the img.
1524          * @param {Roo.EventObject} e
1525          */
1526         "click" : true
1527     });
1528 };
1529
1530 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1531     
1532     imgResponsive: true,
1533     border: '',
1534     src: 'about:blank',
1535     href: false,
1536     target: false,
1537     xsUrl: '',
1538     smUrl: '',
1539     mdUrl: '',
1540     lgUrl: '',
1541
1542     getAutoCreate : function()
1543     {   
1544         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1545             return this.createSingleImg();
1546         }
1547         
1548         var cfg = {
1549             tag: 'div',
1550             cls: 'roo-image-responsive-group',
1551             cn: []
1552         };
1553         var _this = this;
1554         
1555         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1556             
1557             if(!_this[size + 'Url']){
1558                 return;
1559             }
1560             
1561             var img = {
1562                 tag: 'img',
1563                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1564                 html: _this.html || cfg.html,
1565                 src: _this[size + 'Url']
1566             };
1567             
1568             img.cls += ' roo-image-responsive-' + size;
1569             
1570             var s = ['xs', 'sm', 'md', 'lg'];
1571             
1572             s.splice(s.indexOf(size), 1);
1573             
1574             Roo.each(s, function(ss){
1575                 img.cls += ' hidden-' + ss;
1576             });
1577             
1578             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1579                 cfg.cls += ' img-' + _this.border;
1580             }
1581             
1582             if(_this.alt){
1583                 cfg.alt = _this.alt;
1584             }
1585             
1586             if(_this.href){
1587                 var a = {
1588                     tag: 'a',
1589                     href: _this.href,
1590                     cn: [
1591                         img
1592                     ]
1593                 };
1594
1595                 if(this.target){
1596                     a.target = _this.target;
1597                 }
1598             }
1599             
1600             cfg.cn.push((_this.href) ? a : img);
1601             
1602         });
1603         
1604         return cfg;
1605     },
1606     
1607     createSingleImg : function()
1608     {
1609         var cfg = {
1610             tag: 'img',
1611             cls: (this.imgResponsive) ? 'img-responsive' : '',
1612             html : null,
1613             src : 'about:blank'  // just incase src get's set to undefined?!?
1614         };
1615         
1616         cfg.html = this.html || cfg.html;
1617         
1618         cfg.src = this.src || cfg.src;
1619         
1620         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1621             cfg.cls += ' img-' + this.border;
1622         }
1623         
1624         if(this.alt){
1625             cfg.alt = this.alt;
1626         }
1627         
1628         if(this.href){
1629             var a = {
1630                 tag: 'a',
1631                 href: this.href,
1632                 cn: [
1633                     cfg
1634                 ]
1635             };
1636             
1637             if(this.target){
1638                 a.target = this.target;
1639             }
1640             
1641         }
1642         
1643         return (this.href) ? a : cfg;
1644     },
1645     
1646     initEvents: function() 
1647     {
1648         if(!this.href){
1649             this.el.on('click', this.onClick, this);
1650         }
1651         
1652     },
1653     
1654     onClick : function(e)
1655     {
1656         Roo.log('img onclick');
1657         this.fireEvent('click', this, e);
1658     },
1659     /**
1660      * Sets the url of the image - used to update it
1661      * @param {String} url the url of the image
1662      */
1663     
1664     setSrc : function(url)
1665     {
1666         this.src =  url;
1667         
1668         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1669             this.el.dom.src =  url;
1670             return;
1671         }
1672         
1673         this.el.select('img', true).first().dom.src =  url;
1674     }
1675     
1676     
1677    
1678 });
1679
1680  /*
1681  * - LGPL
1682  *
1683  * image
1684  * 
1685  */
1686
1687
1688 /**
1689  * @class Roo.bootstrap.Link
1690  * @extends Roo.bootstrap.Component
1691  * Bootstrap Link Class
1692  * @cfg {String} alt image alternative text
1693  * @cfg {String} href a tag href
1694  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1695  * @cfg {String} html the content of the link.
1696  * @cfg {String} anchor name for the anchor link
1697  * @cfg {String} fa - favicon
1698
1699  * @cfg {Boolean} preventDefault (true | false) default false
1700
1701  * 
1702  * @constructor
1703  * Create a new Input
1704  * @param {Object} config The config object
1705  */
1706
1707 Roo.bootstrap.Link = function(config){
1708     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1709     
1710     this.addEvents({
1711         // img events
1712         /**
1713          * @event click
1714          * The img click event for the img.
1715          * @param {Roo.EventObject} e
1716          */
1717         "click" : true
1718     });
1719 };
1720
1721 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1722     
1723     href: false,
1724     target: false,
1725     preventDefault: false,
1726     anchor : false,
1727     alt : false,
1728     fa: false,
1729
1730
1731     getAutoCreate : function()
1732     {
1733         var html = this.html || '';
1734         
1735         if (this.fa !== false) {
1736             html = '<i class="fa fa-' + this.fa + '"></i>';
1737         }
1738         var cfg = {
1739             tag: 'a'
1740         };
1741         // anchor's do not require html/href...
1742         if (this.anchor === false) {
1743             cfg.html = html;
1744             cfg.href = this.href || '#';
1745         } else {
1746             cfg.name = this.anchor;
1747             if (this.html !== false || this.fa !== false) {
1748                 cfg.html = html;
1749             }
1750             if (this.href !== false) {
1751                 cfg.href = this.href;
1752             }
1753         }
1754         
1755         if(this.alt !== false){
1756             cfg.alt = this.alt;
1757         }
1758         
1759         
1760         if(this.target !== false) {
1761             cfg.target = this.target;
1762         }
1763         
1764         return cfg;
1765     },
1766     
1767     initEvents: function() {
1768         
1769         if(!this.href || this.preventDefault){
1770             this.el.on('click', this.onClick, this);
1771         }
1772     },
1773     
1774     onClick : function(e)
1775     {
1776         if(this.preventDefault){
1777             e.preventDefault();
1778         }
1779         //Roo.log('img onclick');
1780         this.fireEvent('click', this, e);
1781     }
1782    
1783 });
1784
1785  /*
1786  * - LGPL
1787  *
1788  * header
1789  * 
1790  */
1791
1792 /**
1793  * @class Roo.bootstrap.Header
1794  * @extends Roo.bootstrap.Component
1795  * Bootstrap Header class
1796  * @cfg {String} html content of header
1797  * @cfg {Number} level (1|2|3|4|5|6) default 1
1798  * 
1799  * @constructor
1800  * Create a new Header
1801  * @param {Object} config The config object
1802  */
1803
1804
1805 Roo.bootstrap.Header  = function(config){
1806     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1807 };
1808
1809 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1810     
1811     //href : false,
1812     html : false,
1813     level : 1,
1814     
1815     
1816     
1817     getAutoCreate : function(){
1818         
1819         
1820         
1821         var cfg = {
1822             tag: 'h' + (1 *this.level),
1823             html: this.html || ''
1824         } ;
1825         
1826         return cfg;
1827     }
1828    
1829 });
1830
1831  
1832
1833  /*
1834  * Based on:
1835  * Ext JS Library 1.1.1
1836  * Copyright(c) 2006-2007, Ext JS, LLC.
1837  *
1838  * Originally Released Under LGPL - original licence link has changed is not relivant.
1839  *
1840  * Fork - LGPL
1841  * <script type="text/javascript">
1842  */
1843  
1844 /**
1845  * @class Roo.bootstrap.MenuMgr
1846  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1847  * @singleton
1848  */
1849 Roo.bootstrap.MenuMgr = function(){
1850    var menus, active, groups = {}, attached = false, lastShow = new Date();
1851
1852    // private - called when first menu is created
1853    function init(){
1854        menus = {};
1855        active = new Roo.util.MixedCollection();
1856        Roo.get(document).addKeyListener(27, function(){
1857            if(active.length > 0){
1858                hideAll();
1859            }
1860        });
1861    }
1862
1863    // private
1864    function hideAll(){
1865        if(active && active.length > 0){
1866            var c = active.clone();
1867            c.each(function(m){
1868                m.hide();
1869            });
1870        }
1871    }
1872
1873    // private
1874    function onHide(m){
1875        active.remove(m);
1876        if(active.length < 1){
1877            Roo.get(document).un("mouseup", onMouseDown);
1878             
1879            attached = false;
1880        }
1881    }
1882
1883    // private
1884    function onShow(m){
1885        var last = active.last();
1886        lastShow = new Date();
1887        active.add(m);
1888        if(!attached){
1889           Roo.get(document).on("mouseup", onMouseDown);
1890            
1891            attached = true;
1892        }
1893        if(m.parentMenu){
1894           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1895           m.parentMenu.activeChild = m;
1896        }else if(last && last.isVisible()){
1897           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1898        }
1899    }
1900
1901    // private
1902    function onBeforeHide(m){
1903        if(m.activeChild){
1904            m.activeChild.hide();
1905        }
1906        if(m.autoHideTimer){
1907            clearTimeout(m.autoHideTimer);
1908            delete m.autoHideTimer;
1909        }
1910    }
1911
1912    // private
1913    function onBeforeShow(m){
1914        var pm = m.parentMenu;
1915        if(!pm && !m.allowOtherMenus){
1916            hideAll();
1917        }else if(pm && pm.activeChild && active != m){
1918            pm.activeChild.hide();
1919        }
1920    }
1921
1922    // private this should really trigger on mouseup..
1923    function onMouseDown(e){
1924         Roo.log("on Mouse Up");
1925         
1926         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1927             Roo.log("MenuManager hideAll");
1928             hideAll();
1929             e.stopEvent();
1930         }
1931         
1932         
1933    }
1934
1935    // private
1936    function onBeforeCheck(mi, state){
1937        if(state){
1938            var g = groups[mi.group];
1939            for(var i = 0, l = g.length; i < l; i++){
1940                if(g[i] != mi){
1941                    g[i].setChecked(false);
1942                }
1943            }
1944        }
1945    }
1946
1947    return {
1948
1949        /**
1950         * Hides all menus that are currently visible
1951         */
1952        hideAll : function(){
1953             hideAll();  
1954        },
1955
1956        // private
1957        register : function(menu){
1958            if(!menus){
1959                init();
1960            }
1961            menus[menu.id] = menu;
1962            menu.on("beforehide", onBeforeHide);
1963            menu.on("hide", onHide);
1964            menu.on("beforeshow", onBeforeShow);
1965            menu.on("show", onShow);
1966            var g = menu.group;
1967            if(g && menu.events["checkchange"]){
1968                if(!groups[g]){
1969                    groups[g] = [];
1970                }
1971                groups[g].push(menu);
1972                menu.on("checkchange", onCheck);
1973            }
1974        },
1975
1976         /**
1977          * Returns a {@link Roo.menu.Menu} object
1978          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1979          * be used to generate and return a new Menu instance.
1980          */
1981        get : function(menu){
1982            if(typeof menu == "string"){ // menu id
1983                return menus[menu];
1984            }else if(menu.events){  // menu instance
1985                return menu;
1986            }
1987            /*else if(typeof menu.length == 'number'){ // array of menu items?
1988                return new Roo.bootstrap.Menu({items:menu});
1989            }else{ // otherwise, must be a config
1990                return new Roo.bootstrap.Menu(menu);
1991            }
1992            */
1993            return false;
1994        },
1995
1996        // private
1997        unregister : function(menu){
1998            delete menus[menu.id];
1999            menu.un("beforehide", onBeforeHide);
2000            menu.un("hide", onHide);
2001            menu.un("beforeshow", onBeforeShow);
2002            menu.un("show", onShow);
2003            var g = menu.group;
2004            if(g && menu.events["checkchange"]){
2005                groups[g].remove(menu);
2006                menu.un("checkchange", onCheck);
2007            }
2008        },
2009
2010        // private
2011        registerCheckable : function(menuItem){
2012            var g = menuItem.group;
2013            if(g){
2014                if(!groups[g]){
2015                    groups[g] = [];
2016                }
2017                groups[g].push(menuItem);
2018                menuItem.on("beforecheckchange", onBeforeCheck);
2019            }
2020        },
2021
2022        // private
2023        unregisterCheckable : function(menuItem){
2024            var g = menuItem.group;
2025            if(g){
2026                groups[g].remove(menuItem);
2027                menuItem.un("beforecheckchange", onBeforeCheck);
2028            }
2029        }
2030    };
2031 }();/*
2032  * - LGPL
2033  *
2034  * menu
2035  * 
2036  */
2037
2038 /**
2039  * @class Roo.bootstrap.Menu
2040  * @extends Roo.bootstrap.Component
2041  * Bootstrap Menu class - container for MenuItems
2042  * @cfg {String} type (dropdown|treeview|submenu) type of menu
2043  * @cfg {bool} hidden  if the menu should be hidden when rendered.
2044  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
2045  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
2046  * 
2047  * @constructor
2048  * Create a new Menu
2049  * @param {Object} config The config object
2050  */
2051
2052
2053 Roo.bootstrap.Menu = function(config){
2054     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2055     if (this.registerMenu && this.type != 'treeview')  {
2056         Roo.bootstrap.MenuMgr.register(this);
2057     }
2058     
2059     
2060     this.addEvents({
2061         /**
2062          * @event beforeshow
2063          * Fires before this menu is displayed
2064          * @param {Roo.menu.Menu} this
2065          */
2066         beforeshow : true,
2067         /**
2068          * @event beforehide
2069          * Fires before this menu is hidden
2070          * @param {Roo.menu.Menu} this
2071          */
2072         beforehide : true,
2073         /**
2074          * @event show
2075          * Fires after this menu is displayed
2076          * @param {Roo.menu.Menu} this
2077          */
2078         show : true,
2079         /**
2080          * @event hide
2081          * Fires after this menu is hidden
2082          * @param {Roo.menu.Menu} this
2083          */
2084         hide : true,
2085         /**
2086          * @event click
2087          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2088          * @param {Roo.menu.Menu} this
2089          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2090          * @param {Roo.EventObject} e
2091          */
2092         click : true,
2093         /**
2094          * @event mouseover
2095          * Fires when the mouse is hovering over this menu
2096          * @param {Roo.menu.Menu} this
2097          * @param {Roo.EventObject} e
2098          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2099          */
2100         mouseover : true,
2101         /**
2102          * @event mouseout
2103          * Fires when the mouse exits this menu
2104          * @param {Roo.menu.Menu} this
2105          * @param {Roo.EventObject} e
2106          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2107          */
2108         mouseout : true,
2109         /**
2110          * @event itemclick
2111          * Fires when a menu item contained in this menu is clicked
2112          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2113          * @param {Roo.EventObject} e
2114          */
2115         itemclick: true
2116     });
2117     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2118 };
2119
2120 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2121     
2122    /// html : false,
2123     //align : '',
2124     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2125     type: false,
2126     /**
2127      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2128      */
2129     registerMenu : true,
2130     
2131     menuItems :false, // stores the menu items..
2132     
2133     hidden:true,
2134         
2135     parentMenu : false,
2136     
2137     stopEvent : true,
2138     
2139     isLink : false,
2140     
2141     getChildContainer : function() {
2142         return this.el;  
2143     },
2144     
2145     getAutoCreate : function(){
2146          
2147         //if (['right'].indexOf(this.align)!==-1) {
2148         //    cfg.cn[1].cls += ' pull-right'
2149         //}
2150         
2151         
2152         var cfg = {
2153             tag : 'ul',
2154             cls : 'dropdown-menu' ,
2155             style : 'z-index:1000'
2156             
2157         };
2158         
2159         if (this.type === 'submenu') {
2160             cfg.cls = 'submenu active';
2161         }
2162         if (this.type === 'treeview') {
2163             cfg.cls = 'treeview-menu';
2164         }
2165         
2166         return cfg;
2167     },
2168     initEvents : function() {
2169         
2170        // Roo.log("ADD event");
2171        // Roo.log(this.triggerEl.dom);
2172         
2173         this.triggerEl.on('click', this.onTriggerClick, this);
2174         
2175         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2176         
2177         
2178         if (this.triggerEl.hasClass('nav-item')) {
2179             // dropdown toggle on the 'a' in BS4?
2180             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
2181         } else {
2182             this.triggerEl.addClass('dropdown-toggle');
2183         }
2184         if (Roo.isTouch) {
2185             this.el.on('touchstart'  , this.onTouch, this);
2186         }
2187         this.el.on('click' , this.onClick, this);
2188
2189         this.el.on("mouseover", this.onMouseOver, this);
2190         this.el.on("mouseout", this.onMouseOut, this);
2191         
2192     },
2193     
2194     findTargetItem : function(e)
2195     {
2196         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2197         if(!t){
2198             return false;
2199         }
2200         //Roo.log(t);         Roo.log(t.id);
2201         if(t && t.id){
2202             //Roo.log(this.menuitems);
2203             return this.menuitems.get(t.id);
2204             
2205             //return this.items.get(t.menuItemId);
2206         }
2207         
2208         return false;
2209     },
2210     
2211     onTouch : function(e) 
2212     {
2213         Roo.log("menu.onTouch");
2214         //e.stopEvent(); this make the user popdown broken
2215         this.onClick(e);
2216     },
2217     
2218     onClick : function(e)
2219     {
2220         Roo.log("menu.onClick");
2221         
2222         var t = this.findTargetItem(e);
2223         if(!t || t.isContainer){
2224             return;
2225         }
2226         Roo.log(e);
2227         /*
2228         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2229             if(t == this.activeItem && t.shouldDeactivate(e)){
2230                 this.activeItem.deactivate();
2231                 delete this.activeItem;
2232                 return;
2233             }
2234             if(t.canActivate){
2235                 this.setActiveItem(t, true);
2236             }
2237             return;
2238             
2239             
2240         }
2241         */
2242        
2243         Roo.log('pass click event');
2244         
2245         t.onClick(e);
2246         
2247         this.fireEvent("click", this, t, e);
2248         
2249         var _this = this;
2250         
2251         if(!t.href.length || t.href == '#'){
2252             (function() { _this.hide(); }).defer(100);
2253         }
2254         
2255     },
2256     
2257     onMouseOver : function(e){
2258         var t  = this.findTargetItem(e);
2259         //Roo.log(t);
2260         //if(t){
2261         //    if(t.canActivate && !t.disabled){
2262         //        this.setActiveItem(t, true);
2263         //    }
2264         //}
2265         
2266         this.fireEvent("mouseover", this, e, t);
2267     },
2268     isVisible : function(){
2269         return !this.hidden;
2270     },
2271      onMouseOut : function(e){
2272         var t  = this.findTargetItem(e);
2273         
2274         //if(t ){
2275         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2276         //        this.activeItem.deactivate();
2277         //        delete this.activeItem;
2278         //    }
2279         //}
2280         this.fireEvent("mouseout", this, e, t);
2281     },
2282     
2283     
2284     /**
2285      * Displays this menu relative to another element
2286      * @param {String/HTMLElement/Roo.Element} element The element to align to
2287      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2288      * the element (defaults to this.defaultAlign)
2289      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2290      */
2291     show : function(el, pos, parentMenu){
2292         this.parentMenu = parentMenu;
2293         if(!this.el){
2294             this.render();
2295         }
2296         this.fireEvent("beforeshow", this);
2297         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2298     },
2299      /**
2300      * Displays this menu at a specific xy position
2301      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2302      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2303      */
2304     showAt : function(xy, parentMenu, /* private: */_e){
2305         this.parentMenu = parentMenu;
2306         if(!this.el){
2307             this.render();
2308         }
2309         if(_e !== false){
2310             this.fireEvent("beforeshow", this);
2311             //xy = this.el.adjustForConstraints(xy);
2312         }
2313         
2314         //this.el.show();
2315         this.hideMenuItems();
2316         this.hidden = false;
2317         this.triggerEl.addClass('open');
2318         this.el.addClass('show');
2319         
2320         // reassign x when hitting right
2321         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2322             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2323         }
2324         
2325         // reassign y when hitting bottom
2326         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2327             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2328         }
2329         
2330         // but the list may align on trigger left or trigger top... should it be a properity?
2331         
2332         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2333             this.el.setXY(xy);
2334         }
2335         
2336         this.focus();
2337         this.fireEvent("show", this);
2338     },
2339     
2340     focus : function(){
2341         return;
2342         if(!this.hidden){
2343             this.doFocus.defer(50, this);
2344         }
2345     },
2346
2347     doFocus : function(){
2348         if(!this.hidden){
2349             this.focusEl.focus();
2350         }
2351     },
2352
2353     /**
2354      * Hides this menu and optionally all parent menus
2355      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2356      */
2357     hide : function(deep)
2358     {
2359         
2360         this.hideMenuItems();
2361         if(this.el && this.isVisible()){
2362             this.fireEvent("beforehide", this);
2363             if(this.activeItem){
2364                 this.activeItem.deactivate();
2365                 this.activeItem = null;
2366             }
2367             this.triggerEl.removeClass('open');;
2368             this.el.removeClass('show');
2369             this.hidden = true;
2370             this.fireEvent("hide", this);
2371         }
2372         if(deep === true && this.parentMenu){
2373             this.parentMenu.hide(true);
2374         }
2375     },
2376     
2377     onTriggerClick : function(e)
2378     {
2379         Roo.log('trigger click');
2380         
2381         var target = e.getTarget();
2382         
2383         Roo.log(target.nodeName.toLowerCase());
2384         
2385         if(target.nodeName.toLowerCase() === 'i'){
2386             e.preventDefault();
2387         }
2388         
2389     },
2390     
2391     onTriggerPress  : function(e)
2392     {
2393         Roo.log('trigger press');
2394         //Roo.log(e.getTarget());
2395        // Roo.log(this.triggerEl.dom);
2396        
2397         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2398         var pel = Roo.get(e.getTarget());
2399         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2400             Roo.log('is treeview or dropdown?');
2401             return;
2402         }
2403         
2404         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2405             return;
2406         }
2407         
2408         if (this.isVisible()) {
2409             Roo.log('hide');
2410             this.hide();
2411         } else {
2412             Roo.log('show');
2413             this.show(this.triggerEl, '?', false);
2414         }
2415         
2416         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2417             e.stopEvent();
2418         }
2419         
2420     },
2421        
2422     
2423     hideMenuItems : function()
2424     {
2425         Roo.log("hide Menu Items");
2426         if (!this.el) { 
2427             return;
2428         }
2429         //$(backdrop).remove()
2430         this.el.select('.open',true).each(function(aa) {
2431             
2432             aa.removeClass('open');
2433           //var parent = getParent($(this))
2434           //var relatedTarget = { relatedTarget: this }
2435           
2436            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2437           //if (e.isDefaultPrevented()) return
2438            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2439         });
2440     },
2441     addxtypeChild : function (tree, cntr) {
2442         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2443           
2444         this.menuitems.add(comp);
2445         return comp;
2446
2447     },
2448     getEl : function()
2449     {
2450         Roo.log(this.el);
2451         return this.el;
2452     },
2453     
2454     clear : function()
2455     {
2456         this.getEl().dom.innerHTML = '';
2457         this.menuitems.clear();
2458     }
2459 });
2460
2461  
2462  /*
2463  * - LGPL
2464  *
2465  * menu item
2466  * 
2467  */
2468
2469
2470 /**
2471  * @class Roo.bootstrap.MenuItem
2472  * @extends Roo.bootstrap.Component
2473  * Bootstrap MenuItem class
2474  * @cfg {String} html the menu label
2475  * @cfg {String} href the link
2476  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2477  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2478  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2479  * @cfg {String} fa favicon to show on left of menu item.
2480  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2481  * 
2482  * 
2483  * @constructor
2484  * Create a new MenuItem
2485  * @param {Object} config The config object
2486  */
2487
2488
2489 Roo.bootstrap.MenuItem = function(config){
2490     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2491     this.addEvents({
2492         // raw events
2493         /**
2494          * @event click
2495          * The raw click event for the entire grid.
2496          * @param {Roo.bootstrap.MenuItem} this
2497          * @param {Roo.EventObject} e
2498          */
2499         "click" : true
2500     });
2501 };
2502
2503 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2504     
2505     href : false,
2506     html : false,
2507     preventDefault: false,
2508     isContainer : false,
2509     active : false,
2510     fa: false,
2511     
2512     getAutoCreate : function(){
2513         
2514         if(this.isContainer){
2515             return {
2516                 tag: 'li',
2517                 cls: 'dropdown-menu-item '
2518             };
2519         }
2520         var ctag = {
2521             tag: 'span',
2522             html: 'Link'
2523         };
2524         
2525         var anc = {
2526             tag : 'a',
2527             cls : 'dropdown-item',
2528             href : '#',
2529             cn : [  ]
2530         };
2531         
2532         if (this.fa !== false) {
2533             anc.cn.push({
2534                 tag : 'i',
2535                 cls : 'fa fa-' + this.fa
2536             });
2537         }
2538         
2539         anc.cn.push(ctag);
2540         
2541         
2542         var cfg= {
2543             tag: 'li',
2544             cls: 'dropdown-menu-item',
2545             cn: [ anc ]
2546         };
2547         if (this.parent().type == 'treeview') {
2548             cfg.cls = 'treeview-menu';
2549         }
2550         if (this.active) {
2551             cfg.cls += ' active';
2552         }
2553         
2554         
2555         
2556         anc.href = this.href || cfg.cn[0].href ;
2557         ctag.html = this.html || cfg.cn[0].html ;
2558         return cfg;
2559     },
2560     
2561     initEvents: function()
2562     {
2563         if (this.parent().type == 'treeview') {
2564             this.el.select('a').on('click', this.onClick, this);
2565         }
2566         
2567         if (this.menu) {
2568             this.menu.parentType = this.xtype;
2569             this.menu.triggerEl = this.el;
2570             this.menu = this.addxtype(Roo.apply({}, this.menu));
2571         }
2572         
2573     },
2574     onClick : function(e)
2575     {
2576         Roo.log('item on click ');
2577         
2578         if(this.preventDefault){
2579             e.preventDefault();
2580         }
2581         //this.parent().hideMenuItems();
2582         
2583         this.fireEvent('click', this, e);
2584     },
2585     getEl : function()
2586     {
2587         return this.el;
2588     } 
2589 });
2590
2591  
2592
2593  /*
2594  * - LGPL
2595  *
2596  * menu separator
2597  * 
2598  */
2599
2600
2601 /**
2602  * @class Roo.bootstrap.MenuSeparator
2603  * @extends Roo.bootstrap.Component
2604  * Bootstrap MenuSeparator class
2605  * 
2606  * @constructor
2607  * Create a new MenuItem
2608  * @param {Object} config The config object
2609  */
2610
2611
2612 Roo.bootstrap.MenuSeparator = function(config){
2613     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2614 };
2615
2616 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2617     
2618     getAutoCreate : function(){
2619         var cfg = {
2620             cls: 'divider',
2621             tag : 'li'
2622         };
2623         
2624         return cfg;
2625     }
2626    
2627 });
2628
2629  
2630
2631  
2632 /*
2633 * Licence: LGPL
2634 */
2635
2636 /**
2637  * @class Roo.bootstrap.Modal
2638  * @extends Roo.bootstrap.Component
2639  * Bootstrap Modal class
2640  * @cfg {String} title Title of dialog
2641  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2642  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2643  * @cfg {Boolean} specificTitle default false
2644  * @cfg {Array} buttons Array of buttons or standard button set..
2645  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
2646  * @cfg {Boolean} animate default true
2647  * @cfg {Boolean} allow_close default true
2648  * @cfg {Boolean} fitwindow default false
2649  * @cfg {String} size (sm|lg) default empty
2650  * @cfg {Number} max_width set the max width of modal
2651  *
2652  *
2653  * @constructor
2654  * Create a new Modal Dialog
2655  * @param {Object} config The config object
2656  */
2657
2658 Roo.bootstrap.Modal = function(config){
2659     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2660     this.addEvents({
2661         // raw events
2662         /**
2663          * @event btnclick
2664          * The raw btnclick event for the button
2665          * @param {Roo.EventObject} e
2666          */
2667         "btnclick" : true,
2668         /**
2669          * @event resize
2670          * Fire when dialog resize
2671          * @param {Roo.bootstrap.Modal} this
2672          * @param {Roo.EventObject} e
2673          */
2674         "resize" : true
2675     });
2676     this.buttons = this.buttons || [];
2677
2678     if (this.tmpl) {
2679         this.tmpl = Roo.factory(this.tmpl);
2680     }
2681
2682 };
2683
2684 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2685
2686     title : 'test dialog',
2687
2688     buttons : false,
2689
2690     // set on load...
2691
2692     html: false,
2693
2694     tmp: false,
2695
2696     specificTitle: false,
2697
2698     buttonPosition: 'right',
2699
2700     allow_close : true,
2701
2702     animate : true,
2703
2704     fitwindow: false,
2705     
2706      // private
2707     dialogEl: false,
2708     bodyEl:  false,
2709     footerEl:  false,
2710     titleEl:  false,
2711     closeEl:  false,
2712
2713     size: '',
2714     
2715     max_width: 0,
2716     
2717     max_height: 0,
2718     
2719     fit_content: false,
2720
2721     onRender : function(ct, position)
2722     {
2723         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2724
2725         if(!this.el){
2726             var cfg = Roo.apply({},  this.getAutoCreate());
2727             cfg.id = Roo.id();
2728             //if(!cfg.name){
2729             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2730             //}
2731             //if (!cfg.name.length) {
2732             //    delete cfg.name;
2733            // }
2734             if (this.cls) {
2735                 cfg.cls += ' ' + this.cls;
2736             }
2737             if (this.style) {
2738                 cfg.style = this.style;
2739             }
2740             this.el = Roo.get(document.body).createChild(cfg, position);
2741         }
2742         //var type = this.el.dom.type;
2743
2744
2745         if(this.tabIndex !== undefined){
2746             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2747         }
2748
2749         this.dialogEl = this.el.select('.modal-dialog',true).first();
2750         this.bodyEl = this.el.select('.modal-body',true).first();
2751         this.closeEl = this.el.select('.modal-header .close', true).first();
2752         this.headerEl = this.el.select('.modal-header',true).first();
2753         this.titleEl = this.el.select('.modal-title',true).first();
2754         this.footerEl = this.el.select('.modal-footer',true).first();
2755
2756         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2757         
2758         //this.el.addClass("x-dlg-modal");
2759
2760         if (this.buttons.length) {
2761             Roo.each(this.buttons, function(bb) {
2762                 var b = Roo.apply({}, bb);
2763                 b.xns = b.xns || Roo.bootstrap;
2764                 b.xtype = b.xtype || 'Button';
2765                 if (typeof(b.listeners) == 'undefined') {
2766                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2767                 }
2768
2769                 var btn = Roo.factory(b);
2770
2771                 btn.render(this.getButtonContainer());
2772
2773             },this);
2774         }
2775         // render the children.
2776         var nitems = [];
2777
2778         if(typeof(this.items) != 'undefined'){
2779             var items = this.items;
2780             delete this.items;
2781
2782             for(var i =0;i < items.length;i++) {
2783                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2784             }
2785         }
2786
2787         this.items = nitems;
2788
2789         // where are these used - they used to be body/close/footer
2790
2791
2792         this.initEvents();
2793         //this.el.addClass([this.fieldClass, this.cls]);
2794
2795     },
2796
2797     getAutoCreate : function()
2798     {
2799         var bdy = {
2800                 cls : 'modal-body',
2801                 html : this.html || ''
2802         };
2803
2804         var title = {
2805             tag: 'h4',
2806             cls : 'modal-title',
2807             html : this.title
2808         };
2809
2810         if(this.specificTitle){
2811             title = this.title;
2812
2813         }
2814
2815         var header = [];
2816         if (this.allow_close && Roo.bootstrap.version == 3) {
2817             header.push({
2818                 tag: 'button',
2819                 cls : 'close',
2820                 html : '&times'
2821             });
2822         }
2823
2824         header.push(title);
2825
2826         if (this.allow_close && Roo.bootstrap.version == 4) {
2827             header.push({
2828                 tag: 'button',
2829                 cls : 'close',
2830                 html : '&times'
2831             });
2832         }
2833         
2834         var size = '';
2835
2836         if(this.size.length){
2837             size = 'modal-' + this.size;
2838         }
2839         
2840         var footer = Roo.bootstrap.version == 3 ?
2841             {
2842                 cls : 'modal-footer',
2843                 cn : [
2844                     {
2845                         tag: 'div',
2846                         cls: 'btn-' + this.buttonPosition
2847                     }
2848                 ]
2849
2850             } :
2851             {  // BS4 uses mr-auto on left buttons....
2852                 cls : 'modal-footer'
2853             };
2854
2855             
2856
2857         
2858         
2859         var modal = {
2860             cls: "modal",
2861              cn : [
2862                 {
2863                     cls: "modal-dialog " + size,
2864                     cn : [
2865                         {
2866                             cls : "modal-content",
2867                             cn : [
2868                                 {
2869                                     cls : 'modal-header',
2870                                     cn : header
2871                                 },
2872                                 bdy,
2873                                 footer
2874                             ]
2875
2876                         }
2877                     ]
2878
2879                 }
2880             ]
2881         };
2882
2883         if(this.animate){
2884             modal.cls += ' fade';
2885         }
2886
2887         return modal;
2888
2889     },
2890     getChildContainer : function() {
2891
2892          return this.bodyEl;
2893
2894     },
2895     getButtonContainer : function() {
2896         
2897          return Roo.bootstrap.version == 4 ?
2898             this.el.select('.modal-footer',true).first()
2899             : this.el.select('.modal-footer div',true).first();
2900
2901     },
2902     initEvents : function()
2903     {
2904         if (this.allow_close) {
2905             this.closeEl.on('click', this.hide, this);
2906         }
2907         Roo.EventManager.onWindowResize(this.resize, this, true);
2908
2909
2910     },
2911   
2912
2913     resize : function()
2914     {
2915         this.maskEl.setSize(
2916             Roo.lib.Dom.getViewWidth(true),
2917             Roo.lib.Dom.getViewHeight(true)
2918         );
2919         
2920         if (this.fitwindow) {
2921             
2922            
2923             this.setSize(
2924                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2925                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
2926             );
2927             return;
2928         }
2929         
2930         if(this.max_width !== 0) {
2931             
2932             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2933             
2934             if(this.height) {
2935                 this.setSize(w, this.height);
2936                 return;
2937             }
2938             
2939             if(this.max_height) {
2940                 this.setSize(w,Math.min(
2941                     this.max_height,
2942                     Roo.lib.Dom.getViewportHeight(true) - 60
2943                 ));
2944                 
2945                 return;
2946             }
2947             
2948             if(!this.fit_content) {
2949                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2950                 return;
2951             }
2952             
2953             this.setSize(w, Math.min(
2954                 60 +
2955                 this.headerEl.getHeight() + 
2956                 this.footerEl.getHeight() + 
2957                 this.getChildHeight(this.bodyEl.dom.childNodes),
2958                 Roo.lib.Dom.getViewportHeight(true) - 60)
2959             );
2960         }
2961         
2962     },
2963
2964     setSize : function(w,h)
2965     {
2966         if (!w && !h) {
2967             return;
2968         }
2969         
2970         this.resizeTo(w,h);
2971     },
2972
2973     show : function() {
2974
2975         if (!this.rendered) {
2976             this.render();
2977         }
2978
2979         //this.el.setStyle('display', 'block');
2980         this.el.removeClass('hideing');
2981         this.el.dom.style.display='block';
2982         
2983         Roo.get(document.body).addClass('modal-open');
2984  
2985         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2986             
2987             (function(){
2988                 this.el.addClass('show');
2989                 this.el.addClass('in');
2990             }).defer(50, this);
2991         }else{
2992             this.el.addClass('show');
2993             this.el.addClass('in');
2994         }
2995
2996         // not sure how we can show data in here..
2997         //if (this.tmpl) {
2998         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2999         //}
3000
3001         Roo.get(document.body).addClass("x-body-masked");
3002         
3003         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
3004         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3005         this.maskEl.dom.style.display = 'block';
3006         this.maskEl.addClass('show');
3007         
3008         
3009         this.resize();
3010         
3011         this.fireEvent('show', this);
3012
3013         // set zindex here - otherwise it appears to be ignored...
3014         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3015
3016         (function () {
3017             this.items.forEach( function(e) {
3018                 e.layout ? e.layout() : false;
3019
3020             });
3021         }).defer(100,this);
3022
3023     },
3024     hide : function()
3025     {
3026         if(this.fireEvent("beforehide", this) !== false){
3027             
3028             this.maskEl.removeClass('show');
3029             
3030             this.maskEl.dom.style.display = '';
3031             Roo.get(document.body).removeClass("x-body-masked");
3032             this.el.removeClass('in');
3033             this.el.select('.modal-dialog', true).first().setStyle('transform','');
3034
3035             if(this.animate){ // why
3036                 this.el.addClass('hideing');
3037                 this.el.removeClass('show');
3038                 (function(){
3039                     if (!this.el.hasClass('hideing')) {
3040                         return; // it's been shown again...
3041                     }
3042                     
3043                     this.el.dom.style.display='';
3044
3045                     Roo.get(document.body).removeClass('modal-open');
3046                     this.el.removeClass('hideing');
3047                 }).defer(150,this);
3048                 
3049             }else{
3050                 this.el.removeClass('show');
3051                 this.el.dom.style.display='';
3052                 Roo.get(document.body).removeClass('modal-open');
3053
3054             }
3055             this.fireEvent('hide', this);
3056         }
3057     },
3058     isVisible : function()
3059     {
3060         
3061         return this.el.hasClass('show') && !this.el.hasClass('hideing');
3062         
3063     },
3064
3065     addButton : function(str, cb)
3066     {
3067
3068
3069         var b = Roo.apply({}, { html : str } );
3070         b.xns = b.xns || Roo.bootstrap;
3071         b.xtype = b.xtype || 'Button';
3072         if (typeof(b.listeners) == 'undefined') {
3073             b.listeners = { click : cb.createDelegate(this)  };
3074         }
3075
3076         var btn = Roo.factory(b);
3077
3078         btn.render(this.getButtonContainer());
3079
3080         return btn;
3081
3082     },
3083
3084     setDefaultButton : function(btn)
3085     {
3086         //this.el.select('.modal-footer').()
3087     },
3088
3089     resizeTo: function(w,h)
3090     {
3091         this.dialogEl.setWidth(w);
3092         
3093         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
3094
3095         this.bodyEl.setHeight(h - diff);
3096         
3097         this.fireEvent('resize', this);
3098     },
3099     
3100     setContentSize  : function(w, h)
3101     {
3102
3103     },
3104     onButtonClick: function(btn,e)
3105     {
3106         //Roo.log([a,b,c]);
3107         this.fireEvent('btnclick', btn.name, e);
3108     },
3109      /**
3110      * Set the title of the Dialog
3111      * @param {String} str new Title
3112      */
3113     setTitle: function(str) {
3114         this.titleEl.dom.innerHTML = str;
3115     },
3116     /**
3117      * Set the body of the Dialog
3118      * @param {String} str new Title
3119      */
3120     setBody: function(str) {
3121         this.bodyEl.dom.innerHTML = str;
3122     },
3123     /**
3124      * Set the body of the Dialog using the template
3125      * @param {Obj} data - apply this data to the template and replace the body contents.
3126      */
3127     applyBody: function(obj)
3128     {
3129         if (!this.tmpl) {
3130             Roo.log("Error - using apply Body without a template");
3131             //code
3132         }
3133         this.tmpl.overwrite(this.bodyEl, obj);
3134     },
3135     
3136     getChildHeight : function(child_nodes)
3137     {
3138         if(
3139             !child_nodes ||
3140             child_nodes.length == 0
3141         ) {
3142             return;
3143         }
3144         
3145         var child_height = 0;
3146         
3147         for(var i = 0; i < child_nodes.length; i++) {
3148             
3149             /*
3150             * for modal with tabs...
3151             if(child_nodes[i].classList.contains('roo-layout-panel')) {
3152                 
3153                 var layout_childs = child_nodes[i].childNodes;
3154                 
3155                 for(var j = 0; j < layout_childs.length; j++) {
3156                     
3157                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3158                         
3159                         var layout_body_childs = layout_childs[j].childNodes;
3160                         
3161                         for(var k = 0; k < layout_body_childs.length; k++) {
3162                             
3163                             if(layout_body_childs[k].classList.contains('navbar')) {
3164                                 child_height += layout_body_childs[k].offsetHeight;
3165                                 continue;
3166                             }
3167                             
3168                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3169                                 
3170                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3171                                 
3172                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3173                                     
3174                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3175                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3176                                         continue;
3177                                     }
3178                                     
3179                                 }
3180                                 
3181                             }
3182                             
3183                         }
3184                     }
3185                 }
3186                 continue;
3187             }
3188             */
3189             
3190             child_height += child_nodes[i].offsetHeight;
3191             // Roo.log(child_nodes[i].offsetHeight);
3192         }
3193         
3194         return child_height;
3195     }
3196
3197 });
3198
3199
3200 Roo.apply(Roo.bootstrap.Modal,  {
3201     /**
3202          * Button config that displays a single OK button
3203          * @type Object
3204          */
3205         OK :  [{
3206             name : 'ok',
3207             weight : 'primary',
3208             html : 'OK'
3209         }],
3210         /**
3211          * Button config that displays Yes and No buttons
3212          * @type Object
3213          */
3214         YESNO : [
3215             {
3216                 name  : 'no',
3217                 html : 'No'
3218             },
3219             {
3220                 name  :'yes',
3221                 weight : 'primary',
3222                 html : 'Yes'
3223             }
3224         ],
3225
3226         /**
3227          * Button config that displays OK and Cancel buttons
3228          * @type Object
3229          */
3230         OKCANCEL : [
3231             {
3232                name : 'cancel',
3233                 html : 'Cancel'
3234             },
3235             {
3236                 name : 'ok',
3237                 weight : 'primary',
3238                 html : 'OK'
3239             }
3240         ],
3241         /**
3242          * Button config that displays Yes, No and Cancel buttons
3243          * @type Object
3244          */
3245         YESNOCANCEL : [
3246             {
3247                 name : 'yes',
3248                 weight : 'primary',
3249                 html : 'Yes'
3250             },
3251             {
3252                 name : 'no',
3253                 html : 'No'
3254             },
3255             {
3256                 name : 'cancel',
3257                 html : 'Cancel'
3258             }
3259         ],
3260         
3261         zIndex : 10001
3262 });
3263 /*
3264  * - LGPL
3265  *
3266  * messagebox - can be used as a replace
3267  * 
3268  */
3269 /**
3270  * @class Roo.MessageBox
3271  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3272  * Example usage:
3273  *<pre><code>
3274 // Basic alert:
3275 Roo.Msg.alert('Status', 'Changes saved successfully.');
3276
3277 // Prompt for user data:
3278 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3279     if (btn == 'ok'){
3280         // process text value...
3281     }
3282 });
3283
3284 // Show a dialog using config options:
3285 Roo.Msg.show({
3286    title:'Save Changes?',
3287    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3288    buttons: Roo.Msg.YESNOCANCEL,
3289    fn: processResult,
3290    animEl: 'elId'
3291 });
3292 </code></pre>
3293  * @singleton
3294  */
3295 Roo.bootstrap.MessageBox = function(){
3296     var dlg, opt, mask, waitTimer;
3297     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3298     var buttons, activeTextEl, bwidth;
3299
3300     
3301     // private
3302     var handleButton = function(button){
3303         dlg.hide();
3304         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3305     };
3306
3307     // private
3308     var handleHide = function(){
3309         if(opt && opt.cls){
3310             dlg.el.removeClass(opt.cls);
3311         }
3312         //if(waitTimer){
3313         //    Roo.TaskMgr.stop(waitTimer);
3314         //    waitTimer = null;
3315         //}
3316     };
3317
3318     // private
3319     var updateButtons = function(b){
3320         var width = 0;
3321         if(!b){
3322             buttons["ok"].hide();
3323             buttons["cancel"].hide();
3324             buttons["yes"].hide();
3325             buttons["no"].hide();
3326             dlg.footerEl.hide();
3327             
3328             return width;
3329         }
3330         dlg.footerEl.show();
3331         for(var k in buttons){
3332             if(typeof buttons[k] != "function"){
3333                 if(b[k]){
3334                     buttons[k].show();
3335                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3336                     width += buttons[k].el.getWidth()+15;
3337                 }else{
3338                     buttons[k].hide();
3339                 }
3340             }
3341         }
3342         return width;
3343     };
3344
3345     // private
3346     var handleEsc = function(d, k, e){
3347         if(opt && opt.closable !== false){
3348             dlg.hide();
3349         }
3350         if(e){
3351             e.stopEvent();
3352         }
3353     };
3354
3355     return {
3356         /**
3357          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3358          * @return {Roo.BasicDialog} The BasicDialog element
3359          */
3360         getDialog : function(){
3361            if(!dlg){
3362                 dlg = new Roo.bootstrap.Modal( {
3363                     //draggable: true,
3364                     //resizable:false,
3365                     //constraintoviewport:false,
3366                     //fixedcenter:true,
3367                     //collapsible : false,
3368                     //shim:true,
3369                     //modal: true,
3370                 //    width: 'auto',
3371                   //  height:100,
3372                     //buttonAlign:"center",
3373                     closeClick : function(){
3374                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3375                             handleButton("no");
3376                         }else{
3377                             handleButton("cancel");
3378                         }
3379                     }
3380                 });
3381                 dlg.render();
3382                 dlg.on("hide", handleHide);
3383                 mask = dlg.mask;
3384                 //dlg.addKeyListener(27, handleEsc);
3385                 buttons = {};
3386                 this.buttons = buttons;
3387                 var bt = this.buttonText;
3388                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3389                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3390                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3391                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3392                 //Roo.log(buttons);
3393                 bodyEl = dlg.bodyEl.createChild({
3394
3395                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3396                         '<textarea class="roo-mb-textarea"></textarea>' +
3397                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3398                 });
3399                 msgEl = bodyEl.dom.firstChild;
3400                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3401                 textboxEl.enableDisplayMode();
3402                 textboxEl.addKeyListener([10,13], function(){
3403                     if(dlg.isVisible() && opt && opt.buttons){
3404                         if(opt.buttons.ok){
3405                             handleButton("ok");
3406                         }else if(opt.buttons.yes){
3407                             handleButton("yes");
3408                         }
3409                     }
3410                 });
3411                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3412                 textareaEl.enableDisplayMode();
3413                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3414                 progressEl.enableDisplayMode();
3415                 
3416                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3417                 var pf = progressEl.dom.firstChild;
3418                 if (pf) {
3419                     pp = Roo.get(pf.firstChild);
3420                     pp.setHeight(pf.offsetHeight);
3421                 }
3422                 
3423             }
3424             return dlg;
3425         },
3426
3427         /**
3428          * Updates the message box body text
3429          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3430          * the XHTML-compliant non-breaking space character '&amp;#160;')
3431          * @return {Roo.MessageBox} This message box
3432          */
3433         updateText : function(text)
3434         {
3435             if(!dlg.isVisible() && !opt.width){
3436                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3437                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3438             }
3439             msgEl.innerHTML = text || '&#160;';
3440       
3441             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3442             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3443             var w = Math.max(
3444                     Math.min(opt.width || cw , this.maxWidth), 
3445                     Math.max(opt.minWidth || this.minWidth, bwidth)
3446             );
3447             if(opt.prompt){
3448                 activeTextEl.setWidth(w);
3449             }
3450             if(dlg.isVisible()){
3451                 dlg.fixedcenter = false;
3452             }
3453             // to big, make it scroll. = But as usual stupid IE does not support
3454             // !important..
3455             
3456             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3457                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3458                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3459             } else {
3460                 bodyEl.dom.style.height = '';
3461                 bodyEl.dom.style.overflowY = '';
3462             }
3463             if (cw > w) {
3464                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3465             } else {
3466                 bodyEl.dom.style.overflowX = '';
3467             }
3468             
3469             dlg.setContentSize(w, bodyEl.getHeight());
3470             if(dlg.isVisible()){
3471                 dlg.fixedcenter = true;
3472             }
3473             return this;
3474         },
3475
3476         /**
3477          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3478          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3479          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3480          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3481          * @return {Roo.MessageBox} This message box
3482          */
3483         updateProgress : function(value, text){
3484             if(text){
3485                 this.updateText(text);
3486             }
3487             
3488             if (pp) { // weird bug on my firefox - for some reason this is not defined
3489                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3490                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3491             }
3492             return this;
3493         },        
3494
3495         /**
3496          * Returns true if the message box is currently displayed
3497          * @return {Boolean} True if the message box is visible, else false
3498          */
3499         isVisible : function(){
3500             return dlg && dlg.isVisible();  
3501         },
3502
3503         /**
3504          * Hides the message box if it is displayed
3505          */
3506         hide : function(){
3507             if(this.isVisible()){
3508                 dlg.hide();
3509             }  
3510         },
3511
3512         /**
3513          * Displays a new message box, or reinitializes an existing message box, based on the config options
3514          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3515          * The following config object properties are supported:
3516          * <pre>
3517 Property    Type             Description
3518 ----------  ---------------  ------------------------------------------------------------------------------------
3519 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3520                                    closes (defaults to undefined)
3521 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3522                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3523 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3524                                    progress and wait dialogs will ignore this property and always hide the
3525                                    close button as they can only be closed programmatically.
3526 cls               String           A custom CSS class to apply to the message box element
3527 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3528                                    displayed (defaults to 75)
3529 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3530                                    function will be btn (the name of the button that was clicked, if applicable,
3531                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3532                                    Progress and wait dialogs will ignore this option since they do not respond to
3533                                    user actions and can only be closed programmatically, so any required function
3534                                    should be called by the same code after it closes the dialog.
3535 icon              String           A CSS class that provides a background image to be used as an icon for
3536                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3537 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3538 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3539 modal             Boolean          False to allow user interaction with the page while the message box is
3540                                    displayed (defaults to true)
3541 msg               String           A string that will replace the existing message box body text (defaults
3542                                    to the XHTML-compliant non-breaking space character '&#160;')
3543 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3544 progress          Boolean          True to display a progress bar (defaults to false)
3545 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3546 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3547 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3548 title             String           The title text
3549 value             String           The string value to set into the active textbox element if displayed
3550 wait              Boolean          True to display a progress bar (defaults to false)
3551 width             Number           The width of the dialog in pixels
3552 </pre>
3553          *
3554          * Example usage:
3555          * <pre><code>
3556 Roo.Msg.show({
3557    title: 'Address',
3558    msg: 'Please enter your address:',
3559    width: 300,
3560    buttons: Roo.MessageBox.OKCANCEL,
3561    multiline: true,
3562    fn: saveAddress,
3563    animEl: 'addAddressBtn'
3564 });
3565 </code></pre>
3566          * @param {Object} config Configuration options
3567          * @return {Roo.MessageBox} This message box
3568          */
3569         show : function(options)
3570         {
3571             
3572             // this causes nightmares if you show one dialog after another
3573             // especially on callbacks..
3574              
3575             if(this.isVisible()){
3576                 
3577                 this.hide();
3578                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3579                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3580                 Roo.log("New Dialog Message:" +  options.msg )
3581                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3582                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3583                 
3584             }
3585             var d = this.getDialog();
3586             opt = options;
3587             d.setTitle(opt.title || "&#160;");
3588             d.closeEl.setDisplayed(opt.closable !== false);
3589             activeTextEl = textboxEl;
3590             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3591             if(opt.prompt){
3592                 if(opt.multiline){
3593                     textboxEl.hide();
3594                     textareaEl.show();
3595                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3596                         opt.multiline : this.defaultTextHeight);
3597                     activeTextEl = textareaEl;
3598                 }else{
3599                     textboxEl.show();
3600                     textareaEl.hide();
3601                 }
3602             }else{
3603                 textboxEl.hide();
3604                 textareaEl.hide();
3605             }
3606             progressEl.setDisplayed(opt.progress === true);
3607             if (opt.progress) {
3608                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
3609             }
3610             this.updateProgress(0);
3611             activeTextEl.dom.value = opt.value || "";
3612             if(opt.prompt){
3613                 dlg.setDefaultButton(activeTextEl);
3614             }else{
3615                 var bs = opt.buttons;
3616                 var db = null;
3617                 if(bs && bs.ok){
3618                     db = buttons["ok"];
3619                 }else if(bs && bs.yes){
3620                     db = buttons["yes"];
3621                 }
3622                 dlg.setDefaultButton(db);
3623             }
3624             bwidth = updateButtons(opt.buttons);
3625             this.updateText(opt.msg);
3626             if(opt.cls){
3627                 d.el.addClass(opt.cls);
3628             }
3629             d.proxyDrag = opt.proxyDrag === true;
3630             d.modal = opt.modal !== false;
3631             d.mask = opt.modal !== false ? mask : false;
3632             if(!d.isVisible()){
3633                 // force it to the end of the z-index stack so it gets a cursor in FF
3634                 document.body.appendChild(dlg.el.dom);
3635                 d.animateTarget = null;
3636                 d.show(options.animEl);
3637             }
3638             return this;
3639         },
3640
3641         /**
3642          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3643          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3644          * and closing the message box when the process is complete.
3645          * @param {String} title The title bar text
3646          * @param {String} msg The message box body text
3647          * @return {Roo.MessageBox} This message box
3648          */
3649         progress : function(title, msg){
3650             this.show({
3651                 title : title,
3652                 msg : msg,
3653                 buttons: false,
3654                 progress:true,
3655                 closable:false,
3656                 minWidth: this.minProgressWidth,
3657                 modal : true
3658             });
3659             return this;
3660         },
3661
3662         /**
3663          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3664          * If a callback function is passed it will be called after the user clicks the button, and the
3665          * id of the button that was clicked will be passed as the only parameter to the callback
3666          * (could also be the top-right close button).
3667          * @param {String} title The title bar text
3668          * @param {String} msg The message box body text
3669          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3670          * @param {Object} scope (optional) The scope of the callback function
3671          * @return {Roo.MessageBox} This message box
3672          */
3673         alert : function(title, msg, fn, scope)
3674         {
3675             this.show({
3676                 title : title,
3677                 msg : msg,
3678                 buttons: this.OK,
3679                 fn: fn,
3680                 closable : false,
3681                 scope : scope,
3682                 modal : true
3683             });
3684             return this;
3685         },
3686
3687         /**
3688          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3689          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3690          * You are responsible for closing the message box when the process is complete.
3691          * @param {String} msg The message box body text
3692          * @param {String} title (optional) The title bar text
3693          * @return {Roo.MessageBox} This message box
3694          */
3695         wait : function(msg, title){
3696             this.show({
3697                 title : title,
3698                 msg : msg,
3699                 buttons: false,
3700                 closable:false,
3701                 progress:true,
3702                 modal:true,
3703                 width:300,
3704                 wait:true
3705             });
3706             waitTimer = Roo.TaskMgr.start({
3707                 run: function(i){
3708                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3709                 },
3710                 interval: 1000
3711             });
3712             return this;
3713         },
3714
3715         /**
3716          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3717          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3718          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3719          * @param {String} title The title bar text
3720          * @param {String} msg The message box body text
3721          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3722          * @param {Object} scope (optional) The scope of the callback function
3723          * @return {Roo.MessageBox} This message box
3724          */
3725         confirm : function(title, msg, fn, scope){
3726             this.show({
3727                 title : title,
3728                 msg : msg,
3729                 buttons: this.YESNO,
3730                 fn: fn,
3731                 scope : scope,
3732                 modal : true
3733             });
3734             return this;
3735         },
3736
3737         /**
3738          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3739          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3740          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3741          * (could also be the top-right close button) and the text that was entered will be passed as the two
3742          * parameters to the callback.
3743          * @param {String} title The title bar text
3744          * @param {String} msg The message box body text
3745          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3746          * @param {Object} scope (optional) The scope of the callback function
3747          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3748          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3749          * @return {Roo.MessageBox} This message box
3750          */
3751         prompt : function(title, msg, fn, scope, multiline){
3752             this.show({
3753                 title : title,
3754                 msg : msg,
3755                 buttons: this.OKCANCEL,
3756                 fn: fn,
3757                 minWidth:250,
3758                 scope : scope,
3759                 prompt:true,
3760                 multiline: multiline,
3761                 modal : true
3762             });
3763             return this;
3764         },
3765
3766         /**
3767          * Button config that displays a single OK button
3768          * @type Object
3769          */
3770         OK : {ok:true},
3771         /**
3772          * Button config that displays Yes and No buttons
3773          * @type Object
3774          */
3775         YESNO : {yes:true, no:true},
3776         /**
3777          * Button config that displays OK and Cancel buttons
3778          * @type Object
3779          */
3780         OKCANCEL : {ok:true, cancel:true},
3781         /**
3782          * Button config that displays Yes, No and Cancel buttons
3783          * @type Object
3784          */
3785         YESNOCANCEL : {yes:true, no:true, cancel:true},
3786
3787         /**
3788          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3789          * @type Number
3790          */
3791         defaultTextHeight : 75,
3792         /**
3793          * The maximum width in pixels of the message box (defaults to 600)
3794          * @type Number
3795          */
3796         maxWidth : 600,
3797         /**
3798          * The minimum width in pixels of the message box (defaults to 100)
3799          * @type Number
3800          */
3801         minWidth : 100,
3802         /**
3803          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3804          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3805          * @type Number
3806          */
3807         minProgressWidth : 250,
3808         /**
3809          * An object containing the default button text strings that can be overriden for localized language support.
3810          * Supported properties are: ok, cancel, yes and no.
3811          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3812          * @type Object
3813          */
3814         buttonText : {
3815             ok : "OK",
3816             cancel : "Cancel",
3817             yes : "Yes",
3818             no : "No"
3819         }
3820     };
3821 }();
3822
3823 /**
3824  * Shorthand for {@link Roo.MessageBox}
3825  */
3826 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3827 Roo.Msg = Roo.Msg || Roo.MessageBox;
3828 /*
3829  * - LGPL
3830  *
3831  * navbar
3832  * 
3833  */
3834
3835 /**
3836  * @class Roo.bootstrap.Navbar
3837  * @extends Roo.bootstrap.Component
3838  * Bootstrap Navbar class
3839
3840  * @constructor
3841  * Create a new Navbar
3842  * @param {Object} config The config object
3843  */
3844
3845
3846 Roo.bootstrap.Navbar = function(config){
3847     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3848     this.addEvents({
3849         // raw events
3850         /**
3851          * @event beforetoggle
3852          * Fire before toggle the menu
3853          * @param {Roo.EventObject} e
3854          */
3855         "beforetoggle" : true
3856     });
3857 };
3858
3859 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3860     
3861     
3862    
3863     // private
3864     navItems : false,
3865     loadMask : false,
3866     
3867     
3868     getAutoCreate : function(){
3869         
3870         
3871         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3872         
3873     },
3874     
3875     initEvents :function ()
3876     {
3877         //Roo.log(this.el.select('.navbar-toggle',true));
3878         this.el.select('.navbar-toggle',true).on('click', function() {
3879             if(this.fireEvent('beforetoggle', this) !== false){
3880                 var ce = this.el.select('.navbar-collapse',true).first();
3881                 ce.toggleClass('in'); // old...
3882                 if (ce.hasClass('collapse')) {
3883                     // show it...
3884                     ce.removeClass('collapse');
3885                     ce.addClass('show');
3886                     var h = ce.getHeight();
3887                     Roo.log(h);
3888                     ce.removeClass('show');
3889                     // at this point we should be able to see it..
3890                     ce.addClass('collapsing');
3891                     
3892                     ce.setHeight(0); // resize it ...
3893                     ce.on('transitionend', function() {
3894                         Roo.log('done transition');
3895                         ce.removeClass('collapsing');
3896                         ce.addClass('show');
3897                         ce.removeClass('collapse');
3898
3899                         ce.dom.style.height = '';
3900                     }, this, { single: true} );
3901                     ce.setHeight(h);
3902                     
3903                 } else {
3904                     ce.setHeight(ce.getHeight());
3905                     ce.removeClass('show');
3906                     ce.addClass('collapsing');
3907                     
3908                     ce.on('transitionend', function() {
3909                         ce.dom.style.height = '';
3910                         ce.removeClass('collapsing');
3911                         ce.addClass('collapse');
3912                     }, this, { single: true} );
3913                     ce.setHeight(0);
3914                 }
3915             }
3916             
3917         }, this);
3918         
3919         var mark = {
3920             tag: "div",
3921             cls:"x-dlg-mask"
3922         };
3923         
3924         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3925         
3926         var size = this.el.getSize();
3927         this.maskEl.setSize(size.width, size.height);
3928         this.maskEl.enableDisplayMode("block");
3929         this.maskEl.hide();
3930         
3931         if(this.loadMask){
3932             this.maskEl.show();
3933         }
3934     },
3935     
3936     
3937     getChildContainer : function()
3938     {
3939         if (this.el.select('.collapse').getCount()) {
3940             return this.el.select('.collapse',true).first();
3941         }
3942         
3943         return this.el;
3944     },
3945     
3946     mask : function()
3947     {
3948         this.maskEl.show();
3949     },
3950     
3951     unmask : function()
3952     {
3953         this.maskEl.hide();
3954     } 
3955     
3956     
3957     
3958     
3959 });
3960
3961
3962
3963  
3964
3965  /*
3966  * - LGPL
3967  *
3968  * navbar
3969  * 
3970  */
3971
3972 /**
3973  * @class Roo.bootstrap.NavSimplebar
3974  * @extends Roo.bootstrap.Navbar
3975  * Bootstrap Sidebar class
3976  *
3977  * @cfg {Boolean} inverse is inverted color
3978  * 
3979  * @cfg {String} type (nav | pills | tabs)
3980  * @cfg {Boolean} arrangement stacked | justified
3981  * @cfg {String} align (left | right) alignment
3982  * 
3983  * @cfg {Boolean} main (true|false) main nav bar? default false
3984  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3985  * 
3986  * @cfg {String} tag (header|footer|nav|div) default is nav 
3987
3988  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
3989  * 
3990  * 
3991  * @constructor
3992  * Create a new Sidebar
3993  * @param {Object} config The config object
3994  */
3995
3996
3997 Roo.bootstrap.NavSimplebar = function(config){
3998     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3999 };
4000
4001 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
4002     
4003     inverse: false,
4004     
4005     type: false,
4006     arrangement: '',
4007     align : false,
4008     
4009     weight : 'light',
4010     
4011     main : false,
4012     
4013     
4014     tag : false,
4015     
4016     
4017     getAutoCreate : function(){
4018         
4019         
4020         var cfg = {
4021             tag : this.tag || 'div',
4022             cls : 'navbar navbar-expand-lg roo-navbar-simple'
4023         };
4024         if (['light','white'].indexOf(this.weight) > -1) {
4025             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4026         }
4027         cfg.cls += ' bg-' + this.weight;
4028         
4029         if (this.inverse) {
4030             cfg.cls += ' navbar-inverse';
4031             
4032         }
4033         
4034         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
4035         
4036         //if (Roo.bootstrap.version == 4) {
4037         //    return cfg;
4038         //}
4039         
4040         cfg.cn = [
4041             {
4042                 cls: 'nav',
4043                 tag : 'ul'
4044             }
4045         ];
4046         
4047          
4048         this.type = this.type || 'nav';
4049         if (['tabs','pills'].indexOf(this.type) != -1) {
4050             cfg.cn[0].cls += ' nav-' + this.type
4051         
4052         
4053         } else {
4054             if (this.type!=='nav') {
4055                 Roo.log('nav type must be nav/tabs/pills')
4056             }
4057             cfg.cn[0].cls += ' navbar-nav'
4058         }
4059         
4060         
4061         
4062         
4063         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
4064             cfg.cn[0].cls += ' nav-' + this.arrangement;
4065         }
4066         
4067         
4068         if (this.align === 'right') {
4069             cfg.cn[0].cls += ' navbar-right';
4070         }
4071         
4072         
4073         
4074         
4075         return cfg;
4076     
4077         
4078     }
4079     
4080     
4081     
4082 });
4083
4084
4085
4086  
4087
4088  
4089        /*
4090  * - LGPL
4091  *
4092  * navbar
4093  * navbar-fixed-top
4094  * navbar-expand-md  fixed-top 
4095  */
4096
4097 /**
4098  * @class Roo.bootstrap.NavHeaderbar
4099  * @extends Roo.bootstrap.NavSimplebar
4100  * Bootstrap Sidebar class
4101  *
4102  * @cfg {String} brand what is brand
4103  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4104  * @cfg {String} brand_href href of the brand
4105  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
4106  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4107  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4108  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4109  * 
4110  * @constructor
4111  * Create a new Sidebar
4112  * @param {Object} config The config object
4113  */
4114
4115
4116 Roo.bootstrap.NavHeaderbar = function(config){
4117     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4118       
4119 };
4120
4121 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
4122     
4123     position: '',
4124     brand: '',
4125     brand_href: false,
4126     srButton : true,
4127     autohide : false,
4128     desktopCenter : false,
4129    
4130     
4131     getAutoCreate : function(){
4132         
4133         var   cfg = {
4134             tag: this.nav || 'nav',
4135             cls: 'navbar navbar-expand-md',
4136             role: 'navigation',
4137             cn: []
4138         };
4139         
4140         var cn = cfg.cn;
4141         if (this.desktopCenter) {
4142             cn.push({cls : 'container', cn : []});
4143             cn = cn[0].cn;
4144         }
4145         
4146         if(this.srButton){
4147             var btn = {
4148                 tag: 'button',
4149                 type: 'button',
4150                 cls: 'navbar-toggle navbar-toggler',
4151                 'data-toggle': 'collapse',
4152                 cn: [
4153                     {
4154                         tag: 'span',
4155                         cls: 'sr-only',
4156                         html: 'Toggle navigation'
4157                     },
4158                     {
4159                         tag: 'span',
4160                         cls: 'icon-bar navbar-toggler-icon'
4161                     },
4162                     {
4163                         tag: 'span',
4164                         cls: 'icon-bar'
4165                     },
4166                     {
4167                         tag: 'span',
4168                         cls: 'icon-bar'
4169                     }
4170                 ]
4171             };
4172             
4173             cn.push( Roo.bootstrap.version == 4 ? btn : {
4174                 tag: 'div',
4175                 cls: 'navbar-header',
4176                 cn: [
4177                     btn
4178                 ]
4179             });
4180         }
4181         
4182         cn.push({
4183             tag: 'div',
4184             cls: 'collapse navbar-collapse',
4185             cn : []
4186         });
4187         
4188         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4189         
4190         if (['light','white'].indexOf(this.weight) > -1) {
4191             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4192         }
4193         cfg.cls += ' bg-' + this.weight;
4194         
4195         
4196         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4197             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4198             
4199             // tag can override this..
4200             
4201             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4202         }
4203         
4204         if (this.brand !== '') {
4205             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4206             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4207                 tag: 'a',
4208                 href: this.brand_href ? this.brand_href : '#',
4209                 cls: 'navbar-brand',
4210                 cn: [
4211                 this.brand
4212                 ]
4213             });
4214         }
4215         
4216         if(this.main){
4217             cfg.cls += ' main-nav';
4218         }
4219         
4220         
4221         return cfg;
4222
4223         
4224     },
4225     getHeaderChildContainer : function()
4226     {
4227         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4228             return this.el.select('.navbar-header',true).first();
4229         }
4230         
4231         return this.getChildContainer();
4232     },
4233     
4234     
4235     initEvents : function()
4236     {
4237         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4238         
4239         if (this.autohide) {
4240             
4241             var prevScroll = 0;
4242             var ft = this.el;
4243             
4244             Roo.get(document).on('scroll',function(e) {
4245                 var ns = Roo.get(document).getScroll().top;
4246                 var os = prevScroll;
4247                 prevScroll = ns;
4248                 
4249                 if(ns > os){
4250                     ft.removeClass('slideDown');
4251                     ft.addClass('slideUp');
4252                     return;
4253                 }
4254                 ft.removeClass('slideUp');
4255                 ft.addClass('slideDown');
4256                  
4257               
4258           },this);
4259         }
4260     }    
4261     
4262 });
4263
4264
4265
4266  
4267
4268  /*
4269  * - LGPL
4270  *
4271  * navbar
4272  * 
4273  */
4274
4275 /**
4276  * @class Roo.bootstrap.NavSidebar
4277  * @extends Roo.bootstrap.Navbar
4278  * Bootstrap Sidebar class
4279  * 
4280  * @constructor
4281  * Create a new Sidebar
4282  * @param {Object} config The config object
4283  */
4284
4285
4286 Roo.bootstrap.NavSidebar = function(config){
4287     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4288 };
4289
4290 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4291     
4292     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4293     
4294     getAutoCreate : function(){
4295         
4296         
4297         return  {
4298             tag: 'div',
4299             cls: 'sidebar sidebar-nav'
4300         };
4301     
4302         
4303     }
4304     
4305     
4306     
4307 });
4308
4309
4310
4311  
4312
4313  /*
4314  * - LGPL
4315  *
4316  * nav group
4317  * 
4318  */
4319
4320 /**
4321  * @class Roo.bootstrap.NavGroup
4322  * @extends Roo.bootstrap.Component
4323  * Bootstrap NavGroup class
4324  * @cfg {String} align (left|right)
4325  * @cfg {Boolean} inverse
4326  * @cfg {String} type (nav|pills|tab) default nav
4327  * @cfg {String} navId - reference Id for navbar.
4328
4329  * 
4330  * @constructor
4331  * Create a new nav group
4332  * @param {Object} config The config object
4333  */
4334
4335 Roo.bootstrap.NavGroup = function(config){
4336     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4337     this.navItems = [];
4338    
4339     Roo.bootstrap.NavGroup.register(this);
4340      this.addEvents({
4341         /**
4342              * @event changed
4343              * Fires when the active item changes
4344              * @param {Roo.bootstrap.NavGroup} this
4345              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4346              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4347          */
4348         'changed': true
4349      });
4350     
4351 };
4352
4353 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4354     
4355     align: '',
4356     inverse: false,
4357     form: false,
4358     type: 'nav',
4359     navId : '',
4360     // private
4361     
4362     navItems : false, 
4363     
4364     getAutoCreate : function()
4365     {
4366         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4367         
4368         cfg = {
4369             tag : 'ul',
4370             cls: 'nav' 
4371         };
4372         if (Roo.bootstrap.version == 4) {
4373             if (['tabs','pills'].indexOf(this.type) != -1) {
4374                 cfg.cls += ' nav-' + this.type; 
4375             } else {
4376                 cfg.cls += ' navbar-nav';
4377             }
4378         } else {
4379             if (['tabs','pills'].indexOf(this.type) != -1) {
4380                 cfg.cls += ' nav-' + this.type
4381             } else {
4382                 if (this.type !== 'nav') {
4383                     Roo.log('nav type must be nav/tabs/pills')
4384                 }
4385                 cfg.cls += ' navbar-nav'
4386             }
4387         }
4388         
4389         if (this.parent() && this.parent().sidebar) {
4390             cfg = {
4391                 tag: 'ul',
4392                 cls: 'dashboard-menu sidebar-menu'
4393             };
4394             
4395             return cfg;
4396         }
4397         
4398         if (this.form === true) {
4399             cfg = {
4400                 tag: 'form',
4401                 cls: 'navbar-form form-inline'
4402             };
4403             
4404             if (this.align === 'right') {
4405                 cfg.cls += ' navbar-right ml-md-auto';
4406             } else {
4407                 cfg.cls += ' navbar-left';
4408             }
4409         }
4410         
4411         if (this.align === 'right') {
4412             cfg.cls += ' navbar-right ml-md-auto';
4413         } else {
4414             cfg.cls += ' mr-auto';
4415         }
4416         
4417         if (this.inverse) {
4418             cfg.cls += ' navbar-inverse';
4419             
4420         }
4421         
4422         
4423         return cfg;
4424     },
4425     /**
4426     * sets the active Navigation item
4427     * @param {Roo.bootstrap.NavItem} the new current navitem
4428     */
4429     setActiveItem : function(item)
4430     {
4431         var prev = false;
4432         Roo.each(this.navItems, function(v){
4433             if (v == item) {
4434                 return ;
4435             }
4436             if (v.isActive()) {
4437                 v.setActive(false, true);
4438                 prev = v;
4439                 
4440             }
4441             
4442         });
4443
4444         item.setActive(true, true);
4445         this.fireEvent('changed', this, item, prev);
4446         
4447         
4448     },
4449     /**
4450     * gets the active Navigation item
4451     * @return {Roo.bootstrap.NavItem} the current navitem
4452     */
4453     getActive : function()
4454     {
4455         
4456         var prev = false;
4457         Roo.each(this.navItems, function(v){
4458             
4459             if (v.isActive()) {
4460                 prev = v;
4461                 
4462             }
4463             
4464         });
4465         return prev;
4466     },
4467     
4468     indexOfNav : function()
4469     {
4470         
4471         var prev = false;
4472         Roo.each(this.navItems, function(v,i){
4473             
4474             if (v.isActive()) {
4475                 prev = i;
4476                 
4477             }
4478             
4479         });
4480         return prev;
4481     },
4482     /**
4483     * adds a Navigation item
4484     * @param {Roo.bootstrap.NavItem} the navitem to add
4485     */
4486     addItem : function(cfg)
4487     {
4488         if (this.form && Roo.bootstrap.version == 4) {
4489             cfg.tag = 'div';
4490         }
4491         var cn = new Roo.bootstrap.NavItem(cfg);
4492         this.register(cn);
4493         cn.parentId = this.id;
4494         cn.onRender(this.el, null);
4495         return cn;
4496     },
4497     /**
4498     * register a Navigation item
4499     * @param {Roo.bootstrap.NavItem} the navitem to add
4500     */
4501     register : function(item)
4502     {
4503         this.navItems.push( item);
4504         item.navId = this.navId;
4505     
4506     },
4507     
4508     /**
4509     * clear all the Navigation item
4510     */
4511    
4512     clearAll : function()
4513     {
4514         this.navItems = [];
4515         this.el.dom.innerHTML = '';
4516     },
4517     
4518     getNavItem: function(tabId)
4519     {
4520         var ret = false;
4521         Roo.each(this.navItems, function(e) {
4522             if (e.tabId == tabId) {
4523                ret =  e;
4524                return false;
4525             }
4526             return true;
4527             
4528         });
4529         return ret;
4530     },
4531     
4532     setActiveNext : function()
4533     {
4534         var i = this.indexOfNav(this.getActive());
4535         if (i > this.navItems.length) {
4536             return;
4537         }
4538         this.setActiveItem(this.navItems[i+1]);
4539     },
4540     setActivePrev : function()
4541     {
4542         var i = this.indexOfNav(this.getActive());
4543         if (i  < 1) {
4544             return;
4545         }
4546         this.setActiveItem(this.navItems[i-1]);
4547     },
4548     clearWasActive : function(except) {
4549         Roo.each(this.navItems, function(e) {
4550             if (e.tabId != except.tabId && e.was_active) {
4551                e.was_active = false;
4552                return false;
4553             }
4554             return true;
4555             
4556         });
4557     },
4558     getWasActive : function ()
4559     {
4560         var r = false;
4561         Roo.each(this.navItems, function(e) {
4562             if (e.was_active) {
4563                r = e;
4564                return false;
4565             }
4566             return true;
4567             
4568         });
4569         return r;
4570     }
4571     
4572     
4573 });
4574
4575  
4576 Roo.apply(Roo.bootstrap.NavGroup, {
4577     
4578     groups: {},
4579      /**
4580     * register a Navigation Group
4581     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4582     */
4583     register : function(navgrp)
4584     {
4585         this.groups[navgrp.navId] = navgrp;
4586         
4587     },
4588     /**
4589     * fetch a Navigation Group based on the navigation ID
4590     * @param {string} the navgroup to add
4591     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4592     */
4593     get: function(navId) {
4594         if (typeof(this.groups[navId]) == 'undefined') {
4595             return false;
4596             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4597         }
4598         return this.groups[navId] ;
4599     }
4600     
4601     
4602     
4603 });
4604
4605  /*
4606  * - LGPL
4607  *
4608  * row
4609  * 
4610  */
4611
4612 /**
4613  * @class Roo.bootstrap.NavItem
4614  * @extends Roo.bootstrap.Component
4615  * Bootstrap Navbar.NavItem class
4616  * @cfg {String} href  link to
4617  * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
4618
4619  * @cfg {String} html content of button
4620  * @cfg {String} badge text inside badge
4621  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4622  * @cfg {String} glyphicon DEPRICATED - use fa
4623  * @cfg {String} icon DEPRICATED - use fa
4624  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4625  * @cfg {Boolean} active Is item active
4626  * @cfg {Boolean} disabled Is item disabled
4627  
4628  * @cfg {Boolean} preventDefault (true | false) default false
4629  * @cfg {String} tabId the tab that this item activates.
4630  * @cfg {String} tagtype (a|span) render as a href or span?
4631  * @cfg {Boolean} animateRef (true|false) link to element default false  
4632   
4633  * @constructor
4634  * Create a new Navbar Item
4635  * @param {Object} config The config object
4636  */
4637 Roo.bootstrap.NavItem = function(config){
4638     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4639     this.addEvents({
4640         // raw events
4641         /**
4642          * @event click
4643          * The raw click event for the entire grid.
4644          * @param {Roo.EventObject} e
4645          */
4646         "click" : true,
4647          /**
4648             * @event changed
4649             * Fires when the active item active state changes
4650             * @param {Roo.bootstrap.NavItem} this
4651             * @param {boolean} state the new state
4652              
4653          */
4654         'changed': true,
4655         /**
4656             * @event scrollto
4657             * Fires when scroll to element
4658             * @param {Roo.bootstrap.NavItem} this
4659             * @param {Object} options
4660             * @param {Roo.EventObject} e
4661              
4662          */
4663         'scrollto': true
4664     });
4665    
4666 };
4667
4668 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4669     
4670     href: false,
4671     html: '',
4672     badge: '',
4673     icon: false,
4674     fa : false,
4675     glyphicon: false,
4676     active: false,
4677     preventDefault : false,
4678     tabId : false,
4679     tagtype : 'a',
4680     tag: 'li',
4681     disabled : false,
4682     animateRef : false,
4683     was_active : false,
4684     button_weight : '',
4685     button_outline : false,
4686     
4687     navLink: false,
4688     
4689     getAutoCreate : function(){
4690          
4691         var cfg = {
4692             tag: this.tag,
4693             cls: 'nav-item'
4694         };
4695         
4696         if (this.active) {
4697             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4698         }
4699         if (this.disabled) {
4700             cfg.cls += ' disabled';
4701         }
4702         
4703         // BS4 only?
4704         if (this.button_weight.length) {
4705             cfg.tag = this.href ? 'a' : 'button';
4706             cfg.html = this.html || '';
4707             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
4708             if (this.href) {
4709                 cfg.href = this.href;
4710             }
4711             if (this.fa) {
4712                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
4713             }
4714             
4715             // menu .. should add dropdown-menu class - so no need for carat..
4716             
4717             if (this.badge !== '') {
4718                  
4719                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4720             }
4721             return cfg;
4722         }
4723         
4724         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
4725             cfg.cn = [
4726                 {
4727                     tag: this.tagtype,
4728                     href : this.href || "#",
4729                     html: this.html || ''
4730                 }
4731             ];
4732             if (this.tagtype == 'a') {
4733                 cfg.cn[0].cls = 'nav-link';
4734             }
4735             if (this.icon) {
4736                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
4737             }
4738             if (this.fa) {
4739                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
4740             }
4741             if(this.glyphicon) {
4742                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4743             }
4744             
4745             if (this.menu) {
4746                 
4747                 cfg.cn[0].html += " <span class='caret'></span>";
4748              
4749             }
4750             
4751             if (this.badge !== '') {
4752                  
4753                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4754             }
4755         }
4756         
4757         
4758         
4759         return cfg;
4760     },
4761     onRender : function(ct, position)
4762     {
4763        // Roo.log("Call onRender: " + this.xtype);
4764         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
4765             this.tag = 'div';
4766         }
4767         
4768         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
4769         this.navLink = this.el.select('.nav-link',true).first();
4770         return ret;
4771     },
4772       
4773     
4774     initEvents: function() 
4775     {
4776         if (typeof (this.menu) != 'undefined') {
4777             this.menu.parentType = this.xtype;
4778             this.menu.triggerEl = this.el;
4779             this.menu = this.addxtype(Roo.apply({}, this.menu));
4780         }
4781         
4782         this.el.select('a',true).on('click', this.onClick, this);
4783         
4784         if(this.tagtype == 'span'){
4785             this.el.select('span',true).on('click', this.onClick, this);
4786         }
4787        
4788         // at this point parent should be available..
4789         this.parent().register(this);
4790     },
4791     
4792     onClick : function(e)
4793     {
4794         if (e.getTarget('.dropdown-menu-item')) {
4795             // did you click on a menu itemm.... - then don't trigger onclick..
4796             return;
4797         }
4798         
4799         if(
4800                 this.preventDefault || 
4801                 this.href == '#' 
4802         ){
4803             Roo.log("NavItem - prevent Default?");
4804             e.preventDefault();
4805         }
4806         
4807         if (this.disabled) {
4808             return;
4809         }
4810         
4811         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4812         if (tg && tg.transition) {
4813             Roo.log("waiting for the transitionend");
4814             return;
4815         }
4816         
4817         
4818         
4819         //Roo.log("fire event clicked");
4820         if(this.fireEvent('click', this, e) === false){
4821             return;
4822         };
4823         
4824         if(this.tagtype == 'span'){
4825             return;
4826         }
4827         
4828         //Roo.log(this.href);
4829         var ael = this.el.select('a',true).first();
4830         //Roo.log(ael);
4831         
4832         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4833             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4834             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4835                 return; // ignore... - it's a 'hash' to another page.
4836             }
4837             Roo.log("NavItem - prevent Default?");
4838             e.preventDefault();
4839             this.scrollToElement(e);
4840         }
4841         
4842         
4843         var p =  this.parent();
4844    
4845         if (['tabs','pills'].indexOf(p.type)!==-1) {
4846             if (typeof(p.setActiveItem) !== 'undefined') {
4847                 p.setActiveItem(this);
4848             }
4849         }
4850         
4851         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4852         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4853             // remove the collapsed menu expand...
4854             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4855         }
4856     },
4857     
4858     isActive: function () {
4859         return this.active
4860     },
4861     setActive : function(state, fire, is_was_active)
4862     {
4863         if (this.active && !state && this.navId) {
4864             this.was_active = true;
4865             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4866             if (nv) {
4867                 nv.clearWasActive(this);
4868             }
4869             
4870         }
4871         this.active = state;
4872         
4873         if (!state ) {
4874             this.el.removeClass('active');
4875             this.navLink ? this.navLink.removeClass('active') : false;
4876         } else if (!this.el.hasClass('active')) {
4877             
4878             this.el.addClass('active');
4879             if (Roo.bootstrap.version == 4 && this.navLink ) {
4880                 this.navLink.addClass('active');
4881             }
4882             
4883         }
4884         if (fire) {
4885             this.fireEvent('changed', this, state);
4886         }
4887         
4888         // show a panel if it's registered and related..
4889         
4890         if (!this.navId || !this.tabId || !state || is_was_active) {
4891             return;
4892         }
4893         
4894         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4895         if (!tg) {
4896             return;
4897         }
4898         var pan = tg.getPanelByName(this.tabId);
4899         if (!pan) {
4900             return;
4901         }
4902         // if we can not flip to new panel - go back to old nav highlight..
4903         if (false == tg.showPanel(pan)) {
4904             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4905             if (nv) {
4906                 var onav = nv.getWasActive();
4907                 if (onav) {
4908                     onav.setActive(true, false, true);
4909                 }
4910             }
4911             
4912         }
4913         
4914         
4915         
4916     },
4917      // this should not be here...
4918     setDisabled : function(state)
4919     {
4920         this.disabled = state;
4921         if (!state ) {
4922             this.el.removeClass('disabled');
4923         } else if (!this.el.hasClass('disabled')) {
4924             this.el.addClass('disabled');
4925         }
4926         
4927     },
4928     
4929     /**
4930      * Fetch the element to display the tooltip on.
4931      * @return {Roo.Element} defaults to this.el
4932      */
4933     tooltipEl : function()
4934     {
4935         return this.el.select('' + this.tagtype + '', true).first();
4936     },
4937     
4938     scrollToElement : function(e)
4939     {
4940         var c = document.body;
4941         
4942         /*
4943          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4944          */
4945         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4946             c = document.documentElement;
4947         }
4948         
4949         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4950         
4951         if(!target){
4952             return;
4953         }
4954
4955         var o = target.calcOffsetsTo(c);
4956         
4957         var options = {
4958             target : target,
4959             value : o[1]
4960         };
4961         
4962         this.fireEvent('scrollto', this, options, e);
4963         
4964         Roo.get(c).scrollTo('top', options.value, true);
4965         
4966         return;
4967     }
4968 });
4969  
4970
4971  /*
4972  * - LGPL
4973  *
4974  * sidebar item
4975  *
4976  *  li
4977  *    <span> icon </span>
4978  *    <span> text </span>
4979  *    <span>badge </span>
4980  */
4981
4982 /**
4983  * @class Roo.bootstrap.NavSidebarItem
4984  * @extends Roo.bootstrap.NavItem
4985  * Bootstrap Navbar.NavSidebarItem class
4986  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4987  * {Boolean} open is the menu open
4988  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4989  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4990  * {String} buttonSize (sm|md|lg)the extra classes for the button
4991  * {Boolean} showArrow show arrow next to the text (default true)
4992  * @constructor
4993  * Create a new Navbar Button
4994  * @param {Object} config The config object
4995  */
4996 Roo.bootstrap.NavSidebarItem = function(config){
4997     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4998     this.addEvents({
4999         // raw events
5000         /**
5001          * @event click
5002          * The raw click event for the entire grid.
5003          * @param {Roo.EventObject} e
5004          */
5005         "click" : true,
5006          /**
5007             * @event changed
5008             * Fires when the active item active state changes
5009             * @param {Roo.bootstrap.NavSidebarItem} this
5010             * @param {boolean} state the new state
5011              
5012          */
5013         'changed': true
5014     });
5015    
5016 };
5017
5018 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
5019     
5020     badgeWeight : 'default',
5021     
5022     open: false,
5023     
5024     buttonView : false,
5025     
5026     buttonWeight : 'default',
5027     
5028     buttonSize : 'md',
5029     
5030     showArrow : true,
5031     
5032     getAutoCreate : function(){
5033         
5034         
5035         var a = {
5036                 tag: 'a',
5037                 href : this.href || '#',
5038                 cls: '',
5039                 html : '',
5040                 cn : []
5041         };
5042         
5043         if(this.buttonView){
5044             a = {
5045                 tag: 'button',
5046                 href : this.href || '#',
5047                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
5048                 html : this.html,
5049                 cn : []
5050             };
5051         }
5052         
5053         var cfg = {
5054             tag: 'li',
5055             cls: '',
5056             cn: [ a ]
5057         };
5058         
5059         if (this.active) {
5060             cfg.cls += ' active';
5061         }
5062         
5063         if (this.disabled) {
5064             cfg.cls += ' disabled';
5065         }
5066         if (this.open) {
5067             cfg.cls += ' open x-open';
5068         }
5069         // left icon..
5070         if (this.glyphicon || this.icon) {
5071             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
5072             a.cn.push({ tag : 'i', cls : c }) ;
5073         }
5074         
5075         if(!this.buttonView){
5076             var span = {
5077                 tag: 'span',
5078                 html : this.html || ''
5079             };
5080
5081             a.cn.push(span);
5082             
5083         }
5084         
5085         if (this.badge !== '') {
5086             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
5087         }
5088         
5089         if (this.menu) {
5090             
5091             if(this.showArrow){
5092                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5093             }
5094             
5095             a.cls += ' dropdown-toggle treeview' ;
5096         }
5097         
5098         return cfg;
5099     },
5100     
5101     initEvents : function()
5102     { 
5103         if (typeof (this.menu) != 'undefined') {
5104             this.menu.parentType = this.xtype;
5105             this.menu.triggerEl = this.el;
5106             this.menu = this.addxtype(Roo.apply({}, this.menu));
5107         }
5108         
5109         this.el.on('click', this.onClick, this);
5110         
5111         if(this.badge !== ''){
5112             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5113         }
5114         
5115     },
5116     
5117     onClick : function(e)
5118     {
5119         if(this.disabled){
5120             e.preventDefault();
5121             return;
5122         }
5123         
5124         if(this.preventDefault){
5125             e.preventDefault();
5126         }
5127         
5128         this.fireEvent('click', this);
5129     },
5130     
5131     disable : function()
5132     {
5133         this.setDisabled(true);
5134     },
5135     
5136     enable : function()
5137     {
5138         this.setDisabled(false);
5139     },
5140     
5141     setDisabled : function(state)
5142     {
5143         if(this.disabled == state){
5144             return;
5145         }
5146         
5147         this.disabled = state;
5148         
5149         if (state) {
5150             this.el.addClass('disabled');
5151             return;
5152         }
5153         
5154         this.el.removeClass('disabled');
5155         
5156         return;
5157     },
5158     
5159     setActive : function(state)
5160     {
5161         if(this.active == state){
5162             return;
5163         }
5164         
5165         this.active = state;
5166         
5167         if (state) {
5168             this.el.addClass('active');
5169             return;
5170         }
5171         
5172         this.el.removeClass('active');
5173         
5174         return;
5175     },
5176     
5177     isActive: function () 
5178     {
5179         return this.active;
5180     },
5181     
5182     setBadge : function(str)
5183     {
5184         if(!this.badgeEl){
5185             return;
5186         }
5187         
5188         this.badgeEl.dom.innerHTML = str;
5189     }
5190     
5191    
5192      
5193  
5194 });
5195  
5196
5197  /*
5198  * - LGPL
5199  *
5200  * row
5201  * 
5202  */
5203
5204 /**
5205  * @class Roo.bootstrap.Row
5206  * @extends Roo.bootstrap.Component
5207  * Bootstrap Row class (contains columns...)
5208  * 
5209  * @constructor
5210  * Create a new Row
5211  * @param {Object} config The config object
5212  */
5213
5214 Roo.bootstrap.Row = function(config){
5215     Roo.bootstrap.Row.superclass.constructor.call(this, config);
5216 };
5217
5218 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
5219     
5220     getAutoCreate : function(){
5221        return {
5222             cls: 'row clearfix'
5223        };
5224     }
5225     
5226     
5227 });
5228
5229  
5230
5231  /*
5232  * - LGPL
5233  *
5234  * element
5235  * 
5236  */
5237
5238 /**
5239  * @class Roo.bootstrap.Element
5240  * @extends Roo.bootstrap.Component
5241  * Bootstrap Element class
5242  * @cfg {String} html contents of the element
5243  * @cfg {String} tag tag of the element
5244  * @cfg {String} cls class of the element
5245  * @cfg {Boolean} preventDefault (true|false) default false
5246  * @cfg {Boolean} clickable (true|false) default false
5247  * 
5248  * @constructor
5249  * Create a new Element
5250  * @param {Object} config The config object
5251  */
5252
5253 Roo.bootstrap.Element = function(config){
5254     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5255     
5256     this.addEvents({
5257         // raw events
5258         /**
5259          * @event click
5260          * When a element is chick
5261          * @param {Roo.bootstrap.Element} this
5262          * @param {Roo.EventObject} e
5263          */
5264         "click" : true
5265     });
5266 };
5267
5268 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5269     
5270     tag: 'div',
5271     cls: '',
5272     html: '',
5273     preventDefault: false, 
5274     clickable: false,
5275     
5276     getAutoCreate : function(){
5277         
5278         var cfg = {
5279             tag: this.tag,
5280             // cls: this.cls, double assign in parent class Component.js :: onRender
5281             html: this.html
5282         };
5283         
5284         return cfg;
5285     },
5286     
5287     initEvents: function() 
5288     {
5289         Roo.bootstrap.Element.superclass.initEvents.call(this);
5290         
5291         if(this.clickable){
5292             this.el.on('click', this.onClick, this);
5293         }
5294         
5295     },
5296     
5297     onClick : function(e)
5298     {
5299         if(this.preventDefault){
5300             e.preventDefault();
5301         }
5302         
5303         this.fireEvent('click', this, e);
5304     },
5305     
5306     getValue : function()
5307     {
5308         return this.el.dom.innerHTML;
5309     },
5310     
5311     setValue : function(value)
5312     {
5313         this.el.dom.innerHTML = value;
5314     }
5315    
5316 });
5317
5318  
5319
5320  /*
5321  * - LGPL
5322  *
5323  * pagination
5324  * 
5325  */
5326
5327 /**
5328  * @class Roo.bootstrap.Pagination
5329  * @extends Roo.bootstrap.Component
5330  * Bootstrap Pagination class
5331  * @cfg {String} size xs | sm | md | lg
5332  * @cfg {Boolean} inverse false | true
5333  * 
5334  * @constructor
5335  * Create a new Pagination
5336  * @param {Object} config The config object
5337  */
5338
5339 Roo.bootstrap.Pagination = function(config){
5340     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5341 };
5342
5343 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5344     
5345     cls: false,
5346     size: false,
5347     inverse: false,
5348     
5349     getAutoCreate : function(){
5350         var cfg = {
5351             tag: 'ul',
5352                 cls: 'pagination'
5353         };
5354         if (this.inverse) {
5355             cfg.cls += ' inverse';
5356         }
5357         if (this.html) {
5358             cfg.html=this.html;
5359         }
5360         if (this.cls) {
5361             cfg.cls += " " + this.cls;
5362         }
5363         return cfg;
5364     }
5365    
5366 });
5367
5368  
5369
5370  /*
5371  * - LGPL
5372  *
5373  * Pagination item
5374  * 
5375  */
5376
5377
5378 /**
5379  * @class Roo.bootstrap.PaginationItem
5380  * @extends Roo.bootstrap.Component
5381  * Bootstrap PaginationItem class
5382  * @cfg {String} html text
5383  * @cfg {String} href the link
5384  * @cfg {Boolean} preventDefault (true | false) default true
5385  * @cfg {Boolean} active (true | false) default false
5386  * @cfg {Boolean} disabled default false
5387  * 
5388  * 
5389  * @constructor
5390  * Create a new PaginationItem
5391  * @param {Object} config The config object
5392  */
5393
5394
5395 Roo.bootstrap.PaginationItem = function(config){
5396     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5397     this.addEvents({
5398         // raw events
5399         /**
5400          * @event click
5401          * The raw click event for the entire grid.
5402          * @param {Roo.EventObject} e
5403          */
5404         "click" : true
5405     });
5406 };
5407
5408 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5409     
5410     href : false,
5411     html : false,
5412     preventDefault: true,
5413     active : false,
5414     cls : false,
5415     disabled: false,
5416     
5417     getAutoCreate : function(){
5418         var cfg= {
5419             tag: 'li',
5420             cn: [
5421                 {
5422                     tag : 'a',
5423                     href : this.href ? this.href : '#',
5424                     html : this.html ? this.html : ''
5425                 }
5426             ]
5427         };
5428         
5429         if(this.cls){
5430             cfg.cls = this.cls;
5431         }
5432         
5433         if(this.disabled){
5434             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5435         }
5436         
5437         if(this.active){
5438             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5439         }
5440         
5441         return cfg;
5442     },
5443     
5444     initEvents: function() {
5445         
5446         this.el.on('click', this.onClick, this);
5447         
5448     },
5449     onClick : function(e)
5450     {
5451         Roo.log('PaginationItem on click ');
5452         if(this.preventDefault){
5453             e.preventDefault();
5454         }
5455         
5456         if(this.disabled){
5457             return;
5458         }
5459         
5460         this.fireEvent('click', this, e);
5461     }
5462    
5463 });
5464
5465  
5466
5467  /*
5468  * - LGPL
5469  *
5470  * slider
5471  * 
5472  */
5473
5474
5475 /**
5476  * @class Roo.bootstrap.Slider
5477  * @extends Roo.bootstrap.Component
5478  * Bootstrap Slider class
5479  *    
5480  * @constructor
5481  * Create a new Slider
5482  * @param {Object} config The config object
5483  */
5484
5485 Roo.bootstrap.Slider = function(config){
5486     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5487 };
5488
5489 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5490     
5491     getAutoCreate : function(){
5492         
5493         var cfg = {
5494             tag: 'div',
5495             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5496             cn: [
5497                 {
5498                     tag: 'a',
5499                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5500                 }
5501             ]
5502         };
5503         
5504         return cfg;
5505     }
5506    
5507 });
5508
5509  /*
5510  * Based on:
5511  * Ext JS Library 1.1.1
5512  * Copyright(c) 2006-2007, Ext JS, LLC.
5513  *
5514  * Originally Released Under LGPL - original licence link has changed is not relivant.
5515  *
5516  * Fork - LGPL
5517  * <script type="text/javascript">
5518  */
5519  
5520
5521 /**
5522  * @class Roo.grid.ColumnModel
5523  * @extends Roo.util.Observable
5524  * This is the default implementation of a ColumnModel used by the Grid. It defines
5525  * the columns in the grid.
5526  * <br>Usage:<br>
5527  <pre><code>
5528  var colModel = new Roo.grid.ColumnModel([
5529         {header: "Ticker", width: 60, sortable: true, locked: true},
5530         {header: "Company Name", width: 150, sortable: true},
5531         {header: "Market Cap.", width: 100, sortable: true},
5532         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5533         {header: "Employees", width: 100, sortable: true, resizable: false}
5534  ]);
5535  </code></pre>
5536  * <p>
5537  
5538  * The config options listed for this class are options which may appear in each
5539  * individual column definition.
5540  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5541  * @constructor
5542  * @param {Object} config An Array of column config objects. See this class's
5543  * config objects for details.
5544 */
5545 Roo.grid.ColumnModel = function(config){
5546         /**
5547      * The config passed into the constructor
5548      */
5549     this.config = config;
5550     this.lookup = {};
5551
5552     // if no id, create one
5553     // if the column does not have a dataIndex mapping,
5554     // map it to the order it is in the config
5555     for(var i = 0, len = config.length; i < len; i++){
5556         var c = config[i];
5557         if(typeof c.dataIndex == "undefined"){
5558             c.dataIndex = i;
5559         }
5560         if(typeof c.renderer == "string"){
5561             c.renderer = Roo.util.Format[c.renderer];
5562         }
5563         if(typeof c.id == "undefined"){
5564             c.id = Roo.id();
5565         }
5566         if(c.editor && c.editor.xtype){
5567             c.editor  = Roo.factory(c.editor, Roo.grid);
5568         }
5569         if(c.editor && c.editor.isFormField){
5570             c.editor = new Roo.grid.GridEditor(c.editor);
5571         }
5572         this.lookup[c.id] = c;
5573     }
5574
5575     /**
5576      * The width of columns which have no width specified (defaults to 100)
5577      * @type Number
5578      */
5579     this.defaultWidth = 100;
5580
5581     /**
5582      * Default sortable of columns which have no sortable specified (defaults to false)
5583      * @type Boolean
5584      */
5585     this.defaultSortable = false;
5586
5587     this.addEvents({
5588         /**
5589              * @event widthchange
5590              * Fires when the width of a column changes.
5591              * @param {ColumnModel} this
5592              * @param {Number} columnIndex The column index
5593              * @param {Number} newWidth The new width
5594              */
5595             "widthchange": true,
5596         /**
5597              * @event headerchange
5598              * Fires when the text of a header changes.
5599              * @param {ColumnModel} this
5600              * @param {Number} columnIndex The column index
5601              * @param {Number} newText The new header text
5602              */
5603             "headerchange": true,
5604         /**
5605              * @event hiddenchange
5606              * Fires when a column is hidden or "unhidden".
5607              * @param {ColumnModel} this
5608              * @param {Number} columnIndex The column index
5609              * @param {Boolean} hidden true if hidden, false otherwise
5610              */
5611             "hiddenchange": true,
5612             /**
5613          * @event columnmoved
5614          * Fires when a column is moved.
5615          * @param {ColumnModel} this
5616          * @param {Number} oldIndex
5617          * @param {Number} newIndex
5618          */
5619         "columnmoved" : true,
5620         /**
5621          * @event columlockchange
5622          * Fires when a column's locked state is changed
5623          * @param {ColumnModel} this
5624          * @param {Number} colIndex
5625          * @param {Boolean} locked true if locked
5626          */
5627         "columnlockchange" : true
5628     });
5629     Roo.grid.ColumnModel.superclass.constructor.call(this);
5630 };
5631 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5632     /**
5633      * @cfg {String} header The header text to display in the Grid view.
5634      */
5635     /**
5636      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5637      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5638      * specified, the column's index is used as an index into the Record's data Array.
5639      */
5640     /**
5641      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5642      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5643      */
5644     /**
5645      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5646      * Defaults to the value of the {@link #defaultSortable} property.
5647      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5648      */
5649     /**
5650      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5651      */
5652     /**
5653      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5654      */
5655     /**
5656      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5657      */
5658     /**
5659      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5660      */
5661     /**
5662      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5663      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5664      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5665      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5666      */
5667        /**
5668      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5669      */
5670     /**
5671      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5672      */
5673     /**
5674      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5675      */
5676     /**
5677      * @cfg {String} cursor (Optional)
5678      */
5679     /**
5680      * @cfg {String} tooltip (Optional)
5681      */
5682     /**
5683      * @cfg {Number} xs (Optional)
5684      */
5685     /**
5686      * @cfg {Number} sm (Optional)
5687      */
5688     /**
5689      * @cfg {Number} md (Optional)
5690      */
5691     /**
5692      * @cfg {Number} lg (Optional)
5693      */
5694     /**
5695      * Returns the id of the column at the specified index.
5696      * @param {Number} index The column index
5697      * @return {String} the id
5698      */
5699     getColumnId : function(index){
5700         return this.config[index].id;
5701     },
5702
5703     /**
5704      * Returns the column for a specified id.
5705      * @param {String} id The column id
5706      * @return {Object} the column
5707      */
5708     getColumnById : function(id){
5709         return this.lookup[id];
5710     },
5711
5712     
5713     /**
5714      * Returns the column for a specified dataIndex.
5715      * @param {String} dataIndex The column dataIndex
5716      * @return {Object|Boolean} the column or false if not found
5717      */
5718     getColumnByDataIndex: function(dataIndex){
5719         var index = this.findColumnIndex(dataIndex);
5720         return index > -1 ? this.config[index] : false;
5721     },
5722     
5723     /**
5724      * Returns the index for a specified column id.
5725      * @param {String} id The column id
5726      * @return {Number} the index, or -1 if not found
5727      */
5728     getIndexById : function(id){
5729         for(var i = 0, len = this.config.length; i < len; i++){
5730             if(this.config[i].id == id){
5731                 return i;
5732             }
5733         }
5734         return -1;
5735     },
5736     
5737     /**
5738      * Returns the index for a specified column dataIndex.
5739      * @param {String} dataIndex The column dataIndex
5740      * @return {Number} the index, or -1 if not found
5741      */
5742     
5743     findColumnIndex : function(dataIndex){
5744         for(var i = 0, len = this.config.length; i < len; i++){
5745             if(this.config[i].dataIndex == dataIndex){
5746                 return i;
5747             }
5748         }
5749         return -1;
5750     },
5751     
5752     
5753     moveColumn : function(oldIndex, newIndex){
5754         var c = this.config[oldIndex];
5755         this.config.splice(oldIndex, 1);
5756         this.config.splice(newIndex, 0, c);
5757         this.dataMap = null;
5758         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5759     },
5760
5761     isLocked : function(colIndex){
5762         return this.config[colIndex].locked === true;
5763     },
5764
5765     setLocked : function(colIndex, value, suppressEvent){
5766         if(this.isLocked(colIndex) == value){
5767             return;
5768         }
5769         this.config[colIndex].locked = value;
5770         if(!suppressEvent){
5771             this.fireEvent("columnlockchange", this, colIndex, value);
5772         }
5773     },
5774
5775     getTotalLockedWidth : function(){
5776         var totalWidth = 0;
5777         for(var i = 0; i < this.config.length; i++){
5778             if(this.isLocked(i) && !this.isHidden(i)){
5779                 this.totalWidth += this.getColumnWidth(i);
5780             }
5781         }
5782         return totalWidth;
5783     },
5784
5785     getLockedCount : function(){
5786         for(var i = 0, len = this.config.length; i < len; i++){
5787             if(!this.isLocked(i)){
5788                 return i;
5789             }
5790         }
5791         
5792         return this.config.length;
5793     },
5794
5795     /**
5796      * Returns the number of columns.
5797      * @return {Number}
5798      */
5799     getColumnCount : function(visibleOnly){
5800         if(visibleOnly === true){
5801             var c = 0;
5802             for(var i = 0, len = this.config.length; i < len; i++){
5803                 if(!this.isHidden(i)){
5804                     c++;
5805                 }
5806             }
5807             return c;
5808         }
5809         return this.config.length;
5810     },
5811
5812     /**
5813      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5814      * @param {Function} fn
5815      * @param {Object} scope (optional)
5816      * @return {Array} result
5817      */
5818     getColumnsBy : function(fn, scope){
5819         var r = [];
5820         for(var i = 0, len = this.config.length; i < len; i++){
5821             var c = this.config[i];
5822             if(fn.call(scope||this, c, i) === true){
5823                 r[r.length] = c;
5824             }
5825         }
5826         return r;
5827     },
5828
5829     /**
5830      * Returns true if the specified column is sortable.
5831      * @param {Number} col The column index
5832      * @return {Boolean}
5833      */
5834     isSortable : function(col){
5835         if(typeof this.config[col].sortable == "undefined"){
5836             return this.defaultSortable;
5837         }
5838         return this.config[col].sortable;
5839     },
5840
5841     /**
5842      * Returns the rendering (formatting) function defined for the column.
5843      * @param {Number} col The column index.
5844      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5845      */
5846     getRenderer : function(col){
5847         if(!this.config[col].renderer){
5848             return Roo.grid.ColumnModel.defaultRenderer;
5849         }
5850         return this.config[col].renderer;
5851     },
5852
5853     /**
5854      * Sets the rendering (formatting) function for a column.
5855      * @param {Number} col The column index
5856      * @param {Function} fn The function to use to process the cell's raw data
5857      * to return HTML markup for the grid view. The render function is called with
5858      * the following parameters:<ul>
5859      * <li>Data value.</li>
5860      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5861      * <li>css A CSS style string to apply to the table cell.</li>
5862      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5863      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5864      * <li>Row index</li>
5865      * <li>Column index</li>
5866      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5867      */
5868     setRenderer : function(col, fn){
5869         this.config[col].renderer = fn;
5870     },
5871
5872     /**
5873      * Returns the width for the specified column.
5874      * @param {Number} col The column index
5875      * @return {Number}
5876      */
5877     getColumnWidth : function(col){
5878         return this.config[col].width * 1 || this.defaultWidth;
5879     },
5880
5881     /**
5882      * Sets the width for a column.
5883      * @param {Number} col The column index
5884      * @param {Number} width The new width
5885      */
5886     setColumnWidth : function(col, width, suppressEvent){
5887         this.config[col].width = width;
5888         this.totalWidth = null;
5889         if(!suppressEvent){
5890              this.fireEvent("widthchange", this, col, width);
5891         }
5892     },
5893
5894     /**
5895      * Returns the total width of all columns.
5896      * @param {Boolean} includeHidden True to include hidden column widths
5897      * @return {Number}
5898      */
5899     getTotalWidth : function(includeHidden){
5900         if(!this.totalWidth){
5901             this.totalWidth = 0;
5902             for(var i = 0, len = this.config.length; i < len; i++){
5903                 if(includeHidden || !this.isHidden(i)){
5904                     this.totalWidth += this.getColumnWidth(i);
5905                 }
5906             }
5907         }
5908         return this.totalWidth;
5909     },
5910
5911     /**
5912      * Returns the header for the specified column.
5913      * @param {Number} col The column index
5914      * @return {String}
5915      */
5916     getColumnHeader : function(col){
5917         return this.config[col].header;
5918     },
5919
5920     /**
5921      * Sets the header for a column.
5922      * @param {Number} col The column index
5923      * @param {String} header The new header
5924      */
5925     setColumnHeader : function(col, header){
5926         this.config[col].header = header;
5927         this.fireEvent("headerchange", this, col, header);
5928     },
5929
5930     /**
5931      * Returns the tooltip for the specified column.
5932      * @param {Number} col The column index
5933      * @return {String}
5934      */
5935     getColumnTooltip : function(col){
5936             return this.config[col].tooltip;
5937     },
5938     /**
5939      * Sets the tooltip for a column.
5940      * @param {Number} col The column index
5941      * @param {String} tooltip The new tooltip
5942      */
5943     setColumnTooltip : function(col, tooltip){
5944             this.config[col].tooltip = tooltip;
5945     },
5946
5947     /**
5948      * Returns the dataIndex for the specified column.
5949      * @param {Number} col The column index
5950      * @return {Number}
5951      */
5952     getDataIndex : function(col){
5953         return this.config[col].dataIndex;
5954     },
5955
5956     /**
5957      * Sets the dataIndex for a column.
5958      * @param {Number} col The column index
5959      * @param {Number} dataIndex The new dataIndex
5960      */
5961     setDataIndex : function(col, dataIndex){
5962         this.config[col].dataIndex = dataIndex;
5963     },
5964
5965     
5966     
5967     /**
5968      * Returns true if the cell is editable.
5969      * @param {Number} colIndex The column index
5970      * @param {Number} rowIndex The row index - this is nto actually used..?
5971      * @return {Boolean}
5972      */
5973     isCellEditable : function(colIndex, rowIndex){
5974         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5975     },
5976
5977     /**
5978      * Returns the editor defined for the cell/column.
5979      * return false or null to disable editing.
5980      * @param {Number} colIndex The column index
5981      * @param {Number} rowIndex The row index
5982      * @return {Object}
5983      */
5984     getCellEditor : function(colIndex, rowIndex){
5985         return this.config[colIndex].editor;
5986     },
5987
5988     /**
5989      * Sets if a column is editable.
5990      * @param {Number} col The column index
5991      * @param {Boolean} editable True if the column is editable
5992      */
5993     setEditable : function(col, editable){
5994         this.config[col].editable = editable;
5995     },
5996
5997
5998     /**
5999      * Returns true if the column is hidden.
6000      * @param {Number} colIndex The column index
6001      * @return {Boolean}
6002      */
6003     isHidden : function(colIndex){
6004         return this.config[colIndex].hidden;
6005     },
6006
6007
6008     /**
6009      * Returns true if the column width cannot be changed
6010      */
6011     isFixed : function(colIndex){
6012         return this.config[colIndex].fixed;
6013     },
6014
6015     /**
6016      * Returns true if the column can be resized
6017      * @return {Boolean}
6018      */
6019     isResizable : function(colIndex){
6020         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
6021     },
6022     /**
6023      * Sets if a column is hidden.
6024      * @param {Number} colIndex The column index
6025      * @param {Boolean} hidden True if the column is hidden
6026      */
6027     setHidden : function(colIndex, hidden){
6028         this.config[colIndex].hidden = hidden;
6029         this.totalWidth = null;
6030         this.fireEvent("hiddenchange", this, colIndex, hidden);
6031     },
6032
6033     /**
6034      * Sets the editor for a column.
6035      * @param {Number} col The column index
6036      * @param {Object} editor The editor object
6037      */
6038     setEditor : function(col, editor){
6039         this.config[col].editor = editor;
6040     }
6041 });
6042
6043 Roo.grid.ColumnModel.defaultRenderer = function(value)
6044 {
6045     if(typeof value == "object") {
6046         return value;
6047     }
6048         if(typeof value == "string" && value.length < 1){
6049             return "&#160;";
6050         }
6051     
6052         return String.format("{0}", value);
6053 };
6054
6055 // Alias for backwards compatibility
6056 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
6057 /*
6058  * Based on:
6059  * Ext JS Library 1.1.1
6060  * Copyright(c) 2006-2007, Ext JS, LLC.
6061  *
6062  * Originally Released Under LGPL - original licence link has changed is not relivant.
6063  *
6064  * Fork - LGPL
6065  * <script type="text/javascript">
6066  */
6067  
6068 /**
6069  * @class Roo.LoadMask
6070  * A simple utility class for generically masking elements while loading data.  If the element being masked has
6071  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
6072  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
6073  * element's UpdateManager load indicator and will be destroyed after the initial load.
6074  * @constructor
6075  * Create a new LoadMask
6076  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
6077  * @param {Object} config The config object
6078  */
6079 Roo.LoadMask = function(el, config){
6080     this.el = Roo.get(el);
6081     Roo.apply(this, config);
6082     if(this.store){
6083         this.store.on('beforeload', this.onBeforeLoad, this);
6084         this.store.on('load', this.onLoad, this);
6085         this.store.on('loadexception', this.onLoadException, this);
6086         this.removeMask = false;
6087     }else{
6088         var um = this.el.getUpdateManager();
6089         um.showLoadIndicator = false; // disable the default indicator
6090         um.on('beforeupdate', this.onBeforeLoad, this);
6091         um.on('update', this.onLoad, this);
6092         um.on('failure', this.onLoad, this);
6093         this.removeMask = true;
6094     }
6095 };
6096
6097 Roo.LoadMask.prototype = {
6098     /**
6099      * @cfg {Boolean} removeMask
6100      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6101      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
6102      */
6103     /**
6104      * @cfg {String} msg
6105      * The text to display in a centered loading message box (defaults to 'Loading...')
6106      */
6107     msg : 'Loading...',
6108     /**
6109      * @cfg {String} msgCls
6110      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6111      */
6112     msgCls : 'x-mask-loading',
6113
6114     /**
6115      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6116      * @type Boolean
6117      */
6118     disabled: false,
6119
6120     /**
6121      * Disables the mask to prevent it from being displayed
6122      */
6123     disable : function(){
6124        this.disabled = true;
6125     },
6126
6127     /**
6128      * Enables the mask so that it can be displayed
6129      */
6130     enable : function(){
6131         this.disabled = false;
6132     },
6133     
6134     onLoadException : function()
6135     {
6136         Roo.log(arguments);
6137         
6138         if (typeof(arguments[3]) != 'undefined') {
6139             Roo.MessageBox.alert("Error loading",arguments[3]);
6140         } 
6141         /*
6142         try {
6143             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6144                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6145             }   
6146         } catch(e) {
6147             
6148         }
6149         */
6150     
6151         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6152     },
6153     // private
6154     onLoad : function()
6155     {
6156         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6157     },
6158
6159     // private
6160     onBeforeLoad : function(){
6161         if(!this.disabled){
6162             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6163         }
6164     },
6165
6166     // private
6167     destroy : function(){
6168         if(this.store){
6169             this.store.un('beforeload', this.onBeforeLoad, this);
6170             this.store.un('load', this.onLoad, this);
6171             this.store.un('loadexception', this.onLoadException, this);
6172         }else{
6173             var um = this.el.getUpdateManager();
6174             um.un('beforeupdate', this.onBeforeLoad, this);
6175             um.un('update', this.onLoad, this);
6176             um.un('failure', this.onLoad, this);
6177         }
6178     }
6179 };/*
6180  * - LGPL
6181  *
6182  * table
6183  * 
6184  */
6185
6186 /**
6187  * @class Roo.bootstrap.Table
6188  * @extends Roo.bootstrap.Component
6189  * Bootstrap Table class
6190  * @cfg {String} cls table class
6191  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6192  * @cfg {String} bgcolor Specifies the background color for a table
6193  * @cfg {Number} border Specifies whether the table cells should have borders or not
6194  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6195  * @cfg {Number} cellspacing Specifies the space between cells
6196  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6197  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6198  * @cfg {String} sortable Specifies that the table should be sortable
6199  * @cfg {String} summary Specifies a summary of the content of a table
6200  * @cfg {Number} width Specifies the width of a table
6201  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6202  * 
6203  * @cfg {boolean} striped Should the rows be alternative striped
6204  * @cfg {boolean} bordered Add borders to the table
6205  * @cfg {boolean} hover Add hover highlighting
6206  * @cfg {boolean} condensed Format condensed
6207  * @cfg {boolean} responsive Format condensed
6208  * @cfg {Boolean} loadMask (true|false) default false
6209  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6210  * @cfg {Boolean} headerShow (true|false) generate thead, default true
6211  * @cfg {Boolean} rowSelection (true|false) default false
6212  * @cfg {Boolean} cellSelection (true|false) default false
6213  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6214  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
6215  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
6216  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
6217  
6218  * 
6219  * @constructor
6220  * Create a new Table
6221  * @param {Object} config The config object
6222  */
6223
6224 Roo.bootstrap.Table = function(config){
6225     Roo.bootstrap.Table.superclass.constructor.call(this, config);
6226     
6227   
6228     
6229     // BC...
6230     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6231     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6232     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6233     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6234     
6235     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6236     if (this.sm) {
6237         this.sm.grid = this;
6238         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6239         this.sm = this.selModel;
6240         this.sm.xmodule = this.xmodule || false;
6241     }
6242     
6243     if (this.cm && typeof(this.cm.config) == 'undefined') {
6244         this.colModel = new Roo.grid.ColumnModel(this.cm);
6245         this.cm = this.colModel;
6246         this.cm.xmodule = this.xmodule || false;
6247     }
6248     if (this.store) {
6249         this.store= Roo.factory(this.store, Roo.data);
6250         this.ds = this.store;
6251         this.ds.xmodule = this.xmodule || false;
6252          
6253     }
6254     if (this.footer && this.store) {
6255         this.footer.dataSource = this.ds;
6256         this.footer = Roo.factory(this.footer);
6257     }
6258     
6259     /** @private */
6260     this.addEvents({
6261         /**
6262          * @event cellclick
6263          * Fires when a cell is clicked
6264          * @param {Roo.bootstrap.Table} this
6265          * @param {Roo.Element} el
6266          * @param {Number} rowIndex
6267          * @param {Number} columnIndex
6268          * @param {Roo.EventObject} e
6269          */
6270         "cellclick" : true,
6271         /**
6272          * @event celldblclick
6273          * Fires when a cell is double clicked
6274          * @param {Roo.bootstrap.Table} this
6275          * @param {Roo.Element} el
6276          * @param {Number} rowIndex
6277          * @param {Number} columnIndex
6278          * @param {Roo.EventObject} e
6279          */
6280         "celldblclick" : true,
6281         /**
6282          * @event rowclick
6283          * Fires when a row is clicked
6284          * @param {Roo.bootstrap.Table} this
6285          * @param {Roo.Element} el
6286          * @param {Number} rowIndex
6287          * @param {Roo.EventObject} e
6288          */
6289         "rowclick" : true,
6290         /**
6291          * @event rowdblclick
6292          * Fires when a row is double clicked
6293          * @param {Roo.bootstrap.Table} this
6294          * @param {Roo.Element} el
6295          * @param {Number} rowIndex
6296          * @param {Roo.EventObject} e
6297          */
6298         "rowdblclick" : true,
6299         /**
6300          * @event mouseover
6301          * Fires when a mouseover occur
6302          * @param {Roo.bootstrap.Table} this
6303          * @param {Roo.Element} el
6304          * @param {Number} rowIndex
6305          * @param {Number} columnIndex
6306          * @param {Roo.EventObject} e
6307          */
6308         "mouseover" : true,
6309         /**
6310          * @event mouseout
6311          * Fires when a mouseout occur
6312          * @param {Roo.bootstrap.Table} this
6313          * @param {Roo.Element} el
6314          * @param {Number} rowIndex
6315          * @param {Number} columnIndex
6316          * @param {Roo.EventObject} e
6317          */
6318         "mouseout" : true,
6319         /**
6320          * @event rowclass
6321          * Fires when a row is rendered, so you can change add a style to it.
6322          * @param {Roo.bootstrap.Table} this
6323          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6324          */
6325         'rowclass' : true,
6326           /**
6327          * @event rowsrendered
6328          * Fires when all the  rows have been rendered
6329          * @param {Roo.bootstrap.Table} this
6330          */
6331         'rowsrendered' : true,
6332         /**
6333          * @event contextmenu
6334          * The raw contextmenu event for the entire grid.
6335          * @param {Roo.EventObject} e
6336          */
6337         "contextmenu" : true,
6338         /**
6339          * @event rowcontextmenu
6340          * Fires when a row is right clicked
6341          * @param {Roo.bootstrap.Table} this
6342          * @param {Number} rowIndex
6343          * @param {Roo.EventObject} e
6344          */
6345         "rowcontextmenu" : true,
6346         /**
6347          * @event cellcontextmenu
6348          * Fires when a cell is right clicked
6349          * @param {Roo.bootstrap.Table} this
6350          * @param {Number} rowIndex
6351          * @param {Number} cellIndex
6352          * @param {Roo.EventObject} e
6353          */
6354          "cellcontextmenu" : true,
6355          /**
6356          * @event headercontextmenu
6357          * Fires when a header is right clicked
6358          * @param {Roo.bootstrap.Table} this
6359          * @param {Number} columnIndex
6360          * @param {Roo.EventObject} e
6361          */
6362         "headercontextmenu" : true
6363     });
6364 };
6365
6366 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6367     
6368     cls: false,
6369     align: false,
6370     bgcolor: false,
6371     border: false,
6372     cellpadding: false,
6373     cellspacing: false,
6374     frame: false,
6375     rules: false,
6376     sortable: false,
6377     summary: false,
6378     width: false,
6379     striped : false,
6380     scrollBody : false,
6381     bordered: false,
6382     hover:  false,
6383     condensed : false,
6384     responsive : false,
6385     sm : false,
6386     cm : false,
6387     store : false,
6388     loadMask : false,
6389     footerShow : true,
6390     headerShow : true,
6391   
6392     rowSelection : false,
6393     cellSelection : false,
6394     layout : false,
6395     
6396     // Roo.Element - the tbody
6397     mainBody: false,
6398     // Roo.Element - thead element
6399     mainHead: false,
6400     
6401     container: false, // used by gridpanel...
6402     
6403     lazyLoad : false,
6404     
6405     CSS : Roo.util.CSS,
6406     
6407     auto_hide_footer : false,
6408     
6409     getAutoCreate : function()
6410     {
6411         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6412         
6413         cfg = {
6414             tag: 'table',
6415             cls : 'table',
6416             cn : []
6417         };
6418         if (this.scrollBody) {
6419             cfg.cls += ' table-body-fixed';
6420         }    
6421         if (this.striped) {
6422             cfg.cls += ' table-striped';
6423         }
6424         
6425         if (this.hover) {
6426             cfg.cls += ' table-hover';
6427         }
6428         if (this.bordered) {
6429             cfg.cls += ' table-bordered';
6430         }
6431         if (this.condensed) {
6432             cfg.cls += ' table-condensed';
6433         }
6434         if (this.responsive) {
6435             cfg.cls += ' table-responsive';
6436         }
6437         
6438         if (this.cls) {
6439             cfg.cls+=  ' ' +this.cls;
6440         }
6441         
6442         // this lot should be simplifed...
6443         var _t = this;
6444         var cp = [
6445             'align',
6446             'bgcolor',
6447             'border',
6448             'cellpadding',
6449             'cellspacing',
6450             'frame',
6451             'rules',
6452             'sortable',
6453             'summary',
6454             'width'
6455         ].forEach(function(k) {
6456             if (_t[k]) {
6457                 cfg[k] = _t[k];
6458             }
6459         });
6460         
6461         
6462         if (this.layout) {
6463             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6464         }
6465         
6466         if(this.store || this.cm){
6467             if(this.headerShow){
6468                 cfg.cn.push(this.renderHeader());
6469             }
6470             
6471             cfg.cn.push(this.renderBody());
6472             
6473             if(this.footerShow){
6474                 cfg.cn.push(this.renderFooter());
6475             }
6476             // where does this come from?
6477             //cfg.cls+=  ' TableGrid';
6478         }
6479         
6480         return { cn : [ cfg ] };
6481     },
6482     
6483     initEvents : function()
6484     {   
6485         if(!this.store || !this.cm){
6486             return;
6487         }
6488         if (this.selModel) {
6489             this.selModel.initEvents();
6490         }
6491         
6492         
6493         //Roo.log('initEvents with ds!!!!');
6494         
6495         this.mainBody = this.el.select('tbody', true).first();
6496         this.mainHead = this.el.select('thead', true).first();
6497         this.mainFoot = this.el.select('tfoot', true).first();
6498         
6499         
6500         
6501         var _this = this;
6502         
6503         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6504             e.on('click', _this.sort, _this);
6505         });
6506         
6507         this.mainBody.on("click", this.onClick, this);
6508         this.mainBody.on("dblclick", this.onDblClick, this);
6509         
6510         // why is this done????? = it breaks dialogs??
6511         //this.parent().el.setStyle('position', 'relative');
6512         
6513         
6514         if (this.footer) {
6515             this.footer.parentId = this.id;
6516             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6517             
6518             if(this.lazyLoad){
6519                 this.el.select('tfoot tr td').first().addClass('hide');
6520             }
6521         } 
6522         
6523         if(this.loadMask) {
6524             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6525         }
6526         
6527         this.store.on('load', this.onLoad, this);
6528         this.store.on('beforeload', this.onBeforeLoad, this);
6529         this.store.on('update', this.onUpdate, this);
6530         this.store.on('add', this.onAdd, this);
6531         this.store.on("clear", this.clear, this);
6532         
6533         this.el.on("contextmenu", this.onContextMenu, this);
6534         
6535         this.mainBody.on('scroll', this.onBodyScroll, this);
6536         
6537         this.cm.on("headerchange", this.onHeaderChange, this);
6538         
6539         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6540         
6541     },
6542     
6543     onContextMenu : function(e, t)
6544     {
6545         this.processEvent("contextmenu", e);
6546     },
6547     
6548     processEvent : function(name, e)
6549     {
6550         if (name != 'touchstart' ) {
6551             this.fireEvent(name, e);    
6552         }
6553         
6554         var t = e.getTarget();
6555         
6556         var cell = Roo.get(t);
6557         
6558         if(!cell){
6559             return;
6560         }
6561         
6562         if(cell.findParent('tfoot', false, true)){
6563             return;
6564         }
6565         
6566         if(cell.findParent('thead', false, true)){
6567             
6568             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6569                 cell = Roo.get(t).findParent('th', false, true);
6570                 if (!cell) {
6571                     Roo.log("failed to find th in thead?");
6572                     Roo.log(e.getTarget());
6573                     return;
6574                 }
6575             }
6576             
6577             var cellIndex = cell.dom.cellIndex;
6578             
6579             var ename = name == 'touchstart' ? 'click' : name;
6580             this.fireEvent("header" + ename, this, cellIndex, e);
6581             
6582             return;
6583         }
6584         
6585         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6586             cell = Roo.get(t).findParent('td', false, true);
6587             if (!cell) {
6588                 Roo.log("failed to find th in tbody?");
6589                 Roo.log(e.getTarget());
6590                 return;
6591             }
6592         }
6593         
6594         var row = cell.findParent('tr', false, true);
6595         var cellIndex = cell.dom.cellIndex;
6596         var rowIndex = row.dom.rowIndex - 1;
6597         
6598         if(row !== false){
6599             
6600             this.fireEvent("row" + name, this, rowIndex, e);
6601             
6602             if(cell !== false){
6603             
6604                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6605             }
6606         }
6607         
6608     },
6609     
6610     onMouseover : function(e, el)
6611     {
6612         var cell = Roo.get(el);
6613         
6614         if(!cell){
6615             return;
6616         }
6617         
6618         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6619             cell = cell.findParent('td', false, true);
6620         }
6621         
6622         var row = cell.findParent('tr', false, true);
6623         var cellIndex = cell.dom.cellIndex;
6624         var rowIndex = row.dom.rowIndex - 1; // start from 0
6625         
6626         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6627         
6628     },
6629     
6630     onMouseout : function(e, el)
6631     {
6632         var cell = Roo.get(el);
6633         
6634         if(!cell){
6635             return;
6636         }
6637         
6638         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6639             cell = cell.findParent('td', false, true);
6640         }
6641         
6642         var row = cell.findParent('tr', false, true);
6643         var cellIndex = cell.dom.cellIndex;
6644         var rowIndex = row.dom.rowIndex - 1; // start from 0
6645         
6646         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6647         
6648     },
6649     
6650     onClick : function(e, el)
6651     {
6652         var cell = Roo.get(el);
6653         
6654         if(!cell || (!this.cellSelection && !this.rowSelection)){
6655             return;
6656         }
6657         
6658         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6659             cell = cell.findParent('td', false, true);
6660         }
6661         
6662         if(!cell || typeof(cell) == 'undefined'){
6663             return;
6664         }
6665         
6666         var row = cell.findParent('tr', false, true);
6667         
6668         if(!row || typeof(row) == 'undefined'){
6669             return;
6670         }
6671         
6672         var cellIndex = cell.dom.cellIndex;
6673         var rowIndex = this.getRowIndex(row);
6674         
6675         // why??? - should these not be based on SelectionModel?
6676         if(this.cellSelection){
6677             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6678         }
6679         
6680         if(this.rowSelection){
6681             this.fireEvent('rowclick', this, row, rowIndex, e);
6682         }
6683         
6684         
6685     },
6686         
6687     onDblClick : function(e,el)
6688     {
6689         var cell = Roo.get(el);
6690         
6691         if(!cell || (!this.cellSelection && !this.rowSelection)){
6692             return;
6693         }
6694         
6695         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6696             cell = cell.findParent('td', false, true);
6697         }
6698         
6699         if(!cell || typeof(cell) == 'undefined'){
6700             return;
6701         }
6702         
6703         var row = cell.findParent('tr', false, true);
6704         
6705         if(!row || typeof(row) == 'undefined'){
6706             return;
6707         }
6708         
6709         var cellIndex = cell.dom.cellIndex;
6710         var rowIndex = this.getRowIndex(row);
6711         
6712         if(this.cellSelection){
6713             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6714         }
6715         
6716         if(this.rowSelection){
6717             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6718         }
6719     },
6720     
6721     sort : function(e,el)
6722     {
6723         var col = Roo.get(el);
6724         
6725         if(!col.hasClass('sortable')){
6726             return;
6727         }
6728         
6729         var sort = col.attr('sort');
6730         var dir = 'ASC';
6731         
6732         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6733             dir = 'DESC';
6734         }
6735         
6736         this.store.sortInfo = {field : sort, direction : dir};
6737         
6738         if (this.footer) {
6739             Roo.log("calling footer first");
6740             this.footer.onClick('first');
6741         } else {
6742         
6743             this.store.load({ params : { start : 0 } });
6744         }
6745     },
6746     
6747     renderHeader : function()
6748     {
6749         var header = {
6750             tag: 'thead',
6751             cn : []
6752         };
6753         
6754         var cm = this.cm;
6755         this.totalWidth = 0;
6756         
6757         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6758             
6759             var config = cm.config[i];
6760             
6761             var c = {
6762                 tag: 'th',
6763                 cls : 'x-hcol-' + i,
6764                 style : '',
6765                 html: cm.getColumnHeader(i)
6766             };
6767             
6768             var hh = '';
6769             
6770             if(typeof(config.sortable) != 'undefined' && config.sortable){
6771                 c.cls = 'sortable';
6772                 c.html = '<i class="glyphicon"></i>' + c.html;
6773             }
6774             
6775             if(typeof(config.lgHeader) != 'undefined'){
6776                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6777             }
6778             
6779             if(typeof(config.mdHeader) != 'undefined'){
6780                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6781             }
6782             
6783             if(typeof(config.smHeader) != 'undefined'){
6784                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6785             }
6786             
6787             if(typeof(config.xsHeader) != 'undefined'){
6788                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6789             }
6790             
6791             if(hh.length){
6792                 c.html = hh;
6793             }
6794             
6795             if(typeof(config.tooltip) != 'undefined'){
6796                 c.tooltip = config.tooltip;
6797             }
6798             
6799             if(typeof(config.colspan) != 'undefined'){
6800                 c.colspan = config.colspan;
6801             }
6802             
6803             if(typeof(config.hidden) != 'undefined' && config.hidden){
6804                 c.style += ' display:none;';
6805             }
6806             
6807             if(typeof(config.dataIndex) != 'undefined'){
6808                 c.sort = config.dataIndex;
6809             }
6810             
6811            
6812             
6813             if(typeof(config.align) != 'undefined' && config.align.length){
6814                 c.style += ' text-align:' + config.align + ';';
6815             }
6816             
6817             if(typeof(config.width) != 'undefined'){
6818                 c.style += ' width:' + config.width + 'px;';
6819                 this.totalWidth += config.width;
6820             } else {
6821                 this.totalWidth += 100; // assume minimum of 100 per column?
6822             }
6823             
6824             if(typeof(config.cls) != 'undefined'){
6825                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6826             }
6827             
6828             ['xs','sm','md','lg'].map(function(size){
6829                 
6830                 if(typeof(config[size]) == 'undefined'){
6831                     return;
6832                 }
6833                 
6834                 if (!config[size]) { // 0 = hidden
6835                     c.cls += ' hidden-' + size;
6836                     return;
6837                 }
6838                 
6839                 c.cls += ' col-' + size + '-' + config[size];
6840
6841             });
6842             
6843             header.cn.push(c)
6844         }
6845         
6846         return header;
6847     },
6848     
6849     renderBody : function()
6850     {
6851         var body = {
6852             tag: 'tbody',
6853             cn : [
6854                 {
6855                     tag: 'tr',
6856                     cn : [
6857                         {
6858                             tag : 'td',
6859                             colspan :  this.cm.getColumnCount()
6860                         }
6861                     ]
6862                 }
6863             ]
6864         };
6865         
6866         return body;
6867     },
6868     
6869     renderFooter : function()
6870     {
6871         var footer = {
6872             tag: 'tfoot',
6873             cn : [
6874                 {
6875                     tag: 'tr',
6876                     cn : [
6877                         {
6878                             tag : 'td',
6879                             colspan :  this.cm.getColumnCount()
6880                         }
6881                     ]
6882                 }
6883             ]
6884         };
6885         
6886         return footer;
6887     },
6888     
6889     
6890     
6891     onLoad : function()
6892     {
6893 //        Roo.log('ds onload');
6894         this.clear();
6895         
6896         var _this = this;
6897         var cm = this.cm;
6898         var ds = this.store;
6899         
6900         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6901             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6902             if (_this.store.sortInfo) {
6903                     
6904                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6905                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6906                 }
6907                 
6908                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6909                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6910                 }
6911             }
6912         });
6913         
6914         var tbody =  this.mainBody;
6915               
6916         if(ds.getCount() > 0){
6917             ds.data.each(function(d,rowIndex){
6918                 var row =  this.renderRow(cm, ds, rowIndex);
6919                 
6920                 tbody.createChild(row);
6921                 
6922                 var _this = this;
6923                 
6924                 if(row.cellObjects.length){
6925                     Roo.each(row.cellObjects, function(r){
6926                         _this.renderCellObject(r);
6927                     })
6928                 }
6929                 
6930             }, this);
6931         }
6932         
6933         var tfoot = this.el.select('tfoot', true).first();
6934         
6935         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6936             
6937             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6938             
6939             var total = this.ds.getTotalCount();
6940             
6941             if(this.footer.pageSize < total){
6942                 this.mainFoot.show();
6943             }
6944         }
6945         
6946         Roo.each(this.el.select('tbody td', true).elements, function(e){
6947             e.on('mouseover', _this.onMouseover, _this);
6948         });
6949         
6950         Roo.each(this.el.select('tbody td', true).elements, function(e){
6951             e.on('mouseout', _this.onMouseout, _this);
6952         });
6953         this.fireEvent('rowsrendered', this);
6954         
6955         this.autoSize();
6956     },
6957     
6958     
6959     onUpdate : function(ds,record)
6960     {
6961         this.refreshRow(record);
6962         this.autoSize();
6963     },
6964     
6965     onRemove : function(ds, record, index, isUpdate){
6966         if(isUpdate !== true){
6967             this.fireEvent("beforerowremoved", this, index, record);
6968         }
6969         var bt = this.mainBody.dom;
6970         
6971         var rows = this.el.select('tbody > tr', true).elements;
6972         
6973         if(typeof(rows[index]) != 'undefined'){
6974             bt.removeChild(rows[index].dom);
6975         }
6976         
6977 //        if(bt.rows[index]){
6978 //            bt.removeChild(bt.rows[index]);
6979 //        }
6980         
6981         if(isUpdate !== true){
6982             //this.stripeRows(index);
6983             //this.syncRowHeights(index, index);
6984             //this.layout();
6985             this.fireEvent("rowremoved", this, index, record);
6986         }
6987     },
6988     
6989     onAdd : function(ds, records, rowIndex)
6990     {
6991         //Roo.log('on Add called');
6992         // - note this does not handle multiple adding very well..
6993         var bt = this.mainBody.dom;
6994         for (var i =0 ; i < records.length;i++) {
6995             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6996             //Roo.log(records[i]);
6997             //Roo.log(this.store.getAt(rowIndex+i));
6998             this.insertRow(this.store, rowIndex + i, false);
6999             return;
7000         }
7001         
7002     },
7003     
7004     
7005     refreshRow : function(record){
7006         var ds = this.store, index;
7007         if(typeof record == 'number'){
7008             index = record;
7009             record = ds.getAt(index);
7010         }else{
7011             index = ds.indexOf(record);
7012         }
7013         this.insertRow(ds, index, true);
7014         this.autoSize();
7015         this.onRemove(ds, record, index+1, true);
7016         this.autoSize();
7017         //this.syncRowHeights(index, index);
7018         //this.layout();
7019         this.fireEvent("rowupdated", this, index, record);
7020     },
7021     
7022     insertRow : function(dm, rowIndex, isUpdate){
7023         
7024         if(!isUpdate){
7025             this.fireEvent("beforerowsinserted", this, rowIndex);
7026         }
7027             //var s = this.getScrollState();
7028         var row = this.renderRow(this.cm, this.store, rowIndex);
7029         // insert before rowIndex..
7030         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
7031         
7032         var _this = this;
7033                 
7034         if(row.cellObjects.length){
7035             Roo.each(row.cellObjects, function(r){
7036                 _this.renderCellObject(r);
7037             })
7038         }
7039             
7040         if(!isUpdate){
7041             this.fireEvent("rowsinserted", this, rowIndex);
7042             //this.syncRowHeights(firstRow, lastRow);
7043             //this.stripeRows(firstRow);
7044             //this.layout();
7045         }
7046         
7047     },
7048     
7049     
7050     getRowDom : function(rowIndex)
7051     {
7052         var rows = this.el.select('tbody > tr', true).elements;
7053         
7054         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
7055         
7056     },
7057     // returns the object tree for a tr..
7058   
7059     
7060     renderRow : function(cm, ds, rowIndex) 
7061     {
7062         var d = ds.getAt(rowIndex);
7063         
7064         var row = {
7065             tag : 'tr',
7066             cls : 'x-row-' + rowIndex,
7067             cn : []
7068         };
7069             
7070         var cellObjects = [];
7071         
7072         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7073             var config = cm.config[i];
7074             
7075             var renderer = cm.getRenderer(i);
7076             var value = '';
7077             var id = false;
7078             
7079             if(typeof(renderer) !== 'undefined'){
7080                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
7081             }
7082             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
7083             // and are rendered into the cells after the row is rendered - using the id for the element.
7084             
7085             if(typeof(value) === 'object'){
7086                 id = Roo.id();
7087                 cellObjects.push({
7088                     container : id,
7089                     cfg : value 
7090                 })
7091             }
7092             
7093             var rowcfg = {
7094                 record: d,
7095                 rowIndex : rowIndex,
7096                 colIndex : i,
7097                 rowClass : ''
7098             };
7099
7100             this.fireEvent('rowclass', this, rowcfg);
7101             
7102             var td = {
7103                 tag: 'td',
7104                 cls : rowcfg.rowClass + ' x-col-' + i,
7105                 style: '',
7106                 html: (typeof(value) === 'object') ? '' : value
7107             };
7108             
7109             if (id) {
7110                 td.id = id;
7111             }
7112             
7113             if(typeof(config.colspan) != 'undefined'){
7114                 td.colspan = config.colspan;
7115             }
7116             
7117             if(typeof(config.hidden) != 'undefined' && config.hidden){
7118                 td.style += ' display:none;';
7119             }
7120             
7121             if(typeof(config.align) != 'undefined' && config.align.length){
7122                 td.style += ' text-align:' + config.align + ';';
7123             }
7124             if(typeof(config.valign) != 'undefined' && config.valign.length){
7125                 td.style += ' vertical-align:' + config.valign + ';';
7126             }
7127             
7128             if(typeof(config.width) != 'undefined'){
7129                 td.style += ' width:' +  config.width + 'px;';
7130             }
7131             
7132             if(typeof(config.cursor) != 'undefined'){
7133                 td.style += ' cursor:' +  config.cursor + ';';
7134             }
7135             
7136             if(typeof(config.cls) != 'undefined'){
7137                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7138             }
7139             
7140             ['xs','sm','md','lg'].map(function(size){
7141                 
7142                 if(typeof(config[size]) == 'undefined'){
7143                     return;
7144                 }
7145                 
7146                 if (!config[size]) { // 0 = hidden
7147                     td.cls += ' hidden-' + size;
7148                     return;
7149                 }
7150                 
7151                 td.cls += ' col-' + size + '-' + config[size];
7152
7153             });
7154             
7155             row.cn.push(td);
7156            
7157         }
7158         
7159         row.cellObjects = cellObjects;
7160         
7161         return row;
7162           
7163     },
7164     
7165     
7166     
7167     onBeforeLoad : function()
7168     {
7169         
7170     },
7171      /**
7172      * Remove all rows
7173      */
7174     clear : function()
7175     {
7176         this.el.select('tbody', true).first().dom.innerHTML = '';
7177     },
7178     /**
7179      * Show or hide a row.
7180      * @param {Number} rowIndex to show or hide
7181      * @param {Boolean} state hide
7182      */
7183     setRowVisibility : function(rowIndex, state)
7184     {
7185         var bt = this.mainBody.dom;
7186         
7187         var rows = this.el.select('tbody > tr', true).elements;
7188         
7189         if(typeof(rows[rowIndex]) == 'undefined'){
7190             return;
7191         }
7192         rows[rowIndex].dom.style.display = state ? '' : 'none';
7193     },
7194     
7195     
7196     getSelectionModel : function(){
7197         if(!this.selModel){
7198             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7199         }
7200         return this.selModel;
7201     },
7202     /*
7203      * Render the Roo.bootstrap object from renderder
7204      */
7205     renderCellObject : function(r)
7206     {
7207         var _this = this;
7208         
7209         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7210         
7211         var t = r.cfg.render(r.container);
7212         
7213         if(r.cfg.cn){
7214             Roo.each(r.cfg.cn, function(c){
7215                 var child = {
7216                     container: t.getChildContainer(),
7217                     cfg: c
7218                 };
7219                 _this.renderCellObject(child);
7220             })
7221         }
7222     },
7223     
7224     getRowIndex : function(row)
7225     {
7226         var rowIndex = -1;
7227         
7228         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7229             if(el != row){
7230                 return;
7231             }
7232             
7233             rowIndex = index;
7234         });
7235         
7236         return rowIndex;
7237     },
7238      /**
7239      * Returns the grid's underlying element = used by panel.Grid
7240      * @return {Element} The element
7241      */
7242     getGridEl : function(){
7243         return this.el;
7244     },
7245      /**
7246      * Forces a resize - used by panel.Grid
7247      * @return {Element} The element
7248      */
7249     autoSize : function()
7250     {
7251         //var ctr = Roo.get(this.container.dom.parentElement);
7252         var ctr = Roo.get(this.el.dom);
7253         
7254         var thd = this.getGridEl().select('thead',true).first();
7255         var tbd = this.getGridEl().select('tbody', true).first();
7256         var tfd = this.getGridEl().select('tfoot', true).first();
7257         
7258         var cw = ctr.getWidth();
7259         
7260         if (tbd) {
7261             
7262             tbd.setSize(ctr.getWidth(),
7263                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7264             );
7265             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7266             cw -= barsize;
7267         }
7268         cw = Math.max(cw, this.totalWidth);
7269         this.getGridEl().select('tr',true).setWidth(cw);
7270         // resize 'expandable coloumn?
7271         
7272         return; // we doe not have a view in this design..
7273         
7274     },
7275     onBodyScroll: function()
7276     {
7277         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7278         if(this.mainHead){
7279             this.mainHead.setStyle({
7280                 'position' : 'relative',
7281                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7282             });
7283         }
7284         
7285         if(this.lazyLoad){
7286             
7287             var scrollHeight = this.mainBody.dom.scrollHeight;
7288             
7289             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7290             
7291             var height = this.mainBody.getHeight();
7292             
7293             if(scrollHeight - height == scrollTop) {
7294                 
7295                 var total = this.ds.getTotalCount();
7296                 
7297                 if(this.footer.cursor + this.footer.pageSize < total){
7298                     
7299                     this.footer.ds.load({
7300                         params : {
7301                             start : this.footer.cursor + this.footer.pageSize,
7302                             limit : this.footer.pageSize
7303                         },
7304                         add : true
7305                     });
7306                 }
7307             }
7308             
7309         }
7310     },
7311     
7312     onHeaderChange : function()
7313     {
7314         var header = this.renderHeader();
7315         var table = this.el.select('table', true).first();
7316         
7317         this.mainHead.remove();
7318         this.mainHead = table.createChild(header, this.mainBody, false);
7319     },
7320     
7321     onHiddenChange : function(colModel, colIndex, hidden)
7322     {
7323         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7324         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7325         
7326         this.CSS.updateRule(thSelector, "display", "");
7327         this.CSS.updateRule(tdSelector, "display", "");
7328         
7329         if(hidden){
7330             this.CSS.updateRule(thSelector, "display", "none");
7331             this.CSS.updateRule(tdSelector, "display", "none");
7332         }
7333         
7334         this.onHeaderChange();
7335         this.onLoad();
7336     },
7337     
7338     setColumnWidth: function(col_index, width)
7339     {
7340         // width = "md-2 xs-2..."
7341         if(!this.colModel.config[col_index]) {
7342             return;
7343         }
7344         
7345         var w = width.split(" ");
7346         
7347         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7348         
7349         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7350         
7351         
7352         for(var j = 0; j < w.length; j++) {
7353             
7354             if(!w[j]) {
7355                 continue;
7356             }
7357             
7358             var size_cls = w[j].split("-");
7359             
7360             if(!Number.isInteger(size_cls[1] * 1)) {
7361                 continue;
7362             }
7363             
7364             if(!this.colModel.config[col_index][size_cls[0]]) {
7365                 continue;
7366             }
7367             
7368             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7369                 continue;
7370             }
7371             
7372             h_row[0].classList.replace(
7373                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7374                 "col-"+size_cls[0]+"-"+size_cls[1]
7375             );
7376             
7377             for(var i = 0; i < rows.length; i++) {
7378                 
7379                 var size_cls = w[j].split("-");
7380                 
7381                 if(!Number.isInteger(size_cls[1] * 1)) {
7382                     continue;
7383                 }
7384                 
7385                 if(!this.colModel.config[col_index][size_cls[0]]) {
7386                     continue;
7387                 }
7388                 
7389                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7390                     continue;
7391                 }
7392                 
7393                 rows[i].classList.replace(
7394                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7395                     "col-"+size_cls[0]+"-"+size_cls[1]
7396                 );
7397             }
7398             
7399             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7400         }
7401     }
7402 });
7403
7404  
7405
7406  /*
7407  * - LGPL
7408  *
7409  * table cell
7410  * 
7411  */
7412
7413 /**
7414  * @class Roo.bootstrap.TableCell
7415  * @extends Roo.bootstrap.Component
7416  * Bootstrap TableCell class
7417  * @cfg {String} html cell contain text
7418  * @cfg {String} cls cell class
7419  * @cfg {String} tag cell tag (td|th) default td
7420  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7421  * @cfg {String} align Aligns the content in a cell
7422  * @cfg {String} axis Categorizes cells
7423  * @cfg {String} bgcolor Specifies the background color of a cell
7424  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7425  * @cfg {Number} colspan Specifies the number of columns a cell should span
7426  * @cfg {String} headers Specifies one or more header cells a cell is related to
7427  * @cfg {Number} height Sets the height of a cell
7428  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7429  * @cfg {Number} rowspan Sets the number of rows a cell should span
7430  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7431  * @cfg {String} valign Vertical aligns the content in a cell
7432  * @cfg {Number} width Specifies the width of a cell
7433  * 
7434  * @constructor
7435  * Create a new TableCell
7436  * @param {Object} config The config object
7437  */
7438
7439 Roo.bootstrap.TableCell = function(config){
7440     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7441 };
7442
7443 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7444     
7445     html: false,
7446     cls: false,
7447     tag: false,
7448     abbr: false,
7449     align: false,
7450     axis: false,
7451     bgcolor: false,
7452     charoff: false,
7453     colspan: false,
7454     headers: false,
7455     height: false,
7456     nowrap: false,
7457     rowspan: false,
7458     scope: false,
7459     valign: false,
7460     width: false,
7461     
7462     
7463     getAutoCreate : function(){
7464         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7465         
7466         cfg = {
7467             tag: 'td'
7468         };
7469         
7470         if(this.tag){
7471             cfg.tag = this.tag;
7472         }
7473         
7474         if (this.html) {
7475             cfg.html=this.html
7476         }
7477         if (this.cls) {
7478             cfg.cls=this.cls
7479         }
7480         if (this.abbr) {
7481             cfg.abbr=this.abbr
7482         }
7483         if (this.align) {
7484             cfg.align=this.align
7485         }
7486         if (this.axis) {
7487             cfg.axis=this.axis
7488         }
7489         if (this.bgcolor) {
7490             cfg.bgcolor=this.bgcolor
7491         }
7492         if (this.charoff) {
7493             cfg.charoff=this.charoff
7494         }
7495         if (this.colspan) {
7496             cfg.colspan=this.colspan
7497         }
7498         if (this.headers) {
7499             cfg.headers=this.headers
7500         }
7501         if (this.height) {
7502             cfg.height=this.height
7503         }
7504         if (this.nowrap) {
7505             cfg.nowrap=this.nowrap
7506         }
7507         if (this.rowspan) {
7508             cfg.rowspan=this.rowspan
7509         }
7510         if (this.scope) {
7511             cfg.scope=this.scope
7512         }
7513         if (this.valign) {
7514             cfg.valign=this.valign
7515         }
7516         if (this.width) {
7517             cfg.width=this.width
7518         }
7519         
7520         
7521         return cfg;
7522     }
7523    
7524 });
7525
7526  
7527
7528  /*
7529  * - LGPL
7530  *
7531  * table row
7532  * 
7533  */
7534
7535 /**
7536  * @class Roo.bootstrap.TableRow
7537  * @extends Roo.bootstrap.Component
7538  * Bootstrap TableRow class
7539  * @cfg {String} cls row class
7540  * @cfg {String} align Aligns the content in a table row
7541  * @cfg {String} bgcolor Specifies a background color for a table row
7542  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7543  * @cfg {String} valign Vertical aligns the content in a table row
7544  * 
7545  * @constructor
7546  * Create a new TableRow
7547  * @param {Object} config The config object
7548  */
7549
7550 Roo.bootstrap.TableRow = function(config){
7551     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7552 };
7553
7554 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7555     
7556     cls: false,
7557     align: false,
7558     bgcolor: false,
7559     charoff: false,
7560     valign: false,
7561     
7562     getAutoCreate : function(){
7563         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7564         
7565         cfg = {
7566             tag: 'tr'
7567         };
7568             
7569         if(this.cls){
7570             cfg.cls = this.cls;
7571         }
7572         if(this.align){
7573             cfg.align = this.align;
7574         }
7575         if(this.bgcolor){
7576             cfg.bgcolor = this.bgcolor;
7577         }
7578         if(this.charoff){
7579             cfg.charoff = this.charoff;
7580         }
7581         if(this.valign){
7582             cfg.valign = this.valign;
7583         }
7584         
7585         return cfg;
7586     }
7587    
7588 });
7589
7590  
7591
7592  /*
7593  * - LGPL
7594  *
7595  * table body
7596  * 
7597  */
7598
7599 /**
7600  * @class Roo.bootstrap.TableBody
7601  * @extends Roo.bootstrap.Component
7602  * Bootstrap TableBody class
7603  * @cfg {String} cls element class
7604  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7605  * @cfg {String} align Aligns the content inside the element
7606  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7607  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7608  * 
7609  * @constructor
7610  * Create a new TableBody
7611  * @param {Object} config The config object
7612  */
7613
7614 Roo.bootstrap.TableBody = function(config){
7615     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7616 };
7617
7618 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7619     
7620     cls: false,
7621     tag: false,
7622     align: false,
7623     charoff: false,
7624     valign: false,
7625     
7626     getAutoCreate : function(){
7627         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7628         
7629         cfg = {
7630             tag: 'tbody'
7631         };
7632             
7633         if (this.cls) {
7634             cfg.cls=this.cls
7635         }
7636         if(this.tag){
7637             cfg.tag = this.tag;
7638         }
7639         
7640         if(this.align){
7641             cfg.align = this.align;
7642         }
7643         if(this.charoff){
7644             cfg.charoff = this.charoff;
7645         }
7646         if(this.valign){
7647             cfg.valign = this.valign;
7648         }
7649         
7650         return cfg;
7651     }
7652     
7653     
7654 //    initEvents : function()
7655 //    {
7656 //        
7657 //        if(!this.store){
7658 //            return;
7659 //        }
7660 //        
7661 //        this.store = Roo.factory(this.store, Roo.data);
7662 //        this.store.on('load', this.onLoad, this);
7663 //        
7664 //        this.store.load();
7665 //        
7666 //    },
7667 //    
7668 //    onLoad: function () 
7669 //    {   
7670 //        this.fireEvent('load', this);
7671 //    }
7672 //    
7673 //   
7674 });
7675
7676  
7677
7678  /*
7679  * Based on:
7680  * Ext JS Library 1.1.1
7681  * Copyright(c) 2006-2007, Ext JS, LLC.
7682  *
7683  * Originally Released Under LGPL - original licence link has changed is not relivant.
7684  *
7685  * Fork - LGPL
7686  * <script type="text/javascript">
7687  */
7688
7689 // as we use this in bootstrap.
7690 Roo.namespace('Roo.form');
7691  /**
7692  * @class Roo.form.Action
7693  * Internal Class used to handle form actions
7694  * @constructor
7695  * @param {Roo.form.BasicForm} el The form element or its id
7696  * @param {Object} config Configuration options
7697  */
7698
7699  
7700  
7701 // define the action interface
7702 Roo.form.Action = function(form, options){
7703     this.form = form;
7704     this.options = options || {};
7705 };
7706 /**
7707  * Client Validation Failed
7708  * @const 
7709  */
7710 Roo.form.Action.CLIENT_INVALID = 'client';
7711 /**
7712  * Server Validation Failed
7713  * @const 
7714  */
7715 Roo.form.Action.SERVER_INVALID = 'server';
7716  /**
7717  * Connect to Server Failed
7718  * @const 
7719  */
7720 Roo.form.Action.CONNECT_FAILURE = 'connect';
7721 /**
7722  * Reading Data from Server Failed
7723  * @const 
7724  */
7725 Roo.form.Action.LOAD_FAILURE = 'load';
7726
7727 Roo.form.Action.prototype = {
7728     type : 'default',
7729     failureType : undefined,
7730     response : undefined,
7731     result : undefined,
7732
7733     // interface method
7734     run : function(options){
7735
7736     },
7737
7738     // interface method
7739     success : function(response){
7740
7741     },
7742
7743     // interface method
7744     handleResponse : function(response){
7745
7746     },
7747
7748     // default connection failure
7749     failure : function(response){
7750         
7751         this.response = response;
7752         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7753         this.form.afterAction(this, false);
7754     },
7755
7756     processResponse : function(response){
7757         this.response = response;
7758         if(!response.responseText){
7759             return true;
7760         }
7761         this.result = this.handleResponse(response);
7762         return this.result;
7763     },
7764
7765     // utility functions used internally
7766     getUrl : function(appendParams){
7767         var url = this.options.url || this.form.url || this.form.el.dom.action;
7768         if(appendParams){
7769             var p = this.getParams();
7770             if(p){
7771                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7772             }
7773         }
7774         return url;
7775     },
7776
7777     getMethod : function(){
7778         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7779     },
7780
7781     getParams : function(){
7782         var bp = this.form.baseParams;
7783         var p = this.options.params;
7784         if(p){
7785             if(typeof p == "object"){
7786                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7787             }else if(typeof p == 'string' && bp){
7788                 p += '&' + Roo.urlEncode(bp);
7789             }
7790         }else if(bp){
7791             p = Roo.urlEncode(bp);
7792         }
7793         return p;
7794     },
7795
7796     createCallback : function(){
7797         return {
7798             success: this.success,
7799             failure: this.failure,
7800             scope: this,
7801             timeout: (this.form.timeout*1000),
7802             upload: this.form.fileUpload ? this.success : undefined
7803         };
7804     }
7805 };
7806
7807 Roo.form.Action.Submit = function(form, options){
7808     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7809 };
7810
7811 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7812     type : 'submit',
7813
7814     haveProgress : false,
7815     uploadComplete : false,
7816     
7817     // uploadProgress indicator.
7818     uploadProgress : function()
7819     {
7820         if (!this.form.progressUrl) {
7821             return;
7822         }
7823         
7824         if (!this.haveProgress) {
7825             Roo.MessageBox.progress("Uploading", "Uploading");
7826         }
7827         if (this.uploadComplete) {
7828            Roo.MessageBox.hide();
7829            return;
7830         }
7831         
7832         this.haveProgress = true;
7833    
7834         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7835         
7836         var c = new Roo.data.Connection();
7837         c.request({
7838             url : this.form.progressUrl,
7839             params: {
7840                 id : uid
7841             },
7842             method: 'GET',
7843             success : function(req){
7844                //console.log(data);
7845                 var rdata = false;
7846                 var edata;
7847                 try  {
7848                    rdata = Roo.decode(req.responseText)
7849                 } catch (e) {
7850                     Roo.log("Invalid data from server..");
7851                     Roo.log(edata);
7852                     return;
7853                 }
7854                 if (!rdata || !rdata.success) {
7855                     Roo.log(rdata);
7856                     Roo.MessageBox.alert(Roo.encode(rdata));
7857                     return;
7858                 }
7859                 var data = rdata.data;
7860                 
7861                 if (this.uploadComplete) {
7862                    Roo.MessageBox.hide();
7863                    return;
7864                 }
7865                    
7866                 if (data){
7867                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7868                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7869                     );
7870                 }
7871                 this.uploadProgress.defer(2000,this);
7872             },
7873        
7874             failure: function(data) {
7875                 Roo.log('progress url failed ');
7876                 Roo.log(data);
7877             },
7878             scope : this
7879         });
7880            
7881     },
7882     
7883     
7884     run : function()
7885     {
7886         // run get Values on the form, so it syncs any secondary forms.
7887         this.form.getValues();
7888         
7889         var o = this.options;
7890         var method = this.getMethod();
7891         var isPost = method == 'POST';
7892         if(o.clientValidation === false || this.form.isValid()){
7893             
7894             if (this.form.progressUrl) {
7895                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7896                     (new Date() * 1) + '' + Math.random());
7897                     
7898             } 
7899             
7900             
7901             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7902                 form:this.form.el.dom,
7903                 url:this.getUrl(!isPost),
7904                 method: method,
7905                 params:isPost ? this.getParams() : null,
7906                 isUpload: this.form.fileUpload
7907             }));
7908             
7909             this.uploadProgress();
7910
7911         }else if (o.clientValidation !== false){ // client validation failed
7912             this.failureType = Roo.form.Action.CLIENT_INVALID;
7913             this.form.afterAction(this, false);
7914         }
7915     },
7916
7917     success : function(response)
7918     {
7919         this.uploadComplete= true;
7920         if (this.haveProgress) {
7921             Roo.MessageBox.hide();
7922         }
7923         
7924         
7925         var result = this.processResponse(response);
7926         if(result === true || result.success){
7927             this.form.afterAction(this, true);
7928             return;
7929         }
7930         if(result.errors){
7931             this.form.markInvalid(result.errors);
7932             this.failureType = Roo.form.Action.SERVER_INVALID;
7933         }
7934         this.form.afterAction(this, false);
7935     },
7936     failure : function(response)
7937     {
7938         this.uploadComplete= true;
7939         if (this.haveProgress) {
7940             Roo.MessageBox.hide();
7941         }
7942         
7943         this.response = response;
7944         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7945         this.form.afterAction(this, false);
7946     },
7947     
7948     handleResponse : function(response){
7949         if(this.form.errorReader){
7950             var rs = this.form.errorReader.read(response);
7951             var errors = [];
7952             if(rs.records){
7953                 for(var i = 0, len = rs.records.length; i < len; i++) {
7954                     var r = rs.records[i];
7955                     errors[i] = r.data;
7956                 }
7957             }
7958             if(errors.length < 1){
7959                 errors = null;
7960             }
7961             return {
7962                 success : rs.success,
7963                 errors : errors
7964             };
7965         }
7966         var ret = false;
7967         try {
7968             ret = Roo.decode(response.responseText);
7969         } catch (e) {
7970             ret = {
7971                 success: false,
7972                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7973                 errors : []
7974             };
7975         }
7976         return ret;
7977         
7978     }
7979 });
7980
7981
7982 Roo.form.Action.Load = function(form, options){
7983     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7984     this.reader = this.form.reader;
7985 };
7986
7987 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7988     type : 'load',
7989
7990     run : function(){
7991         
7992         Roo.Ajax.request(Roo.apply(
7993                 this.createCallback(), {
7994                     method:this.getMethod(),
7995                     url:this.getUrl(false),
7996                     params:this.getParams()
7997         }));
7998     },
7999
8000     success : function(response){
8001         
8002         var result = this.processResponse(response);
8003         if(result === true || !result.success || !result.data){
8004             this.failureType = Roo.form.Action.LOAD_FAILURE;
8005             this.form.afterAction(this, false);
8006             return;
8007         }
8008         this.form.clearInvalid();
8009         this.form.setValues(result.data);
8010         this.form.afterAction(this, true);
8011     },
8012
8013     handleResponse : function(response){
8014         if(this.form.reader){
8015             var rs = this.form.reader.read(response);
8016             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
8017             return {
8018                 success : rs.success,
8019                 data : data
8020             };
8021         }
8022         return Roo.decode(response.responseText);
8023     }
8024 });
8025
8026 Roo.form.Action.ACTION_TYPES = {
8027     'load' : Roo.form.Action.Load,
8028     'submit' : Roo.form.Action.Submit
8029 };/*
8030  * - LGPL
8031  *
8032  * form
8033  *
8034  */
8035
8036 /**
8037  * @class Roo.bootstrap.Form
8038  * @extends Roo.bootstrap.Component
8039  * Bootstrap Form class
8040  * @cfg {String} method  GET | POST (default POST)
8041  * @cfg {String} labelAlign top | left (default top)
8042  * @cfg {String} align left  | right - for navbars
8043  * @cfg {Boolean} loadMask load mask when submit (default true)
8044
8045  *
8046  * @constructor
8047  * Create a new Form
8048  * @param {Object} config The config object
8049  */
8050
8051
8052 Roo.bootstrap.Form = function(config){
8053     
8054     Roo.bootstrap.Form.superclass.constructor.call(this, config);
8055     
8056     Roo.bootstrap.Form.popover.apply();
8057     
8058     this.addEvents({
8059         /**
8060          * @event clientvalidation
8061          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
8062          * @param {Form} this
8063          * @param {Boolean} valid true if the form has passed client-side validation
8064          */
8065         clientvalidation: true,
8066         /**
8067          * @event beforeaction
8068          * Fires before any action is performed. Return false to cancel the action.
8069          * @param {Form} this
8070          * @param {Action} action The action to be performed
8071          */
8072         beforeaction: true,
8073         /**
8074          * @event actionfailed
8075          * Fires when an action fails.
8076          * @param {Form} this
8077          * @param {Action} action The action that failed
8078          */
8079         actionfailed : true,
8080         /**
8081          * @event actioncomplete
8082          * Fires when an action is completed.
8083          * @param {Form} this
8084          * @param {Action} action The action that completed
8085          */
8086         actioncomplete : true
8087     });
8088 };
8089
8090 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
8091
8092      /**
8093      * @cfg {String} method
8094      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8095      */
8096     method : 'POST',
8097     /**
8098      * @cfg {String} url
8099      * The URL to use for form actions if one isn't supplied in the action options.
8100      */
8101     /**
8102      * @cfg {Boolean} fileUpload
8103      * Set to true if this form is a file upload.
8104      */
8105
8106     /**
8107      * @cfg {Object} baseParams
8108      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8109      */
8110
8111     /**
8112      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8113      */
8114     timeout: 30,
8115     /**
8116      * @cfg {Sting} align (left|right) for navbar forms
8117      */
8118     align : 'left',
8119
8120     // private
8121     activeAction : null,
8122
8123     /**
8124      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8125      * element by passing it or its id or mask the form itself by passing in true.
8126      * @type Mixed
8127      */
8128     waitMsgTarget : false,
8129
8130     loadMask : true,
8131     
8132     /**
8133      * @cfg {Boolean} errorMask (true|false) default false
8134      */
8135     errorMask : false,
8136     
8137     /**
8138      * @cfg {Number} maskOffset Default 100
8139      */
8140     maskOffset : 100,
8141     
8142     /**
8143      * @cfg {Boolean} maskBody
8144      */
8145     maskBody : false,
8146
8147     getAutoCreate : function(){
8148
8149         var cfg = {
8150             tag: 'form',
8151             method : this.method || 'POST',
8152             id : this.id || Roo.id(),
8153             cls : ''
8154         };
8155         if (this.parent().xtype.match(/^Nav/)) {
8156             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8157
8158         }
8159
8160         if (this.labelAlign == 'left' ) {
8161             cfg.cls += ' form-horizontal';
8162         }
8163
8164
8165         return cfg;
8166     },
8167     initEvents : function()
8168     {
8169         this.el.on('submit', this.onSubmit, this);
8170         // this was added as random key presses on the form where triggering form submit.
8171         this.el.on('keypress', function(e) {
8172             if (e.getCharCode() != 13) {
8173                 return true;
8174             }
8175             // we might need to allow it for textareas.. and some other items.
8176             // check e.getTarget().
8177
8178             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8179                 return true;
8180             }
8181
8182             Roo.log("keypress blocked");
8183
8184             e.preventDefault();
8185             return false;
8186         });
8187         
8188     },
8189     // private
8190     onSubmit : function(e){
8191         e.stopEvent();
8192     },
8193
8194      /**
8195      * Returns true if client-side validation on the form is successful.
8196      * @return Boolean
8197      */
8198     isValid : function(){
8199         var items = this.getItems();
8200         var valid = true;
8201         var target = false;
8202         
8203         items.each(function(f){
8204             
8205             if(f.validate()){
8206                 return;
8207             }
8208             
8209             Roo.log('invalid field: ' + f.name);
8210             
8211             valid = false;
8212
8213             if(!target && f.el.isVisible(true)){
8214                 target = f;
8215             }
8216            
8217         });
8218         
8219         if(this.errorMask && !valid){
8220             Roo.bootstrap.Form.popover.mask(this, target);
8221         }
8222         
8223         return valid;
8224     },
8225     
8226     /**
8227      * Returns true if any fields in this form have changed since their original load.
8228      * @return Boolean
8229      */
8230     isDirty : function(){
8231         var dirty = false;
8232         var items = this.getItems();
8233         items.each(function(f){
8234            if(f.isDirty()){
8235                dirty = true;
8236                return false;
8237            }
8238            return true;
8239         });
8240         return dirty;
8241     },
8242      /**
8243      * Performs a predefined action (submit or load) or custom actions you define on this form.
8244      * @param {String} actionName The name of the action type
8245      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8246      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8247      * accept other config options):
8248      * <pre>
8249 Property          Type             Description
8250 ----------------  ---------------  ----------------------------------------------------------------------------------
8251 url               String           The url for the action (defaults to the form's url)
8252 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8253 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8254 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8255                                    validate the form on the client (defaults to false)
8256      * </pre>
8257      * @return {BasicForm} this
8258      */
8259     doAction : function(action, options){
8260         if(typeof action == 'string'){
8261             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8262         }
8263         if(this.fireEvent('beforeaction', this, action) !== false){
8264             this.beforeAction(action);
8265             action.run.defer(100, action);
8266         }
8267         return this;
8268     },
8269
8270     // private
8271     beforeAction : function(action){
8272         var o = action.options;
8273         
8274         if(this.loadMask){
8275             
8276             if(this.maskBody){
8277                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8278             } else {
8279                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8280             }
8281         }
8282         // not really supported yet.. ??
8283
8284         //if(this.waitMsgTarget === true){
8285         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8286         //}else if(this.waitMsgTarget){
8287         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8288         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8289         //}else {
8290         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8291        // }
8292
8293     },
8294
8295     // private
8296     afterAction : function(action, success){
8297         this.activeAction = null;
8298         var o = action.options;
8299
8300         if(this.loadMask){
8301             
8302             if(this.maskBody){
8303                 Roo.get(document.body).unmask();
8304             } else {
8305                 this.el.unmask();
8306             }
8307         }
8308         
8309         //if(this.waitMsgTarget === true){
8310 //            this.el.unmask();
8311         //}else if(this.waitMsgTarget){
8312         //    this.waitMsgTarget.unmask();
8313         //}else{
8314         //    Roo.MessageBox.updateProgress(1);
8315         //    Roo.MessageBox.hide();
8316        // }
8317         //
8318         if(success){
8319             if(o.reset){
8320                 this.reset();
8321             }
8322             Roo.callback(o.success, o.scope, [this, action]);
8323             this.fireEvent('actioncomplete', this, action);
8324
8325         }else{
8326
8327             // failure condition..
8328             // we have a scenario where updates need confirming.
8329             // eg. if a locking scenario exists..
8330             // we look for { errors : { needs_confirm : true }} in the response.
8331             if (
8332                 (typeof(action.result) != 'undefined')  &&
8333                 (typeof(action.result.errors) != 'undefined')  &&
8334                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8335            ){
8336                 var _t = this;
8337                 Roo.log("not supported yet");
8338                  /*
8339
8340                 Roo.MessageBox.confirm(
8341                     "Change requires confirmation",
8342                     action.result.errorMsg,
8343                     function(r) {
8344                         if (r != 'yes') {
8345                             return;
8346                         }
8347                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8348                     }
8349
8350                 );
8351                 */
8352
8353
8354                 return;
8355             }
8356
8357             Roo.callback(o.failure, o.scope, [this, action]);
8358             // show an error message if no failed handler is set..
8359             if (!this.hasListener('actionfailed')) {
8360                 Roo.log("need to add dialog support");
8361                 /*
8362                 Roo.MessageBox.alert("Error",
8363                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8364                         action.result.errorMsg :
8365                         "Saving Failed, please check your entries or try again"
8366                 );
8367                 */
8368             }
8369
8370             this.fireEvent('actionfailed', this, action);
8371         }
8372
8373     },
8374     /**
8375      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8376      * @param {String} id The value to search for
8377      * @return Field
8378      */
8379     findField : function(id){
8380         var items = this.getItems();
8381         var field = items.get(id);
8382         if(!field){
8383              items.each(function(f){
8384                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8385                     field = f;
8386                     return false;
8387                 }
8388                 return true;
8389             });
8390         }
8391         return field || null;
8392     },
8393      /**
8394      * Mark fields in this form invalid in bulk.
8395      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8396      * @return {BasicForm} this
8397      */
8398     markInvalid : function(errors){
8399         if(errors instanceof Array){
8400             for(var i = 0, len = errors.length; i < len; i++){
8401                 var fieldError = errors[i];
8402                 var f = this.findField(fieldError.id);
8403                 if(f){
8404                     f.markInvalid(fieldError.msg);
8405                 }
8406             }
8407         }else{
8408             var field, id;
8409             for(id in errors){
8410                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8411                     field.markInvalid(errors[id]);
8412                 }
8413             }
8414         }
8415         //Roo.each(this.childForms || [], function (f) {
8416         //    f.markInvalid(errors);
8417         //});
8418
8419         return this;
8420     },
8421
8422     /**
8423      * Set values for fields in this form in bulk.
8424      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8425      * @return {BasicForm} this
8426      */
8427     setValues : function(values){
8428         if(values instanceof Array){ // array of objects
8429             for(var i = 0, len = values.length; i < len; i++){
8430                 var v = values[i];
8431                 var f = this.findField(v.id);
8432                 if(f){
8433                     f.setValue(v.value);
8434                     if(this.trackResetOnLoad){
8435                         f.originalValue = f.getValue();
8436                     }
8437                 }
8438             }
8439         }else{ // object hash
8440             var field, id;
8441             for(id in values){
8442                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8443
8444                     if (field.setFromData &&
8445                         field.valueField &&
8446                         field.displayField &&
8447                         // combos' with local stores can
8448                         // be queried via setValue()
8449                         // to set their value..
8450                         (field.store && !field.store.isLocal)
8451                         ) {
8452                         // it's a combo
8453                         var sd = { };
8454                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8455                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8456                         field.setFromData(sd);
8457
8458                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8459                         
8460                         field.setFromData(values);
8461                         
8462                     } else {
8463                         field.setValue(values[id]);
8464                     }
8465
8466
8467                     if(this.trackResetOnLoad){
8468                         field.originalValue = field.getValue();
8469                     }
8470                 }
8471             }
8472         }
8473
8474         //Roo.each(this.childForms || [], function (f) {
8475         //    f.setValues(values);
8476         //});
8477
8478         return this;
8479     },
8480
8481     /**
8482      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8483      * they are returned as an array.
8484      * @param {Boolean} asString
8485      * @return {Object}
8486      */
8487     getValues : function(asString){
8488         //if (this.childForms) {
8489             // copy values from the child forms
8490         //    Roo.each(this.childForms, function (f) {
8491         //        this.setValues(f.getValues());
8492         //    }, this);
8493         //}
8494
8495
8496
8497         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8498         if(asString === true){
8499             return fs;
8500         }
8501         return Roo.urlDecode(fs);
8502     },
8503
8504     /**
8505      * Returns the fields in this form as an object with key/value pairs.
8506      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8507      * @return {Object}
8508      */
8509     getFieldValues : function(with_hidden)
8510     {
8511         var items = this.getItems();
8512         var ret = {};
8513         items.each(function(f){
8514             
8515             if (!f.getName()) {
8516                 return;
8517             }
8518             
8519             var v = f.getValue();
8520             
8521             if (f.inputType =='radio') {
8522                 if (typeof(ret[f.getName()]) == 'undefined') {
8523                     ret[f.getName()] = ''; // empty..
8524                 }
8525
8526                 if (!f.el.dom.checked) {
8527                     return;
8528
8529                 }
8530                 v = f.el.dom.value;
8531
8532             }
8533             
8534             if(f.xtype == 'MoneyField'){
8535                 ret[f.currencyName] = f.getCurrency();
8536             }
8537
8538             // not sure if this supported any more..
8539             if ((typeof(v) == 'object') && f.getRawValue) {
8540                 v = f.getRawValue() ; // dates..
8541             }
8542             // combo boxes where name != hiddenName...
8543             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8544                 ret[f.name] = f.getRawValue();
8545             }
8546             ret[f.getName()] = v;
8547         });
8548
8549         return ret;
8550     },
8551
8552     /**
8553      * Clears all invalid messages in this form.
8554      * @return {BasicForm} this
8555      */
8556     clearInvalid : function(){
8557         var items = this.getItems();
8558
8559         items.each(function(f){
8560            f.clearInvalid();
8561         });
8562
8563         return this;
8564     },
8565
8566     /**
8567      * Resets this form.
8568      * @return {BasicForm} this
8569      */
8570     reset : function(){
8571         var items = this.getItems();
8572         items.each(function(f){
8573             f.reset();
8574         });
8575
8576         Roo.each(this.childForms || [], function (f) {
8577             f.reset();
8578         });
8579
8580
8581         return this;
8582     },
8583     
8584     getItems : function()
8585     {
8586         var r=new Roo.util.MixedCollection(false, function(o){
8587             return o.id || (o.id = Roo.id());
8588         });
8589         var iter = function(el) {
8590             if (el.inputEl) {
8591                 r.add(el);
8592             }
8593             if (!el.items) {
8594                 return;
8595             }
8596             Roo.each(el.items,function(e) {
8597                 iter(e);
8598             });
8599         };
8600
8601         iter(this);
8602         return r;
8603     },
8604     
8605     hideFields : function(items)
8606     {
8607         Roo.each(items, function(i){
8608             
8609             var f = this.findField(i);
8610             
8611             if(!f){
8612                 return;
8613             }
8614             
8615             f.hide();
8616             
8617         }, this);
8618     },
8619     
8620     showFields : function(items)
8621     {
8622         Roo.each(items, function(i){
8623             
8624             var f = this.findField(i);
8625             
8626             if(!f){
8627                 return;
8628             }
8629             
8630             f.show();
8631             
8632         }, this);
8633     }
8634
8635 });
8636
8637 Roo.apply(Roo.bootstrap.Form, {
8638     
8639     popover : {
8640         
8641         padding : 5,
8642         
8643         isApplied : false,
8644         
8645         isMasked : false,
8646         
8647         form : false,
8648         
8649         target : false,
8650         
8651         toolTip : false,
8652         
8653         intervalID : false,
8654         
8655         maskEl : false,
8656         
8657         apply : function()
8658         {
8659             if(this.isApplied){
8660                 return;
8661             }
8662             
8663             this.maskEl = {
8664                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8665                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8666                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8667                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8668             };
8669             
8670             this.maskEl.top.enableDisplayMode("block");
8671             this.maskEl.left.enableDisplayMode("block");
8672             this.maskEl.bottom.enableDisplayMode("block");
8673             this.maskEl.right.enableDisplayMode("block");
8674             
8675             this.toolTip = new Roo.bootstrap.Tooltip({
8676                 cls : 'roo-form-error-popover',
8677                 alignment : {
8678                     'left' : ['r-l', [-2,0], 'right'],
8679                     'right' : ['l-r', [2,0], 'left'],
8680                     'bottom' : ['tl-bl', [0,2], 'top'],
8681                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8682                 }
8683             });
8684             
8685             this.toolTip.render(Roo.get(document.body));
8686
8687             this.toolTip.el.enableDisplayMode("block");
8688             
8689             Roo.get(document.body).on('click', function(){
8690                 this.unmask();
8691             }, this);
8692             
8693             Roo.get(document.body).on('touchstart', function(){
8694                 this.unmask();
8695             }, this);
8696             
8697             this.isApplied = true
8698         },
8699         
8700         mask : function(form, target)
8701         {
8702             this.form = form;
8703             
8704             this.target = target;
8705             
8706             if(!this.form.errorMask || !target.el){
8707                 return;
8708             }
8709             
8710             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8711             
8712             Roo.log(scrollable);
8713             
8714             var ot = this.target.el.calcOffsetsTo(scrollable);
8715             
8716             var scrollTo = ot[1] - this.form.maskOffset;
8717             
8718             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8719             
8720             scrollable.scrollTo('top', scrollTo);
8721             
8722             var box = this.target.el.getBox();
8723             Roo.log(box);
8724             var zIndex = Roo.bootstrap.Modal.zIndex++;
8725
8726             
8727             this.maskEl.top.setStyle('position', 'absolute');
8728             this.maskEl.top.setStyle('z-index', zIndex);
8729             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8730             this.maskEl.top.setLeft(0);
8731             this.maskEl.top.setTop(0);
8732             this.maskEl.top.show();
8733             
8734             this.maskEl.left.setStyle('position', 'absolute');
8735             this.maskEl.left.setStyle('z-index', zIndex);
8736             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8737             this.maskEl.left.setLeft(0);
8738             this.maskEl.left.setTop(box.y - this.padding);
8739             this.maskEl.left.show();
8740
8741             this.maskEl.bottom.setStyle('position', 'absolute');
8742             this.maskEl.bottom.setStyle('z-index', zIndex);
8743             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8744             this.maskEl.bottom.setLeft(0);
8745             this.maskEl.bottom.setTop(box.bottom + this.padding);
8746             this.maskEl.bottom.show();
8747
8748             this.maskEl.right.setStyle('position', 'absolute');
8749             this.maskEl.right.setStyle('z-index', zIndex);
8750             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8751             this.maskEl.right.setLeft(box.right + this.padding);
8752             this.maskEl.right.setTop(box.y - this.padding);
8753             this.maskEl.right.show();
8754
8755             this.toolTip.bindEl = this.target.el;
8756
8757             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8758
8759             var tip = this.target.blankText;
8760
8761             if(this.target.getValue() !== '' ) {
8762                 
8763                 if (this.target.invalidText.length) {
8764                     tip = this.target.invalidText;
8765                 } else if (this.target.regexText.length){
8766                     tip = this.target.regexText;
8767                 }
8768             }
8769
8770             this.toolTip.show(tip);
8771
8772             this.intervalID = window.setInterval(function() {
8773                 Roo.bootstrap.Form.popover.unmask();
8774             }, 10000);
8775
8776             window.onwheel = function(){ return false;};
8777             
8778             (function(){ this.isMasked = true; }).defer(500, this);
8779             
8780         },
8781         
8782         unmask : function()
8783         {
8784             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8785                 return;
8786             }
8787             
8788             this.maskEl.top.setStyle('position', 'absolute');
8789             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8790             this.maskEl.top.hide();
8791
8792             this.maskEl.left.setStyle('position', 'absolute');
8793             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8794             this.maskEl.left.hide();
8795
8796             this.maskEl.bottom.setStyle('position', 'absolute');
8797             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8798             this.maskEl.bottom.hide();
8799
8800             this.maskEl.right.setStyle('position', 'absolute');
8801             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8802             this.maskEl.right.hide();
8803             
8804             this.toolTip.hide();
8805             
8806             this.toolTip.el.hide();
8807             
8808             window.onwheel = function(){ return true;};
8809             
8810             if(this.intervalID){
8811                 window.clearInterval(this.intervalID);
8812                 this.intervalID = false;
8813             }
8814             
8815             this.isMasked = false;
8816             
8817         }
8818         
8819     }
8820     
8821 });
8822
8823 /*
8824  * Based on:
8825  * Ext JS Library 1.1.1
8826  * Copyright(c) 2006-2007, Ext JS, LLC.
8827  *
8828  * Originally Released Under LGPL - original licence link has changed is not relivant.
8829  *
8830  * Fork - LGPL
8831  * <script type="text/javascript">
8832  */
8833 /**
8834  * @class Roo.form.VTypes
8835  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8836  * @singleton
8837  */
8838 Roo.form.VTypes = function(){
8839     // closure these in so they are only created once.
8840     var alpha = /^[a-zA-Z_]+$/;
8841     var alphanum = /^[a-zA-Z0-9_]+$/;
8842     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8843     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8844
8845     // All these messages and functions are configurable
8846     return {
8847         /**
8848          * The function used to validate email addresses
8849          * @param {String} value The email address
8850          */
8851         'email' : function(v){
8852             return email.test(v);
8853         },
8854         /**
8855          * The error text to display when the email validation function returns false
8856          * @type String
8857          */
8858         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8859         /**
8860          * The keystroke filter mask to be applied on email input
8861          * @type RegExp
8862          */
8863         'emailMask' : /[a-z0-9_\.\-@]/i,
8864
8865         /**
8866          * The function used to validate URLs
8867          * @param {String} value The URL
8868          */
8869         'url' : function(v){
8870             return url.test(v);
8871         },
8872         /**
8873          * The error text to display when the url validation function returns false
8874          * @type String
8875          */
8876         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8877         
8878         /**
8879          * The function used to validate alpha values
8880          * @param {String} value The value
8881          */
8882         'alpha' : function(v){
8883             return alpha.test(v);
8884         },
8885         /**
8886          * The error text to display when the alpha validation function returns false
8887          * @type String
8888          */
8889         'alphaText' : 'This field should only contain letters and _',
8890         /**
8891          * The keystroke filter mask to be applied on alpha input
8892          * @type RegExp
8893          */
8894         'alphaMask' : /[a-z_]/i,
8895
8896         /**
8897          * The function used to validate alphanumeric values
8898          * @param {String} value The value
8899          */
8900         'alphanum' : function(v){
8901             return alphanum.test(v);
8902         },
8903         /**
8904          * The error text to display when the alphanumeric validation function returns false
8905          * @type String
8906          */
8907         'alphanumText' : 'This field should only contain letters, numbers and _',
8908         /**
8909          * The keystroke filter mask to be applied on alphanumeric input
8910          * @type RegExp
8911          */
8912         'alphanumMask' : /[a-z0-9_]/i
8913     };
8914 }();/*
8915  * - LGPL
8916  *
8917  * Input
8918  * 
8919  */
8920
8921 /**
8922  * @class Roo.bootstrap.Input
8923  * @extends Roo.bootstrap.Component
8924  * Bootstrap Input class
8925  * @cfg {Boolean} disabled is it disabled
8926  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8927  * @cfg {String} name name of the input
8928  * @cfg {string} fieldLabel - the label associated
8929  * @cfg {string} placeholder - placeholder to put in text.
8930  * @cfg {string}  before - input group add on before
8931  * @cfg {string} after - input group add on after
8932  * @cfg {string} size - (lg|sm) or leave empty..
8933  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8934  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8935  * @cfg {Number} md colspan out of 12 for computer-sized screens
8936  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8937  * @cfg {string} value default value of the input
8938  * @cfg {Number} labelWidth set the width of label 
8939  * @cfg {Number} labellg set the width of label (1-12)
8940  * @cfg {Number} labelmd set the width of label (1-12)
8941  * @cfg {Number} labelsm set the width of label (1-12)
8942  * @cfg {Number} labelxs set the width of label (1-12)
8943  * @cfg {String} labelAlign (top|left)
8944  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8945  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8946  * @cfg {String} indicatorpos (left|right) default left
8947  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8948  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8949
8950  * @cfg {String} align (left|center|right) Default left
8951  * @cfg {Boolean} forceFeedback (true|false) Default false
8952  * 
8953  * @constructor
8954  * Create a new Input
8955  * @param {Object} config The config object
8956  */
8957
8958 Roo.bootstrap.Input = function(config){
8959     
8960     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8961     
8962     this.addEvents({
8963         /**
8964          * @event focus
8965          * Fires when this field receives input focus.
8966          * @param {Roo.form.Field} this
8967          */
8968         focus : true,
8969         /**
8970          * @event blur
8971          * Fires when this field loses input focus.
8972          * @param {Roo.form.Field} this
8973          */
8974         blur : true,
8975         /**
8976          * @event specialkey
8977          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8978          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8979          * @param {Roo.form.Field} this
8980          * @param {Roo.EventObject} e The event object
8981          */
8982         specialkey : true,
8983         /**
8984          * @event change
8985          * Fires just before the field blurs if the field value has changed.
8986          * @param {Roo.form.Field} this
8987          * @param {Mixed} newValue The new value
8988          * @param {Mixed} oldValue The original value
8989          */
8990         change : true,
8991         /**
8992          * @event invalid
8993          * Fires after the field has been marked as invalid.
8994          * @param {Roo.form.Field} this
8995          * @param {String} msg The validation message
8996          */
8997         invalid : true,
8998         /**
8999          * @event valid
9000          * Fires after the field has been validated with no errors.
9001          * @param {Roo.form.Field} this
9002          */
9003         valid : true,
9004          /**
9005          * @event keyup
9006          * Fires after the key up
9007          * @param {Roo.form.Field} this
9008          * @param {Roo.EventObject}  e The event Object
9009          */
9010         keyup : true
9011     });
9012 };
9013
9014 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
9015      /**
9016      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
9017       automatic validation (defaults to "keyup").
9018      */
9019     validationEvent : "keyup",
9020      /**
9021      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
9022      */
9023     validateOnBlur : true,
9024     /**
9025      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
9026      */
9027     validationDelay : 250,
9028      /**
9029      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
9030      */
9031     focusClass : "x-form-focus",  // not needed???
9032     
9033        
9034     /**
9035      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9036      */
9037     invalidClass : "has-warning",
9038     
9039     /**
9040      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9041      */
9042     validClass : "has-success",
9043     
9044     /**
9045      * @cfg {Boolean} hasFeedback (true|false) default true
9046      */
9047     hasFeedback : true,
9048     
9049     /**
9050      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9051      */
9052     invalidFeedbackClass : "glyphicon-warning-sign",
9053     
9054     /**
9055      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9056      */
9057     validFeedbackClass : "glyphicon-ok",
9058     
9059     /**
9060      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
9061      */
9062     selectOnFocus : false,
9063     
9064      /**
9065      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
9066      */
9067     maskRe : null,
9068        /**
9069      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
9070      */
9071     vtype : null,
9072     
9073       /**
9074      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
9075      */
9076     disableKeyFilter : false,
9077     
9078        /**
9079      * @cfg {Boolean} disabled True to disable the field (defaults to false).
9080      */
9081     disabled : false,
9082      /**
9083      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
9084      */
9085     allowBlank : true,
9086     /**
9087      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9088      */
9089     blankText : "Please complete this mandatory field",
9090     
9091      /**
9092      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9093      */
9094     minLength : 0,
9095     /**
9096      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9097      */
9098     maxLength : Number.MAX_VALUE,
9099     /**
9100      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9101      */
9102     minLengthText : "The minimum length for this field is {0}",
9103     /**
9104      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9105      */
9106     maxLengthText : "The maximum length for this field is {0}",
9107   
9108     
9109     /**
9110      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9111      * If available, this function will be called only after the basic validators all return true, and will be passed the
9112      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9113      */
9114     validator : null,
9115     /**
9116      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9117      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9118      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
9119      */
9120     regex : null,
9121     /**
9122      * @cfg {String} regexText -- Depricated - use Invalid Text
9123      */
9124     regexText : "",
9125     
9126     /**
9127      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9128      */
9129     invalidText : "",
9130     
9131     
9132     
9133     autocomplete: false,
9134     
9135     
9136     fieldLabel : '',
9137     inputType : 'text',
9138     
9139     name : false,
9140     placeholder: false,
9141     before : false,
9142     after : false,
9143     size : false,
9144     hasFocus : false,
9145     preventMark: false,
9146     isFormField : true,
9147     value : '',
9148     labelWidth : 2,
9149     labelAlign : false,
9150     readOnly : false,
9151     align : false,
9152     formatedValue : false,
9153     forceFeedback : false,
9154     
9155     indicatorpos : 'left',
9156     
9157     labellg : 0,
9158     labelmd : 0,
9159     labelsm : 0,
9160     labelxs : 0,
9161     
9162     capture : '',
9163     accept : '',
9164     
9165     parentLabelAlign : function()
9166     {
9167         var parent = this;
9168         while (parent.parent()) {
9169             parent = parent.parent();
9170             if (typeof(parent.labelAlign) !='undefined') {
9171                 return parent.labelAlign;
9172             }
9173         }
9174         return 'left';
9175         
9176     },
9177     
9178     getAutoCreate : function()
9179     {
9180         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9181         
9182         var id = Roo.id();
9183         
9184         var cfg = {};
9185         
9186         if(this.inputType != 'hidden'){
9187             cfg.cls = 'form-group' //input-group
9188         }
9189         
9190         var input =  {
9191             tag: 'input',
9192             id : id,
9193             type : this.inputType,
9194             value : this.value,
9195             cls : 'form-control',
9196             placeholder : this.placeholder || '',
9197             autocomplete : this.autocomplete || 'new-password'
9198         };
9199         
9200         if(this.capture.length){
9201             input.capture = this.capture;
9202         }
9203         
9204         if(this.accept.length){
9205             input.accept = this.accept + "/*";
9206         }
9207         
9208         if(this.align){
9209             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9210         }
9211         
9212         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9213             input.maxLength = this.maxLength;
9214         }
9215         
9216         if (this.disabled) {
9217             input.disabled=true;
9218         }
9219         
9220         if (this.readOnly) {
9221             input.readonly=true;
9222         }
9223         
9224         if (this.name) {
9225             input.name = this.name;
9226         }
9227         
9228         if (this.size) {
9229             input.cls += ' input-' + this.size;
9230         }
9231         
9232         var settings=this;
9233         ['xs','sm','md','lg'].map(function(size){
9234             if (settings[size]) {
9235                 cfg.cls += ' col-' + size + '-' + settings[size];
9236             }
9237         });
9238         
9239         var inputblock = input;
9240         
9241         var feedback = {
9242             tag: 'span',
9243             cls: 'glyphicon form-control-feedback'
9244         };
9245             
9246         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9247             
9248             inputblock = {
9249                 cls : 'has-feedback',
9250                 cn :  [
9251                     input,
9252                     feedback
9253                 ] 
9254             };  
9255         }
9256         
9257         if (this.before || this.after) {
9258             
9259             inputblock = {
9260                 cls : 'input-group',
9261                 cn :  [] 
9262             };
9263             
9264             if (this.before && typeof(this.before) == 'string') {
9265                 
9266                 inputblock.cn.push({
9267                     tag :'span',
9268                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9269                     html : this.before
9270                 });
9271             }
9272             if (this.before && typeof(this.before) == 'object') {
9273                 this.before = Roo.factory(this.before);
9274                 
9275                 inputblock.cn.push({
9276                     tag :'span',
9277                     cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9278                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9279                 });
9280             }
9281             
9282             inputblock.cn.push(input);
9283             
9284             if (this.after && typeof(this.after) == 'string') {
9285                 inputblock.cn.push({
9286                     tag :'span',
9287                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9288                     html : this.after
9289                 });
9290             }
9291             if (this.after && typeof(this.after) == 'object') {
9292                 this.after = Roo.factory(this.after);
9293                 
9294                 inputblock.cn.push({
9295                     tag :'span',
9296                     cls : 'roo-input-after input-group-append input-group-text input-group-' +
9297                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9298                 });
9299             }
9300             
9301             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9302                 inputblock.cls += ' has-feedback';
9303                 inputblock.cn.push(feedback);
9304             }
9305         };
9306         var indicator = {
9307             tag : 'i',
9308             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9309             tooltip : 'This field is required'
9310         };
9311         if (Roo.bootstrap.version == 4) {
9312             indicator = {
9313                 tag : 'i',
9314                 style : 'display-none'
9315             };
9316         }
9317         if (align ==='left' && this.fieldLabel.length) {
9318             
9319             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
9320             
9321             cfg.cn = [
9322                 indicator,
9323                 {
9324                     tag: 'label',
9325                     'for' :  id,
9326                     cls : 'control-label col-form-label',
9327                     html : this.fieldLabel
9328
9329                 },
9330                 {
9331                     cls : "", 
9332                     cn: [
9333                         inputblock
9334                     ]
9335                 }
9336             ];
9337             
9338             var labelCfg = cfg.cn[1];
9339             var contentCfg = cfg.cn[2];
9340             
9341             if(this.indicatorpos == 'right'){
9342                 cfg.cn = [
9343                     {
9344                         tag: 'label',
9345                         'for' :  id,
9346                         cls : 'control-label col-form-label',
9347                         cn : [
9348                             {
9349                                 tag : 'span',
9350                                 html : this.fieldLabel
9351                             },
9352                             indicator
9353                         ]
9354                     },
9355                     {
9356                         cls : "",
9357                         cn: [
9358                             inputblock
9359                         ]
9360                     }
9361
9362                 ];
9363                 
9364                 labelCfg = cfg.cn[0];
9365                 contentCfg = cfg.cn[1];
9366             
9367             }
9368             
9369             if(this.labelWidth > 12){
9370                 labelCfg.style = "width: " + this.labelWidth + 'px';
9371             }
9372             
9373             if(this.labelWidth < 13 && this.labelmd == 0){
9374                 this.labelmd = this.labelWidth;
9375             }
9376             
9377             if(this.labellg > 0){
9378                 labelCfg.cls += ' col-lg-' + this.labellg;
9379                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9380             }
9381             
9382             if(this.labelmd > 0){
9383                 labelCfg.cls += ' col-md-' + this.labelmd;
9384                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9385             }
9386             
9387             if(this.labelsm > 0){
9388                 labelCfg.cls += ' col-sm-' + this.labelsm;
9389                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9390             }
9391             
9392             if(this.labelxs > 0){
9393                 labelCfg.cls += ' col-xs-' + this.labelxs;
9394                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9395             }
9396             
9397             
9398         } else if ( this.fieldLabel.length) {
9399                 
9400             cfg.cn = [
9401                 {
9402                     tag : 'i',
9403                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9404                     tooltip : 'This field is required'
9405                 },
9406                 {
9407                     tag: 'label',
9408                    //cls : 'input-group-addon',
9409                     html : this.fieldLabel
9410
9411                 },
9412
9413                inputblock
9414
9415            ];
9416            
9417            if(this.indicatorpos == 'right'){
9418                 
9419                 cfg.cn = [
9420                     {
9421                         tag: 'label',
9422                        //cls : 'input-group-addon',
9423                         html : this.fieldLabel
9424
9425                     },
9426                     {
9427                         tag : 'i',
9428                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9429                         tooltip : 'This field is required'
9430                     },
9431
9432                    inputblock
9433
9434                ];
9435
9436             }
9437
9438         } else {
9439             
9440             cfg.cn = [
9441
9442                     inputblock
9443
9444             ];
9445                 
9446                 
9447         };
9448         
9449         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9450            cfg.cls += ' navbar-form';
9451         }
9452         
9453         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
9454             // on BS4 we do this only if not form 
9455             cfg.cls += ' navbar-form';
9456             cfg.tag = 'li';
9457         }
9458         
9459         return cfg;
9460         
9461     },
9462     /**
9463      * return the real input element.
9464      */
9465     inputEl: function ()
9466     {
9467         return this.el.select('input.form-control',true).first();
9468     },
9469     
9470     tooltipEl : function()
9471     {
9472         return this.inputEl();
9473     },
9474     
9475     indicatorEl : function()
9476     {
9477         if (Roo.bootstrap.version == 4) {
9478             return false; // not enabled in v4 yet.
9479         }
9480         
9481         var indicator = this.el.select('i.roo-required-indicator',true).first();
9482         
9483         if(!indicator){
9484             return false;
9485         }
9486         
9487         return indicator;
9488         
9489     },
9490     
9491     setDisabled : function(v)
9492     {
9493         var i  = this.inputEl().dom;
9494         if (!v) {
9495             i.removeAttribute('disabled');
9496             return;
9497             
9498         }
9499         i.setAttribute('disabled','true');
9500     },
9501     initEvents : function()
9502     {
9503           
9504         this.inputEl().on("keydown" , this.fireKey,  this);
9505         this.inputEl().on("focus", this.onFocus,  this);
9506         this.inputEl().on("blur", this.onBlur,  this);
9507         
9508         this.inputEl().relayEvent('keyup', this);
9509         
9510         this.indicator = this.indicatorEl();
9511         
9512         if(this.indicator){
9513             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9514         }
9515  
9516         // reference to original value for reset
9517         this.originalValue = this.getValue();
9518         //Roo.form.TextField.superclass.initEvents.call(this);
9519         if(this.validationEvent == 'keyup'){
9520             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9521             this.inputEl().on('keyup', this.filterValidation, this);
9522         }
9523         else if(this.validationEvent !== false){
9524             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9525         }
9526         
9527         if(this.selectOnFocus){
9528             this.on("focus", this.preFocus, this);
9529             
9530         }
9531         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9532             this.inputEl().on("keypress", this.filterKeys, this);
9533         } else {
9534             this.inputEl().relayEvent('keypress', this);
9535         }
9536        /* if(this.grow){
9537             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9538             this.el.on("click", this.autoSize,  this);
9539         }
9540         */
9541         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9542             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9543         }
9544         
9545         if (typeof(this.before) == 'object') {
9546             this.before.render(this.el.select('.roo-input-before',true).first());
9547         }
9548         if (typeof(this.after) == 'object') {
9549             this.after.render(this.el.select('.roo-input-after',true).first());
9550         }
9551         
9552         this.inputEl().on('change', this.onChange, this);
9553         
9554     },
9555     filterValidation : function(e){
9556         if(!e.isNavKeyPress()){
9557             this.validationTask.delay(this.validationDelay);
9558         }
9559     },
9560      /**
9561      * Validates the field value
9562      * @return {Boolean} True if the value is valid, else false
9563      */
9564     validate : function(){
9565         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9566         if(this.disabled || this.validateValue(this.getRawValue())){
9567             this.markValid();
9568             return true;
9569         }
9570         
9571         this.markInvalid();
9572         return false;
9573     },
9574     
9575     
9576     /**
9577      * Validates a value according to the field's validation rules and marks the field as invalid
9578      * if the validation fails
9579      * @param {Mixed} value The value to validate
9580      * @return {Boolean} True if the value is valid, else false
9581      */
9582     validateValue : function(value)
9583     {
9584         if(this.getVisibilityEl().hasClass('hidden')){
9585             return true;
9586         }
9587         
9588         if(value.length < 1)  { // if it's blank
9589             if(this.allowBlank){
9590                 return true;
9591             }
9592             return false;
9593         }
9594         
9595         if(value.length < this.minLength){
9596             return false;
9597         }
9598         if(value.length > this.maxLength){
9599             return false;
9600         }
9601         if(this.vtype){
9602             var vt = Roo.form.VTypes;
9603             if(!vt[this.vtype](value, this)){
9604                 return false;
9605             }
9606         }
9607         if(typeof this.validator == "function"){
9608             var msg = this.validator(value);
9609             if(msg !== true){
9610                 return false;
9611             }
9612             if (typeof(msg) == 'string') {
9613                 this.invalidText = msg;
9614             }
9615         }
9616         
9617         if(this.regex && !this.regex.test(value)){
9618             return false;
9619         }
9620         
9621         return true;
9622     },
9623     
9624      // private
9625     fireKey : function(e){
9626         //Roo.log('field ' + e.getKey());
9627         if(e.isNavKeyPress()){
9628             this.fireEvent("specialkey", this, e);
9629         }
9630     },
9631     focus : function (selectText){
9632         if(this.rendered){
9633             this.inputEl().focus();
9634             if(selectText === true){
9635                 this.inputEl().dom.select();
9636             }
9637         }
9638         return this;
9639     } ,
9640     
9641     onFocus : function(){
9642         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9643            // this.el.addClass(this.focusClass);
9644         }
9645         if(!this.hasFocus){
9646             this.hasFocus = true;
9647             this.startValue = this.getValue();
9648             this.fireEvent("focus", this);
9649         }
9650     },
9651     
9652     beforeBlur : Roo.emptyFn,
9653
9654     
9655     // private
9656     onBlur : function(){
9657         this.beforeBlur();
9658         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9659             //this.el.removeClass(this.focusClass);
9660         }
9661         this.hasFocus = false;
9662         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9663             this.validate();
9664         }
9665         var v = this.getValue();
9666         if(String(v) !== String(this.startValue)){
9667             this.fireEvent('change', this, v, this.startValue);
9668         }
9669         this.fireEvent("blur", this);
9670     },
9671     
9672     onChange : function(e)
9673     {
9674         var v = this.getValue();
9675         if(String(v) !== String(this.startValue)){
9676             this.fireEvent('change', this, v, this.startValue);
9677         }
9678         
9679     },
9680     
9681     /**
9682      * Resets the current field value to the originally loaded value and clears any validation messages
9683      */
9684     reset : function(){
9685         this.setValue(this.originalValue);
9686         this.validate();
9687     },
9688      /**
9689      * Returns the name of the field
9690      * @return {Mixed} name The name field
9691      */
9692     getName: function(){
9693         return this.name;
9694     },
9695      /**
9696      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9697      * @return {Mixed} value The field value
9698      */
9699     getValue : function(){
9700         
9701         var v = this.inputEl().getValue();
9702         
9703         return v;
9704     },
9705     /**
9706      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9707      * @return {Mixed} value The field value
9708      */
9709     getRawValue : function(){
9710         var v = this.inputEl().getValue();
9711         
9712         return v;
9713     },
9714     
9715     /**
9716      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9717      * @param {Mixed} value The value to set
9718      */
9719     setRawValue : function(v){
9720         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9721     },
9722     
9723     selectText : function(start, end){
9724         var v = this.getRawValue();
9725         if(v.length > 0){
9726             start = start === undefined ? 0 : start;
9727             end = end === undefined ? v.length : end;
9728             var d = this.inputEl().dom;
9729             if(d.setSelectionRange){
9730                 d.setSelectionRange(start, end);
9731             }else if(d.createTextRange){
9732                 var range = d.createTextRange();
9733                 range.moveStart("character", start);
9734                 range.moveEnd("character", v.length-end);
9735                 range.select();
9736             }
9737         }
9738     },
9739     
9740     /**
9741      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9742      * @param {Mixed} value The value to set
9743      */
9744     setValue : function(v){
9745         this.value = v;
9746         if(this.rendered){
9747             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9748             this.validate();
9749         }
9750     },
9751     
9752     /*
9753     processValue : function(value){
9754         if(this.stripCharsRe){
9755             var newValue = value.replace(this.stripCharsRe, '');
9756             if(newValue !== value){
9757                 this.setRawValue(newValue);
9758                 return newValue;
9759             }
9760         }
9761         return value;
9762     },
9763   */
9764     preFocus : function(){
9765         
9766         if(this.selectOnFocus){
9767             this.inputEl().dom.select();
9768         }
9769     },
9770     filterKeys : function(e){
9771         var k = e.getKey();
9772         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9773             return;
9774         }
9775         var c = e.getCharCode(), cc = String.fromCharCode(c);
9776         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9777             return;
9778         }
9779         if(!this.maskRe.test(cc)){
9780             e.stopEvent();
9781         }
9782     },
9783      /**
9784      * Clear any invalid styles/messages for this field
9785      */
9786     clearInvalid : function(){
9787         
9788         if(!this.el || this.preventMark){ // not rendered
9789             return;
9790         }
9791         
9792         
9793         this.el.removeClass([this.invalidClass, 'is-invalid']);
9794         
9795         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9796             
9797             var feedback = this.el.select('.form-control-feedback', true).first();
9798             
9799             if(feedback){
9800                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9801             }
9802             
9803         }
9804         
9805         if(this.indicator){
9806             this.indicator.removeClass('visible');
9807             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9808         }
9809         
9810         this.fireEvent('valid', this);
9811     },
9812     
9813      /**
9814      * Mark this field as valid
9815      */
9816     markValid : function()
9817     {
9818         if(!this.el  || this.preventMark){ // not rendered...
9819             return;
9820         }
9821         
9822         this.el.removeClass([this.invalidClass, this.validClass]);
9823         this.inputEl().removeClass(['is-valid', 'is-invalid']);
9824
9825         var feedback = this.el.select('.form-control-feedback', true).first();
9826             
9827         if(feedback){
9828             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9829         }
9830         
9831         if(this.indicator){
9832             this.indicator.removeClass('visible');
9833             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9834         }
9835         
9836         if(this.disabled){
9837             return;
9838         }
9839         
9840         if(this.allowBlank && !this.getRawValue().length){
9841             return;
9842         }
9843         if (Roo.bootstrap.version == 3) {
9844             this.el.addClass(this.validClass);
9845         } else {
9846             this.inputEl().addClass('is-valid');
9847         }
9848
9849         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9850             
9851             var feedback = this.el.select('.form-control-feedback', true).first();
9852             
9853             if(feedback){
9854                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9855                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9856             }
9857             
9858         }
9859         
9860         this.fireEvent('valid', this);
9861     },
9862     
9863      /**
9864      * Mark this field as invalid
9865      * @param {String} msg The validation message
9866      */
9867     markInvalid : function(msg)
9868     {
9869         if(!this.el  || this.preventMark){ // not rendered
9870             return;
9871         }
9872         
9873         this.el.removeClass([this.invalidClass, this.validClass]);
9874         this.inputEl().removeClass(['is-valid', 'is-invalid']);
9875         
9876         var feedback = this.el.select('.form-control-feedback', true).first();
9877             
9878         if(feedback){
9879             this.el.select('.form-control-feedback', true).first().removeClass(
9880                     [this.invalidFeedbackClass, this.validFeedbackClass]);
9881         }
9882
9883         if(this.disabled){
9884             return;
9885         }
9886         
9887         if(this.allowBlank && !this.getRawValue().length){
9888             return;
9889         }
9890         
9891         if(this.indicator){
9892             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9893             this.indicator.addClass('visible');
9894         }
9895         if (Roo.bootstrap.version == 3) {
9896             this.el.addClass(this.invalidClass);
9897         } else {
9898             this.inputEl().addClass('is-invalid');
9899         }
9900         
9901         
9902         
9903         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9904             
9905             var feedback = this.el.select('.form-control-feedback', true).first();
9906             
9907             if(feedback){
9908                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9909                 
9910                 if(this.getValue().length || this.forceFeedback){
9911                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9912                 }
9913                 
9914             }
9915             
9916         }
9917         
9918         this.fireEvent('invalid', this, msg);
9919     },
9920     // private
9921     SafariOnKeyDown : function(event)
9922     {
9923         // this is a workaround for a password hang bug on chrome/ webkit.
9924         if (this.inputEl().dom.type != 'password') {
9925             return;
9926         }
9927         
9928         var isSelectAll = false;
9929         
9930         if(this.inputEl().dom.selectionEnd > 0){
9931             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9932         }
9933         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9934             event.preventDefault();
9935             this.setValue('');
9936             return;
9937         }
9938         
9939         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9940             
9941             event.preventDefault();
9942             // this is very hacky as keydown always get's upper case.
9943             //
9944             var cc = String.fromCharCode(event.getCharCode());
9945             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9946             
9947         }
9948     },
9949     adjustWidth : function(tag, w){
9950         tag = tag.toLowerCase();
9951         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9952             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9953                 if(tag == 'input'){
9954                     return w + 2;
9955                 }
9956                 if(tag == 'textarea'){
9957                     return w-2;
9958                 }
9959             }else if(Roo.isOpera){
9960                 if(tag == 'input'){
9961                     return w + 2;
9962                 }
9963                 if(tag == 'textarea'){
9964                     return w-2;
9965                 }
9966             }
9967         }
9968         return w;
9969     },
9970     
9971     setFieldLabel : function(v)
9972     {
9973         if(!this.rendered){
9974             return;
9975         }
9976         
9977         if(this.indicatorEl()){
9978             var ar = this.el.select('label > span',true);
9979             
9980             if (ar.elements.length) {
9981                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9982                 this.fieldLabel = v;
9983                 return;
9984             }
9985             
9986             var br = this.el.select('label',true);
9987             
9988             if(br.elements.length) {
9989                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9990                 this.fieldLabel = v;
9991                 return;
9992             }
9993             
9994             Roo.log('Cannot Found any of label > span || label in input');
9995             return;
9996         }
9997         
9998         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9999         this.fieldLabel = v;
10000         
10001         
10002     }
10003 });
10004
10005  
10006 /*
10007  * - LGPL
10008  *
10009  * Input
10010  * 
10011  */
10012
10013 /**
10014  * @class Roo.bootstrap.TextArea
10015  * @extends Roo.bootstrap.Input
10016  * Bootstrap TextArea class
10017  * @cfg {Number} cols Specifies the visible width of a text area
10018  * @cfg {Number} rows Specifies the visible number of lines in a text area
10019  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
10020  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
10021  * @cfg {string} html text
10022  * 
10023  * @constructor
10024  * Create a new TextArea
10025  * @param {Object} config The config object
10026  */
10027
10028 Roo.bootstrap.TextArea = function(config){
10029     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
10030    
10031 };
10032
10033 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
10034      
10035     cols : false,
10036     rows : 5,
10037     readOnly : false,
10038     warp : 'soft',
10039     resize : false,
10040     value: false,
10041     html: false,
10042     
10043     getAutoCreate : function(){
10044         
10045         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10046         
10047         var id = Roo.id();
10048         
10049         var cfg = {};
10050         
10051         if(this.inputType != 'hidden'){
10052             cfg.cls = 'form-group' //input-group
10053         }
10054         
10055         var input =  {
10056             tag: 'textarea',
10057             id : id,
10058             warp : this.warp,
10059             rows : this.rows,
10060             value : this.value || '',
10061             html: this.html || '',
10062             cls : 'form-control',
10063             placeholder : this.placeholder || '' 
10064             
10065         };
10066         
10067         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10068             input.maxLength = this.maxLength;
10069         }
10070         
10071         if(this.resize){
10072             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
10073         }
10074         
10075         if(this.cols){
10076             input.cols = this.cols;
10077         }
10078         
10079         if (this.readOnly) {
10080             input.readonly = true;
10081         }
10082         
10083         if (this.name) {
10084             input.name = this.name;
10085         }
10086         
10087         if (this.size) {
10088             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
10089         }
10090         
10091         var settings=this;
10092         ['xs','sm','md','lg'].map(function(size){
10093             if (settings[size]) {
10094                 cfg.cls += ' col-' + size + '-' + settings[size];
10095             }
10096         });
10097         
10098         var inputblock = input;
10099         
10100         if(this.hasFeedback && !this.allowBlank){
10101             
10102             var feedback = {
10103                 tag: 'span',
10104                 cls: 'glyphicon form-control-feedback'
10105             };
10106
10107             inputblock = {
10108                 cls : 'has-feedback',
10109                 cn :  [
10110                     input,
10111                     feedback
10112                 ] 
10113             };  
10114         }
10115         
10116         
10117         if (this.before || this.after) {
10118             
10119             inputblock = {
10120                 cls : 'input-group',
10121                 cn :  [] 
10122             };
10123             if (this.before) {
10124                 inputblock.cn.push({
10125                     tag :'span',
10126                     cls : 'input-group-addon',
10127                     html : this.before
10128                 });
10129             }
10130             
10131             inputblock.cn.push(input);
10132             
10133             if(this.hasFeedback && !this.allowBlank){
10134                 inputblock.cls += ' has-feedback';
10135                 inputblock.cn.push(feedback);
10136             }
10137             
10138             if (this.after) {
10139                 inputblock.cn.push({
10140                     tag :'span',
10141                     cls : 'input-group-addon',
10142                     html : this.after
10143                 });
10144             }
10145             
10146         }
10147         
10148         if (align ==='left' && this.fieldLabel.length) {
10149             cfg.cn = [
10150                 {
10151                     tag: 'label',
10152                     'for' :  id,
10153                     cls : 'control-label',
10154                     html : this.fieldLabel
10155                 },
10156                 {
10157                     cls : "",
10158                     cn: [
10159                         inputblock
10160                     ]
10161                 }
10162
10163             ];
10164             
10165             if(this.labelWidth > 12){
10166                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10167             }
10168
10169             if(this.labelWidth < 13 && this.labelmd == 0){
10170                 this.labelmd = this.labelWidth;
10171             }
10172
10173             if(this.labellg > 0){
10174                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10175                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10176             }
10177
10178             if(this.labelmd > 0){
10179                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10180                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10181             }
10182
10183             if(this.labelsm > 0){
10184                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10185                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10186             }
10187
10188             if(this.labelxs > 0){
10189                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10190                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10191             }
10192             
10193         } else if ( this.fieldLabel.length) {
10194             cfg.cn = [
10195
10196                {
10197                    tag: 'label',
10198                    //cls : 'input-group-addon',
10199                    html : this.fieldLabel
10200
10201                },
10202
10203                inputblock
10204
10205            ];
10206
10207         } else {
10208
10209             cfg.cn = [
10210
10211                 inputblock
10212
10213             ];
10214                 
10215         }
10216         
10217         if (this.disabled) {
10218             input.disabled=true;
10219         }
10220         
10221         return cfg;
10222         
10223     },
10224     /**
10225      * return the real textarea element.
10226      */
10227     inputEl: function ()
10228     {
10229         return this.el.select('textarea.form-control',true).first();
10230     },
10231     
10232     /**
10233      * Clear any invalid styles/messages for this field
10234      */
10235     clearInvalid : function()
10236     {
10237         
10238         if(!this.el || this.preventMark){ // not rendered
10239             return;
10240         }
10241         
10242         var label = this.el.select('label', true).first();
10243         var icon = this.el.select('i.fa-star', true).first();
10244         
10245         if(label && icon){
10246             icon.remove();
10247         }
10248         this.el.removeClass( this.validClass);
10249         this.inputEl().removeClass('is-invalid');
10250          
10251         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10252             
10253             var feedback = this.el.select('.form-control-feedback', true).first();
10254             
10255             if(feedback){
10256                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10257             }
10258             
10259         }
10260         
10261         this.fireEvent('valid', this);
10262     },
10263     
10264      /**
10265      * Mark this field as valid
10266      */
10267     markValid : function()
10268     {
10269         if(!this.el  || this.preventMark){ // not rendered
10270             return;
10271         }
10272         
10273         this.el.removeClass([this.invalidClass, this.validClass]);
10274         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10275         
10276         var feedback = this.el.select('.form-control-feedback', true).first();
10277             
10278         if(feedback){
10279             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10280         }
10281
10282         if(this.disabled || this.allowBlank){
10283             return;
10284         }
10285         
10286         var label = this.el.select('label', true).first();
10287         var icon = this.el.select('i.fa-star', true).first();
10288         
10289         if(label && icon){
10290             icon.remove();
10291         }
10292         if (Roo.bootstrap.version == 3) {
10293             this.el.addClass(this.validClass);
10294         } else {
10295             this.inputEl().addClass('is-valid');
10296         }
10297         
10298         
10299         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10300             
10301             var feedback = this.el.select('.form-control-feedback', true).first();
10302             
10303             if(feedback){
10304                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10305                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10306             }
10307             
10308         }
10309         
10310         this.fireEvent('valid', this);
10311     },
10312     
10313      /**
10314      * Mark this field as invalid
10315      * @param {String} msg The validation message
10316      */
10317     markInvalid : function(msg)
10318     {
10319         if(!this.el  || this.preventMark){ // not rendered
10320             return;
10321         }
10322         
10323         this.el.removeClass([this.invalidClass, this.validClass]);
10324         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10325         
10326         var feedback = this.el.select('.form-control-feedback', true).first();
10327             
10328         if(feedback){
10329             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10330         }
10331
10332         if(this.disabled || this.allowBlank){
10333             return;
10334         }
10335         
10336         var label = this.el.select('label', true).first();
10337         var icon = this.el.select('i.fa-star', true).first();
10338         
10339         if(!this.getValue().length && label && !icon){
10340             this.el.createChild({
10341                 tag : 'i',
10342                 cls : 'text-danger fa fa-lg fa-star',
10343                 tooltip : 'This field is required',
10344                 style : 'margin-right:5px;'
10345             }, label, true);
10346         }
10347         
10348         if (Roo.bootstrap.version == 3) {
10349             this.el.addClass(this.invalidClass);
10350         } else {
10351             this.inputEl().addClass('is-invalid');
10352         }
10353         
10354         // fixme ... this may be depricated need to test..
10355         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10356             
10357             var feedback = this.el.select('.form-control-feedback', true).first();
10358             
10359             if(feedback){
10360                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10361                 
10362                 if(this.getValue().length || this.forceFeedback){
10363                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10364                 }
10365                 
10366             }
10367             
10368         }
10369         
10370         this.fireEvent('invalid', this, msg);
10371     }
10372 });
10373
10374  
10375 /*
10376  * - LGPL
10377  *
10378  * trigger field - base class for combo..
10379  * 
10380  */
10381  
10382 /**
10383  * @class Roo.bootstrap.TriggerField
10384  * @extends Roo.bootstrap.Input
10385  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10386  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10387  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10388  * for which you can provide a custom implementation.  For example:
10389  * <pre><code>
10390 var trigger = new Roo.bootstrap.TriggerField();
10391 trigger.onTriggerClick = myTriggerFn;
10392 trigger.applyTo('my-field');
10393 </code></pre>
10394  *
10395  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10396  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10397  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10398  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10399  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10400
10401  * @constructor
10402  * Create a new TriggerField.
10403  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10404  * to the base TextField)
10405  */
10406 Roo.bootstrap.TriggerField = function(config){
10407     this.mimicing = false;
10408     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10409 };
10410
10411 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10412     /**
10413      * @cfg {String} triggerClass A CSS class to apply to the trigger
10414      */
10415      /**
10416      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10417      */
10418     hideTrigger:false,
10419
10420     /**
10421      * @cfg {Boolean} removable (true|false) special filter default false
10422      */
10423     removable : false,
10424     
10425     /** @cfg {Boolean} grow @hide */
10426     /** @cfg {Number} growMin @hide */
10427     /** @cfg {Number} growMax @hide */
10428
10429     /**
10430      * @hide 
10431      * @method
10432      */
10433     autoSize: Roo.emptyFn,
10434     // private
10435     monitorTab : true,
10436     // private
10437     deferHeight : true,
10438
10439     
10440     actionMode : 'wrap',
10441     
10442     caret : false,
10443     
10444     
10445     getAutoCreate : function(){
10446        
10447         var align = this.labelAlign || this.parentLabelAlign();
10448         
10449         var id = Roo.id();
10450         
10451         var cfg = {
10452             cls: 'form-group' //input-group
10453         };
10454         
10455         
10456         var input =  {
10457             tag: 'input',
10458             id : id,
10459             type : this.inputType,
10460             cls : 'form-control',
10461             autocomplete: 'new-password',
10462             placeholder : this.placeholder || '' 
10463             
10464         };
10465         if (this.name) {
10466             input.name = this.name;
10467         }
10468         if (this.size) {
10469             input.cls += ' input-' + this.size;
10470         }
10471         
10472         if (this.disabled) {
10473             input.disabled=true;
10474         }
10475         
10476         var inputblock = input;
10477         
10478         if(this.hasFeedback && !this.allowBlank){
10479             
10480             var feedback = {
10481                 tag: 'span',
10482                 cls: 'glyphicon form-control-feedback'
10483             };
10484             
10485             if(this.removable && !this.editable && !this.tickable){
10486                 inputblock = {
10487                     cls : 'has-feedback',
10488                     cn :  [
10489                         inputblock,
10490                         {
10491                             tag: 'button',
10492                             html : 'x',
10493                             cls : 'roo-combo-removable-btn close'
10494                         },
10495                         feedback
10496                     ] 
10497                 };
10498             } else {
10499                 inputblock = {
10500                     cls : 'has-feedback',
10501                     cn :  [
10502                         inputblock,
10503                         feedback
10504                     ] 
10505                 };
10506             }
10507
10508         } else {
10509             if(this.removable && !this.editable && !this.tickable){
10510                 inputblock = {
10511                     cls : 'roo-removable',
10512                     cn :  [
10513                         inputblock,
10514                         {
10515                             tag: 'button',
10516                             html : 'x',
10517                             cls : 'roo-combo-removable-btn close'
10518                         }
10519                     ] 
10520                 };
10521             }
10522         }
10523         
10524         if (this.before || this.after) {
10525             
10526             inputblock = {
10527                 cls : 'input-group',
10528                 cn :  [] 
10529             };
10530             if (this.before) {
10531                 inputblock.cn.push({
10532                     tag :'span',
10533                     cls : 'input-group-addon input-group-prepend input-group-text',
10534                     html : this.before
10535                 });
10536             }
10537             
10538             inputblock.cn.push(input);
10539             
10540             if(this.hasFeedback && !this.allowBlank){
10541                 inputblock.cls += ' has-feedback';
10542                 inputblock.cn.push(feedback);
10543             }
10544             
10545             if (this.after) {
10546                 inputblock.cn.push({
10547                     tag :'span',
10548                     cls : 'input-group-addon input-group-append input-group-text',
10549                     html : this.after
10550                 });
10551             }
10552             
10553         };
10554         
10555       
10556         
10557         var ibwrap = inputblock;
10558         
10559         if(this.multiple){
10560             ibwrap = {
10561                 tag: 'ul',
10562                 cls: 'roo-select2-choices',
10563                 cn:[
10564                     {
10565                         tag: 'li',
10566                         cls: 'roo-select2-search-field',
10567                         cn: [
10568
10569                             inputblock
10570                         ]
10571                     }
10572                 ]
10573             };
10574                 
10575         }
10576         
10577         var combobox = {
10578             cls: 'roo-select2-container input-group',
10579             cn: [
10580                  {
10581                     tag: 'input',
10582                     type : 'hidden',
10583                     cls: 'form-hidden-field'
10584                 },
10585                 ibwrap
10586             ]
10587         };
10588         
10589         if(!this.multiple && this.showToggleBtn){
10590             
10591             var caret = {
10592                         tag: 'span',
10593                         cls: 'caret'
10594              };
10595             if (this.caret != false) {
10596                 caret = {
10597                      tag: 'i',
10598                      cls: 'fa fa-' + this.caret
10599                 };
10600                 
10601             }
10602             
10603             combobox.cn.push({
10604                 tag :'span',
10605                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10606                 cn : [
10607                     caret,
10608                     {
10609                         tag: 'span',
10610                         cls: 'combobox-clear',
10611                         cn  : [
10612                             {
10613                                 tag : 'i',
10614                                 cls: 'icon-remove'
10615                             }
10616                         ]
10617                     }
10618                 ]
10619
10620             })
10621         }
10622         
10623         if(this.multiple){
10624             combobox.cls += ' roo-select2-container-multi';
10625         }
10626          var indicator = {
10627             tag : 'i',
10628             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10629             tooltip : 'This field is required'
10630         };
10631         if (Roo.bootstrap.version == 4) {
10632             indicator = {
10633                 tag : 'i',
10634                 style : 'display:none'
10635             };
10636         }
10637         
10638         
10639         if (align ==='left' && this.fieldLabel.length) {
10640             
10641             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10642
10643             cfg.cn = [
10644                 indicator,
10645                 {
10646                     tag: 'label',
10647                     'for' :  id,
10648                     cls : 'control-label',
10649                     html : this.fieldLabel
10650
10651                 },
10652                 {
10653                     cls : "", 
10654                     cn: [
10655                         combobox
10656                     ]
10657                 }
10658
10659             ];
10660             
10661             var labelCfg = cfg.cn[1];
10662             var contentCfg = cfg.cn[2];
10663             
10664             if(this.indicatorpos == 'right'){
10665                 cfg.cn = [
10666                     {
10667                         tag: 'label',
10668                         'for' :  id,
10669                         cls : 'control-label',
10670                         cn : [
10671                             {
10672                                 tag : 'span',
10673                                 html : this.fieldLabel
10674                             },
10675                             indicator
10676                         ]
10677                     },
10678                     {
10679                         cls : "", 
10680                         cn: [
10681                             combobox
10682                         ]
10683                     }
10684
10685                 ];
10686                 
10687                 labelCfg = cfg.cn[0];
10688                 contentCfg = cfg.cn[1];
10689             }
10690             
10691             if(this.labelWidth > 12){
10692                 labelCfg.style = "width: " + this.labelWidth + 'px';
10693             }
10694             
10695             if(this.labelWidth < 13 && this.labelmd == 0){
10696                 this.labelmd = this.labelWidth;
10697             }
10698             
10699             if(this.labellg > 0){
10700                 labelCfg.cls += ' col-lg-' + this.labellg;
10701                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10702             }
10703             
10704             if(this.labelmd > 0){
10705                 labelCfg.cls += ' col-md-' + this.labelmd;
10706                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10707             }
10708             
10709             if(this.labelsm > 0){
10710                 labelCfg.cls += ' col-sm-' + this.labelsm;
10711                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10712             }
10713             
10714             if(this.labelxs > 0){
10715                 labelCfg.cls += ' col-xs-' + this.labelxs;
10716                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10717             }
10718             
10719         } else if ( this.fieldLabel.length) {
10720 //                Roo.log(" label");
10721             cfg.cn = [
10722                 indicator,
10723                {
10724                    tag: 'label',
10725                    //cls : 'input-group-addon',
10726                    html : this.fieldLabel
10727
10728                },
10729
10730                combobox
10731
10732             ];
10733             
10734             if(this.indicatorpos == 'right'){
10735                 
10736                 cfg.cn = [
10737                     {
10738                        tag: 'label',
10739                        cn : [
10740                            {
10741                                tag : 'span',
10742                                html : this.fieldLabel
10743                            },
10744                            indicator
10745                        ]
10746
10747                     },
10748                     combobox
10749
10750                 ];
10751
10752             }
10753
10754         } else {
10755             
10756 //                Roo.log(" no label && no align");
10757                 cfg = combobox
10758                      
10759                 
10760         }
10761         
10762         var settings=this;
10763         ['xs','sm','md','lg'].map(function(size){
10764             if (settings[size]) {
10765                 cfg.cls += ' col-' + size + '-' + settings[size];
10766             }
10767         });
10768         
10769         return cfg;
10770         
10771     },
10772     
10773     
10774     
10775     // private
10776     onResize : function(w, h){
10777 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10778 //        if(typeof w == 'number'){
10779 //            var x = w - this.trigger.getWidth();
10780 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10781 //            this.trigger.setStyle('left', x+'px');
10782 //        }
10783     },
10784
10785     // private
10786     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10787
10788     // private
10789     getResizeEl : function(){
10790         return this.inputEl();
10791     },
10792
10793     // private
10794     getPositionEl : function(){
10795         return this.inputEl();
10796     },
10797
10798     // private
10799     alignErrorIcon : function(){
10800         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10801     },
10802
10803     // private
10804     initEvents : function(){
10805         
10806         this.createList();
10807         
10808         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10809         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10810         if(!this.multiple && this.showToggleBtn){
10811             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10812             if(this.hideTrigger){
10813                 this.trigger.setDisplayed(false);
10814             }
10815             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10816         }
10817         
10818         if(this.multiple){
10819             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10820         }
10821         
10822         if(this.removable && !this.editable && !this.tickable){
10823             var close = this.closeTriggerEl();
10824             
10825             if(close){
10826                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10827                 close.on('click', this.removeBtnClick, this, close);
10828             }
10829         }
10830         
10831         //this.trigger.addClassOnOver('x-form-trigger-over');
10832         //this.trigger.addClassOnClick('x-form-trigger-click');
10833         
10834         //if(!this.width){
10835         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10836         //}
10837     },
10838     
10839     closeTriggerEl : function()
10840     {
10841         var close = this.el.select('.roo-combo-removable-btn', true).first();
10842         return close ? close : false;
10843     },
10844     
10845     removeBtnClick : function(e, h, el)
10846     {
10847         e.preventDefault();
10848         
10849         if(this.fireEvent("remove", this) !== false){
10850             this.reset();
10851             this.fireEvent("afterremove", this)
10852         }
10853     },
10854     
10855     createList : function()
10856     {
10857         this.list = Roo.get(document.body).createChild({
10858             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
10859             cls: 'typeahead typeahead-long dropdown-menu',
10860             style: 'display:none'
10861         });
10862         
10863         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10864         
10865     },
10866
10867     // private
10868     initTrigger : function(){
10869        
10870     },
10871
10872     // private
10873     onDestroy : function(){
10874         if(this.trigger){
10875             this.trigger.removeAllListeners();
10876           //  this.trigger.remove();
10877         }
10878         //if(this.wrap){
10879         //    this.wrap.remove();
10880         //}
10881         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10882     },
10883
10884     // private
10885     onFocus : function(){
10886         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10887         /*
10888         if(!this.mimicing){
10889             this.wrap.addClass('x-trigger-wrap-focus');
10890             this.mimicing = true;
10891             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10892             if(this.monitorTab){
10893                 this.el.on("keydown", this.checkTab, this);
10894             }
10895         }
10896         */
10897     },
10898
10899     // private
10900     checkTab : function(e){
10901         if(e.getKey() == e.TAB){
10902             this.triggerBlur();
10903         }
10904     },
10905
10906     // private
10907     onBlur : function(){
10908         // do nothing
10909     },
10910
10911     // private
10912     mimicBlur : function(e, t){
10913         /*
10914         if(!this.wrap.contains(t) && this.validateBlur()){
10915             this.triggerBlur();
10916         }
10917         */
10918     },
10919
10920     // private
10921     triggerBlur : function(){
10922         this.mimicing = false;
10923         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10924         if(this.monitorTab){
10925             this.el.un("keydown", this.checkTab, this);
10926         }
10927         //this.wrap.removeClass('x-trigger-wrap-focus');
10928         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10929     },
10930
10931     // private
10932     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10933     validateBlur : function(e, t){
10934         return true;
10935     },
10936
10937     // private
10938     onDisable : function(){
10939         this.inputEl().dom.disabled = true;
10940         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10941         //if(this.wrap){
10942         //    this.wrap.addClass('x-item-disabled');
10943         //}
10944     },
10945
10946     // private
10947     onEnable : function(){
10948         this.inputEl().dom.disabled = false;
10949         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10950         //if(this.wrap){
10951         //    this.el.removeClass('x-item-disabled');
10952         //}
10953     },
10954
10955     // private
10956     onShow : function(){
10957         var ae = this.getActionEl();
10958         
10959         if(ae){
10960             ae.dom.style.display = '';
10961             ae.dom.style.visibility = 'visible';
10962         }
10963     },
10964
10965     // private
10966     
10967     onHide : function(){
10968         var ae = this.getActionEl();
10969         ae.dom.style.display = 'none';
10970     },
10971
10972     /**
10973      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10974      * by an implementing function.
10975      * @method
10976      * @param {EventObject} e
10977      */
10978     onTriggerClick : Roo.emptyFn
10979 });
10980  /*
10981  * Based on:
10982  * Ext JS Library 1.1.1
10983  * Copyright(c) 2006-2007, Ext JS, LLC.
10984  *
10985  * Originally Released Under LGPL - original licence link has changed is not relivant.
10986  *
10987  * Fork - LGPL
10988  * <script type="text/javascript">
10989  */
10990
10991
10992 /**
10993  * @class Roo.data.SortTypes
10994  * @singleton
10995  * Defines the default sorting (casting?) comparison functions used when sorting data.
10996  */
10997 Roo.data.SortTypes = {
10998     /**
10999      * Default sort that does nothing
11000      * @param {Mixed} s The value being converted
11001      * @return {Mixed} The comparison value
11002      */
11003     none : function(s){
11004         return s;
11005     },
11006     
11007     /**
11008      * The regular expression used to strip tags
11009      * @type {RegExp}
11010      * @property
11011      */
11012     stripTagsRE : /<\/?[^>]+>/gi,
11013     
11014     /**
11015      * Strips all HTML tags to sort on text only
11016      * @param {Mixed} s The value being converted
11017      * @return {String} The comparison value
11018      */
11019     asText : function(s){
11020         return String(s).replace(this.stripTagsRE, "");
11021     },
11022     
11023     /**
11024      * Strips all HTML tags to sort on text only - Case insensitive
11025      * @param {Mixed} s The value being converted
11026      * @return {String} The comparison value
11027      */
11028     asUCText : function(s){
11029         return String(s).toUpperCase().replace(this.stripTagsRE, "");
11030     },
11031     
11032     /**
11033      * Case insensitive string
11034      * @param {Mixed} s The value being converted
11035      * @return {String} The comparison value
11036      */
11037     asUCString : function(s) {
11038         return String(s).toUpperCase();
11039     },
11040     
11041     /**
11042      * Date sorting
11043      * @param {Mixed} s The value being converted
11044      * @return {Number} The comparison value
11045      */
11046     asDate : function(s) {
11047         if(!s){
11048             return 0;
11049         }
11050         if(s instanceof Date){
11051             return s.getTime();
11052         }
11053         return Date.parse(String(s));
11054     },
11055     
11056     /**
11057      * Float sorting
11058      * @param {Mixed} s The value being converted
11059      * @return {Float} The comparison value
11060      */
11061     asFloat : function(s) {
11062         var val = parseFloat(String(s).replace(/,/g, ""));
11063         if(isNaN(val)) {
11064             val = 0;
11065         }
11066         return val;
11067     },
11068     
11069     /**
11070      * Integer sorting
11071      * @param {Mixed} s The value being converted
11072      * @return {Number} The comparison value
11073      */
11074     asInt : function(s) {
11075         var val = parseInt(String(s).replace(/,/g, ""));
11076         if(isNaN(val)) {
11077             val = 0;
11078         }
11079         return val;
11080     }
11081 };/*
11082  * Based on:
11083  * Ext JS Library 1.1.1
11084  * Copyright(c) 2006-2007, Ext JS, LLC.
11085  *
11086  * Originally Released Under LGPL - original licence link has changed is not relivant.
11087  *
11088  * Fork - LGPL
11089  * <script type="text/javascript">
11090  */
11091
11092 /**
11093 * @class Roo.data.Record
11094  * Instances of this class encapsulate both record <em>definition</em> information, and record
11095  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
11096  * to access Records cached in an {@link Roo.data.Store} object.<br>
11097  * <p>
11098  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
11099  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
11100  * objects.<br>
11101  * <p>
11102  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
11103  * @constructor
11104  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
11105  * {@link #create}. The parameters are the same.
11106  * @param {Array} data An associative Array of data values keyed by the field name.
11107  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
11108  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
11109  * not specified an integer id is generated.
11110  */
11111 Roo.data.Record = function(data, id){
11112     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11113     this.data = data;
11114 };
11115
11116 /**
11117  * Generate a constructor for a specific record layout.
11118  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11119  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11120  * Each field definition object may contain the following properties: <ul>
11121  * <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,
11122  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11123  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11124  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11125  * is being used, then this is a string containing the javascript expression to reference the data relative to 
11126  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11127  * to the data item relative to the record element. If the mapping expression is the same as the field name,
11128  * this may be omitted.</p></li>
11129  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11130  * <ul><li>auto (Default, implies no conversion)</li>
11131  * <li>string</li>
11132  * <li>int</li>
11133  * <li>float</li>
11134  * <li>boolean</li>
11135  * <li>date</li></ul></p></li>
11136  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11137  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11138  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11139  * by the Reader into an object that will be stored in the Record. It is passed the
11140  * following parameters:<ul>
11141  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11142  * </ul></p></li>
11143  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11144  * </ul>
11145  * <br>usage:<br><pre><code>
11146 var TopicRecord = Roo.data.Record.create(
11147     {name: 'title', mapping: 'topic_title'},
11148     {name: 'author', mapping: 'username'},
11149     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11150     {name: 'lastPost', mapping: 'post_time', type: 'date'},
11151     {name: 'lastPoster', mapping: 'user2'},
11152     {name: 'excerpt', mapping: 'post_text'}
11153 );
11154
11155 var myNewRecord = new TopicRecord({
11156     title: 'Do my job please',
11157     author: 'noobie',
11158     totalPosts: 1,
11159     lastPost: new Date(),
11160     lastPoster: 'Animal',
11161     excerpt: 'No way dude!'
11162 });
11163 myStore.add(myNewRecord);
11164 </code></pre>
11165  * @method create
11166  * @static
11167  */
11168 Roo.data.Record.create = function(o){
11169     var f = function(){
11170         f.superclass.constructor.apply(this, arguments);
11171     };
11172     Roo.extend(f, Roo.data.Record);
11173     var p = f.prototype;
11174     p.fields = new Roo.util.MixedCollection(false, function(field){
11175         return field.name;
11176     });
11177     for(var i = 0, len = o.length; i < len; i++){
11178         p.fields.add(new Roo.data.Field(o[i]));
11179     }
11180     f.getField = function(name){
11181         return p.fields.get(name);  
11182     };
11183     return f;
11184 };
11185
11186 Roo.data.Record.AUTO_ID = 1000;
11187 Roo.data.Record.EDIT = 'edit';
11188 Roo.data.Record.REJECT = 'reject';
11189 Roo.data.Record.COMMIT = 'commit';
11190
11191 Roo.data.Record.prototype = {
11192     /**
11193      * Readonly flag - true if this record has been modified.
11194      * @type Boolean
11195      */
11196     dirty : false,
11197     editing : false,
11198     error: null,
11199     modified: null,
11200
11201     // private
11202     join : function(store){
11203         this.store = store;
11204     },
11205
11206     /**
11207      * Set the named field to the specified value.
11208      * @param {String} name The name of the field to set.
11209      * @param {Object} value The value to set the field to.
11210      */
11211     set : function(name, value){
11212         if(this.data[name] == value){
11213             return;
11214         }
11215         this.dirty = true;
11216         if(!this.modified){
11217             this.modified = {};
11218         }
11219         if(typeof this.modified[name] == 'undefined'){
11220             this.modified[name] = this.data[name];
11221         }
11222         this.data[name] = value;
11223         if(!this.editing && this.store){
11224             this.store.afterEdit(this);
11225         }       
11226     },
11227
11228     /**
11229      * Get the value of the named field.
11230      * @param {String} name The name of the field to get the value of.
11231      * @return {Object} The value of the field.
11232      */
11233     get : function(name){
11234         return this.data[name]; 
11235     },
11236
11237     // private
11238     beginEdit : function(){
11239         this.editing = true;
11240         this.modified = {}; 
11241     },
11242
11243     // private
11244     cancelEdit : function(){
11245         this.editing = false;
11246         delete this.modified;
11247     },
11248
11249     // private
11250     endEdit : function(){
11251         this.editing = false;
11252         if(this.dirty && this.store){
11253             this.store.afterEdit(this);
11254         }
11255     },
11256
11257     /**
11258      * Usually called by the {@link Roo.data.Store} which owns the Record.
11259      * Rejects all changes made to the Record since either creation, or the last commit operation.
11260      * Modified fields are reverted to their original values.
11261      * <p>
11262      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11263      * of reject operations.
11264      */
11265     reject : function(){
11266         var m = this.modified;
11267         for(var n in m){
11268             if(typeof m[n] != "function"){
11269                 this.data[n] = m[n];
11270             }
11271         }
11272         this.dirty = false;
11273         delete this.modified;
11274         this.editing = false;
11275         if(this.store){
11276             this.store.afterReject(this);
11277         }
11278     },
11279
11280     /**
11281      * Usually called by the {@link Roo.data.Store} which owns the Record.
11282      * Commits all changes made to the Record since either creation, or the last commit operation.
11283      * <p>
11284      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11285      * of commit operations.
11286      */
11287     commit : function(){
11288         this.dirty = false;
11289         delete this.modified;
11290         this.editing = false;
11291         if(this.store){
11292             this.store.afterCommit(this);
11293         }
11294     },
11295
11296     // private
11297     hasError : function(){
11298         return this.error != null;
11299     },
11300
11301     // private
11302     clearError : function(){
11303         this.error = null;
11304     },
11305
11306     /**
11307      * Creates a copy of this record.
11308      * @param {String} id (optional) A new record id if you don't want to use this record's id
11309      * @return {Record}
11310      */
11311     copy : function(newId) {
11312         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11313     }
11314 };/*
11315  * Based on:
11316  * Ext JS Library 1.1.1
11317  * Copyright(c) 2006-2007, Ext JS, LLC.
11318  *
11319  * Originally Released Under LGPL - original licence link has changed is not relivant.
11320  *
11321  * Fork - LGPL
11322  * <script type="text/javascript">
11323  */
11324
11325
11326
11327 /**
11328  * @class Roo.data.Store
11329  * @extends Roo.util.Observable
11330  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11331  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11332  * <p>
11333  * 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
11334  * has no knowledge of the format of the data returned by the Proxy.<br>
11335  * <p>
11336  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11337  * instances from the data object. These records are cached and made available through accessor functions.
11338  * @constructor
11339  * Creates a new Store.
11340  * @param {Object} config A config object containing the objects needed for the Store to access data,
11341  * and read the data into Records.
11342  */
11343 Roo.data.Store = function(config){
11344     this.data = new Roo.util.MixedCollection(false);
11345     this.data.getKey = function(o){
11346         return o.id;
11347     };
11348     this.baseParams = {};
11349     // private
11350     this.paramNames = {
11351         "start" : "start",
11352         "limit" : "limit",
11353         "sort" : "sort",
11354         "dir" : "dir",
11355         "multisort" : "_multisort"
11356     };
11357
11358     if(config && config.data){
11359         this.inlineData = config.data;
11360         delete config.data;
11361     }
11362
11363     Roo.apply(this, config);
11364     
11365     if(this.reader){ // reader passed
11366         this.reader = Roo.factory(this.reader, Roo.data);
11367         this.reader.xmodule = this.xmodule || false;
11368         if(!this.recordType){
11369             this.recordType = this.reader.recordType;
11370         }
11371         if(this.reader.onMetaChange){
11372             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11373         }
11374     }
11375
11376     if(this.recordType){
11377         this.fields = this.recordType.prototype.fields;
11378     }
11379     this.modified = [];
11380
11381     this.addEvents({
11382         /**
11383          * @event datachanged
11384          * Fires when the data cache has changed, and a widget which is using this Store
11385          * as a Record cache should refresh its view.
11386          * @param {Store} this
11387          */
11388         datachanged : true,
11389         /**
11390          * @event metachange
11391          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11392          * @param {Store} this
11393          * @param {Object} meta The JSON metadata
11394          */
11395         metachange : true,
11396         /**
11397          * @event add
11398          * Fires when Records have been added to the Store
11399          * @param {Store} this
11400          * @param {Roo.data.Record[]} records The array of Records added
11401          * @param {Number} index The index at which the record(s) were added
11402          */
11403         add : true,
11404         /**
11405          * @event remove
11406          * Fires when a Record has been removed from the Store
11407          * @param {Store} this
11408          * @param {Roo.data.Record} record The Record that was removed
11409          * @param {Number} index The index at which the record was removed
11410          */
11411         remove : true,
11412         /**
11413          * @event update
11414          * Fires when a Record has been updated
11415          * @param {Store} this
11416          * @param {Roo.data.Record} record The Record that was updated
11417          * @param {String} operation The update operation being performed.  Value may be one of:
11418          * <pre><code>
11419  Roo.data.Record.EDIT
11420  Roo.data.Record.REJECT
11421  Roo.data.Record.COMMIT
11422          * </code></pre>
11423          */
11424         update : true,
11425         /**
11426          * @event clear
11427          * Fires when the data cache has been cleared.
11428          * @param {Store} this
11429          */
11430         clear : true,
11431         /**
11432          * @event beforeload
11433          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11434          * the load action will be canceled.
11435          * @param {Store} this
11436          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11437          */
11438         beforeload : true,
11439         /**
11440          * @event beforeloadadd
11441          * Fires after a new set of Records has been loaded.
11442          * @param {Store} this
11443          * @param {Roo.data.Record[]} records The Records that were loaded
11444          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11445          */
11446         beforeloadadd : true,
11447         /**
11448          * @event load
11449          * Fires after a new set of Records has been loaded, before they are added to the store.
11450          * @param {Store} this
11451          * @param {Roo.data.Record[]} records The Records that were loaded
11452          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11453          * @params {Object} return from reader
11454          */
11455         load : true,
11456         /**
11457          * @event loadexception
11458          * Fires if an exception occurs in the Proxy during loading.
11459          * Called with the signature of the Proxy's "loadexception" event.
11460          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11461          * 
11462          * @param {Proxy} 
11463          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11464          * @param {Object} load options 
11465          * @param {Object} jsonData from your request (normally this contains the Exception)
11466          */
11467         loadexception : true
11468     });
11469     
11470     if(this.proxy){
11471         this.proxy = Roo.factory(this.proxy, Roo.data);
11472         this.proxy.xmodule = this.xmodule || false;
11473         this.relayEvents(this.proxy,  ["loadexception"]);
11474     }
11475     this.sortToggle = {};
11476     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11477
11478     Roo.data.Store.superclass.constructor.call(this);
11479
11480     if(this.inlineData){
11481         this.loadData(this.inlineData);
11482         delete this.inlineData;
11483     }
11484 };
11485
11486 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11487      /**
11488     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11489     * without a remote query - used by combo/forms at present.
11490     */
11491     
11492     /**
11493     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11494     */
11495     /**
11496     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11497     */
11498     /**
11499     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11500     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11501     */
11502     /**
11503     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11504     * on any HTTP request
11505     */
11506     /**
11507     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11508     */
11509     /**
11510     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11511     */
11512     multiSort: false,
11513     /**
11514     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11515     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11516     */
11517     remoteSort : false,
11518
11519     /**
11520     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11521      * loaded or when a record is removed. (defaults to false).
11522     */
11523     pruneModifiedRecords : false,
11524
11525     // private
11526     lastOptions : null,
11527
11528     /**
11529      * Add Records to the Store and fires the add event.
11530      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11531      */
11532     add : function(records){
11533         records = [].concat(records);
11534         for(var i = 0, len = records.length; i < len; i++){
11535             records[i].join(this);
11536         }
11537         var index = this.data.length;
11538         this.data.addAll(records);
11539         this.fireEvent("add", this, records, index);
11540     },
11541
11542     /**
11543      * Remove a Record from the Store and fires the remove event.
11544      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11545      */
11546     remove : function(record){
11547         var index = this.data.indexOf(record);
11548         this.data.removeAt(index);
11549  
11550         if(this.pruneModifiedRecords){
11551             this.modified.remove(record);
11552         }
11553         this.fireEvent("remove", this, record, index);
11554     },
11555
11556     /**
11557      * Remove all Records from the Store and fires the clear event.
11558      */
11559     removeAll : function(){
11560         this.data.clear();
11561         if(this.pruneModifiedRecords){
11562             this.modified = [];
11563         }
11564         this.fireEvent("clear", this);
11565     },
11566
11567     /**
11568      * Inserts Records to the Store at the given index and fires the add event.
11569      * @param {Number} index The start index at which to insert the passed Records.
11570      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11571      */
11572     insert : function(index, records){
11573         records = [].concat(records);
11574         for(var i = 0, len = records.length; i < len; i++){
11575             this.data.insert(index, records[i]);
11576             records[i].join(this);
11577         }
11578         this.fireEvent("add", this, records, index);
11579     },
11580
11581     /**
11582      * Get the index within the cache of the passed Record.
11583      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11584      * @return {Number} The index of the passed Record. Returns -1 if not found.
11585      */
11586     indexOf : function(record){
11587         return this.data.indexOf(record);
11588     },
11589
11590     /**
11591      * Get the index within the cache of the Record with the passed id.
11592      * @param {String} id The id of the Record to find.
11593      * @return {Number} The index of the Record. Returns -1 if not found.
11594      */
11595     indexOfId : function(id){
11596         return this.data.indexOfKey(id);
11597     },
11598
11599     /**
11600      * Get the Record with the specified id.
11601      * @param {String} id The id of the Record to find.
11602      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11603      */
11604     getById : function(id){
11605         return this.data.key(id);
11606     },
11607
11608     /**
11609      * Get the Record at the specified index.
11610      * @param {Number} index The index of the Record to find.
11611      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11612      */
11613     getAt : function(index){
11614         return this.data.itemAt(index);
11615     },
11616
11617     /**
11618      * Returns a range of Records between specified indices.
11619      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11620      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11621      * @return {Roo.data.Record[]} An array of Records
11622      */
11623     getRange : function(start, end){
11624         return this.data.getRange(start, end);
11625     },
11626
11627     // private
11628     storeOptions : function(o){
11629         o = Roo.apply({}, o);
11630         delete o.callback;
11631         delete o.scope;
11632         this.lastOptions = o;
11633     },
11634
11635     /**
11636      * Loads the Record cache from the configured Proxy using the configured Reader.
11637      * <p>
11638      * If using remote paging, then the first load call must specify the <em>start</em>
11639      * and <em>limit</em> properties in the options.params property to establish the initial
11640      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11641      * <p>
11642      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11643      * and this call will return before the new data has been loaded. Perform any post-processing
11644      * in a callback function, or in a "load" event handler.</strong>
11645      * <p>
11646      * @param {Object} options An object containing properties which control loading options:<ul>
11647      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11648      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11649      * passed the following arguments:<ul>
11650      * <li>r : Roo.data.Record[]</li>
11651      * <li>options: Options object from the load call</li>
11652      * <li>success: Boolean success indicator</li></ul></li>
11653      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11654      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11655      * </ul>
11656      */
11657     load : function(options){
11658         options = options || {};
11659         if(this.fireEvent("beforeload", this, options) !== false){
11660             this.storeOptions(options);
11661             var p = Roo.apply(options.params || {}, this.baseParams);
11662             // if meta was not loaded from remote source.. try requesting it.
11663             if (!this.reader.metaFromRemote) {
11664                 p._requestMeta = 1;
11665             }
11666             if(this.sortInfo && this.remoteSort){
11667                 var pn = this.paramNames;
11668                 p[pn["sort"]] = this.sortInfo.field;
11669                 p[pn["dir"]] = this.sortInfo.direction;
11670             }
11671             if (this.multiSort) {
11672                 var pn = this.paramNames;
11673                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11674             }
11675             
11676             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11677         }
11678     },
11679
11680     /**
11681      * Reloads the Record cache from the configured Proxy using the configured Reader and
11682      * the options from the last load operation performed.
11683      * @param {Object} options (optional) An object containing properties which may override the options
11684      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11685      * the most recently used options are reused).
11686      */
11687     reload : function(options){
11688         this.load(Roo.applyIf(options||{}, this.lastOptions));
11689     },
11690
11691     // private
11692     // Called as a callback by the Reader during a load operation.
11693     loadRecords : function(o, options, success){
11694         if(!o || success === false){
11695             if(success !== false){
11696                 this.fireEvent("load", this, [], options, o);
11697             }
11698             if(options.callback){
11699                 options.callback.call(options.scope || this, [], options, false);
11700             }
11701             return;
11702         }
11703         // if data returned failure - throw an exception.
11704         if (o.success === false) {
11705             // show a message if no listener is registered.
11706             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11707                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11708             }
11709             // loadmask wil be hooked into this..
11710             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11711             return;
11712         }
11713         var r = o.records, t = o.totalRecords || r.length;
11714         
11715         this.fireEvent("beforeloadadd", this, r, options, o);
11716         
11717         if(!options || options.add !== true){
11718             if(this.pruneModifiedRecords){
11719                 this.modified = [];
11720             }
11721             for(var i = 0, len = r.length; i < len; i++){
11722                 r[i].join(this);
11723             }
11724             if(this.snapshot){
11725                 this.data = this.snapshot;
11726                 delete this.snapshot;
11727             }
11728             this.data.clear();
11729             this.data.addAll(r);
11730             this.totalLength = t;
11731             this.applySort();
11732             this.fireEvent("datachanged", this);
11733         }else{
11734             this.totalLength = Math.max(t, this.data.length+r.length);
11735             this.add(r);
11736         }
11737         
11738         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11739                 
11740             var e = new Roo.data.Record({});
11741
11742             e.set(this.parent.displayField, this.parent.emptyTitle);
11743             e.set(this.parent.valueField, '');
11744
11745             this.insert(0, e);
11746         }
11747             
11748         this.fireEvent("load", this, r, options, o);
11749         if(options.callback){
11750             options.callback.call(options.scope || this, r, options, true);
11751         }
11752     },
11753
11754
11755     /**
11756      * Loads data from a passed data block. A Reader which understands the format of the data
11757      * must have been configured in the constructor.
11758      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11759      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11760      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11761      */
11762     loadData : function(o, append){
11763         var r = this.reader.readRecords(o);
11764         this.loadRecords(r, {add: append}, true);
11765     },
11766
11767     /**
11768      * Gets the number of cached records.
11769      * <p>
11770      * <em>If using paging, this may not be the total size of the dataset. If the data object
11771      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11772      * the data set size</em>
11773      */
11774     getCount : function(){
11775         return this.data.length || 0;
11776     },
11777
11778     /**
11779      * Gets the total number of records in the dataset as returned by the server.
11780      * <p>
11781      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11782      * the dataset size</em>
11783      */
11784     getTotalCount : function(){
11785         return this.totalLength || 0;
11786     },
11787
11788     /**
11789      * Returns the sort state of the Store as an object with two properties:
11790      * <pre><code>
11791  field {String} The name of the field by which the Records are sorted
11792  direction {String} The sort order, "ASC" or "DESC"
11793      * </code></pre>
11794      */
11795     getSortState : function(){
11796         return this.sortInfo;
11797     },
11798
11799     // private
11800     applySort : function(){
11801         if(this.sortInfo && !this.remoteSort){
11802             var s = this.sortInfo, f = s.field;
11803             var st = this.fields.get(f).sortType;
11804             var fn = function(r1, r2){
11805                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11806                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11807             };
11808             this.data.sort(s.direction, fn);
11809             if(this.snapshot && this.snapshot != this.data){
11810                 this.snapshot.sort(s.direction, fn);
11811             }
11812         }
11813     },
11814
11815     /**
11816      * Sets the default sort column and order to be used by the next load operation.
11817      * @param {String} fieldName The name of the field to sort by.
11818      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11819      */
11820     setDefaultSort : function(field, dir){
11821         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11822     },
11823
11824     /**
11825      * Sort the Records.
11826      * If remote sorting is used, the sort is performed on the server, and the cache is
11827      * reloaded. If local sorting is used, the cache is sorted internally.
11828      * @param {String} fieldName The name of the field to sort by.
11829      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11830      */
11831     sort : function(fieldName, dir){
11832         var f = this.fields.get(fieldName);
11833         if(!dir){
11834             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11835             
11836             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11837                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11838             }else{
11839                 dir = f.sortDir;
11840             }
11841         }
11842         this.sortToggle[f.name] = dir;
11843         this.sortInfo = {field: f.name, direction: dir};
11844         if(!this.remoteSort){
11845             this.applySort();
11846             this.fireEvent("datachanged", this);
11847         }else{
11848             this.load(this.lastOptions);
11849         }
11850     },
11851
11852     /**
11853      * Calls the specified function for each of the Records in the cache.
11854      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11855      * Returning <em>false</em> aborts and exits the iteration.
11856      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11857      */
11858     each : function(fn, scope){
11859         this.data.each(fn, scope);
11860     },
11861
11862     /**
11863      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11864      * (e.g., during paging).
11865      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11866      */
11867     getModifiedRecords : function(){
11868         return this.modified;
11869     },
11870
11871     // private
11872     createFilterFn : function(property, value, anyMatch){
11873         if(!value.exec){ // not a regex
11874             value = String(value);
11875             if(value.length == 0){
11876                 return false;
11877             }
11878             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11879         }
11880         return function(r){
11881             return value.test(r.data[property]);
11882         };
11883     },
11884
11885     /**
11886      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11887      * @param {String} property A field on your records
11888      * @param {Number} start The record index to start at (defaults to 0)
11889      * @param {Number} end The last record index to include (defaults to length - 1)
11890      * @return {Number} The sum
11891      */
11892     sum : function(property, start, end){
11893         var rs = this.data.items, v = 0;
11894         start = start || 0;
11895         end = (end || end === 0) ? end : rs.length-1;
11896
11897         for(var i = start; i <= end; i++){
11898             v += (rs[i].data[property] || 0);
11899         }
11900         return v;
11901     },
11902
11903     /**
11904      * Filter the records by a specified property.
11905      * @param {String} field A field on your records
11906      * @param {String/RegExp} value Either a string that the field
11907      * should start with or a RegExp to test against the field
11908      * @param {Boolean} anyMatch True to match any part not just the beginning
11909      */
11910     filter : function(property, value, anyMatch){
11911         var fn = this.createFilterFn(property, value, anyMatch);
11912         return fn ? this.filterBy(fn) : this.clearFilter();
11913     },
11914
11915     /**
11916      * Filter by a function. The specified function will be called with each
11917      * record in this data source. If the function returns true the record is included,
11918      * otherwise it is filtered.
11919      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11920      * @param {Object} scope (optional) The scope of the function (defaults to this)
11921      */
11922     filterBy : function(fn, scope){
11923         this.snapshot = this.snapshot || this.data;
11924         this.data = this.queryBy(fn, scope||this);
11925         this.fireEvent("datachanged", this);
11926     },
11927
11928     /**
11929      * Query the records by a specified property.
11930      * @param {String} field A field on your records
11931      * @param {String/RegExp} value Either a string that the field
11932      * should start with or a RegExp to test against the field
11933      * @param {Boolean} anyMatch True to match any part not just the beginning
11934      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11935      */
11936     query : function(property, value, anyMatch){
11937         var fn = this.createFilterFn(property, value, anyMatch);
11938         return fn ? this.queryBy(fn) : this.data.clone();
11939     },
11940
11941     /**
11942      * Query by a function. The specified function will be called with each
11943      * record in this data source. If the function returns true the record is included
11944      * in the results.
11945      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11946      * @param {Object} scope (optional) The scope of the function (defaults to this)
11947       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11948      **/
11949     queryBy : function(fn, scope){
11950         var data = this.snapshot || this.data;
11951         return data.filterBy(fn, scope||this);
11952     },
11953
11954     /**
11955      * Collects unique values for a particular dataIndex from this store.
11956      * @param {String} dataIndex The property to collect
11957      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11958      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11959      * @return {Array} An array of the unique values
11960      **/
11961     collect : function(dataIndex, allowNull, bypassFilter){
11962         var d = (bypassFilter === true && this.snapshot) ?
11963                 this.snapshot.items : this.data.items;
11964         var v, sv, r = [], l = {};
11965         for(var i = 0, len = d.length; i < len; i++){
11966             v = d[i].data[dataIndex];
11967             sv = String(v);
11968             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11969                 l[sv] = true;
11970                 r[r.length] = v;
11971             }
11972         }
11973         return r;
11974     },
11975
11976     /**
11977      * Revert to a view of the Record cache with no filtering applied.
11978      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11979      */
11980     clearFilter : function(suppressEvent){
11981         if(this.snapshot && this.snapshot != this.data){
11982             this.data = this.snapshot;
11983             delete this.snapshot;
11984             if(suppressEvent !== true){
11985                 this.fireEvent("datachanged", this);
11986             }
11987         }
11988     },
11989
11990     // private
11991     afterEdit : function(record){
11992         if(this.modified.indexOf(record) == -1){
11993             this.modified.push(record);
11994         }
11995         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11996     },
11997     
11998     // private
11999     afterReject : function(record){
12000         this.modified.remove(record);
12001         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
12002     },
12003
12004     // private
12005     afterCommit : function(record){
12006         this.modified.remove(record);
12007         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
12008     },
12009
12010     /**
12011      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
12012      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
12013      */
12014     commitChanges : function(){
12015         var m = this.modified.slice(0);
12016         this.modified = [];
12017         for(var i = 0, len = m.length; i < len; i++){
12018             m[i].commit();
12019         }
12020     },
12021
12022     /**
12023      * Cancel outstanding changes on all changed records.
12024      */
12025     rejectChanges : function(){
12026         var m = this.modified.slice(0);
12027         this.modified = [];
12028         for(var i = 0, len = m.length; i < len; i++){
12029             m[i].reject();
12030         }
12031     },
12032
12033     onMetaChange : function(meta, rtype, o){
12034         this.recordType = rtype;
12035         this.fields = rtype.prototype.fields;
12036         delete this.snapshot;
12037         this.sortInfo = meta.sortInfo || this.sortInfo;
12038         this.modified = [];
12039         this.fireEvent('metachange', this, this.reader.meta);
12040     },
12041     
12042     moveIndex : function(data, type)
12043     {
12044         var index = this.indexOf(data);
12045         
12046         var newIndex = index + type;
12047         
12048         this.remove(data);
12049         
12050         this.insert(newIndex, data);
12051         
12052     }
12053 });/*
12054  * Based on:
12055  * Ext JS Library 1.1.1
12056  * Copyright(c) 2006-2007, Ext JS, LLC.
12057  *
12058  * Originally Released Under LGPL - original licence link has changed is not relivant.
12059  *
12060  * Fork - LGPL
12061  * <script type="text/javascript">
12062  */
12063
12064 /**
12065  * @class Roo.data.SimpleStore
12066  * @extends Roo.data.Store
12067  * Small helper class to make creating Stores from Array data easier.
12068  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
12069  * @cfg {Array} fields An array of field definition objects, or field name strings.
12070  * @cfg {Array} data The multi-dimensional array of data
12071  * @constructor
12072  * @param {Object} config
12073  */
12074 Roo.data.SimpleStore = function(config){
12075     Roo.data.SimpleStore.superclass.constructor.call(this, {
12076         isLocal : true,
12077         reader: new Roo.data.ArrayReader({
12078                 id: config.id
12079             },
12080             Roo.data.Record.create(config.fields)
12081         ),
12082         proxy : new Roo.data.MemoryProxy(config.data)
12083     });
12084     this.load();
12085 };
12086 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
12087  * Based on:
12088  * Ext JS Library 1.1.1
12089  * Copyright(c) 2006-2007, Ext JS, LLC.
12090  *
12091  * Originally Released Under LGPL - original licence link has changed is not relivant.
12092  *
12093  * Fork - LGPL
12094  * <script type="text/javascript">
12095  */
12096
12097 /**
12098 /**
12099  * @extends Roo.data.Store
12100  * @class Roo.data.JsonStore
12101  * Small helper class to make creating Stores for JSON data easier. <br/>
12102 <pre><code>
12103 var store = new Roo.data.JsonStore({
12104     url: 'get-images.php',
12105     root: 'images',
12106     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
12107 });
12108 </code></pre>
12109  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12110  * JsonReader and HttpProxy (unless inline data is provided).</b>
12111  * @cfg {Array} fields An array of field definition objects, or field name strings.
12112  * @constructor
12113  * @param {Object} config
12114  */
12115 Roo.data.JsonStore = function(c){
12116     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12117         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12118         reader: new Roo.data.JsonReader(c, c.fields)
12119     }));
12120 };
12121 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12122  * Based on:
12123  * Ext JS Library 1.1.1
12124  * Copyright(c) 2006-2007, Ext JS, LLC.
12125  *
12126  * Originally Released Under LGPL - original licence link has changed is not relivant.
12127  *
12128  * Fork - LGPL
12129  * <script type="text/javascript">
12130  */
12131
12132  
12133 Roo.data.Field = function(config){
12134     if(typeof config == "string"){
12135         config = {name: config};
12136     }
12137     Roo.apply(this, config);
12138     
12139     if(!this.type){
12140         this.type = "auto";
12141     }
12142     
12143     var st = Roo.data.SortTypes;
12144     // named sortTypes are supported, here we look them up
12145     if(typeof this.sortType == "string"){
12146         this.sortType = st[this.sortType];
12147     }
12148     
12149     // set default sortType for strings and dates
12150     if(!this.sortType){
12151         switch(this.type){
12152             case "string":
12153                 this.sortType = st.asUCString;
12154                 break;
12155             case "date":
12156                 this.sortType = st.asDate;
12157                 break;
12158             default:
12159                 this.sortType = st.none;
12160         }
12161     }
12162
12163     // define once
12164     var stripRe = /[\$,%]/g;
12165
12166     // prebuilt conversion function for this field, instead of
12167     // switching every time we're reading a value
12168     if(!this.convert){
12169         var cv, dateFormat = this.dateFormat;
12170         switch(this.type){
12171             case "":
12172             case "auto":
12173             case undefined:
12174                 cv = function(v){ return v; };
12175                 break;
12176             case "string":
12177                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12178                 break;
12179             case "int":
12180                 cv = function(v){
12181                     return v !== undefined && v !== null && v !== '' ?
12182                            parseInt(String(v).replace(stripRe, ""), 10) : '';
12183                     };
12184                 break;
12185             case "float":
12186                 cv = function(v){
12187                     return v !== undefined && v !== null && v !== '' ?
12188                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
12189                     };
12190                 break;
12191             case "bool":
12192             case "boolean":
12193                 cv = function(v){ return v === true || v === "true" || v == 1; };
12194                 break;
12195             case "date":
12196                 cv = function(v){
12197                     if(!v){
12198                         return '';
12199                     }
12200                     if(v instanceof Date){
12201                         return v;
12202                     }
12203                     if(dateFormat){
12204                         if(dateFormat == "timestamp"){
12205                             return new Date(v*1000);
12206                         }
12207                         return Date.parseDate(v, dateFormat);
12208                     }
12209                     var parsed = Date.parse(v);
12210                     return parsed ? new Date(parsed) : null;
12211                 };
12212              break;
12213             
12214         }
12215         this.convert = cv;
12216     }
12217 };
12218
12219 Roo.data.Field.prototype = {
12220     dateFormat: null,
12221     defaultValue: "",
12222     mapping: null,
12223     sortType : null,
12224     sortDir : "ASC"
12225 };/*
12226  * Based on:
12227  * Ext JS Library 1.1.1
12228  * Copyright(c) 2006-2007, Ext JS, LLC.
12229  *
12230  * Originally Released Under LGPL - original licence link has changed is not relivant.
12231  *
12232  * Fork - LGPL
12233  * <script type="text/javascript">
12234  */
12235  
12236 // Base class for reading structured data from a data source.  This class is intended to be
12237 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12238
12239 /**
12240  * @class Roo.data.DataReader
12241  * Base class for reading structured data from a data source.  This class is intended to be
12242  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12243  */
12244
12245 Roo.data.DataReader = function(meta, recordType){
12246     
12247     this.meta = meta;
12248     
12249     this.recordType = recordType instanceof Array ? 
12250         Roo.data.Record.create(recordType) : recordType;
12251 };
12252
12253 Roo.data.DataReader.prototype = {
12254      /**
12255      * Create an empty record
12256      * @param {Object} data (optional) - overlay some values
12257      * @return {Roo.data.Record} record created.
12258      */
12259     newRow :  function(d) {
12260         var da =  {};
12261         this.recordType.prototype.fields.each(function(c) {
12262             switch( c.type) {
12263                 case 'int' : da[c.name] = 0; break;
12264                 case 'date' : da[c.name] = new Date(); break;
12265                 case 'float' : da[c.name] = 0.0; break;
12266                 case 'boolean' : da[c.name] = false; break;
12267                 default : da[c.name] = ""; break;
12268             }
12269             
12270         });
12271         return new this.recordType(Roo.apply(da, d));
12272     }
12273     
12274 };/*
12275  * Based on:
12276  * Ext JS Library 1.1.1
12277  * Copyright(c) 2006-2007, Ext JS, LLC.
12278  *
12279  * Originally Released Under LGPL - original licence link has changed is not relivant.
12280  *
12281  * Fork - LGPL
12282  * <script type="text/javascript">
12283  */
12284
12285 /**
12286  * @class Roo.data.DataProxy
12287  * @extends Roo.data.Observable
12288  * This class is an abstract base class for implementations which provide retrieval of
12289  * unformatted data objects.<br>
12290  * <p>
12291  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12292  * (of the appropriate type which knows how to parse the data object) to provide a block of
12293  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12294  * <p>
12295  * Custom implementations must implement the load method as described in
12296  * {@link Roo.data.HttpProxy#load}.
12297  */
12298 Roo.data.DataProxy = function(){
12299     this.addEvents({
12300         /**
12301          * @event beforeload
12302          * Fires before a network request is made to retrieve a data object.
12303          * @param {Object} This DataProxy object.
12304          * @param {Object} params The params parameter to the load function.
12305          */
12306         beforeload : true,
12307         /**
12308          * @event load
12309          * Fires before the load method's callback is called.
12310          * @param {Object} This DataProxy object.
12311          * @param {Object} o The data object.
12312          * @param {Object} arg The callback argument object passed to the load function.
12313          */
12314         load : true,
12315         /**
12316          * @event loadexception
12317          * Fires if an Exception occurs during data retrieval.
12318          * @param {Object} This DataProxy object.
12319          * @param {Object} o The data object.
12320          * @param {Object} arg The callback argument object passed to the load function.
12321          * @param {Object} e The Exception.
12322          */
12323         loadexception : true
12324     });
12325     Roo.data.DataProxy.superclass.constructor.call(this);
12326 };
12327
12328 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12329
12330     /**
12331      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12332      */
12333 /*
12334  * Based on:
12335  * Ext JS Library 1.1.1
12336  * Copyright(c) 2006-2007, Ext JS, LLC.
12337  *
12338  * Originally Released Under LGPL - original licence link has changed is not relivant.
12339  *
12340  * Fork - LGPL
12341  * <script type="text/javascript">
12342  */
12343 /**
12344  * @class Roo.data.MemoryProxy
12345  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12346  * to the Reader when its load method is called.
12347  * @constructor
12348  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12349  */
12350 Roo.data.MemoryProxy = function(data){
12351     if (data.data) {
12352         data = data.data;
12353     }
12354     Roo.data.MemoryProxy.superclass.constructor.call(this);
12355     this.data = data;
12356 };
12357
12358 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12359     
12360     /**
12361      * Load data from the requested source (in this case an in-memory
12362      * data object passed to the constructor), read the data object into
12363      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12364      * process that block using the passed callback.
12365      * @param {Object} params This parameter is not used by the MemoryProxy class.
12366      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12367      * object into a block of Roo.data.Records.
12368      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12369      * The function must be passed <ul>
12370      * <li>The Record block object</li>
12371      * <li>The "arg" argument from the load function</li>
12372      * <li>A boolean success indicator</li>
12373      * </ul>
12374      * @param {Object} scope The scope in which to call the callback
12375      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12376      */
12377     load : function(params, reader, callback, scope, arg){
12378         params = params || {};
12379         var result;
12380         try {
12381             result = reader.readRecords(this.data);
12382         }catch(e){
12383             this.fireEvent("loadexception", this, arg, null, e);
12384             callback.call(scope, null, arg, false);
12385             return;
12386         }
12387         callback.call(scope, result, arg, true);
12388     },
12389     
12390     // private
12391     update : function(params, records){
12392         
12393     }
12394 });/*
12395  * Based on:
12396  * Ext JS Library 1.1.1
12397  * Copyright(c) 2006-2007, Ext JS, LLC.
12398  *
12399  * Originally Released Under LGPL - original licence link has changed is not relivant.
12400  *
12401  * Fork - LGPL
12402  * <script type="text/javascript">
12403  */
12404 /**
12405  * @class Roo.data.HttpProxy
12406  * @extends Roo.data.DataProxy
12407  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12408  * configured to reference a certain URL.<br><br>
12409  * <p>
12410  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12411  * from which the running page was served.<br><br>
12412  * <p>
12413  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12414  * <p>
12415  * Be aware that to enable the browser to parse an XML document, the server must set
12416  * the Content-Type header in the HTTP response to "text/xml".
12417  * @constructor
12418  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12419  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12420  * will be used to make the request.
12421  */
12422 Roo.data.HttpProxy = function(conn){
12423     Roo.data.HttpProxy.superclass.constructor.call(this);
12424     // is conn a conn config or a real conn?
12425     this.conn = conn;
12426     this.useAjax = !conn || !conn.events;
12427   
12428 };
12429
12430 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12431     // thse are take from connection...
12432     
12433     /**
12434      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12435      */
12436     /**
12437      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12438      * extra parameters to each request made by this object. (defaults to undefined)
12439      */
12440     /**
12441      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12442      *  to each request made by this object. (defaults to undefined)
12443      */
12444     /**
12445      * @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)
12446      */
12447     /**
12448      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12449      */
12450      /**
12451      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12452      * @type Boolean
12453      */
12454   
12455
12456     /**
12457      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12458      * @type Boolean
12459      */
12460     /**
12461      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12462      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12463      * a finer-grained basis than the DataProxy events.
12464      */
12465     getConnection : function(){
12466         return this.useAjax ? Roo.Ajax : this.conn;
12467     },
12468
12469     /**
12470      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12471      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12472      * process that block using the passed callback.
12473      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12474      * for the request to the remote server.
12475      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12476      * object into a block of Roo.data.Records.
12477      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12478      * The function must be passed <ul>
12479      * <li>The Record block object</li>
12480      * <li>The "arg" argument from the load function</li>
12481      * <li>A boolean success indicator</li>
12482      * </ul>
12483      * @param {Object} scope The scope in which to call the callback
12484      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12485      */
12486     load : function(params, reader, callback, scope, arg){
12487         if(this.fireEvent("beforeload", this, params) !== false){
12488             var  o = {
12489                 params : params || {},
12490                 request: {
12491                     callback : callback,
12492                     scope : scope,
12493                     arg : arg
12494                 },
12495                 reader: reader,
12496                 callback : this.loadResponse,
12497                 scope: this
12498             };
12499             if(this.useAjax){
12500                 Roo.applyIf(o, this.conn);
12501                 if(this.activeRequest){
12502                     Roo.Ajax.abort(this.activeRequest);
12503                 }
12504                 this.activeRequest = Roo.Ajax.request(o);
12505             }else{
12506                 this.conn.request(o);
12507             }
12508         }else{
12509             callback.call(scope||this, null, arg, false);
12510         }
12511     },
12512
12513     // private
12514     loadResponse : function(o, success, response){
12515         delete this.activeRequest;
12516         if(!success){
12517             this.fireEvent("loadexception", this, o, response);
12518             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12519             return;
12520         }
12521         var result;
12522         try {
12523             result = o.reader.read(response);
12524         }catch(e){
12525             this.fireEvent("loadexception", this, o, response, e);
12526             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12527             return;
12528         }
12529         
12530         this.fireEvent("load", this, o, o.request.arg);
12531         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12532     },
12533
12534     // private
12535     update : function(dataSet){
12536
12537     },
12538
12539     // private
12540     updateResponse : function(dataSet){
12541
12542     }
12543 });/*
12544  * Based on:
12545  * Ext JS Library 1.1.1
12546  * Copyright(c) 2006-2007, Ext JS, LLC.
12547  *
12548  * Originally Released Under LGPL - original licence link has changed is not relivant.
12549  *
12550  * Fork - LGPL
12551  * <script type="text/javascript">
12552  */
12553
12554 /**
12555  * @class Roo.data.ScriptTagProxy
12556  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12557  * other than the originating domain of the running page.<br><br>
12558  * <p>
12559  * <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
12560  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12561  * <p>
12562  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12563  * source code that is used as the source inside a &lt;script> tag.<br><br>
12564  * <p>
12565  * In order for the browser to process the returned data, the server must wrap the data object
12566  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12567  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12568  * depending on whether the callback name was passed:
12569  * <p>
12570  * <pre><code>
12571 boolean scriptTag = false;
12572 String cb = request.getParameter("callback");
12573 if (cb != null) {
12574     scriptTag = true;
12575     response.setContentType("text/javascript");
12576 } else {
12577     response.setContentType("application/x-json");
12578 }
12579 Writer out = response.getWriter();
12580 if (scriptTag) {
12581     out.write(cb + "(");
12582 }
12583 out.print(dataBlock.toJsonString());
12584 if (scriptTag) {
12585     out.write(");");
12586 }
12587 </pre></code>
12588  *
12589  * @constructor
12590  * @param {Object} config A configuration object.
12591  */
12592 Roo.data.ScriptTagProxy = function(config){
12593     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12594     Roo.apply(this, config);
12595     this.head = document.getElementsByTagName("head")[0];
12596 };
12597
12598 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12599
12600 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12601     /**
12602      * @cfg {String} url The URL from which to request the data object.
12603      */
12604     /**
12605      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12606      */
12607     timeout : 30000,
12608     /**
12609      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12610      * the server the name of the callback function set up by the load call to process the returned data object.
12611      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12612      * javascript output which calls this named function passing the data object as its only parameter.
12613      */
12614     callbackParam : "callback",
12615     /**
12616      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12617      * name to the request.
12618      */
12619     nocache : true,
12620
12621     /**
12622      * Load data from the configured URL, read the data object into
12623      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12624      * process that block using the passed callback.
12625      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12626      * for the request to the remote server.
12627      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12628      * object into a block of Roo.data.Records.
12629      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12630      * The function must be passed <ul>
12631      * <li>The Record block object</li>
12632      * <li>The "arg" argument from the load function</li>
12633      * <li>A boolean success indicator</li>
12634      * </ul>
12635      * @param {Object} scope The scope in which to call the callback
12636      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12637      */
12638     load : function(params, reader, callback, scope, arg){
12639         if(this.fireEvent("beforeload", this, params) !== false){
12640
12641             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12642
12643             var url = this.url;
12644             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12645             if(this.nocache){
12646                 url += "&_dc=" + (new Date().getTime());
12647             }
12648             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12649             var trans = {
12650                 id : transId,
12651                 cb : "stcCallback"+transId,
12652                 scriptId : "stcScript"+transId,
12653                 params : params,
12654                 arg : arg,
12655                 url : url,
12656                 callback : callback,
12657                 scope : scope,
12658                 reader : reader
12659             };
12660             var conn = this;
12661
12662             window[trans.cb] = function(o){
12663                 conn.handleResponse(o, trans);
12664             };
12665
12666             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12667
12668             if(this.autoAbort !== false){
12669                 this.abort();
12670             }
12671
12672             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12673
12674             var script = document.createElement("script");
12675             script.setAttribute("src", url);
12676             script.setAttribute("type", "text/javascript");
12677             script.setAttribute("id", trans.scriptId);
12678             this.head.appendChild(script);
12679
12680             this.trans = trans;
12681         }else{
12682             callback.call(scope||this, null, arg, false);
12683         }
12684     },
12685
12686     // private
12687     isLoading : function(){
12688         return this.trans ? true : false;
12689     },
12690
12691     /**
12692      * Abort the current server request.
12693      */
12694     abort : function(){
12695         if(this.isLoading()){
12696             this.destroyTrans(this.trans);
12697         }
12698     },
12699
12700     // private
12701     destroyTrans : function(trans, isLoaded){
12702         this.head.removeChild(document.getElementById(trans.scriptId));
12703         clearTimeout(trans.timeoutId);
12704         if(isLoaded){
12705             window[trans.cb] = undefined;
12706             try{
12707                 delete window[trans.cb];
12708             }catch(e){}
12709         }else{
12710             // if hasn't been loaded, wait for load to remove it to prevent script error
12711             window[trans.cb] = function(){
12712                 window[trans.cb] = undefined;
12713                 try{
12714                     delete window[trans.cb];
12715                 }catch(e){}
12716             };
12717         }
12718     },
12719
12720     // private
12721     handleResponse : function(o, trans){
12722         this.trans = false;
12723         this.destroyTrans(trans, true);
12724         var result;
12725         try {
12726             result = trans.reader.readRecords(o);
12727         }catch(e){
12728             this.fireEvent("loadexception", this, o, trans.arg, e);
12729             trans.callback.call(trans.scope||window, null, trans.arg, false);
12730             return;
12731         }
12732         this.fireEvent("load", this, o, trans.arg);
12733         trans.callback.call(trans.scope||window, result, trans.arg, true);
12734     },
12735
12736     // private
12737     handleFailure : function(trans){
12738         this.trans = false;
12739         this.destroyTrans(trans, false);
12740         this.fireEvent("loadexception", this, null, trans.arg);
12741         trans.callback.call(trans.scope||window, null, trans.arg, false);
12742     }
12743 });/*
12744  * Based on:
12745  * Ext JS Library 1.1.1
12746  * Copyright(c) 2006-2007, Ext JS, LLC.
12747  *
12748  * Originally Released Under LGPL - original licence link has changed is not relivant.
12749  *
12750  * Fork - LGPL
12751  * <script type="text/javascript">
12752  */
12753
12754 /**
12755  * @class Roo.data.JsonReader
12756  * @extends Roo.data.DataReader
12757  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12758  * based on mappings in a provided Roo.data.Record constructor.
12759  * 
12760  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12761  * in the reply previously. 
12762  * 
12763  * <p>
12764  * Example code:
12765  * <pre><code>
12766 var RecordDef = Roo.data.Record.create([
12767     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12768     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12769 ]);
12770 var myReader = new Roo.data.JsonReader({
12771     totalProperty: "results",    // The property which contains the total dataset size (optional)
12772     root: "rows",                // The property which contains an Array of row objects
12773     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12774 }, RecordDef);
12775 </code></pre>
12776  * <p>
12777  * This would consume a JSON file like this:
12778  * <pre><code>
12779 { 'results': 2, 'rows': [
12780     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12781     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12782 }
12783 </code></pre>
12784  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12785  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12786  * paged from the remote server.
12787  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12788  * @cfg {String} root name of the property which contains the Array of row objects.
12789  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12790  * @cfg {Array} fields Array of field definition objects
12791  * @constructor
12792  * Create a new JsonReader
12793  * @param {Object} meta Metadata configuration options
12794  * @param {Object} recordType Either an Array of field definition objects,
12795  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12796  */
12797 Roo.data.JsonReader = function(meta, recordType){
12798     
12799     meta = meta || {};
12800     // set some defaults:
12801     Roo.applyIf(meta, {
12802         totalProperty: 'total',
12803         successProperty : 'success',
12804         root : 'data',
12805         id : 'id'
12806     });
12807     
12808     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12809 };
12810 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12811     
12812     /**
12813      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12814      * Used by Store query builder to append _requestMeta to params.
12815      * 
12816      */
12817     metaFromRemote : false,
12818     /**
12819      * This method is only used by a DataProxy which has retrieved data from a remote server.
12820      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12821      * @return {Object} data A data block which is used by an Roo.data.Store object as
12822      * a cache of Roo.data.Records.
12823      */
12824     read : function(response){
12825         var json = response.responseText;
12826        
12827         var o = /* eval:var:o */ eval("("+json+")");
12828         if(!o) {
12829             throw {message: "JsonReader.read: Json object not found"};
12830         }
12831         
12832         if(o.metaData){
12833             
12834             delete this.ef;
12835             this.metaFromRemote = true;
12836             this.meta = o.metaData;
12837             this.recordType = Roo.data.Record.create(o.metaData.fields);
12838             this.onMetaChange(this.meta, this.recordType, o);
12839         }
12840         return this.readRecords(o);
12841     },
12842
12843     // private function a store will implement
12844     onMetaChange : function(meta, recordType, o){
12845
12846     },
12847
12848     /**
12849          * @ignore
12850          */
12851     simpleAccess: function(obj, subsc) {
12852         return obj[subsc];
12853     },
12854
12855         /**
12856          * @ignore
12857          */
12858     getJsonAccessor: function(){
12859         var re = /[\[\.]/;
12860         return function(expr) {
12861             try {
12862                 return(re.test(expr))
12863                     ? new Function("obj", "return obj." + expr)
12864                     : function(obj){
12865                         return obj[expr];
12866                     };
12867             } catch(e){}
12868             return Roo.emptyFn;
12869         };
12870     }(),
12871
12872     /**
12873      * Create a data block containing Roo.data.Records from an XML document.
12874      * @param {Object} o An object which contains an Array of row objects in the property specified
12875      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12876      * which contains the total size of the dataset.
12877      * @return {Object} data A data block which is used by an Roo.data.Store object as
12878      * a cache of Roo.data.Records.
12879      */
12880     readRecords : function(o){
12881         /**
12882          * After any data loads, the raw JSON data is available for further custom processing.
12883          * @type Object
12884          */
12885         this.o = o;
12886         var s = this.meta, Record = this.recordType,
12887             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12888
12889 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12890         if (!this.ef) {
12891             if(s.totalProperty) {
12892                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12893                 }
12894                 if(s.successProperty) {
12895                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12896                 }
12897                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12898                 if (s.id) {
12899                         var g = this.getJsonAccessor(s.id);
12900                         this.getId = function(rec) {
12901                                 var r = g(rec);  
12902                                 return (r === undefined || r === "") ? null : r;
12903                         };
12904                 } else {
12905                         this.getId = function(){return null;};
12906                 }
12907             this.ef = [];
12908             for(var jj = 0; jj < fl; jj++){
12909                 f = fi[jj];
12910                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12911                 this.ef[jj] = this.getJsonAccessor(map);
12912             }
12913         }
12914
12915         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12916         if(s.totalProperty){
12917             var vt = parseInt(this.getTotal(o), 10);
12918             if(!isNaN(vt)){
12919                 totalRecords = vt;
12920             }
12921         }
12922         if(s.successProperty){
12923             var vs = this.getSuccess(o);
12924             if(vs === false || vs === 'false'){
12925                 success = false;
12926             }
12927         }
12928         var records = [];
12929         for(var i = 0; i < c; i++){
12930                 var n = root[i];
12931             var values = {};
12932             var id = this.getId(n);
12933             for(var j = 0; j < fl; j++){
12934                 f = fi[j];
12935             var v = this.ef[j](n);
12936             if (!f.convert) {
12937                 Roo.log('missing convert for ' + f.name);
12938                 Roo.log(f);
12939                 continue;
12940             }
12941             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12942             }
12943             var record = new Record(values, id);
12944             record.json = n;
12945             records[i] = record;
12946         }
12947         return {
12948             raw : o,
12949             success : success,
12950             records : records,
12951             totalRecords : totalRecords
12952         };
12953     }
12954 });/*
12955  * Based on:
12956  * Ext JS Library 1.1.1
12957  * Copyright(c) 2006-2007, Ext JS, LLC.
12958  *
12959  * Originally Released Under LGPL - original licence link has changed is not relivant.
12960  *
12961  * Fork - LGPL
12962  * <script type="text/javascript">
12963  */
12964
12965 /**
12966  * @class Roo.data.ArrayReader
12967  * @extends Roo.data.DataReader
12968  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12969  * Each element of that Array represents a row of data fields. The
12970  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12971  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12972  * <p>
12973  * Example code:.
12974  * <pre><code>
12975 var RecordDef = Roo.data.Record.create([
12976     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12977     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12978 ]);
12979 var myReader = new Roo.data.ArrayReader({
12980     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12981 }, RecordDef);
12982 </code></pre>
12983  * <p>
12984  * This would consume an Array like this:
12985  * <pre><code>
12986 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12987   </code></pre>
12988  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12989  * @constructor
12990  * Create a new JsonReader
12991  * @param {Object} meta Metadata configuration options.
12992  * @param {Object} recordType Either an Array of field definition objects
12993  * as specified to {@link Roo.data.Record#create},
12994  * or an {@link Roo.data.Record} object
12995  * created using {@link Roo.data.Record#create}.
12996  */
12997 Roo.data.ArrayReader = function(meta, recordType){
12998     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12999 };
13000
13001 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
13002     /**
13003      * Create a data block containing Roo.data.Records from an XML document.
13004      * @param {Object} o An Array of row objects which represents the dataset.
13005      * @return {Object} data A data block which is used by an Roo.data.Store object as
13006      * a cache of Roo.data.Records.
13007      */
13008     readRecords : function(o){
13009         var sid = this.meta ? this.meta.id : null;
13010         var recordType = this.recordType, fields = recordType.prototype.fields;
13011         var records = [];
13012         var root = o;
13013             for(var i = 0; i < root.length; i++){
13014                     var n = root[i];
13015                 var values = {};
13016                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
13017                 for(var j = 0, jlen = fields.length; j < jlen; j++){
13018                 var f = fields.items[j];
13019                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
13020                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
13021                 v = f.convert(v);
13022                 values[f.name] = v;
13023             }
13024                 var record = new recordType(values, id);
13025                 record.json = n;
13026                 records[records.length] = record;
13027             }
13028             return {
13029                 records : records,
13030                 totalRecords : records.length
13031             };
13032     }
13033 });/*
13034  * - LGPL
13035  * * 
13036  */
13037
13038 /**
13039  * @class Roo.bootstrap.ComboBox
13040  * @extends Roo.bootstrap.TriggerField
13041  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
13042  * @cfg {Boolean} append (true|false) default false
13043  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
13044  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
13045  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
13046  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
13047  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
13048  * @cfg {Boolean} animate default true
13049  * @cfg {Boolean} emptyResultText only for touch device
13050  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
13051  * @cfg {String} emptyTitle default ''
13052  * @constructor
13053  * Create a new ComboBox.
13054  * @param {Object} config Configuration options
13055  */
13056 Roo.bootstrap.ComboBox = function(config){
13057     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
13058     this.addEvents({
13059         /**
13060          * @event expand
13061          * Fires when the dropdown list is expanded
13062         * @param {Roo.bootstrap.ComboBox} combo This combo box
13063         */
13064         'expand' : true,
13065         /**
13066          * @event collapse
13067          * Fires when the dropdown list is collapsed
13068         * @param {Roo.bootstrap.ComboBox} combo This combo box
13069         */
13070         'collapse' : true,
13071         /**
13072          * @event beforeselect
13073          * Fires before a list item is selected. Return false to cancel the selection.
13074         * @param {Roo.bootstrap.ComboBox} combo This combo box
13075         * @param {Roo.data.Record} record The data record returned from the underlying store
13076         * @param {Number} index The index of the selected item in the dropdown list
13077         */
13078         'beforeselect' : true,
13079         /**
13080          * @event select
13081          * Fires when a list item is selected
13082         * @param {Roo.bootstrap.ComboBox} combo This combo box
13083         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
13084         * @param {Number} index The index of the selected item in the dropdown list
13085         */
13086         'select' : true,
13087         /**
13088          * @event beforequery
13089          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
13090          * The event object passed has these properties:
13091         * @param {Roo.bootstrap.ComboBox} combo This combo box
13092         * @param {String} query The query
13093         * @param {Boolean} forceAll true to force "all" query
13094         * @param {Boolean} cancel true to cancel the query
13095         * @param {Object} e The query event object
13096         */
13097         'beforequery': true,
13098          /**
13099          * @event add
13100          * Fires when the 'add' icon is pressed (add a listener to enable add button)
13101         * @param {Roo.bootstrap.ComboBox} combo This combo box
13102         */
13103         'add' : true,
13104         /**
13105          * @event edit
13106          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
13107         * @param {Roo.bootstrap.ComboBox} combo This combo box
13108         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13109         */
13110         'edit' : true,
13111         /**
13112          * @event remove
13113          * Fires when the remove value from the combobox array
13114         * @param {Roo.bootstrap.ComboBox} combo This combo box
13115         */
13116         'remove' : true,
13117         /**
13118          * @event afterremove
13119          * Fires when the remove value from the combobox array
13120         * @param {Roo.bootstrap.ComboBox} combo This combo box
13121         */
13122         'afterremove' : true,
13123         /**
13124          * @event specialfilter
13125          * Fires when specialfilter
13126             * @param {Roo.bootstrap.ComboBox} combo This combo box
13127             */
13128         'specialfilter' : true,
13129         /**
13130          * @event tick
13131          * Fires when tick the element
13132             * @param {Roo.bootstrap.ComboBox} combo This combo box
13133             */
13134         'tick' : true,
13135         /**
13136          * @event touchviewdisplay
13137          * Fires when touch view require special display (default is using displayField)
13138             * @param {Roo.bootstrap.ComboBox} combo This combo box
13139             * @param {Object} cfg set html .
13140             */
13141         'touchviewdisplay' : true
13142         
13143     });
13144     
13145     this.item = [];
13146     this.tickItems = [];
13147     
13148     this.selectedIndex = -1;
13149     if(this.mode == 'local'){
13150         if(config.queryDelay === undefined){
13151             this.queryDelay = 10;
13152         }
13153         if(config.minChars === undefined){
13154             this.minChars = 0;
13155         }
13156     }
13157 };
13158
13159 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13160      
13161     /**
13162      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13163      * rendering into an Roo.Editor, defaults to false)
13164      */
13165     /**
13166      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13167      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13168      */
13169     /**
13170      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13171      */
13172     /**
13173      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13174      * the dropdown list (defaults to undefined, with no header element)
13175      */
13176
13177      /**
13178      * @cfg {String/Roo.Template} tpl The template to use to render the output
13179      */
13180      
13181      /**
13182      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13183      */
13184     listWidth: undefined,
13185     /**
13186      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13187      * mode = 'remote' or 'text' if mode = 'local')
13188      */
13189     displayField: undefined,
13190     
13191     /**
13192      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13193      * mode = 'remote' or 'value' if mode = 'local'). 
13194      * Note: use of a valueField requires the user make a selection
13195      * in order for a value to be mapped.
13196      */
13197     valueField: undefined,
13198     /**
13199      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13200      */
13201     modalTitle : '',
13202     
13203     /**
13204      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13205      * field's data value (defaults to the underlying DOM element's name)
13206      */
13207     hiddenName: undefined,
13208     /**
13209      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13210      */
13211     listClass: '',
13212     /**
13213      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13214      */
13215     selectedClass: 'active',
13216     
13217     /**
13218      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13219      */
13220     shadow:'sides',
13221     /**
13222      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13223      * anchor positions (defaults to 'tl-bl')
13224      */
13225     listAlign: 'tl-bl?',
13226     /**
13227      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13228      */
13229     maxHeight: 300,
13230     /**
13231      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
13232      * query specified by the allQuery config option (defaults to 'query')
13233      */
13234     triggerAction: 'query',
13235     /**
13236      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13237      * (defaults to 4, does not apply if editable = false)
13238      */
13239     minChars : 4,
13240     /**
13241      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13242      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13243      */
13244     typeAhead: false,
13245     /**
13246      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13247      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13248      */
13249     queryDelay: 500,
13250     /**
13251      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13252      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13253      */
13254     pageSize: 0,
13255     /**
13256      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13257      * when editable = true (defaults to false)
13258      */
13259     selectOnFocus:false,
13260     /**
13261      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13262      */
13263     queryParam: 'query',
13264     /**
13265      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13266      * when mode = 'remote' (defaults to 'Loading...')
13267      */
13268     loadingText: 'Loading...',
13269     /**
13270      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13271      */
13272     resizable: false,
13273     /**
13274      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13275      */
13276     handleHeight : 8,
13277     /**
13278      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13279      * traditional select (defaults to true)
13280      */
13281     editable: true,
13282     /**
13283      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13284      */
13285     allQuery: '',
13286     /**
13287      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13288      */
13289     mode: 'remote',
13290     /**
13291      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13292      * listWidth has a higher value)
13293      */
13294     minListWidth : 70,
13295     /**
13296      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13297      * allow the user to set arbitrary text into the field (defaults to false)
13298      */
13299     forceSelection:false,
13300     /**
13301      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13302      * if typeAhead = true (defaults to 250)
13303      */
13304     typeAheadDelay : 250,
13305     /**
13306      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13307      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13308      */
13309     valueNotFoundText : undefined,
13310     /**
13311      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13312      */
13313     blockFocus : false,
13314     
13315     /**
13316      * @cfg {Boolean} disableClear Disable showing of clear button.
13317      */
13318     disableClear : false,
13319     /**
13320      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13321      */
13322     alwaysQuery : false,
13323     
13324     /**
13325      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13326      */
13327     multiple : false,
13328     
13329     /**
13330      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
13331      */
13332     invalidClass : "has-warning",
13333     
13334     /**
13335      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
13336      */
13337     validClass : "has-success",
13338     
13339     /**
13340      * @cfg {Boolean} specialFilter (true|false) special filter default false
13341      */
13342     specialFilter : false,
13343     
13344     /**
13345      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13346      */
13347     mobileTouchView : true,
13348     
13349     /**
13350      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13351      */
13352     useNativeIOS : false,
13353     
13354     /**
13355      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13356      */
13357     mobile_restrict_height : false,
13358     
13359     ios_options : false,
13360     
13361     //private
13362     addicon : false,
13363     editicon: false,
13364     
13365     page: 0,
13366     hasQuery: false,
13367     append: false,
13368     loadNext: false,
13369     autoFocus : true,
13370     tickable : false,
13371     btnPosition : 'right',
13372     triggerList : true,
13373     showToggleBtn : true,
13374     animate : true,
13375     emptyResultText: 'Empty',
13376     triggerText : 'Select',
13377     emptyTitle : '',
13378     
13379     // element that contains real text value.. (when hidden is used..)
13380     
13381     getAutoCreate : function()
13382     {   
13383         var cfg = false;
13384         //render
13385         /*
13386          * Render classic select for iso
13387          */
13388         
13389         if(Roo.isIOS && this.useNativeIOS){
13390             cfg = this.getAutoCreateNativeIOS();
13391             return cfg;
13392         }
13393         
13394         /*
13395          * Touch Devices
13396          */
13397         
13398         if(Roo.isTouch && this.mobileTouchView){
13399             cfg = this.getAutoCreateTouchView();
13400             return cfg;;
13401         }
13402         
13403         /*
13404          *  Normal ComboBox
13405          */
13406         if(!this.tickable){
13407             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13408             return cfg;
13409         }
13410         
13411         /*
13412          *  ComboBox with tickable selections
13413          */
13414              
13415         var align = this.labelAlign || this.parentLabelAlign();
13416         
13417         cfg = {
13418             cls : 'form-group roo-combobox-tickable' //input-group
13419         };
13420         
13421         var btn_text_select = '';
13422         var btn_text_done = '';
13423         var btn_text_cancel = '';
13424         
13425         if (this.btn_text_show) {
13426             btn_text_select = 'Select';
13427             btn_text_done = 'Done';
13428             btn_text_cancel = 'Cancel'; 
13429         }
13430         
13431         var buttons = {
13432             tag : 'div',
13433             cls : 'tickable-buttons',
13434             cn : [
13435                 {
13436                     tag : 'button',
13437                     type : 'button',
13438                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13439                     //html : this.triggerText
13440                     html: btn_text_select
13441                 },
13442                 {
13443                     tag : 'button',
13444                     type : 'button',
13445                     name : 'ok',
13446                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13447                     //html : 'Done'
13448                     html: btn_text_done
13449                 },
13450                 {
13451                     tag : 'button',
13452                     type : 'button',
13453                     name : 'cancel',
13454                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13455                     //html : 'Cancel'
13456                     html: btn_text_cancel
13457                 }
13458             ]
13459         };
13460         
13461         if(this.editable){
13462             buttons.cn.unshift({
13463                 tag: 'input',
13464                 cls: 'roo-select2-search-field-input'
13465             });
13466         }
13467         
13468         var _this = this;
13469         
13470         Roo.each(buttons.cn, function(c){
13471             if (_this.size) {
13472                 c.cls += ' btn-' + _this.size;
13473             }
13474
13475             if (_this.disabled) {
13476                 c.disabled = true;
13477             }
13478         });
13479         
13480         var box = {
13481             tag: 'div',
13482             style : 'display: contents',
13483             cn: [
13484                 {
13485                     tag: 'input',
13486                     type : 'hidden',
13487                     cls: 'form-hidden-field'
13488                 },
13489                 {
13490                     tag: 'ul',
13491                     cls: 'roo-select2-choices',
13492                     cn:[
13493                         {
13494                             tag: 'li',
13495                             cls: 'roo-select2-search-field',
13496                             cn: [
13497                                 buttons
13498                             ]
13499                         }
13500                     ]
13501                 }
13502             ]
13503         };
13504         
13505         var combobox = {
13506             cls: 'roo-select2-container input-group roo-select2-container-multi',
13507             cn: [
13508                 
13509                 box
13510 //                {
13511 //                    tag: 'ul',
13512 //                    cls: 'typeahead typeahead-long dropdown-menu',
13513 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13514 //                }
13515             ]
13516         };
13517         
13518         if(this.hasFeedback && !this.allowBlank){
13519             
13520             var feedback = {
13521                 tag: 'span',
13522                 cls: 'glyphicon form-control-feedback'
13523             };
13524
13525             combobox.cn.push(feedback);
13526         }
13527         
13528         var indicator = {
13529             tag : 'i',
13530             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13531             tooltip : 'This field is required'
13532         };
13533         if (Roo.bootstrap.version == 4) {
13534             indicator = {
13535                 tag : 'i',
13536                 style : 'display:none'
13537             };
13538         }
13539         if (align ==='left' && this.fieldLabel.length) {
13540             
13541             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13542             
13543             cfg.cn = [
13544                 indicator,
13545                 {
13546                     tag: 'label',
13547                     'for' :  id,
13548                     cls : 'control-label col-form-label',
13549                     html : this.fieldLabel
13550
13551                 },
13552                 {
13553                     cls : "", 
13554                     cn: [
13555                         combobox
13556                     ]
13557                 }
13558
13559             ];
13560             
13561             var labelCfg = cfg.cn[1];
13562             var contentCfg = cfg.cn[2];
13563             
13564
13565             if(this.indicatorpos == 'right'){
13566                 
13567                 cfg.cn = [
13568                     {
13569                         tag: 'label',
13570                         'for' :  id,
13571                         cls : 'control-label col-form-label',
13572                         cn : [
13573                             {
13574                                 tag : 'span',
13575                                 html : this.fieldLabel
13576                             },
13577                             indicator
13578                         ]
13579                     },
13580                     {
13581                         cls : "",
13582                         cn: [
13583                             combobox
13584                         ]
13585                     }
13586
13587                 ];
13588                 
13589                 
13590                 
13591                 labelCfg = cfg.cn[0];
13592                 contentCfg = cfg.cn[1];
13593             
13594             }
13595             
13596             if(this.labelWidth > 12){
13597                 labelCfg.style = "width: " + this.labelWidth + 'px';
13598             }
13599             
13600             if(this.labelWidth < 13 && this.labelmd == 0){
13601                 this.labelmd = this.labelWidth;
13602             }
13603             
13604             if(this.labellg > 0){
13605                 labelCfg.cls += ' col-lg-' + this.labellg;
13606                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13607             }
13608             
13609             if(this.labelmd > 0){
13610                 labelCfg.cls += ' col-md-' + this.labelmd;
13611                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13612             }
13613             
13614             if(this.labelsm > 0){
13615                 labelCfg.cls += ' col-sm-' + this.labelsm;
13616                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13617             }
13618             
13619             if(this.labelxs > 0){
13620                 labelCfg.cls += ' col-xs-' + this.labelxs;
13621                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13622             }
13623                 
13624                 
13625         } else if ( this.fieldLabel.length) {
13626 //                Roo.log(" label");
13627                  cfg.cn = [
13628                    indicator,
13629                     {
13630                         tag: 'label',
13631                         //cls : 'input-group-addon',
13632                         html : this.fieldLabel
13633                     },
13634                     combobox
13635                 ];
13636                 
13637                 if(this.indicatorpos == 'right'){
13638                     cfg.cn = [
13639                         {
13640                             tag: 'label',
13641                             //cls : 'input-group-addon',
13642                             html : this.fieldLabel
13643                         },
13644                         indicator,
13645                         combobox
13646                     ];
13647                     
13648                 }
13649
13650         } else {
13651             
13652 //                Roo.log(" no label && no align");
13653                 cfg = combobox
13654                      
13655                 
13656         }
13657          
13658         var settings=this;
13659         ['xs','sm','md','lg'].map(function(size){
13660             if (settings[size]) {
13661                 cfg.cls += ' col-' + size + '-' + settings[size];
13662             }
13663         });
13664         
13665         return cfg;
13666         
13667     },
13668     
13669     _initEventsCalled : false,
13670     
13671     // private
13672     initEvents: function()
13673     {   
13674         if (this._initEventsCalled) { // as we call render... prevent looping...
13675             return;
13676         }
13677         this._initEventsCalled = true;
13678         
13679         if (!this.store) {
13680             throw "can not find store for combo";
13681         }
13682         
13683         this.indicator = this.indicatorEl();
13684         
13685         this.store = Roo.factory(this.store, Roo.data);
13686         this.store.parent = this;
13687         
13688         // if we are building from html. then this element is so complex, that we can not really
13689         // use the rendered HTML.
13690         // so we have to trash and replace the previous code.
13691         if (Roo.XComponent.build_from_html) {
13692             // remove this element....
13693             var e = this.el.dom, k=0;
13694             while (e ) { e = e.previousSibling;  ++k;}
13695
13696             this.el.remove();
13697             
13698             this.el=false;
13699             this.rendered = false;
13700             
13701             this.render(this.parent().getChildContainer(true), k);
13702         }
13703         
13704         if(Roo.isIOS && this.useNativeIOS){
13705             this.initIOSView();
13706             return;
13707         }
13708         
13709         /*
13710          * Touch Devices
13711          */
13712         
13713         if(Roo.isTouch && this.mobileTouchView){
13714             this.initTouchView();
13715             return;
13716         }
13717         
13718         if(this.tickable){
13719             this.initTickableEvents();
13720             return;
13721         }
13722         
13723         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13724         
13725         if(this.hiddenName){
13726             
13727             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13728             
13729             this.hiddenField.dom.value =
13730                 this.hiddenValue !== undefined ? this.hiddenValue :
13731                 this.value !== undefined ? this.value : '';
13732
13733             // prevent input submission
13734             this.el.dom.removeAttribute('name');
13735             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13736              
13737              
13738         }
13739         //if(Roo.isGecko){
13740         //    this.el.dom.setAttribute('autocomplete', 'off');
13741         //}
13742         
13743         var cls = 'x-combo-list';
13744         
13745         //this.list = new Roo.Layer({
13746         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13747         //});
13748         
13749         var _this = this;
13750         
13751         (function(){
13752             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13753             _this.list.setWidth(lw);
13754         }).defer(100);
13755         
13756         this.list.on('mouseover', this.onViewOver, this);
13757         this.list.on('mousemove', this.onViewMove, this);
13758         this.list.on('scroll', this.onViewScroll, this);
13759         
13760         /*
13761         this.list.swallowEvent('mousewheel');
13762         this.assetHeight = 0;
13763
13764         if(this.title){
13765             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13766             this.assetHeight += this.header.getHeight();
13767         }
13768
13769         this.innerList = this.list.createChild({cls:cls+'-inner'});
13770         this.innerList.on('mouseover', this.onViewOver, this);
13771         this.innerList.on('mousemove', this.onViewMove, this);
13772         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13773         
13774         if(this.allowBlank && !this.pageSize && !this.disableClear){
13775             this.footer = this.list.createChild({cls:cls+'-ft'});
13776             this.pageTb = new Roo.Toolbar(this.footer);
13777            
13778         }
13779         if(this.pageSize){
13780             this.footer = this.list.createChild({cls:cls+'-ft'});
13781             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13782                     {pageSize: this.pageSize});
13783             
13784         }
13785         
13786         if (this.pageTb && this.allowBlank && !this.disableClear) {
13787             var _this = this;
13788             this.pageTb.add(new Roo.Toolbar.Fill(), {
13789                 cls: 'x-btn-icon x-btn-clear',
13790                 text: '&#160;',
13791                 handler: function()
13792                 {
13793                     _this.collapse();
13794                     _this.clearValue();
13795                     _this.onSelect(false, -1);
13796                 }
13797             });
13798         }
13799         if (this.footer) {
13800             this.assetHeight += this.footer.getHeight();
13801         }
13802         */
13803             
13804         if(!this.tpl){
13805             this.tpl = Roo.bootstrap.version == 4 ?
13806                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
13807                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13808         }
13809
13810         this.view = new Roo.View(this.list, this.tpl, {
13811             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13812         });
13813         //this.view.wrapEl.setDisplayed(false);
13814         this.view.on('click', this.onViewClick, this);
13815         
13816         
13817         this.store.on('beforeload', this.onBeforeLoad, this);
13818         this.store.on('load', this.onLoad, this);
13819         this.store.on('loadexception', this.onLoadException, this);
13820         /*
13821         if(this.resizable){
13822             this.resizer = new Roo.Resizable(this.list,  {
13823                pinned:true, handles:'se'
13824             });
13825             this.resizer.on('resize', function(r, w, h){
13826                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13827                 this.listWidth = w;
13828                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13829                 this.restrictHeight();
13830             }, this);
13831             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13832         }
13833         */
13834         if(!this.editable){
13835             this.editable = true;
13836             this.setEditable(false);
13837         }
13838         
13839         /*
13840         
13841         if (typeof(this.events.add.listeners) != 'undefined') {
13842             
13843             this.addicon = this.wrap.createChild(
13844                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13845        
13846             this.addicon.on('click', function(e) {
13847                 this.fireEvent('add', this);
13848             }, this);
13849         }
13850         if (typeof(this.events.edit.listeners) != 'undefined') {
13851             
13852             this.editicon = this.wrap.createChild(
13853                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13854             if (this.addicon) {
13855                 this.editicon.setStyle('margin-left', '40px');
13856             }
13857             this.editicon.on('click', function(e) {
13858                 
13859                 // we fire even  if inothing is selected..
13860                 this.fireEvent('edit', this, this.lastData );
13861                 
13862             }, this);
13863         }
13864         */
13865         
13866         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13867             "up" : function(e){
13868                 this.inKeyMode = true;
13869                 this.selectPrev();
13870             },
13871
13872             "down" : function(e){
13873                 if(!this.isExpanded()){
13874                     this.onTriggerClick();
13875                 }else{
13876                     this.inKeyMode = true;
13877                     this.selectNext();
13878                 }
13879             },
13880
13881             "enter" : function(e){
13882 //                this.onViewClick();
13883                 //return true;
13884                 this.collapse();
13885                 
13886                 if(this.fireEvent("specialkey", this, e)){
13887                     this.onViewClick(false);
13888                 }
13889                 
13890                 return true;
13891             },
13892
13893             "esc" : function(e){
13894                 this.collapse();
13895             },
13896
13897             "tab" : function(e){
13898                 this.collapse();
13899                 
13900                 if(this.fireEvent("specialkey", this, e)){
13901                     this.onViewClick(false);
13902                 }
13903                 
13904                 return true;
13905             },
13906
13907             scope : this,
13908
13909             doRelay : function(foo, bar, hname){
13910                 if(hname == 'down' || this.scope.isExpanded()){
13911                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13912                 }
13913                 return true;
13914             },
13915
13916             forceKeyDown: true
13917         });
13918         
13919         
13920         this.queryDelay = Math.max(this.queryDelay || 10,
13921                 this.mode == 'local' ? 10 : 250);
13922         
13923         
13924         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13925         
13926         if(this.typeAhead){
13927             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13928         }
13929         if(this.editable !== false){
13930             this.inputEl().on("keyup", this.onKeyUp, this);
13931         }
13932         if(this.forceSelection){
13933             this.inputEl().on('blur', this.doForce, this);
13934         }
13935         
13936         if(this.multiple){
13937             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13938             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13939         }
13940     },
13941     
13942     initTickableEvents: function()
13943     {   
13944         this.createList();
13945         
13946         if(this.hiddenName){
13947             
13948             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13949             
13950             this.hiddenField.dom.value =
13951                 this.hiddenValue !== undefined ? this.hiddenValue :
13952                 this.value !== undefined ? this.value : '';
13953
13954             // prevent input submission
13955             this.el.dom.removeAttribute('name');
13956             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13957              
13958              
13959         }
13960         
13961 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13962         
13963         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13964         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13965         if(this.triggerList){
13966             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13967         }
13968          
13969         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13970         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13971         
13972         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13973         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13974         
13975         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13976         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13977         
13978         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13979         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13980         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13981         
13982         this.okBtn.hide();
13983         this.cancelBtn.hide();
13984         
13985         var _this = this;
13986         
13987         (function(){
13988             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13989             _this.list.setWidth(lw);
13990         }).defer(100);
13991         
13992         this.list.on('mouseover', this.onViewOver, this);
13993         this.list.on('mousemove', this.onViewMove, this);
13994         
13995         this.list.on('scroll', this.onViewScroll, this);
13996         
13997         if(!this.tpl){
13998             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
13999                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
14000         }
14001
14002         this.view = new Roo.View(this.list, this.tpl, {
14003             singleSelect:true,
14004             tickable:true,
14005             parent:this,
14006             store: this.store,
14007             selectedClass: this.selectedClass
14008         });
14009         
14010         //this.view.wrapEl.setDisplayed(false);
14011         this.view.on('click', this.onViewClick, this);
14012         
14013         
14014         
14015         this.store.on('beforeload', this.onBeforeLoad, this);
14016         this.store.on('load', this.onLoad, this);
14017         this.store.on('loadexception', this.onLoadException, this);
14018         
14019         if(this.editable){
14020             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
14021                 "up" : function(e){
14022                     this.inKeyMode = true;
14023                     this.selectPrev();
14024                 },
14025
14026                 "down" : function(e){
14027                     this.inKeyMode = true;
14028                     this.selectNext();
14029                 },
14030
14031                 "enter" : function(e){
14032                     if(this.fireEvent("specialkey", this, e)){
14033                         this.onViewClick(false);
14034                     }
14035                     
14036                     return true;
14037                 },
14038
14039                 "esc" : function(e){
14040                     this.onTickableFooterButtonClick(e, false, false);
14041                 },
14042
14043                 "tab" : function(e){
14044                     this.fireEvent("specialkey", this, e);
14045                     
14046                     this.onTickableFooterButtonClick(e, false, false);
14047                     
14048                     return true;
14049                 },
14050
14051                 scope : this,
14052
14053                 doRelay : function(e, fn, key){
14054                     if(this.scope.isExpanded()){
14055                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14056                     }
14057                     return true;
14058                 },
14059
14060                 forceKeyDown: true
14061             });
14062         }
14063         
14064         this.queryDelay = Math.max(this.queryDelay || 10,
14065                 this.mode == 'local' ? 10 : 250);
14066         
14067         
14068         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14069         
14070         if(this.typeAhead){
14071             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14072         }
14073         
14074         if(this.editable !== false){
14075             this.tickableInputEl().on("keyup", this.onKeyUp, this);
14076         }
14077         
14078         this.indicator = this.indicatorEl();
14079         
14080         if(this.indicator){
14081             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
14082             this.indicator.hide();
14083         }
14084         
14085     },
14086
14087     onDestroy : function(){
14088         if(this.view){
14089             this.view.setStore(null);
14090             this.view.el.removeAllListeners();
14091             this.view.el.remove();
14092             this.view.purgeListeners();
14093         }
14094         if(this.list){
14095             this.list.dom.innerHTML  = '';
14096         }
14097         
14098         if(this.store){
14099             this.store.un('beforeload', this.onBeforeLoad, this);
14100             this.store.un('load', this.onLoad, this);
14101             this.store.un('loadexception', this.onLoadException, this);
14102         }
14103         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
14104     },
14105
14106     // private
14107     fireKey : function(e){
14108         if(e.isNavKeyPress() && !this.list.isVisible()){
14109             this.fireEvent("specialkey", this, e);
14110         }
14111     },
14112
14113     // private
14114     onResize: function(w, h){
14115 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14116 //        
14117 //        if(typeof w != 'number'){
14118 //            // we do not handle it!?!?
14119 //            return;
14120 //        }
14121 //        var tw = this.trigger.getWidth();
14122 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
14123 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
14124 //        var x = w - tw;
14125 //        this.inputEl().setWidth( this.adjustWidth('input', x));
14126 //            
14127 //        //this.trigger.setStyle('left', x+'px');
14128 //        
14129 //        if(this.list && this.listWidth === undefined){
14130 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14131 //            this.list.setWidth(lw);
14132 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14133 //        }
14134         
14135     
14136         
14137     },
14138
14139     /**
14140      * Allow or prevent the user from directly editing the field text.  If false is passed,
14141      * the user will only be able to select from the items defined in the dropdown list.  This method
14142      * is the runtime equivalent of setting the 'editable' config option at config time.
14143      * @param {Boolean} value True to allow the user to directly edit the field text
14144      */
14145     setEditable : function(value){
14146         if(value == this.editable){
14147             return;
14148         }
14149         this.editable = value;
14150         if(!value){
14151             this.inputEl().dom.setAttribute('readOnly', true);
14152             this.inputEl().on('mousedown', this.onTriggerClick,  this);
14153             this.inputEl().addClass('x-combo-noedit');
14154         }else{
14155             this.inputEl().dom.setAttribute('readOnly', false);
14156             this.inputEl().un('mousedown', this.onTriggerClick,  this);
14157             this.inputEl().removeClass('x-combo-noedit');
14158         }
14159     },
14160
14161     // private
14162     
14163     onBeforeLoad : function(combo,opts){
14164         if(!this.hasFocus){
14165             return;
14166         }
14167          if (!opts.add) {
14168             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14169          }
14170         this.restrictHeight();
14171         this.selectedIndex = -1;
14172     },
14173
14174     // private
14175     onLoad : function(){
14176         
14177         this.hasQuery = false;
14178         
14179         if(!this.hasFocus){
14180             return;
14181         }
14182         
14183         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14184             this.loading.hide();
14185         }
14186         
14187         if(this.store.getCount() > 0){
14188             
14189             this.expand();
14190             this.restrictHeight();
14191             if(this.lastQuery == this.allQuery){
14192                 if(this.editable && !this.tickable){
14193                     this.inputEl().dom.select();
14194                 }
14195                 
14196                 if(
14197                     !this.selectByValue(this.value, true) &&
14198                     this.autoFocus && 
14199                     (
14200                         !this.store.lastOptions ||
14201                         typeof(this.store.lastOptions.add) == 'undefined' || 
14202                         this.store.lastOptions.add != true
14203                     )
14204                 ){
14205                     this.select(0, true);
14206                 }
14207             }else{
14208                 if(this.autoFocus){
14209                     this.selectNext();
14210                 }
14211                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14212                     this.taTask.delay(this.typeAheadDelay);
14213                 }
14214             }
14215         }else{
14216             this.onEmptyResults();
14217         }
14218         
14219         //this.el.focus();
14220     },
14221     // private
14222     onLoadException : function()
14223     {
14224         this.hasQuery = false;
14225         
14226         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14227             this.loading.hide();
14228         }
14229         
14230         if(this.tickable && this.editable){
14231             return;
14232         }
14233         
14234         this.collapse();
14235         // only causes errors at present
14236         //Roo.log(this.store.reader.jsonData);
14237         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14238             // fixme
14239             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14240         //}
14241         
14242         
14243     },
14244     // private
14245     onTypeAhead : function(){
14246         if(this.store.getCount() > 0){
14247             var r = this.store.getAt(0);
14248             var newValue = r.data[this.displayField];
14249             var len = newValue.length;
14250             var selStart = this.getRawValue().length;
14251             
14252             if(selStart != len){
14253                 this.setRawValue(newValue);
14254                 this.selectText(selStart, newValue.length);
14255             }
14256         }
14257     },
14258
14259     // private
14260     onSelect : function(record, index){
14261         
14262         if(this.fireEvent('beforeselect', this, record, index) !== false){
14263         
14264             this.setFromData(index > -1 ? record.data : false);
14265             
14266             this.collapse();
14267             this.fireEvent('select', this, record, index);
14268         }
14269     },
14270
14271     /**
14272      * Returns the currently selected field value or empty string if no value is set.
14273      * @return {String} value The selected value
14274      */
14275     getValue : function()
14276     {
14277         if(Roo.isIOS && this.useNativeIOS){
14278             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14279         }
14280         
14281         if(this.multiple){
14282             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14283         }
14284         
14285         if(this.valueField){
14286             return typeof this.value != 'undefined' ? this.value : '';
14287         }else{
14288             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14289         }
14290     },
14291     
14292     getRawValue : function()
14293     {
14294         if(Roo.isIOS && this.useNativeIOS){
14295             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14296         }
14297         
14298         var v = this.inputEl().getValue();
14299         
14300         return v;
14301     },
14302
14303     /**
14304      * Clears any text/value currently set in the field
14305      */
14306     clearValue : function(){
14307         
14308         if(this.hiddenField){
14309             this.hiddenField.dom.value = '';
14310         }
14311         this.value = '';
14312         this.setRawValue('');
14313         this.lastSelectionText = '';
14314         this.lastData = false;
14315         
14316         var close = this.closeTriggerEl();
14317         
14318         if(close){
14319             close.hide();
14320         }
14321         
14322         this.validate();
14323         
14324     },
14325
14326     /**
14327      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14328      * will be displayed in the field.  If the value does not match the data value of an existing item,
14329      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14330      * Otherwise the field will be blank (although the value will still be set).
14331      * @param {String} value The value to match
14332      */
14333     setValue : function(v)
14334     {
14335         if(Roo.isIOS && this.useNativeIOS){
14336             this.setIOSValue(v);
14337             return;
14338         }
14339         
14340         if(this.multiple){
14341             this.syncValue();
14342             return;
14343         }
14344         
14345         var text = v;
14346         if(this.valueField){
14347             var r = this.findRecord(this.valueField, v);
14348             if(r){
14349                 text = r.data[this.displayField];
14350             }else if(this.valueNotFoundText !== undefined){
14351                 text = this.valueNotFoundText;
14352             }
14353         }
14354         this.lastSelectionText = text;
14355         if(this.hiddenField){
14356             this.hiddenField.dom.value = v;
14357         }
14358         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14359         this.value = v;
14360         
14361         var close = this.closeTriggerEl();
14362         
14363         if(close){
14364             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14365         }
14366         
14367         this.validate();
14368     },
14369     /**
14370      * @property {Object} the last set data for the element
14371      */
14372     
14373     lastData : false,
14374     /**
14375      * Sets the value of the field based on a object which is related to the record format for the store.
14376      * @param {Object} value the value to set as. or false on reset?
14377      */
14378     setFromData : function(o){
14379         
14380         if(this.multiple){
14381             this.addItem(o);
14382             return;
14383         }
14384             
14385         var dv = ''; // display value
14386         var vv = ''; // value value..
14387         this.lastData = o;
14388         if (this.displayField) {
14389             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14390         } else {
14391             // this is an error condition!!!
14392             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14393         }
14394         
14395         if(this.valueField){
14396             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14397         }
14398         
14399         var close = this.closeTriggerEl();
14400         
14401         if(close){
14402             if(dv.length || vv * 1 > 0){
14403                 close.show() ;
14404                 this.blockFocus=true;
14405             } else {
14406                 close.hide();
14407             }             
14408         }
14409         
14410         if(this.hiddenField){
14411             this.hiddenField.dom.value = vv;
14412             
14413             this.lastSelectionText = dv;
14414             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14415             this.value = vv;
14416             return;
14417         }
14418         // no hidden field.. - we store the value in 'value', but still display
14419         // display field!!!!
14420         this.lastSelectionText = dv;
14421         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14422         this.value = vv;
14423         
14424         
14425         
14426     },
14427     // private
14428     reset : function(){
14429         // overridden so that last data is reset..
14430         
14431         if(this.multiple){
14432             this.clearItem();
14433             return;
14434         }
14435         
14436         this.setValue(this.originalValue);
14437         //this.clearInvalid();
14438         this.lastData = false;
14439         if (this.view) {
14440             this.view.clearSelections();
14441         }
14442         
14443         this.validate();
14444     },
14445     // private
14446     findRecord : function(prop, value){
14447         var record;
14448         if(this.store.getCount() > 0){
14449             this.store.each(function(r){
14450                 if(r.data[prop] == value){
14451                     record = r;
14452                     return false;
14453                 }
14454                 return true;
14455             });
14456         }
14457         return record;
14458     },
14459     
14460     getName: function()
14461     {
14462         // returns hidden if it's set..
14463         if (!this.rendered) {return ''};
14464         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14465         
14466     },
14467     // private
14468     onViewMove : function(e, t){
14469         this.inKeyMode = false;
14470     },
14471
14472     // private
14473     onViewOver : function(e, t){
14474         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14475             return;
14476         }
14477         var item = this.view.findItemFromChild(t);
14478         
14479         if(item){
14480             var index = this.view.indexOf(item);
14481             this.select(index, false);
14482         }
14483     },
14484
14485     // private
14486     onViewClick : function(view, doFocus, el, e)
14487     {
14488         var index = this.view.getSelectedIndexes()[0];
14489         
14490         var r = this.store.getAt(index);
14491         
14492         if(this.tickable){
14493             
14494             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14495                 return;
14496             }
14497             
14498             var rm = false;
14499             var _this = this;
14500             
14501             Roo.each(this.tickItems, function(v,k){
14502                 
14503                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14504                     Roo.log(v);
14505                     _this.tickItems.splice(k, 1);
14506                     
14507                     if(typeof(e) == 'undefined' && view == false){
14508                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14509                     }
14510                     
14511                     rm = true;
14512                     return;
14513                 }
14514             });
14515             
14516             if(rm){
14517                 return;
14518             }
14519             
14520             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14521                 this.tickItems.push(r.data);
14522             }
14523             
14524             if(typeof(e) == 'undefined' && view == false){
14525                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14526             }
14527                     
14528             return;
14529         }
14530         
14531         if(r){
14532             this.onSelect(r, index);
14533         }
14534         if(doFocus !== false && !this.blockFocus){
14535             this.inputEl().focus();
14536         }
14537     },
14538
14539     // private
14540     restrictHeight : function(){
14541         //this.innerList.dom.style.height = '';
14542         //var inner = this.innerList.dom;
14543         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14544         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14545         //this.list.beginUpdate();
14546         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14547         this.list.alignTo(this.inputEl(), this.listAlign);
14548         this.list.alignTo(this.inputEl(), this.listAlign);
14549         //this.list.endUpdate();
14550     },
14551
14552     // private
14553     onEmptyResults : function(){
14554         
14555         if(this.tickable && this.editable){
14556             this.hasFocus = false;
14557             this.restrictHeight();
14558             return;
14559         }
14560         
14561         this.collapse();
14562     },
14563
14564     /**
14565      * Returns true if the dropdown list is expanded, else false.
14566      */
14567     isExpanded : function(){
14568         return this.list.isVisible();
14569     },
14570
14571     /**
14572      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14573      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14574      * @param {String} value The data value of the item to select
14575      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14576      * selected item if it is not currently in view (defaults to true)
14577      * @return {Boolean} True if the value matched an item in the list, else false
14578      */
14579     selectByValue : function(v, scrollIntoView){
14580         if(v !== undefined && v !== null){
14581             var r = this.findRecord(this.valueField || this.displayField, v);
14582             if(r){
14583                 this.select(this.store.indexOf(r), scrollIntoView);
14584                 return true;
14585             }
14586         }
14587         return false;
14588     },
14589
14590     /**
14591      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14592      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14593      * @param {Number} index The zero-based index of the list item to select
14594      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14595      * selected item if it is not currently in view (defaults to true)
14596      */
14597     select : function(index, scrollIntoView){
14598         this.selectedIndex = index;
14599         this.view.select(index);
14600         if(scrollIntoView !== false){
14601             var el = this.view.getNode(index);
14602             /*
14603              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14604              */
14605             if(el){
14606                 this.list.scrollChildIntoView(el, false);
14607             }
14608         }
14609     },
14610
14611     // private
14612     selectNext : function(){
14613         var ct = this.store.getCount();
14614         if(ct > 0){
14615             if(this.selectedIndex == -1){
14616                 this.select(0);
14617             }else if(this.selectedIndex < ct-1){
14618                 this.select(this.selectedIndex+1);
14619             }
14620         }
14621     },
14622
14623     // private
14624     selectPrev : function(){
14625         var ct = this.store.getCount();
14626         if(ct > 0){
14627             if(this.selectedIndex == -1){
14628                 this.select(0);
14629             }else if(this.selectedIndex != 0){
14630                 this.select(this.selectedIndex-1);
14631             }
14632         }
14633     },
14634
14635     // private
14636     onKeyUp : function(e){
14637         if(this.editable !== false && !e.isSpecialKey()){
14638             this.lastKey = e.getKey();
14639             this.dqTask.delay(this.queryDelay);
14640         }
14641     },
14642
14643     // private
14644     validateBlur : function(){
14645         return !this.list || !this.list.isVisible();   
14646     },
14647
14648     // private
14649     initQuery : function(){
14650         
14651         var v = this.getRawValue();
14652         
14653         if(this.tickable && this.editable){
14654             v = this.tickableInputEl().getValue();
14655         }
14656         
14657         this.doQuery(v);
14658     },
14659
14660     // private
14661     doForce : function(){
14662         if(this.inputEl().dom.value.length > 0){
14663             this.inputEl().dom.value =
14664                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14665              
14666         }
14667     },
14668
14669     /**
14670      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14671      * query allowing the query action to be canceled if needed.
14672      * @param {String} query The SQL query to execute
14673      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14674      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14675      * saved in the current store (defaults to false)
14676      */
14677     doQuery : function(q, forceAll){
14678         
14679         if(q === undefined || q === null){
14680             q = '';
14681         }
14682         var qe = {
14683             query: q,
14684             forceAll: forceAll,
14685             combo: this,
14686             cancel:false
14687         };
14688         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14689             return false;
14690         }
14691         q = qe.query;
14692         
14693         forceAll = qe.forceAll;
14694         if(forceAll === true || (q.length >= this.minChars)){
14695             
14696             this.hasQuery = true;
14697             
14698             if(this.lastQuery != q || this.alwaysQuery){
14699                 this.lastQuery = q;
14700                 if(this.mode == 'local'){
14701                     this.selectedIndex = -1;
14702                     if(forceAll){
14703                         this.store.clearFilter();
14704                     }else{
14705                         
14706                         if(this.specialFilter){
14707                             this.fireEvent('specialfilter', this);
14708                             this.onLoad();
14709                             return;
14710                         }
14711                         
14712                         this.store.filter(this.displayField, q);
14713                     }
14714                     
14715                     this.store.fireEvent("datachanged", this.store);
14716                     
14717                     this.onLoad();
14718                     
14719                     
14720                 }else{
14721                     
14722                     this.store.baseParams[this.queryParam] = q;
14723                     
14724                     var options = {params : this.getParams(q)};
14725                     
14726                     if(this.loadNext){
14727                         options.add = true;
14728                         options.params.start = this.page * this.pageSize;
14729                     }
14730                     
14731                     this.store.load(options);
14732                     
14733                     /*
14734                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14735                      *  we should expand the list on onLoad
14736                      *  so command out it
14737                      */
14738 //                    this.expand();
14739                 }
14740             }else{
14741                 this.selectedIndex = -1;
14742                 this.onLoad();   
14743             }
14744         }
14745         
14746         this.loadNext = false;
14747     },
14748     
14749     // private
14750     getParams : function(q){
14751         var p = {};
14752         //p[this.queryParam] = q;
14753         
14754         if(this.pageSize){
14755             p.start = 0;
14756             p.limit = this.pageSize;
14757         }
14758         return p;
14759     },
14760
14761     /**
14762      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14763      */
14764     collapse : function(){
14765         if(!this.isExpanded()){
14766             return;
14767         }
14768         
14769         this.list.hide();
14770         
14771         this.hasFocus = false;
14772         
14773         if(this.tickable){
14774             this.okBtn.hide();
14775             this.cancelBtn.hide();
14776             this.trigger.show();
14777             
14778             if(this.editable){
14779                 this.tickableInputEl().dom.value = '';
14780                 this.tickableInputEl().blur();
14781             }
14782             
14783         }
14784         
14785         Roo.get(document).un('mousedown', this.collapseIf, this);
14786         Roo.get(document).un('mousewheel', this.collapseIf, this);
14787         if (!this.editable) {
14788             Roo.get(document).un('keydown', this.listKeyPress, this);
14789         }
14790         this.fireEvent('collapse', this);
14791         
14792         this.validate();
14793     },
14794
14795     // private
14796     collapseIf : function(e){
14797         var in_combo  = e.within(this.el);
14798         var in_list =  e.within(this.list);
14799         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14800         
14801         if (in_combo || in_list || is_list) {
14802             //e.stopPropagation();
14803             return;
14804         }
14805         
14806         if(this.tickable){
14807             this.onTickableFooterButtonClick(e, false, false);
14808         }
14809
14810         this.collapse();
14811         
14812     },
14813
14814     /**
14815      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14816      */
14817     expand : function(){
14818        
14819         if(this.isExpanded() || !this.hasFocus){
14820             return;
14821         }
14822         
14823         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14824         this.list.setWidth(lw);
14825         
14826         Roo.log('expand');
14827         
14828         this.list.show();
14829         
14830         this.restrictHeight();
14831         
14832         if(this.tickable){
14833             
14834             this.tickItems = Roo.apply([], this.item);
14835             
14836             this.okBtn.show();
14837             this.cancelBtn.show();
14838             this.trigger.hide();
14839             
14840             if(this.editable){
14841                 this.tickableInputEl().focus();
14842             }
14843             
14844         }
14845         
14846         Roo.get(document).on('mousedown', this.collapseIf, this);
14847         Roo.get(document).on('mousewheel', this.collapseIf, this);
14848         if (!this.editable) {
14849             Roo.get(document).on('keydown', this.listKeyPress, this);
14850         }
14851         
14852         this.fireEvent('expand', this);
14853     },
14854
14855     // private
14856     // Implements the default empty TriggerField.onTriggerClick function
14857     onTriggerClick : function(e)
14858     {
14859         Roo.log('trigger click');
14860         
14861         if(this.disabled || !this.triggerList){
14862             return;
14863         }
14864         
14865         this.page = 0;
14866         this.loadNext = false;
14867         
14868         if(this.isExpanded()){
14869             this.collapse();
14870             if (!this.blockFocus) {
14871                 this.inputEl().focus();
14872             }
14873             
14874         }else {
14875             this.hasFocus = true;
14876             if(this.triggerAction == 'all') {
14877                 this.doQuery(this.allQuery, true);
14878             } else {
14879                 this.doQuery(this.getRawValue());
14880             }
14881             if (!this.blockFocus) {
14882                 this.inputEl().focus();
14883             }
14884         }
14885     },
14886     
14887     onTickableTriggerClick : function(e)
14888     {
14889         if(this.disabled){
14890             return;
14891         }
14892         
14893         this.page = 0;
14894         this.loadNext = false;
14895         this.hasFocus = true;
14896         
14897         if(this.triggerAction == 'all') {
14898             this.doQuery(this.allQuery, true);
14899         } else {
14900             this.doQuery(this.getRawValue());
14901         }
14902     },
14903     
14904     onSearchFieldClick : function(e)
14905     {
14906         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14907             this.onTickableFooterButtonClick(e, false, false);
14908             return;
14909         }
14910         
14911         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14912             return;
14913         }
14914         
14915         this.page = 0;
14916         this.loadNext = false;
14917         this.hasFocus = true;
14918         
14919         if(this.triggerAction == 'all') {
14920             this.doQuery(this.allQuery, true);
14921         } else {
14922             this.doQuery(this.getRawValue());
14923         }
14924     },
14925     
14926     listKeyPress : function(e)
14927     {
14928         //Roo.log('listkeypress');
14929         // scroll to first matching element based on key pres..
14930         if (e.isSpecialKey()) {
14931             return false;
14932         }
14933         var k = String.fromCharCode(e.getKey()).toUpperCase();
14934         //Roo.log(k);
14935         var match  = false;
14936         var csel = this.view.getSelectedNodes();
14937         var cselitem = false;
14938         if (csel.length) {
14939             var ix = this.view.indexOf(csel[0]);
14940             cselitem  = this.store.getAt(ix);
14941             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14942                 cselitem = false;
14943             }
14944             
14945         }
14946         
14947         this.store.each(function(v) { 
14948             if (cselitem) {
14949                 // start at existing selection.
14950                 if (cselitem.id == v.id) {
14951                     cselitem = false;
14952                 }
14953                 return true;
14954             }
14955                 
14956             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14957                 match = this.store.indexOf(v);
14958                 return false;
14959             }
14960             return true;
14961         }, this);
14962         
14963         if (match === false) {
14964             return true; // no more action?
14965         }
14966         // scroll to?
14967         this.view.select(match);
14968         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14969         sn.scrollIntoView(sn.dom.parentNode, false);
14970     },
14971     
14972     onViewScroll : function(e, t){
14973         
14974         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){
14975             return;
14976         }
14977         
14978         this.hasQuery = true;
14979         
14980         this.loading = this.list.select('.loading', true).first();
14981         
14982         if(this.loading === null){
14983             this.list.createChild({
14984                 tag: 'div',
14985                 cls: 'loading roo-select2-more-results roo-select2-active',
14986                 html: 'Loading more results...'
14987             });
14988             
14989             this.loading = this.list.select('.loading', true).first();
14990             
14991             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14992             
14993             this.loading.hide();
14994         }
14995         
14996         this.loading.show();
14997         
14998         var _combo = this;
14999         
15000         this.page++;
15001         this.loadNext = true;
15002         
15003         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
15004         
15005         return;
15006     },
15007     
15008     addItem : function(o)
15009     {   
15010         var dv = ''; // display value
15011         
15012         if (this.displayField) {
15013             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
15014         } else {
15015             // this is an error condition!!!
15016             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
15017         }
15018         
15019         if(!dv.length){
15020             return;
15021         }
15022         
15023         var choice = this.choices.createChild({
15024             tag: 'li',
15025             cls: 'roo-select2-search-choice',
15026             cn: [
15027                 {
15028                     tag: 'div',
15029                     html: dv
15030                 },
15031                 {
15032                     tag: 'a',
15033                     href: '#',
15034                     cls: 'roo-select2-search-choice-close fa fa-times',
15035                     tabindex: '-1'
15036                 }
15037             ]
15038             
15039         }, this.searchField);
15040         
15041         var close = choice.select('a.roo-select2-search-choice-close', true).first();
15042         
15043         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
15044         
15045         this.item.push(o);
15046         
15047         this.lastData = o;
15048         
15049         this.syncValue();
15050         
15051         this.inputEl().dom.value = '';
15052         
15053         this.validate();
15054     },
15055     
15056     onRemoveItem : function(e, _self, o)
15057     {
15058         e.preventDefault();
15059         
15060         this.lastItem = Roo.apply([], this.item);
15061         
15062         var index = this.item.indexOf(o.data) * 1;
15063         
15064         if( index < 0){
15065             Roo.log('not this item?!');
15066             return;
15067         }
15068         
15069         this.item.splice(index, 1);
15070         o.item.remove();
15071         
15072         this.syncValue();
15073         
15074         this.fireEvent('remove', this, e);
15075         
15076         this.validate();
15077         
15078     },
15079     
15080     syncValue : function()
15081     {
15082         if(!this.item.length){
15083             this.clearValue();
15084             return;
15085         }
15086             
15087         var value = [];
15088         var _this = this;
15089         Roo.each(this.item, function(i){
15090             if(_this.valueField){
15091                 value.push(i[_this.valueField]);
15092                 return;
15093             }
15094
15095             value.push(i);
15096         });
15097
15098         this.value = value.join(',');
15099
15100         if(this.hiddenField){
15101             this.hiddenField.dom.value = this.value;
15102         }
15103         
15104         this.store.fireEvent("datachanged", this.store);
15105         
15106         this.validate();
15107     },
15108     
15109     clearItem : function()
15110     {
15111         if(!this.multiple){
15112             return;
15113         }
15114         
15115         this.item = [];
15116         
15117         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15118            c.remove();
15119         });
15120         
15121         this.syncValue();
15122         
15123         this.validate();
15124         
15125         if(this.tickable && !Roo.isTouch){
15126             this.view.refresh();
15127         }
15128     },
15129     
15130     inputEl: function ()
15131     {
15132         if(Roo.isIOS && this.useNativeIOS){
15133             return this.el.select('select.roo-ios-select', true).first();
15134         }
15135         
15136         if(Roo.isTouch && this.mobileTouchView){
15137             return this.el.select('input.form-control',true).first();
15138         }
15139         
15140         if(this.tickable){
15141             return this.searchField;
15142         }
15143         
15144         return this.el.select('input.form-control',true).first();
15145     },
15146     
15147     onTickableFooterButtonClick : function(e, btn, el)
15148     {
15149         e.preventDefault();
15150         
15151         this.lastItem = Roo.apply([], this.item);
15152         
15153         if(btn && btn.name == 'cancel'){
15154             this.tickItems = Roo.apply([], this.item);
15155             this.collapse();
15156             return;
15157         }
15158         
15159         this.clearItem();
15160         
15161         var _this = this;
15162         
15163         Roo.each(this.tickItems, function(o){
15164             _this.addItem(o);
15165         });
15166         
15167         this.collapse();
15168         
15169     },
15170     
15171     validate : function()
15172     {
15173         if(this.getVisibilityEl().hasClass('hidden')){
15174             return true;
15175         }
15176         
15177         var v = this.getRawValue();
15178         
15179         if(this.multiple){
15180             v = this.getValue();
15181         }
15182         
15183         if(this.disabled || this.allowBlank || v.length){
15184             this.markValid();
15185             return true;
15186         }
15187         
15188         this.markInvalid();
15189         return false;
15190     },
15191     
15192     tickableInputEl : function()
15193     {
15194         if(!this.tickable || !this.editable){
15195             return this.inputEl();
15196         }
15197         
15198         return this.inputEl().select('.roo-select2-search-field-input', true).first();
15199     },
15200     
15201     
15202     getAutoCreateTouchView : function()
15203     {
15204         var id = Roo.id();
15205         
15206         var cfg = {
15207             cls: 'form-group' //input-group
15208         };
15209         
15210         var input =  {
15211             tag: 'input',
15212             id : id,
15213             type : this.inputType,
15214             cls : 'form-control x-combo-noedit',
15215             autocomplete: 'new-password',
15216             placeholder : this.placeholder || '',
15217             readonly : true
15218         };
15219         
15220         if (this.name) {
15221             input.name = this.name;
15222         }
15223         
15224         if (this.size) {
15225             input.cls += ' input-' + this.size;
15226         }
15227         
15228         if (this.disabled) {
15229             input.disabled = true;
15230         }
15231         
15232         var inputblock = {
15233             cls : '',
15234             cn : [
15235                 input
15236             ]
15237         };
15238         
15239         if(this.before){
15240             inputblock.cls += ' input-group';
15241             
15242             inputblock.cn.unshift({
15243                 tag :'span',
15244                 cls : 'input-group-addon input-group-prepend input-group-text',
15245                 html : this.before
15246             });
15247         }
15248         
15249         if(this.removable && !this.multiple){
15250             inputblock.cls += ' roo-removable';
15251             
15252             inputblock.cn.push({
15253                 tag: 'button',
15254                 html : 'x',
15255                 cls : 'roo-combo-removable-btn close'
15256             });
15257         }
15258
15259         if(this.hasFeedback && !this.allowBlank){
15260             
15261             inputblock.cls += ' has-feedback';
15262             
15263             inputblock.cn.push({
15264                 tag: 'span',
15265                 cls: 'glyphicon form-control-feedback'
15266             });
15267             
15268         }
15269         
15270         if (this.after) {
15271             
15272             inputblock.cls += (this.before) ? '' : ' input-group';
15273             
15274             inputblock.cn.push({
15275                 tag :'span',
15276                 cls : 'input-group-addon input-group-append input-group-text',
15277                 html : this.after
15278             });
15279         }
15280
15281         
15282         var ibwrap = inputblock;
15283         
15284         if(this.multiple){
15285             ibwrap = {
15286                 tag: 'ul',
15287                 cls: 'roo-select2-choices',
15288                 cn:[
15289                     {
15290                         tag: 'li',
15291                         cls: 'roo-select2-search-field',
15292                         cn: [
15293
15294                             inputblock
15295                         ]
15296                     }
15297                 ]
15298             };
15299         
15300             
15301         }
15302         
15303         var combobox = {
15304             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15305             cn: [
15306                 {
15307                     tag: 'input',
15308                     type : 'hidden',
15309                     cls: 'form-hidden-field'
15310                 },
15311                 ibwrap
15312             ]
15313         };
15314         
15315         if(!this.multiple && this.showToggleBtn){
15316             
15317             var caret = {
15318                         tag: 'span',
15319                         cls: 'caret'
15320             };
15321             
15322             if (this.caret != false) {
15323                 caret = {
15324                      tag: 'i',
15325                      cls: 'fa fa-' + this.caret
15326                 };
15327                 
15328             }
15329             
15330             combobox.cn.push({
15331                 tag :'span',
15332                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15333                 cn : [
15334                     caret,
15335                     {
15336                         tag: 'span',
15337                         cls: 'combobox-clear',
15338                         cn  : [
15339                             {
15340                                 tag : 'i',
15341                                 cls: 'icon-remove'
15342                             }
15343                         ]
15344                     }
15345                 ]
15346
15347             })
15348         }
15349         
15350         if(this.multiple){
15351             combobox.cls += ' roo-select2-container-multi';
15352         }
15353         
15354         var align = this.labelAlign || this.parentLabelAlign();
15355         
15356         if (align ==='left' && this.fieldLabel.length) {
15357
15358             cfg.cn = [
15359                 {
15360                    tag : 'i',
15361                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15362                    tooltip : 'This field is required'
15363                 },
15364                 {
15365                     tag: 'label',
15366                     cls : 'control-label col-form-label',
15367                     html : this.fieldLabel
15368
15369                 },
15370                 {
15371                     cls : '', 
15372                     cn: [
15373                         combobox
15374                     ]
15375                 }
15376             ];
15377             
15378             var labelCfg = cfg.cn[1];
15379             var contentCfg = cfg.cn[2];
15380             
15381
15382             if(this.indicatorpos == 'right'){
15383                 cfg.cn = [
15384                     {
15385                         tag: 'label',
15386                         'for' :  id,
15387                         cls : 'control-label col-form-label',
15388                         cn : [
15389                             {
15390                                 tag : 'span',
15391                                 html : this.fieldLabel
15392                             },
15393                             {
15394                                 tag : 'i',
15395                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15396                                 tooltip : 'This field is required'
15397                             }
15398                         ]
15399                     },
15400                     {
15401                         cls : "",
15402                         cn: [
15403                             combobox
15404                         ]
15405                     }
15406
15407                 ];
15408                 
15409                 labelCfg = cfg.cn[0];
15410                 contentCfg = cfg.cn[1];
15411             }
15412             
15413            
15414             
15415             if(this.labelWidth > 12){
15416                 labelCfg.style = "width: " + this.labelWidth + 'px';
15417             }
15418             
15419             if(this.labelWidth < 13 && this.labelmd == 0){
15420                 this.labelmd = this.labelWidth;
15421             }
15422             
15423             if(this.labellg > 0){
15424                 labelCfg.cls += ' col-lg-' + this.labellg;
15425                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15426             }
15427             
15428             if(this.labelmd > 0){
15429                 labelCfg.cls += ' col-md-' + this.labelmd;
15430                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15431             }
15432             
15433             if(this.labelsm > 0){
15434                 labelCfg.cls += ' col-sm-' + this.labelsm;
15435                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15436             }
15437             
15438             if(this.labelxs > 0){
15439                 labelCfg.cls += ' col-xs-' + this.labelxs;
15440                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15441             }
15442                 
15443                 
15444         } else if ( this.fieldLabel.length) {
15445             cfg.cn = [
15446                 {
15447                    tag : 'i',
15448                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15449                    tooltip : 'This field is required'
15450                 },
15451                 {
15452                     tag: 'label',
15453                     cls : 'control-label',
15454                     html : this.fieldLabel
15455
15456                 },
15457                 {
15458                     cls : '', 
15459                     cn: [
15460                         combobox
15461                     ]
15462                 }
15463             ];
15464             
15465             if(this.indicatorpos == 'right'){
15466                 cfg.cn = [
15467                     {
15468                         tag: 'label',
15469                         cls : 'control-label',
15470                         html : this.fieldLabel,
15471                         cn : [
15472                             {
15473                                tag : 'i',
15474                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15475                                tooltip : 'This field is required'
15476                             }
15477                         ]
15478                     },
15479                     {
15480                         cls : '', 
15481                         cn: [
15482                             combobox
15483                         ]
15484                     }
15485                 ];
15486             }
15487         } else {
15488             cfg.cn = combobox;    
15489         }
15490         
15491         
15492         var settings = this;
15493         
15494         ['xs','sm','md','lg'].map(function(size){
15495             if (settings[size]) {
15496                 cfg.cls += ' col-' + size + '-' + settings[size];
15497             }
15498         });
15499         
15500         return cfg;
15501     },
15502     
15503     initTouchView : function()
15504     {
15505         this.renderTouchView();
15506         
15507         this.touchViewEl.on('scroll', function(){
15508             this.el.dom.scrollTop = 0;
15509         }, this);
15510         
15511         this.originalValue = this.getValue();
15512         
15513         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15514         
15515         this.inputEl().on("click", this.showTouchView, this);
15516         if (this.triggerEl) {
15517             this.triggerEl.on("click", this.showTouchView, this);
15518         }
15519         
15520         
15521         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15522         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15523         
15524         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15525         
15526         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15527         this.store.on('load', this.onTouchViewLoad, this);
15528         this.store.on('loadexception', this.onTouchViewLoadException, this);
15529         
15530         if(this.hiddenName){
15531             
15532             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15533             
15534             this.hiddenField.dom.value =
15535                 this.hiddenValue !== undefined ? this.hiddenValue :
15536                 this.value !== undefined ? this.value : '';
15537         
15538             this.el.dom.removeAttribute('name');
15539             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15540         }
15541         
15542         if(this.multiple){
15543             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15544             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15545         }
15546         
15547         if(this.removable && !this.multiple){
15548             var close = this.closeTriggerEl();
15549             if(close){
15550                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15551                 close.on('click', this.removeBtnClick, this, close);
15552             }
15553         }
15554         /*
15555          * fix the bug in Safari iOS8
15556          */
15557         this.inputEl().on("focus", function(e){
15558             document.activeElement.blur();
15559         }, this);
15560         
15561         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15562         
15563         return;
15564         
15565         
15566     },
15567     
15568     renderTouchView : function()
15569     {
15570         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15571         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15572         
15573         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15574         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15575         
15576         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15577         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15578         this.touchViewBodyEl.setStyle('overflow', 'auto');
15579         
15580         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15581         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15582         
15583         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15584         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15585         
15586     },
15587     
15588     showTouchView : function()
15589     {
15590         if(this.disabled){
15591             return;
15592         }
15593         
15594         this.touchViewHeaderEl.hide();
15595
15596         if(this.modalTitle.length){
15597             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15598             this.touchViewHeaderEl.show();
15599         }
15600
15601         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15602         this.touchViewEl.show();
15603
15604         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15605         
15606         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15607         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15608
15609         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15610
15611         if(this.modalTitle.length){
15612             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15613         }
15614         
15615         this.touchViewBodyEl.setHeight(bodyHeight);
15616
15617         if(this.animate){
15618             var _this = this;
15619             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15620         }else{
15621             this.touchViewEl.addClass('in');
15622         }
15623         
15624         if(this._touchViewMask){
15625             Roo.get(document.body).addClass("x-body-masked");
15626             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15627             this._touchViewMask.setStyle('z-index', 10000);
15628             this._touchViewMask.addClass('show');
15629         }
15630         
15631         this.doTouchViewQuery();
15632         
15633     },
15634     
15635     hideTouchView : function()
15636     {
15637         this.touchViewEl.removeClass('in');
15638
15639         if(this.animate){
15640             var _this = this;
15641             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15642         }else{
15643             this.touchViewEl.setStyle('display', 'none');
15644         }
15645         
15646         if(this._touchViewMask){
15647             this._touchViewMask.removeClass('show');
15648             Roo.get(document.body).removeClass("x-body-masked");
15649         }
15650     },
15651     
15652     setTouchViewValue : function()
15653     {
15654         if(this.multiple){
15655             this.clearItem();
15656         
15657             var _this = this;
15658
15659             Roo.each(this.tickItems, function(o){
15660                 this.addItem(o);
15661             }, this);
15662         }
15663         
15664         this.hideTouchView();
15665     },
15666     
15667     doTouchViewQuery : function()
15668     {
15669         var qe = {
15670             query: '',
15671             forceAll: true,
15672             combo: this,
15673             cancel:false
15674         };
15675         
15676         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15677             return false;
15678         }
15679         
15680         if(!this.alwaysQuery || this.mode == 'local'){
15681             this.onTouchViewLoad();
15682             return;
15683         }
15684         
15685         this.store.load();
15686     },
15687     
15688     onTouchViewBeforeLoad : function(combo,opts)
15689     {
15690         return;
15691     },
15692
15693     // private
15694     onTouchViewLoad : function()
15695     {
15696         if(this.store.getCount() < 1){
15697             this.onTouchViewEmptyResults();
15698             return;
15699         }
15700         
15701         this.clearTouchView();
15702         
15703         var rawValue = this.getRawValue();
15704         
15705         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15706         
15707         this.tickItems = [];
15708         
15709         this.store.data.each(function(d, rowIndex){
15710             var row = this.touchViewListGroup.createChild(template);
15711             
15712             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15713                 row.addClass(d.data.cls);
15714             }
15715             
15716             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15717                 var cfg = {
15718                     data : d.data,
15719                     html : d.data[this.displayField]
15720                 };
15721                 
15722                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15723                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15724                 }
15725             }
15726             row.removeClass('selected');
15727             if(!this.multiple && this.valueField &&
15728                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15729             {
15730                 // radio buttons..
15731                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15732                 row.addClass('selected');
15733             }
15734             
15735             if(this.multiple && this.valueField &&
15736                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15737             {
15738                 
15739                 // checkboxes...
15740                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15741                 this.tickItems.push(d.data);
15742             }
15743             
15744             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15745             
15746         }, this);
15747         
15748         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15749         
15750         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15751
15752         if(this.modalTitle.length){
15753             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15754         }
15755
15756         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15757         
15758         if(this.mobile_restrict_height && listHeight < bodyHeight){
15759             this.touchViewBodyEl.setHeight(listHeight);
15760         }
15761         
15762         var _this = this;
15763         
15764         if(firstChecked && listHeight > bodyHeight){
15765             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15766         }
15767         
15768     },
15769     
15770     onTouchViewLoadException : function()
15771     {
15772         this.hideTouchView();
15773     },
15774     
15775     onTouchViewEmptyResults : function()
15776     {
15777         this.clearTouchView();
15778         
15779         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15780         
15781         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15782         
15783     },
15784     
15785     clearTouchView : function()
15786     {
15787         this.touchViewListGroup.dom.innerHTML = '';
15788     },
15789     
15790     onTouchViewClick : function(e, el, o)
15791     {
15792         e.preventDefault();
15793         
15794         var row = o.row;
15795         var rowIndex = o.rowIndex;
15796         
15797         var r = this.store.getAt(rowIndex);
15798         
15799         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15800             
15801             if(!this.multiple){
15802                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15803                     c.dom.removeAttribute('checked');
15804                 }, this);
15805
15806                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15807
15808                 this.setFromData(r.data);
15809
15810                 var close = this.closeTriggerEl();
15811
15812                 if(close){
15813                     close.show();
15814                 }
15815
15816                 this.hideTouchView();
15817
15818                 this.fireEvent('select', this, r, rowIndex);
15819
15820                 return;
15821             }
15822
15823             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15824                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15825                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15826                 return;
15827             }
15828
15829             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15830             this.addItem(r.data);
15831             this.tickItems.push(r.data);
15832         }
15833     },
15834     
15835     getAutoCreateNativeIOS : function()
15836     {
15837         var cfg = {
15838             cls: 'form-group' //input-group,
15839         };
15840         
15841         var combobox =  {
15842             tag: 'select',
15843             cls : 'roo-ios-select'
15844         };
15845         
15846         if (this.name) {
15847             combobox.name = this.name;
15848         }
15849         
15850         if (this.disabled) {
15851             combobox.disabled = true;
15852         }
15853         
15854         var settings = this;
15855         
15856         ['xs','sm','md','lg'].map(function(size){
15857             if (settings[size]) {
15858                 cfg.cls += ' col-' + size + '-' + settings[size];
15859             }
15860         });
15861         
15862         cfg.cn = combobox;
15863         
15864         return cfg;
15865         
15866     },
15867     
15868     initIOSView : function()
15869     {
15870         this.store.on('load', this.onIOSViewLoad, this);
15871         
15872         return;
15873     },
15874     
15875     onIOSViewLoad : function()
15876     {
15877         if(this.store.getCount() < 1){
15878             return;
15879         }
15880         
15881         this.clearIOSView();
15882         
15883         if(this.allowBlank) {
15884             
15885             var default_text = '-- SELECT --';
15886             
15887             if(this.placeholder.length){
15888                 default_text = this.placeholder;
15889             }
15890             
15891             if(this.emptyTitle.length){
15892                 default_text += ' - ' + this.emptyTitle + ' -';
15893             }
15894             
15895             var opt = this.inputEl().createChild({
15896                 tag: 'option',
15897                 value : 0,
15898                 html : default_text
15899             });
15900             
15901             var o = {};
15902             o[this.valueField] = 0;
15903             o[this.displayField] = default_text;
15904             
15905             this.ios_options.push({
15906                 data : o,
15907                 el : opt
15908             });
15909             
15910         }
15911         
15912         this.store.data.each(function(d, rowIndex){
15913             
15914             var html = '';
15915             
15916             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15917                 html = d.data[this.displayField];
15918             }
15919             
15920             var value = '';
15921             
15922             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15923                 value = d.data[this.valueField];
15924             }
15925             
15926             var option = {
15927                 tag: 'option',
15928                 value : value,
15929                 html : html
15930             };
15931             
15932             if(this.value == d.data[this.valueField]){
15933                 option['selected'] = true;
15934             }
15935             
15936             var opt = this.inputEl().createChild(option);
15937             
15938             this.ios_options.push({
15939                 data : d.data,
15940                 el : opt
15941             });
15942             
15943         }, this);
15944         
15945         this.inputEl().on('change', function(){
15946            this.fireEvent('select', this);
15947         }, this);
15948         
15949     },
15950     
15951     clearIOSView: function()
15952     {
15953         this.inputEl().dom.innerHTML = '';
15954         
15955         this.ios_options = [];
15956     },
15957     
15958     setIOSValue: function(v)
15959     {
15960         this.value = v;
15961         
15962         if(!this.ios_options){
15963             return;
15964         }
15965         
15966         Roo.each(this.ios_options, function(opts){
15967            
15968            opts.el.dom.removeAttribute('selected');
15969            
15970            if(opts.data[this.valueField] != v){
15971                return;
15972            }
15973            
15974            opts.el.dom.setAttribute('selected', true);
15975            
15976         }, this);
15977     }
15978
15979     /** 
15980     * @cfg {Boolean} grow 
15981     * @hide 
15982     */
15983     /** 
15984     * @cfg {Number} growMin 
15985     * @hide 
15986     */
15987     /** 
15988     * @cfg {Number} growMax 
15989     * @hide 
15990     */
15991     /**
15992      * @hide
15993      * @method autoSize
15994      */
15995 });
15996
15997 Roo.apply(Roo.bootstrap.ComboBox,  {
15998     
15999     header : {
16000         tag: 'div',
16001         cls: 'modal-header',
16002         cn: [
16003             {
16004                 tag: 'h4',
16005                 cls: 'modal-title'
16006             }
16007         ]
16008     },
16009     
16010     body : {
16011         tag: 'div',
16012         cls: 'modal-body',
16013         cn: [
16014             {
16015                 tag: 'ul',
16016                 cls: 'list-group'
16017             }
16018         ]
16019     },
16020     
16021     listItemRadio : {
16022         tag: 'li',
16023         cls: 'list-group-item',
16024         cn: [
16025             {
16026                 tag: 'span',
16027                 cls: 'roo-combobox-list-group-item-value'
16028             },
16029             {
16030                 tag: 'div',
16031                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
16032                 cn: [
16033                     {
16034                         tag: 'input',
16035                         type: 'radio'
16036                     },
16037                     {
16038                         tag: 'label'
16039                     }
16040                 ]
16041             }
16042         ]
16043     },
16044     
16045     listItemCheckbox : {
16046         tag: 'li',
16047         cls: 'list-group-item',
16048         cn: [
16049             {
16050                 tag: 'span',
16051                 cls: 'roo-combobox-list-group-item-value'
16052             },
16053             {
16054                 tag: 'div',
16055                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
16056                 cn: [
16057                     {
16058                         tag: 'input',
16059                         type: 'checkbox'
16060                     },
16061                     {
16062                         tag: 'label'
16063                     }
16064                 ]
16065             }
16066         ]
16067     },
16068     
16069     emptyResult : {
16070         tag: 'div',
16071         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
16072     },
16073     
16074     footer : {
16075         tag: 'div',
16076         cls: 'modal-footer',
16077         cn: [
16078             {
16079                 tag: 'div',
16080                 cls: 'row',
16081                 cn: [
16082                     {
16083                         tag: 'div',
16084                         cls: 'col-xs-6 text-left',
16085                         cn: {
16086                             tag: 'button',
16087                             cls: 'btn btn-danger roo-touch-view-cancel',
16088                             html: 'Cancel'
16089                         }
16090                     },
16091                     {
16092                         tag: 'div',
16093                         cls: 'col-xs-6 text-right',
16094                         cn: {
16095                             tag: 'button',
16096                             cls: 'btn btn-success roo-touch-view-ok',
16097                             html: 'OK'
16098                         }
16099                     }
16100                 ]
16101             }
16102         ]
16103         
16104     }
16105 });
16106
16107 Roo.apply(Roo.bootstrap.ComboBox,  {
16108     
16109     touchViewTemplate : {
16110         tag: 'div',
16111         cls: 'modal fade roo-combobox-touch-view',
16112         cn: [
16113             {
16114                 tag: 'div',
16115                 cls: 'modal-dialog',
16116                 style : 'position:fixed', // we have to fix position....
16117                 cn: [
16118                     {
16119                         tag: 'div',
16120                         cls: 'modal-content',
16121                         cn: [
16122                             Roo.bootstrap.ComboBox.header,
16123                             Roo.bootstrap.ComboBox.body,
16124                             Roo.bootstrap.ComboBox.footer
16125                         ]
16126                     }
16127                 ]
16128             }
16129         ]
16130     }
16131 });/*
16132  * Based on:
16133  * Ext JS Library 1.1.1
16134  * Copyright(c) 2006-2007, Ext JS, LLC.
16135  *
16136  * Originally Released Under LGPL - original licence link has changed is not relivant.
16137  *
16138  * Fork - LGPL
16139  * <script type="text/javascript">
16140  */
16141
16142 /**
16143  * @class Roo.View
16144  * @extends Roo.util.Observable
16145  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
16146  * This class also supports single and multi selection modes. <br>
16147  * Create a data model bound view:
16148  <pre><code>
16149  var store = new Roo.data.Store(...);
16150
16151  var view = new Roo.View({
16152     el : "my-element",
16153     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
16154  
16155     singleSelect: true,
16156     selectedClass: "ydataview-selected",
16157     store: store
16158  });
16159
16160  // listen for node click?
16161  view.on("click", function(vw, index, node, e){
16162  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16163  });
16164
16165  // load XML data
16166  dataModel.load("foobar.xml");
16167  </code></pre>
16168  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16169  * <br><br>
16170  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16171  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16172  * 
16173  * Note: old style constructor is still suported (container, template, config)
16174  * 
16175  * @constructor
16176  * Create a new View
16177  * @param {Object} config The config object
16178  * 
16179  */
16180 Roo.View = function(config, depreciated_tpl, depreciated_config){
16181     
16182     this.parent = false;
16183     
16184     if (typeof(depreciated_tpl) == 'undefined') {
16185         // new way.. - universal constructor.
16186         Roo.apply(this, config);
16187         this.el  = Roo.get(this.el);
16188     } else {
16189         // old format..
16190         this.el  = Roo.get(config);
16191         this.tpl = depreciated_tpl;
16192         Roo.apply(this, depreciated_config);
16193     }
16194     this.wrapEl  = this.el.wrap().wrap();
16195     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16196     
16197     
16198     if(typeof(this.tpl) == "string"){
16199         this.tpl = new Roo.Template(this.tpl);
16200     } else {
16201         // support xtype ctors..
16202         this.tpl = new Roo.factory(this.tpl, Roo);
16203     }
16204     
16205     
16206     this.tpl.compile();
16207     
16208     /** @private */
16209     this.addEvents({
16210         /**
16211          * @event beforeclick
16212          * Fires before a click is processed. Returns false to cancel the default action.
16213          * @param {Roo.View} this
16214          * @param {Number} index The index of the target node
16215          * @param {HTMLElement} node The target node
16216          * @param {Roo.EventObject} e The raw event object
16217          */
16218             "beforeclick" : true,
16219         /**
16220          * @event click
16221          * Fires when a template node is clicked.
16222          * @param {Roo.View} this
16223          * @param {Number} index The index of the target node
16224          * @param {HTMLElement} node The target node
16225          * @param {Roo.EventObject} e The raw event object
16226          */
16227             "click" : true,
16228         /**
16229          * @event dblclick
16230          * Fires when a template node is double clicked.
16231          * @param {Roo.View} this
16232          * @param {Number} index The index of the target node
16233          * @param {HTMLElement} node The target node
16234          * @param {Roo.EventObject} e The raw event object
16235          */
16236             "dblclick" : true,
16237         /**
16238          * @event contextmenu
16239          * Fires when a template node is right clicked.
16240          * @param {Roo.View} this
16241          * @param {Number} index The index of the target node
16242          * @param {HTMLElement} node The target node
16243          * @param {Roo.EventObject} e The raw event object
16244          */
16245             "contextmenu" : true,
16246         /**
16247          * @event selectionchange
16248          * Fires when the selected nodes change.
16249          * @param {Roo.View} this
16250          * @param {Array} selections Array of the selected nodes
16251          */
16252             "selectionchange" : true,
16253     
16254         /**
16255          * @event beforeselect
16256          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16257          * @param {Roo.View} this
16258          * @param {HTMLElement} node The node to be selected
16259          * @param {Array} selections Array of currently selected nodes
16260          */
16261             "beforeselect" : true,
16262         /**
16263          * @event preparedata
16264          * Fires on every row to render, to allow you to change the data.
16265          * @param {Roo.View} this
16266          * @param {Object} data to be rendered (change this)
16267          */
16268           "preparedata" : true
16269           
16270           
16271         });
16272
16273
16274
16275     this.el.on({
16276         "click": this.onClick,
16277         "dblclick": this.onDblClick,
16278         "contextmenu": this.onContextMenu,
16279         scope:this
16280     });
16281
16282     this.selections = [];
16283     this.nodes = [];
16284     this.cmp = new Roo.CompositeElementLite([]);
16285     if(this.store){
16286         this.store = Roo.factory(this.store, Roo.data);
16287         this.setStore(this.store, true);
16288     }
16289     
16290     if ( this.footer && this.footer.xtype) {
16291            
16292          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16293         
16294         this.footer.dataSource = this.store;
16295         this.footer.container = fctr;
16296         this.footer = Roo.factory(this.footer, Roo);
16297         fctr.insertFirst(this.el);
16298         
16299         // this is a bit insane - as the paging toolbar seems to detach the el..
16300 //        dom.parentNode.parentNode.parentNode
16301          // they get detached?
16302     }
16303     
16304     
16305     Roo.View.superclass.constructor.call(this);
16306     
16307     
16308 };
16309
16310 Roo.extend(Roo.View, Roo.util.Observable, {
16311     
16312      /**
16313      * @cfg {Roo.data.Store} store Data store to load data from.
16314      */
16315     store : false,
16316     
16317     /**
16318      * @cfg {String|Roo.Element} el The container element.
16319      */
16320     el : '',
16321     
16322     /**
16323      * @cfg {String|Roo.Template} tpl The template used by this View 
16324      */
16325     tpl : false,
16326     /**
16327      * @cfg {String} dataName the named area of the template to use as the data area
16328      *                          Works with domtemplates roo-name="name"
16329      */
16330     dataName: false,
16331     /**
16332      * @cfg {String} selectedClass The css class to add to selected nodes
16333      */
16334     selectedClass : "x-view-selected",
16335      /**
16336      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16337      */
16338     emptyText : "",
16339     
16340     /**
16341      * @cfg {String} text to display on mask (default Loading)
16342      */
16343     mask : false,
16344     /**
16345      * @cfg {Boolean} multiSelect Allow multiple selection
16346      */
16347     multiSelect : false,
16348     /**
16349      * @cfg {Boolean} singleSelect Allow single selection
16350      */
16351     singleSelect:  false,
16352     
16353     /**
16354      * @cfg {Boolean} toggleSelect - selecting 
16355      */
16356     toggleSelect : false,
16357     
16358     /**
16359      * @cfg {Boolean} tickable - selecting 
16360      */
16361     tickable : false,
16362     
16363     /**
16364      * Returns the element this view is bound to.
16365      * @return {Roo.Element}
16366      */
16367     getEl : function(){
16368         return this.wrapEl;
16369     },
16370     
16371     
16372
16373     /**
16374      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16375      */
16376     refresh : function(){
16377         //Roo.log('refresh');
16378         var t = this.tpl;
16379         
16380         // if we are using something like 'domtemplate', then
16381         // the what gets used is:
16382         // t.applySubtemplate(NAME, data, wrapping data..)
16383         // the outer template then get' applied with
16384         //     the store 'extra data'
16385         // and the body get's added to the
16386         //      roo-name="data" node?
16387         //      <span class='roo-tpl-{name}'></span> ?????
16388         
16389         
16390         
16391         this.clearSelections();
16392         this.el.update("");
16393         var html = [];
16394         var records = this.store.getRange();
16395         if(records.length < 1) {
16396             
16397             // is this valid??  = should it render a template??
16398             
16399             this.el.update(this.emptyText);
16400             return;
16401         }
16402         var el = this.el;
16403         if (this.dataName) {
16404             this.el.update(t.apply(this.store.meta)); //????
16405             el = this.el.child('.roo-tpl-' + this.dataName);
16406         }
16407         
16408         for(var i = 0, len = records.length; i < len; i++){
16409             var data = this.prepareData(records[i].data, i, records[i]);
16410             this.fireEvent("preparedata", this, data, i, records[i]);
16411             
16412             var d = Roo.apply({}, data);
16413             
16414             if(this.tickable){
16415                 Roo.apply(d, {'roo-id' : Roo.id()});
16416                 
16417                 var _this = this;
16418             
16419                 Roo.each(this.parent.item, function(item){
16420                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16421                         return;
16422                     }
16423                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16424                 });
16425             }
16426             
16427             html[html.length] = Roo.util.Format.trim(
16428                 this.dataName ?
16429                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16430                     t.apply(d)
16431             );
16432         }
16433         
16434         
16435         
16436         el.update(html.join(""));
16437         this.nodes = el.dom.childNodes;
16438         this.updateIndexes(0);
16439     },
16440     
16441
16442     /**
16443      * Function to override to reformat the data that is sent to
16444      * the template for each node.
16445      * DEPRICATED - use the preparedata event handler.
16446      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16447      * a JSON object for an UpdateManager bound view).
16448      */
16449     prepareData : function(data, index, record)
16450     {
16451         this.fireEvent("preparedata", this, data, index, record);
16452         return data;
16453     },
16454
16455     onUpdate : function(ds, record){
16456         // Roo.log('on update');   
16457         this.clearSelections();
16458         var index = this.store.indexOf(record);
16459         var n = this.nodes[index];
16460         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16461         n.parentNode.removeChild(n);
16462         this.updateIndexes(index, index);
16463     },
16464
16465     
16466     
16467 // --------- FIXME     
16468     onAdd : function(ds, records, index)
16469     {
16470         //Roo.log(['on Add', ds, records, index] );        
16471         this.clearSelections();
16472         if(this.nodes.length == 0){
16473             this.refresh();
16474             return;
16475         }
16476         var n = this.nodes[index];
16477         for(var i = 0, len = records.length; i < len; i++){
16478             var d = this.prepareData(records[i].data, i, records[i]);
16479             if(n){
16480                 this.tpl.insertBefore(n, d);
16481             }else{
16482                 
16483                 this.tpl.append(this.el, d);
16484             }
16485         }
16486         this.updateIndexes(index);
16487     },
16488
16489     onRemove : function(ds, record, index){
16490        // Roo.log('onRemove');
16491         this.clearSelections();
16492         var el = this.dataName  ?
16493             this.el.child('.roo-tpl-' + this.dataName) :
16494             this.el; 
16495         
16496         el.dom.removeChild(this.nodes[index]);
16497         this.updateIndexes(index);
16498     },
16499
16500     /**
16501      * Refresh an individual node.
16502      * @param {Number} index
16503      */
16504     refreshNode : function(index){
16505         this.onUpdate(this.store, this.store.getAt(index));
16506     },
16507
16508     updateIndexes : function(startIndex, endIndex){
16509         var ns = this.nodes;
16510         startIndex = startIndex || 0;
16511         endIndex = endIndex || ns.length - 1;
16512         for(var i = startIndex; i <= endIndex; i++){
16513             ns[i].nodeIndex = i;
16514         }
16515     },
16516
16517     /**
16518      * Changes the data store this view uses and refresh the view.
16519      * @param {Store} store
16520      */
16521     setStore : function(store, initial){
16522         if(!initial && this.store){
16523             this.store.un("datachanged", this.refresh);
16524             this.store.un("add", this.onAdd);
16525             this.store.un("remove", this.onRemove);
16526             this.store.un("update", this.onUpdate);
16527             this.store.un("clear", this.refresh);
16528             this.store.un("beforeload", this.onBeforeLoad);
16529             this.store.un("load", this.onLoad);
16530             this.store.un("loadexception", this.onLoad);
16531         }
16532         if(store){
16533           
16534             store.on("datachanged", this.refresh, this);
16535             store.on("add", this.onAdd, this);
16536             store.on("remove", this.onRemove, this);
16537             store.on("update", this.onUpdate, this);
16538             store.on("clear", this.refresh, this);
16539             store.on("beforeload", this.onBeforeLoad, this);
16540             store.on("load", this.onLoad, this);
16541             store.on("loadexception", this.onLoad, this);
16542         }
16543         
16544         if(store){
16545             this.refresh();
16546         }
16547     },
16548     /**
16549      * onbeforeLoad - masks the loading area.
16550      *
16551      */
16552     onBeforeLoad : function(store,opts)
16553     {
16554          //Roo.log('onBeforeLoad');   
16555         if (!opts.add) {
16556             this.el.update("");
16557         }
16558         this.el.mask(this.mask ? this.mask : "Loading" ); 
16559     },
16560     onLoad : function ()
16561     {
16562         this.el.unmask();
16563     },
16564     
16565
16566     /**
16567      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16568      * @param {HTMLElement} node
16569      * @return {HTMLElement} The template node
16570      */
16571     findItemFromChild : function(node){
16572         var el = this.dataName  ?
16573             this.el.child('.roo-tpl-' + this.dataName,true) :
16574             this.el.dom; 
16575         
16576         if(!node || node.parentNode == el){
16577                     return node;
16578             }
16579             var p = node.parentNode;
16580             while(p && p != el){
16581             if(p.parentNode == el){
16582                 return p;
16583             }
16584             p = p.parentNode;
16585         }
16586             return null;
16587     },
16588
16589     /** @ignore */
16590     onClick : function(e){
16591         var item = this.findItemFromChild(e.getTarget());
16592         if(item){
16593             var index = this.indexOf(item);
16594             if(this.onItemClick(item, index, e) !== false){
16595                 this.fireEvent("click", this, index, item, e);
16596             }
16597         }else{
16598             this.clearSelections();
16599         }
16600     },
16601
16602     /** @ignore */
16603     onContextMenu : function(e){
16604         var item = this.findItemFromChild(e.getTarget());
16605         if(item){
16606             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16607         }
16608     },
16609
16610     /** @ignore */
16611     onDblClick : function(e){
16612         var item = this.findItemFromChild(e.getTarget());
16613         if(item){
16614             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16615         }
16616     },
16617
16618     onItemClick : function(item, index, e)
16619     {
16620         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16621             return false;
16622         }
16623         if (this.toggleSelect) {
16624             var m = this.isSelected(item) ? 'unselect' : 'select';
16625             //Roo.log(m);
16626             var _t = this;
16627             _t[m](item, true, false);
16628             return true;
16629         }
16630         if(this.multiSelect || this.singleSelect){
16631             if(this.multiSelect && e.shiftKey && this.lastSelection){
16632                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16633             }else{
16634                 this.select(item, this.multiSelect && e.ctrlKey);
16635                 this.lastSelection = item;
16636             }
16637             
16638             if(!this.tickable){
16639                 e.preventDefault();
16640             }
16641             
16642         }
16643         return true;
16644     },
16645
16646     /**
16647      * Get the number of selected nodes.
16648      * @return {Number}
16649      */
16650     getSelectionCount : function(){
16651         return this.selections.length;
16652     },
16653
16654     /**
16655      * Get the currently selected nodes.
16656      * @return {Array} An array of HTMLElements
16657      */
16658     getSelectedNodes : function(){
16659         return this.selections;
16660     },
16661
16662     /**
16663      * Get the indexes of the selected nodes.
16664      * @return {Array}
16665      */
16666     getSelectedIndexes : function(){
16667         var indexes = [], s = this.selections;
16668         for(var i = 0, len = s.length; i < len; i++){
16669             indexes.push(s[i].nodeIndex);
16670         }
16671         return indexes;
16672     },
16673
16674     /**
16675      * Clear all selections
16676      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16677      */
16678     clearSelections : function(suppressEvent){
16679         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16680             this.cmp.elements = this.selections;
16681             this.cmp.removeClass(this.selectedClass);
16682             this.selections = [];
16683             if(!suppressEvent){
16684                 this.fireEvent("selectionchange", this, this.selections);
16685             }
16686         }
16687     },
16688
16689     /**
16690      * Returns true if the passed node is selected
16691      * @param {HTMLElement/Number} node The node or node index
16692      * @return {Boolean}
16693      */
16694     isSelected : function(node){
16695         var s = this.selections;
16696         if(s.length < 1){
16697             return false;
16698         }
16699         node = this.getNode(node);
16700         return s.indexOf(node) !== -1;
16701     },
16702
16703     /**
16704      * Selects nodes.
16705      * @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
16706      * @param {Boolean} keepExisting (optional) true to keep existing selections
16707      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16708      */
16709     select : function(nodeInfo, keepExisting, suppressEvent){
16710         if(nodeInfo instanceof Array){
16711             if(!keepExisting){
16712                 this.clearSelections(true);
16713             }
16714             for(var i = 0, len = nodeInfo.length; i < len; i++){
16715                 this.select(nodeInfo[i], true, true);
16716             }
16717             return;
16718         } 
16719         var node = this.getNode(nodeInfo);
16720         if(!node || this.isSelected(node)){
16721             return; // already selected.
16722         }
16723         if(!keepExisting){
16724             this.clearSelections(true);
16725         }
16726         
16727         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16728             Roo.fly(node).addClass(this.selectedClass);
16729             this.selections.push(node);
16730             if(!suppressEvent){
16731                 this.fireEvent("selectionchange", this, this.selections);
16732             }
16733         }
16734         
16735         
16736     },
16737       /**
16738      * Unselects nodes.
16739      * @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
16740      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16741      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16742      */
16743     unselect : function(nodeInfo, keepExisting, suppressEvent)
16744     {
16745         if(nodeInfo instanceof Array){
16746             Roo.each(this.selections, function(s) {
16747                 this.unselect(s, nodeInfo);
16748             }, this);
16749             return;
16750         }
16751         var node = this.getNode(nodeInfo);
16752         if(!node || !this.isSelected(node)){
16753             //Roo.log("not selected");
16754             return; // not selected.
16755         }
16756         // fireevent???
16757         var ns = [];
16758         Roo.each(this.selections, function(s) {
16759             if (s == node ) {
16760                 Roo.fly(node).removeClass(this.selectedClass);
16761
16762                 return;
16763             }
16764             ns.push(s);
16765         },this);
16766         
16767         this.selections= ns;
16768         this.fireEvent("selectionchange", this, this.selections);
16769     },
16770
16771     /**
16772      * Gets a template node.
16773      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16774      * @return {HTMLElement} The node or null if it wasn't found
16775      */
16776     getNode : function(nodeInfo){
16777         if(typeof nodeInfo == "string"){
16778             return document.getElementById(nodeInfo);
16779         }else if(typeof nodeInfo == "number"){
16780             return this.nodes[nodeInfo];
16781         }
16782         return nodeInfo;
16783     },
16784
16785     /**
16786      * Gets a range template nodes.
16787      * @param {Number} startIndex
16788      * @param {Number} endIndex
16789      * @return {Array} An array of nodes
16790      */
16791     getNodes : function(start, end){
16792         var ns = this.nodes;
16793         start = start || 0;
16794         end = typeof end == "undefined" ? ns.length - 1 : end;
16795         var nodes = [];
16796         if(start <= end){
16797             for(var i = start; i <= end; i++){
16798                 nodes.push(ns[i]);
16799             }
16800         } else{
16801             for(var i = start; i >= end; i--){
16802                 nodes.push(ns[i]);
16803             }
16804         }
16805         return nodes;
16806     },
16807
16808     /**
16809      * Finds the index of the passed node
16810      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16811      * @return {Number} The index of the node or -1
16812      */
16813     indexOf : function(node){
16814         node = this.getNode(node);
16815         if(typeof node.nodeIndex == "number"){
16816             return node.nodeIndex;
16817         }
16818         var ns = this.nodes;
16819         for(var i = 0, len = ns.length; i < len; i++){
16820             if(ns[i] == node){
16821                 return i;
16822             }
16823         }
16824         return -1;
16825     }
16826 });
16827 /*
16828  * - LGPL
16829  *
16830  * based on jquery fullcalendar
16831  * 
16832  */
16833
16834 Roo.bootstrap = Roo.bootstrap || {};
16835 /**
16836  * @class Roo.bootstrap.Calendar
16837  * @extends Roo.bootstrap.Component
16838  * Bootstrap Calendar class
16839  * @cfg {Boolean} loadMask (true|false) default false
16840  * @cfg {Object} header generate the user specific header of the calendar, default false
16841
16842  * @constructor
16843  * Create a new Container
16844  * @param {Object} config The config object
16845  */
16846
16847
16848
16849 Roo.bootstrap.Calendar = function(config){
16850     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16851      this.addEvents({
16852         /**
16853              * @event select
16854              * Fires when a date is selected
16855              * @param {DatePicker} this
16856              * @param {Date} date The selected date
16857              */
16858         'select': true,
16859         /**
16860              * @event monthchange
16861              * Fires when the displayed month changes 
16862              * @param {DatePicker} this
16863              * @param {Date} date The selected month
16864              */
16865         'monthchange': true,
16866         /**
16867              * @event evententer
16868              * Fires when mouse over an event
16869              * @param {Calendar} this
16870              * @param {event} Event
16871              */
16872         'evententer': true,
16873         /**
16874              * @event eventleave
16875              * Fires when the mouse leaves an
16876              * @param {Calendar} this
16877              * @param {event}
16878              */
16879         'eventleave': true,
16880         /**
16881              * @event eventclick
16882              * Fires when the mouse click an
16883              * @param {Calendar} this
16884              * @param {event}
16885              */
16886         'eventclick': true
16887         
16888     });
16889
16890 };
16891
16892 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16893     
16894      /**
16895      * @cfg {Number} startDay
16896      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16897      */
16898     startDay : 0,
16899     
16900     loadMask : false,
16901     
16902     header : false,
16903       
16904     getAutoCreate : function(){
16905         
16906         
16907         var fc_button = function(name, corner, style, content ) {
16908             return Roo.apply({},{
16909                 tag : 'span',
16910                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16911                          (corner.length ?
16912                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16913                             ''
16914                         ),
16915                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16916                 unselectable: 'on'
16917             });
16918         };
16919         
16920         var header = {};
16921         
16922         if(!this.header){
16923             header = {
16924                 tag : 'table',
16925                 cls : 'fc-header',
16926                 style : 'width:100%',
16927                 cn : [
16928                     {
16929                         tag: 'tr',
16930                         cn : [
16931                             {
16932                                 tag : 'td',
16933                                 cls : 'fc-header-left',
16934                                 cn : [
16935                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16936                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16937                                     { tag: 'span', cls: 'fc-header-space' },
16938                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16939
16940
16941                                 ]
16942                             },
16943
16944                             {
16945                                 tag : 'td',
16946                                 cls : 'fc-header-center',
16947                                 cn : [
16948                                     {
16949                                         tag: 'span',
16950                                         cls: 'fc-header-title',
16951                                         cn : {
16952                                             tag: 'H2',
16953                                             html : 'month / year'
16954                                         }
16955                                     }
16956
16957                                 ]
16958                             },
16959                             {
16960                                 tag : 'td',
16961                                 cls : 'fc-header-right',
16962                                 cn : [
16963                               /*      fc_button('month', 'left', '', 'month' ),
16964                                     fc_button('week', '', '', 'week' ),
16965                                     fc_button('day', 'right', '', 'day' )
16966                                 */    
16967
16968                                 ]
16969                             }
16970
16971                         ]
16972                     }
16973                 ]
16974             };
16975         }
16976         
16977         header = this.header;
16978         
16979        
16980         var cal_heads = function() {
16981             var ret = [];
16982             // fixme - handle this.
16983             
16984             for (var i =0; i < Date.dayNames.length; i++) {
16985                 var d = Date.dayNames[i];
16986                 ret.push({
16987                     tag: 'th',
16988                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16989                     html : d.substring(0,3)
16990                 });
16991                 
16992             }
16993             ret[0].cls += ' fc-first';
16994             ret[6].cls += ' fc-last';
16995             return ret;
16996         };
16997         var cal_cell = function(n) {
16998             return  {
16999                 tag: 'td',
17000                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
17001                 cn : [
17002                     {
17003                         cn : [
17004                             {
17005                                 cls: 'fc-day-number',
17006                                 html: 'D'
17007                             },
17008                             {
17009                                 cls: 'fc-day-content',
17010                              
17011                                 cn : [
17012                                      {
17013                                         style: 'position: relative;' // height: 17px;
17014                                     }
17015                                 ]
17016                             }
17017                             
17018                             
17019                         ]
17020                     }
17021                 ]
17022                 
17023             }
17024         };
17025         var cal_rows = function() {
17026             
17027             var ret = [];
17028             for (var r = 0; r < 6; r++) {
17029                 var row= {
17030                     tag : 'tr',
17031                     cls : 'fc-week',
17032                     cn : []
17033                 };
17034                 
17035                 for (var i =0; i < Date.dayNames.length; i++) {
17036                     var d = Date.dayNames[i];
17037                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
17038
17039                 }
17040                 row.cn[0].cls+=' fc-first';
17041                 row.cn[0].cn[0].style = 'min-height:90px';
17042                 row.cn[6].cls+=' fc-last';
17043                 ret.push(row);
17044                 
17045             }
17046             ret[0].cls += ' fc-first';
17047             ret[4].cls += ' fc-prev-last';
17048             ret[5].cls += ' fc-last';
17049             return ret;
17050             
17051         };
17052         
17053         var cal_table = {
17054             tag: 'table',
17055             cls: 'fc-border-separate',
17056             style : 'width:100%',
17057             cellspacing  : 0,
17058             cn : [
17059                 { 
17060                     tag: 'thead',
17061                     cn : [
17062                         { 
17063                             tag: 'tr',
17064                             cls : 'fc-first fc-last',
17065                             cn : cal_heads()
17066                         }
17067                     ]
17068                 },
17069                 { 
17070                     tag: 'tbody',
17071                     cn : cal_rows()
17072                 }
17073                   
17074             ]
17075         };
17076          
17077          var cfg = {
17078             cls : 'fc fc-ltr',
17079             cn : [
17080                 header,
17081                 {
17082                     cls : 'fc-content',
17083                     style : "position: relative;",
17084                     cn : [
17085                         {
17086                             cls : 'fc-view fc-view-month fc-grid',
17087                             style : 'position: relative',
17088                             unselectable : 'on',
17089                             cn : [
17090                                 {
17091                                     cls : 'fc-event-container',
17092                                     style : 'position:absolute;z-index:8;top:0;left:0;'
17093                                 },
17094                                 cal_table
17095                             ]
17096                         }
17097                     ]
17098     
17099                 }
17100            ] 
17101             
17102         };
17103         
17104          
17105         
17106         return cfg;
17107     },
17108     
17109     
17110     initEvents : function()
17111     {
17112         if(!this.store){
17113             throw "can not find store for calendar";
17114         }
17115         
17116         var mark = {
17117             tag: "div",
17118             cls:"x-dlg-mask",
17119             style: "text-align:center",
17120             cn: [
17121                 {
17122                     tag: "div",
17123                     style: "background-color:white;width:50%;margin:250 auto",
17124                     cn: [
17125                         {
17126                             tag: "img",
17127                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
17128                         },
17129                         {
17130                             tag: "span",
17131                             html: "Loading"
17132                         }
17133                         
17134                     ]
17135                 }
17136             ]
17137         };
17138         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17139         
17140         var size = this.el.select('.fc-content', true).first().getSize();
17141         this.maskEl.setSize(size.width, size.height);
17142         this.maskEl.enableDisplayMode("block");
17143         if(!this.loadMask){
17144             this.maskEl.hide();
17145         }
17146         
17147         this.store = Roo.factory(this.store, Roo.data);
17148         this.store.on('load', this.onLoad, this);
17149         this.store.on('beforeload', this.onBeforeLoad, this);
17150         
17151         this.resize();
17152         
17153         this.cells = this.el.select('.fc-day',true);
17154         //Roo.log(this.cells);
17155         this.textNodes = this.el.query('.fc-day-number');
17156         this.cells.addClassOnOver('fc-state-hover');
17157         
17158         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17159         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17160         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17161         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17162         
17163         this.on('monthchange', this.onMonthChange, this);
17164         
17165         this.update(new Date().clearTime());
17166     },
17167     
17168     resize : function() {
17169         var sz  = this.el.getSize();
17170         
17171         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17172         this.el.select('.fc-day-content div',true).setHeight(34);
17173     },
17174     
17175     
17176     // private
17177     showPrevMonth : function(e){
17178         this.update(this.activeDate.add("mo", -1));
17179     },
17180     showToday : function(e){
17181         this.update(new Date().clearTime());
17182     },
17183     // private
17184     showNextMonth : function(e){
17185         this.update(this.activeDate.add("mo", 1));
17186     },
17187
17188     // private
17189     showPrevYear : function(){
17190         this.update(this.activeDate.add("y", -1));
17191     },
17192
17193     // private
17194     showNextYear : function(){
17195         this.update(this.activeDate.add("y", 1));
17196     },
17197
17198     
17199    // private
17200     update : function(date)
17201     {
17202         var vd = this.activeDate;
17203         this.activeDate = date;
17204 //        if(vd && this.el){
17205 //            var t = date.getTime();
17206 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17207 //                Roo.log('using add remove');
17208 //                
17209 //                this.fireEvent('monthchange', this, date);
17210 //                
17211 //                this.cells.removeClass("fc-state-highlight");
17212 //                this.cells.each(function(c){
17213 //                   if(c.dateValue == t){
17214 //                       c.addClass("fc-state-highlight");
17215 //                       setTimeout(function(){
17216 //                            try{c.dom.firstChild.focus();}catch(e){}
17217 //                       }, 50);
17218 //                       return false;
17219 //                   }
17220 //                   return true;
17221 //                });
17222 //                return;
17223 //            }
17224 //        }
17225         
17226         var days = date.getDaysInMonth();
17227         
17228         var firstOfMonth = date.getFirstDateOfMonth();
17229         var startingPos = firstOfMonth.getDay()-this.startDay;
17230         
17231         if(startingPos < this.startDay){
17232             startingPos += 7;
17233         }
17234         
17235         var pm = date.add(Date.MONTH, -1);
17236         var prevStart = pm.getDaysInMonth()-startingPos;
17237 //        
17238         this.cells = this.el.select('.fc-day',true);
17239         this.textNodes = this.el.query('.fc-day-number');
17240         this.cells.addClassOnOver('fc-state-hover');
17241         
17242         var cells = this.cells.elements;
17243         var textEls = this.textNodes;
17244         
17245         Roo.each(cells, function(cell){
17246             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17247         });
17248         
17249         days += startingPos;
17250
17251         // convert everything to numbers so it's fast
17252         var day = 86400000;
17253         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17254         //Roo.log(d);
17255         //Roo.log(pm);
17256         //Roo.log(prevStart);
17257         
17258         var today = new Date().clearTime().getTime();
17259         var sel = date.clearTime().getTime();
17260         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17261         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17262         var ddMatch = this.disabledDatesRE;
17263         var ddText = this.disabledDatesText;
17264         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17265         var ddaysText = this.disabledDaysText;
17266         var format = this.format;
17267         
17268         var setCellClass = function(cal, cell){
17269             cell.row = 0;
17270             cell.events = [];
17271             cell.more = [];
17272             //Roo.log('set Cell Class');
17273             cell.title = "";
17274             var t = d.getTime();
17275             
17276             //Roo.log(d);
17277             
17278             cell.dateValue = t;
17279             if(t == today){
17280                 cell.className += " fc-today";
17281                 cell.className += " fc-state-highlight";
17282                 cell.title = cal.todayText;
17283             }
17284             if(t == sel){
17285                 // disable highlight in other month..
17286                 //cell.className += " fc-state-highlight";
17287                 
17288             }
17289             // disabling
17290             if(t < min) {
17291                 cell.className = " fc-state-disabled";
17292                 cell.title = cal.minText;
17293                 return;
17294             }
17295             if(t > max) {
17296                 cell.className = " fc-state-disabled";
17297                 cell.title = cal.maxText;
17298                 return;
17299             }
17300             if(ddays){
17301                 if(ddays.indexOf(d.getDay()) != -1){
17302                     cell.title = ddaysText;
17303                     cell.className = " fc-state-disabled";
17304                 }
17305             }
17306             if(ddMatch && format){
17307                 var fvalue = d.dateFormat(format);
17308                 if(ddMatch.test(fvalue)){
17309                     cell.title = ddText.replace("%0", fvalue);
17310                     cell.className = " fc-state-disabled";
17311                 }
17312             }
17313             
17314             if (!cell.initialClassName) {
17315                 cell.initialClassName = cell.dom.className;
17316             }
17317             
17318             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17319         };
17320
17321         var i = 0;
17322         
17323         for(; i < startingPos; i++) {
17324             textEls[i].innerHTML = (++prevStart);
17325             d.setDate(d.getDate()+1);
17326             
17327             cells[i].className = "fc-past fc-other-month";
17328             setCellClass(this, cells[i]);
17329         }
17330         
17331         var intDay = 0;
17332         
17333         for(; i < days; i++){
17334             intDay = i - startingPos + 1;
17335             textEls[i].innerHTML = (intDay);
17336             d.setDate(d.getDate()+1);
17337             
17338             cells[i].className = ''; // "x-date-active";
17339             setCellClass(this, cells[i]);
17340         }
17341         var extraDays = 0;
17342         
17343         for(; i < 42; i++) {
17344             textEls[i].innerHTML = (++extraDays);
17345             d.setDate(d.getDate()+1);
17346             
17347             cells[i].className = "fc-future fc-other-month";
17348             setCellClass(this, cells[i]);
17349         }
17350         
17351         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17352         
17353         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17354         
17355         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17356         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17357         
17358         if(totalRows != 6){
17359             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17360             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17361         }
17362         
17363         this.fireEvent('monthchange', this, date);
17364         
17365         
17366         /*
17367         if(!this.internalRender){
17368             var main = this.el.dom.firstChild;
17369             var w = main.offsetWidth;
17370             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17371             Roo.fly(main).setWidth(w);
17372             this.internalRender = true;
17373             // opera does not respect the auto grow header center column
17374             // then, after it gets a width opera refuses to recalculate
17375             // without a second pass
17376             if(Roo.isOpera && !this.secondPass){
17377                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17378                 this.secondPass = true;
17379                 this.update.defer(10, this, [date]);
17380             }
17381         }
17382         */
17383         
17384     },
17385     
17386     findCell : function(dt) {
17387         dt = dt.clearTime().getTime();
17388         var ret = false;
17389         this.cells.each(function(c){
17390             //Roo.log("check " +c.dateValue + '?=' + dt);
17391             if(c.dateValue == dt){
17392                 ret = c;
17393                 return false;
17394             }
17395             return true;
17396         });
17397         
17398         return ret;
17399     },
17400     
17401     findCells : function(ev) {
17402         var s = ev.start.clone().clearTime().getTime();
17403        // Roo.log(s);
17404         var e= ev.end.clone().clearTime().getTime();
17405        // Roo.log(e);
17406         var ret = [];
17407         this.cells.each(function(c){
17408              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17409             
17410             if(c.dateValue > e){
17411                 return ;
17412             }
17413             if(c.dateValue < s){
17414                 return ;
17415             }
17416             ret.push(c);
17417         });
17418         
17419         return ret;    
17420     },
17421     
17422 //    findBestRow: function(cells)
17423 //    {
17424 //        var ret = 0;
17425 //        
17426 //        for (var i =0 ; i < cells.length;i++) {
17427 //            ret  = Math.max(cells[i].rows || 0,ret);
17428 //        }
17429 //        return ret;
17430 //        
17431 //    },
17432     
17433     
17434     addItem : function(ev)
17435     {
17436         // look for vertical location slot in
17437         var cells = this.findCells(ev);
17438         
17439 //        ev.row = this.findBestRow(cells);
17440         
17441         // work out the location.
17442         
17443         var crow = false;
17444         var rows = [];
17445         for(var i =0; i < cells.length; i++) {
17446             
17447             cells[i].row = cells[0].row;
17448             
17449             if(i == 0){
17450                 cells[i].row = cells[i].row + 1;
17451             }
17452             
17453             if (!crow) {
17454                 crow = {
17455                     start : cells[i],
17456                     end :  cells[i]
17457                 };
17458                 continue;
17459             }
17460             if (crow.start.getY() == cells[i].getY()) {
17461                 // on same row.
17462                 crow.end = cells[i];
17463                 continue;
17464             }
17465             // different row.
17466             rows.push(crow);
17467             crow = {
17468                 start: cells[i],
17469                 end : cells[i]
17470             };
17471             
17472         }
17473         
17474         rows.push(crow);
17475         ev.els = [];
17476         ev.rows = rows;
17477         ev.cells = cells;
17478         
17479         cells[0].events.push(ev);
17480         
17481         this.calevents.push(ev);
17482     },
17483     
17484     clearEvents: function() {
17485         
17486         if(!this.calevents){
17487             return;
17488         }
17489         
17490         Roo.each(this.cells.elements, function(c){
17491             c.row = 0;
17492             c.events = [];
17493             c.more = [];
17494         });
17495         
17496         Roo.each(this.calevents, function(e) {
17497             Roo.each(e.els, function(el) {
17498                 el.un('mouseenter' ,this.onEventEnter, this);
17499                 el.un('mouseleave' ,this.onEventLeave, this);
17500                 el.remove();
17501             },this);
17502         },this);
17503         
17504         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17505             e.remove();
17506         });
17507         
17508     },
17509     
17510     renderEvents: function()
17511     {   
17512         var _this = this;
17513         
17514         this.cells.each(function(c) {
17515             
17516             if(c.row < 5){
17517                 return;
17518             }
17519             
17520             var ev = c.events;
17521             
17522             var r = 4;
17523             if(c.row != c.events.length){
17524                 r = 4 - (4 - (c.row - c.events.length));
17525             }
17526             
17527             c.events = ev.slice(0, r);
17528             c.more = ev.slice(r);
17529             
17530             if(c.more.length && c.more.length == 1){
17531                 c.events.push(c.more.pop());
17532             }
17533             
17534             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17535             
17536         });
17537             
17538         this.cells.each(function(c) {
17539             
17540             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17541             
17542             
17543             for (var e = 0; e < c.events.length; e++){
17544                 var ev = c.events[e];
17545                 var rows = ev.rows;
17546                 
17547                 for(var i = 0; i < rows.length; i++) {
17548                 
17549                     // how many rows should it span..
17550
17551                     var  cfg = {
17552                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17553                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17554
17555                         unselectable : "on",
17556                         cn : [
17557                             {
17558                                 cls: 'fc-event-inner',
17559                                 cn : [
17560     //                                {
17561     //                                  tag:'span',
17562     //                                  cls: 'fc-event-time',
17563     //                                  html : cells.length > 1 ? '' : ev.time
17564     //                                },
17565                                     {
17566                                       tag:'span',
17567                                       cls: 'fc-event-title',
17568                                       html : String.format('{0}', ev.title)
17569                                     }
17570
17571
17572                                 ]
17573                             },
17574                             {
17575                                 cls: 'ui-resizable-handle ui-resizable-e',
17576                                 html : '&nbsp;&nbsp;&nbsp'
17577                             }
17578
17579                         ]
17580                     };
17581
17582                     if (i == 0) {
17583                         cfg.cls += ' fc-event-start';
17584                     }
17585                     if ((i+1) == rows.length) {
17586                         cfg.cls += ' fc-event-end';
17587                     }
17588
17589                     var ctr = _this.el.select('.fc-event-container',true).first();
17590                     var cg = ctr.createChild(cfg);
17591
17592                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17593                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17594
17595                     var r = (c.more.length) ? 1 : 0;
17596                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17597                     cg.setWidth(ebox.right - sbox.x -2);
17598
17599                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17600                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17601                     cg.on('click', _this.onEventClick, _this, ev);
17602
17603                     ev.els.push(cg);
17604                     
17605                 }
17606                 
17607             }
17608             
17609             
17610             if(c.more.length){
17611                 var  cfg = {
17612                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17613                     style : 'position: absolute',
17614                     unselectable : "on",
17615                     cn : [
17616                         {
17617                             cls: 'fc-event-inner',
17618                             cn : [
17619                                 {
17620                                   tag:'span',
17621                                   cls: 'fc-event-title',
17622                                   html : 'More'
17623                                 }
17624
17625
17626                             ]
17627                         },
17628                         {
17629                             cls: 'ui-resizable-handle ui-resizable-e',
17630                             html : '&nbsp;&nbsp;&nbsp'
17631                         }
17632
17633                     ]
17634                 };
17635
17636                 var ctr = _this.el.select('.fc-event-container',true).first();
17637                 var cg = ctr.createChild(cfg);
17638
17639                 var sbox = c.select('.fc-day-content',true).first().getBox();
17640                 var ebox = c.select('.fc-day-content',true).first().getBox();
17641                 //Roo.log(cg);
17642                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17643                 cg.setWidth(ebox.right - sbox.x -2);
17644
17645                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17646                 
17647             }
17648             
17649         });
17650         
17651         
17652         
17653     },
17654     
17655     onEventEnter: function (e, el,event,d) {
17656         this.fireEvent('evententer', this, el, event);
17657     },
17658     
17659     onEventLeave: function (e, el,event,d) {
17660         this.fireEvent('eventleave', this, el, event);
17661     },
17662     
17663     onEventClick: function (e, el,event,d) {
17664         this.fireEvent('eventclick', this, el, event);
17665     },
17666     
17667     onMonthChange: function () {
17668         this.store.load();
17669     },
17670     
17671     onMoreEventClick: function(e, el, more)
17672     {
17673         var _this = this;
17674         
17675         this.calpopover.placement = 'right';
17676         this.calpopover.setTitle('More');
17677         
17678         this.calpopover.setContent('');
17679         
17680         var ctr = this.calpopover.el.select('.popover-content', true).first();
17681         
17682         Roo.each(more, function(m){
17683             var cfg = {
17684                 cls : 'fc-event-hori fc-event-draggable',
17685                 html : m.title
17686             };
17687             var cg = ctr.createChild(cfg);
17688             
17689             cg.on('click', _this.onEventClick, _this, m);
17690         });
17691         
17692         this.calpopover.show(el);
17693         
17694         
17695     },
17696     
17697     onLoad: function () 
17698     {   
17699         this.calevents = [];
17700         var cal = this;
17701         
17702         if(this.store.getCount() > 0){
17703             this.store.data.each(function(d){
17704                cal.addItem({
17705                     id : d.data.id,
17706                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17707                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17708                     time : d.data.start_time,
17709                     title : d.data.title,
17710                     description : d.data.description,
17711                     venue : d.data.venue
17712                 });
17713             });
17714         }
17715         
17716         this.renderEvents();
17717         
17718         if(this.calevents.length && this.loadMask){
17719             this.maskEl.hide();
17720         }
17721     },
17722     
17723     onBeforeLoad: function()
17724     {
17725         this.clearEvents();
17726         if(this.loadMask){
17727             this.maskEl.show();
17728         }
17729     }
17730 });
17731
17732  
17733  /*
17734  * - LGPL
17735  *
17736  * element
17737  * 
17738  */
17739
17740 /**
17741  * @class Roo.bootstrap.Popover
17742  * @extends Roo.bootstrap.Component
17743  * Bootstrap Popover class
17744  * @cfg {String} html contents of the popover   (or false to use children..)
17745  * @cfg {String} title of popover (or false to hide)
17746  * @cfg {String} placement how it is placed
17747  * @cfg {String} trigger click || hover (or false to trigger manually)
17748  * @cfg {String} over what (parent or false to trigger manually.)
17749  * @cfg {Number} delay - delay before showing
17750  
17751  * @constructor
17752  * Create a new Popover
17753  * @param {Object} config The config object
17754  */
17755
17756 Roo.bootstrap.Popover = function(config){
17757     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17758     
17759     this.addEvents({
17760         // raw events
17761          /**
17762          * @event show
17763          * After the popover show
17764          * 
17765          * @param {Roo.bootstrap.Popover} this
17766          */
17767         "show" : true,
17768         /**
17769          * @event hide
17770          * After the popover hide
17771          * 
17772          * @param {Roo.bootstrap.Popover} this
17773          */
17774         "hide" : true
17775     });
17776 };
17777
17778 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17779     
17780     title: 'Fill in a title',
17781     html: false,
17782     
17783     placement : 'right',
17784     trigger : 'hover', // hover
17785     
17786     delay : 0,
17787     
17788     over: 'parent',
17789     
17790     can_build_overlaid : false,
17791     
17792     getChildContainer : function()
17793     {
17794         return this.el.select('.popover-content',true).first();
17795     },
17796     
17797     getAutoCreate : function(){
17798          
17799         var cfg = {
17800            cls : 'popover roo-dynamic',
17801            style: 'display:block',
17802            cn : [
17803                 {
17804                     cls : 'arrow'
17805                 },
17806                 {
17807                     cls : 'popover-inner',
17808                     cn : [
17809                         {
17810                             tag: 'h3',
17811                             cls: 'popover-title popover-header',
17812                             html : this.title
17813                         },
17814                         {
17815                             cls : 'popover-content popover-body',
17816                             html : this.html
17817                         }
17818                     ]
17819                     
17820                 }
17821            ]
17822         };
17823         
17824         return cfg;
17825     },
17826     setTitle: function(str)
17827     {
17828         this.title = str;
17829         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17830     },
17831     setContent: function(str)
17832     {
17833         this.html = str;
17834         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17835     },
17836     // as it get's added to the bottom of the page.
17837     onRender : function(ct, position)
17838     {
17839         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17840         if(!this.el){
17841             var cfg = Roo.apply({},  this.getAutoCreate());
17842             cfg.id = Roo.id();
17843             
17844             if (this.cls) {
17845                 cfg.cls += ' ' + this.cls;
17846             }
17847             if (this.style) {
17848                 cfg.style = this.style;
17849             }
17850             //Roo.log("adding to ");
17851             this.el = Roo.get(document.body).createChild(cfg, position);
17852 //            Roo.log(this.el);
17853         }
17854         this.initEvents();
17855     },
17856     
17857     initEvents : function()
17858     {
17859         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17860         this.el.enableDisplayMode('block');
17861         this.el.hide();
17862         if (this.over === false) {
17863             return; 
17864         }
17865         if (this.triggers === false) {
17866             return;
17867         }
17868         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17869         var triggers = this.trigger ? this.trigger.split(' ') : [];
17870         Roo.each(triggers, function(trigger) {
17871         
17872             if (trigger == 'click') {
17873                 on_el.on('click', this.toggle, this);
17874             } else if (trigger != 'manual') {
17875                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17876                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17877       
17878                 on_el.on(eventIn  ,this.enter, this);
17879                 on_el.on(eventOut, this.leave, this);
17880             }
17881         }, this);
17882         
17883     },
17884     
17885     
17886     // private
17887     timeout : null,
17888     hoverState : null,
17889     
17890     toggle : function () {
17891         this.hoverState == 'in' ? this.leave() : this.enter();
17892     },
17893     
17894     enter : function () {
17895         
17896         clearTimeout(this.timeout);
17897     
17898         this.hoverState = 'in';
17899     
17900         if (!this.delay || !this.delay.show) {
17901             this.show();
17902             return;
17903         }
17904         var _t = this;
17905         this.timeout = setTimeout(function () {
17906             if (_t.hoverState == 'in') {
17907                 _t.show();
17908             }
17909         }, this.delay.show)
17910     },
17911     
17912     leave : function() {
17913         clearTimeout(this.timeout);
17914     
17915         this.hoverState = 'out';
17916     
17917         if (!this.delay || !this.delay.hide) {
17918             this.hide();
17919             return;
17920         }
17921         var _t = this;
17922         this.timeout = setTimeout(function () {
17923             if (_t.hoverState == 'out') {
17924                 _t.hide();
17925             }
17926         }, this.delay.hide)
17927     },
17928     
17929     show : function (on_el)
17930     {
17931         if (!on_el) {
17932             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17933         }
17934         
17935         // set content.
17936         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17937         if (this.html !== false) {
17938             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17939         }
17940         this.el.removeClass([
17941             'fade','top','bottom', 'left', 'right','in',
17942             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
17943         ]);
17944         if (!this.title.length) {
17945             this.el.select('.popover-title',true).hide();
17946         }
17947         
17948         var placement = typeof this.placement == 'function' ?
17949             this.placement.call(this, this.el, on_el) :
17950             this.placement;
17951             
17952         var autoToken = /\s?auto?\s?/i;
17953         var autoPlace = autoToken.test(placement);
17954         if (autoPlace) {
17955             placement = placement.replace(autoToken, '') || 'top';
17956         }
17957         
17958         //this.el.detach()
17959         //this.el.setXY([0,0]);
17960         this.el.show();
17961         this.el.dom.style.display='block';
17962         this.el.addClass(placement);
17963         
17964         //this.el.appendTo(on_el);
17965         
17966         var p = this.getPosition();
17967         var box = this.el.getBox();
17968         
17969         if (autoPlace) {
17970             // fixme..
17971         }
17972         var align = Roo.bootstrap.Popover.alignment[placement];
17973         
17974 //        Roo.log(align);
17975         this.el.alignTo(on_el, align[0],align[1]);
17976         //var arrow = this.el.select('.arrow',true).first();
17977         //arrow.set(align[2], 
17978         
17979         this.el.addClass('in');
17980         
17981         
17982         if (this.el.hasClass('fade')) {
17983             // fade it?
17984         }
17985         
17986         this.hoverState = 'in';
17987         
17988         this.fireEvent('show', this);
17989         
17990     },
17991     hide : function()
17992     {
17993         this.el.setXY([0,0]);
17994         this.el.removeClass('in');
17995         this.el.hide();
17996         this.hoverState = null;
17997         
17998         this.fireEvent('hide', this);
17999     }
18000     
18001 });
18002
18003 Roo.bootstrap.Popover.alignment = {
18004     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
18005     'right' : ['l-r', [10,0], 'left bs-popover-left'],
18006     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
18007     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
18008 };
18009
18010  /*
18011  * - LGPL
18012  *
18013  * Progress
18014  * 
18015  */
18016
18017 /**
18018  * @class Roo.bootstrap.Progress
18019  * @extends Roo.bootstrap.Component
18020  * Bootstrap Progress class
18021  * @cfg {Boolean} striped striped of the progress bar
18022  * @cfg {Boolean} active animated of the progress bar
18023  * 
18024  * 
18025  * @constructor
18026  * Create a new Progress
18027  * @param {Object} config The config object
18028  */
18029
18030 Roo.bootstrap.Progress = function(config){
18031     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
18032 };
18033
18034 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
18035     
18036     striped : false,
18037     active: false,
18038     
18039     getAutoCreate : function(){
18040         var cfg = {
18041             tag: 'div',
18042             cls: 'progress'
18043         };
18044         
18045         
18046         if(this.striped){
18047             cfg.cls += ' progress-striped';
18048         }
18049       
18050         if(this.active){
18051             cfg.cls += ' active';
18052         }
18053         
18054         
18055         return cfg;
18056     }
18057    
18058 });
18059
18060  
18061
18062  /*
18063  * - LGPL
18064  *
18065  * ProgressBar
18066  * 
18067  */
18068
18069 /**
18070  * @class Roo.bootstrap.ProgressBar
18071  * @extends Roo.bootstrap.Component
18072  * Bootstrap ProgressBar class
18073  * @cfg {Number} aria_valuenow aria-value now
18074  * @cfg {Number} aria_valuemin aria-value min
18075  * @cfg {Number} aria_valuemax aria-value max
18076  * @cfg {String} label label for the progress bar
18077  * @cfg {String} panel (success | info | warning | danger )
18078  * @cfg {String} role role of the progress bar
18079  * @cfg {String} sr_only text
18080  * 
18081  * 
18082  * @constructor
18083  * Create a new ProgressBar
18084  * @param {Object} config The config object
18085  */
18086
18087 Roo.bootstrap.ProgressBar = function(config){
18088     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
18089 };
18090
18091 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
18092     
18093     aria_valuenow : 0,
18094     aria_valuemin : 0,
18095     aria_valuemax : 100,
18096     label : false,
18097     panel : false,
18098     role : false,
18099     sr_only: false,
18100     
18101     getAutoCreate : function()
18102     {
18103         
18104         var cfg = {
18105             tag: 'div',
18106             cls: 'progress-bar',
18107             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18108         };
18109         
18110         if(this.sr_only){
18111             cfg.cn = {
18112                 tag: 'span',
18113                 cls: 'sr-only',
18114                 html: this.sr_only
18115             }
18116         }
18117         
18118         if(this.role){
18119             cfg.role = this.role;
18120         }
18121         
18122         if(this.aria_valuenow){
18123             cfg['aria-valuenow'] = this.aria_valuenow;
18124         }
18125         
18126         if(this.aria_valuemin){
18127             cfg['aria-valuemin'] = this.aria_valuemin;
18128         }
18129         
18130         if(this.aria_valuemax){
18131             cfg['aria-valuemax'] = this.aria_valuemax;
18132         }
18133         
18134         if(this.label && !this.sr_only){
18135             cfg.html = this.label;
18136         }
18137         
18138         if(this.panel){
18139             cfg.cls += ' progress-bar-' + this.panel;
18140         }
18141         
18142         return cfg;
18143     },
18144     
18145     update : function(aria_valuenow)
18146     {
18147         this.aria_valuenow = aria_valuenow;
18148         
18149         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18150     }
18151    
18152 });
18153
18154  
18155
18156  /*
18157  * - LGPL
18158  *
18159  * column
18160  * 
18161  */
18162
18163 /**
18164  * @class Roo.bootstrap.TabGroup
18165  * @extends Roo.bootstrap.Column
18166  * Bootstrap Column class
18167  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18168  * @cfg {Boolean} carousel true to make the group behave like a carousel
18169  * @cfg {Boolean} bullets show bullets for the panels
18170  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18171  * @cfg {Number} timer auto slide timer .. default 0 millisecond
18172  * @cfg {Boolean} showarrow (true|false) show arrow default true
18173  * 
18174  * @constructor
18175  * Create a new TabGroup
18176  * @param {Object} config The config object
18177  */
18178
18179 Roo.bootstrap.TabGroup = function(config){
18180     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18181     if (!this.navId) {
18182         this.navId = Roo.id();
18183     }
18184     this.tabs = [];
18185     Roo.bootstrap.TabGroup.register(this);
18186     
18187 };
18188
18189 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
18190     
18191     carousel : false,
18192     transition : false,
18193     bullets : 0,
18194     timer : 0,
18195     autoslide : false,
18196     slideFn : false,
18197     slideOnTouch : false,
18198     showarrow : true,
18199     
18200     getAutoCreate : function()
18201     {
18202         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18203         
18204         cfg.cls += ' tab-content';
18205         
18206         if (this.carousel) {
18207             cfg.cls += ' carousel slide';
18208             
18209             cfg.cn = [{
18210                cls : 'carousel-inner',
18211                cn : []
18212             }];
18213         
18214             if(this.bullets  && !Roo.isTouch){
18215                 
18216                 var bullets = {
18217                     cls : 'carousel-bullets',
18218                     cn : []
18219                 };
18220                
18221                 if(this.bullets_cls){
18222                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18223                 }
18224                 
18225                 bullets.cn.push({
18226                     cls : 'clear'
18227                 });
18228                 
18229                 cfg.cn[0].cn.push(bullets);
18230             }
18231             
18232             if(this.showarrow){
18233                 cfg.cn[0].cn.push({
18234                     tag : 'div',
18235                     class : 'carousel-arrow',
18236                     cn : [
18237                         {
18238                             tag : 'div',
18239                             class : 'carousel-prev',
18240                             cn : [
18241                                 {
18242                                     tag : 'i',
18243                                     class : 'fa fa-chevron-left'
18244                                 }
18245                             ]
18246                         },
18247                         {
18248                             tag : 'div',
18249                             class : 'carousel-next',
18250                             cn : [
18251                                 {
18252                                     tag : 'i',
18253                                     class : 'fa fa-chevron-right'
18254                                 }
18255                             ]
18256                         }
18257                     ]
18258                 });
18259             }
18260             
18261         }
18262         
18263         return cfg;
18264     },
18265     
18266     initEvents:  function()
18267     {
18268 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18269 //            this.el.on("touchstart", this.onTouchStart, this);
18270 //        }
18271         
18272         if(this.autoslide){
18273             var _this = this;
18274             
18275             this.slideFn = window.setInterval(function() {
18276                 _this.showPanelNext();
18277             }, this.timer);
18278         }
18279         
18280         if(this.showarrow){
18281             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18282             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18283         }
18284         
18285         
18286     },
18287     
18288 //    onTouchStart : function(e, el, o)
18289 //    {
18290 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18291 //            return;
18292 //        }
18293 //        
18294 //        this.showPanelNext();
18295 //    },
18296     
18297     
18298     getChildContainer : function()
18299     {
18300         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18301     },
18302     
18303     /**
18304     * register a Navigation item
18305     * @param {Roo.bootstrap.NavItem} the navitem to add
18306     */
18307     register : function(item)
18308     {
18309         this.tabs.push( item);
18310         item.navId = this.navId; // not really needed..
18311         this.addBullet();
18312     
18313     },
18314     
18315     getActivePanel : function()
18316     {
18317         var r = false;
18318         Roo.each(this.tabs, function(t) {
18319             if (t.active) {
18320                 r = t;
18321                 return false;
18322             }
18323             return null;
18324         });
18325         return r;
18326         
18327     },
18328     getPanelByName : function(n)
18329     {
18330         var r = false;
18331         Roo.each(this.tabs, function(t) {
18332             if (t.tabId == n) {
18333                 r = t;
18334                 return false;
18335             }
18336             return null;
18337         });
18338         return r;
18339     },
18340     indexOfPanel : function(p)
18341     {
18342         var r = false;
18343         Roo.each(this.tabs, function(t,i) {
18344             if (t.tabId == p.tabId) {
18345                 r = i;
18346                 return false;
18347             }
18348             return null;
18349         });
18350         return r;
18351     },
18352     /**
18353      * show a specific panel
18354      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18355      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18356      */
18357     showPanel : function (pan)
18358     {
18359         if(this.transition || typeof(pan) == 'undefined'){
18360             Roo.log("waiting for the transitionend");
18361             return false;
18362         }
18363         
18364         if (typeof(pan) == 'number') {
18365             pan = this.tabs[pan];
18366         }
18367         
18368         if (typeof(pan) == 'string') {
18369             pan = this.getPanelByName(pan);
18370         }
18371         
18372         var cur = this.getActivePanel();
18373         
18374         if(!pan || !cur){
18375             Roo.log('pan or acitve pan is undefined');
18376             return false;
18377         }
18378         
18379         if (pan.tabId == this.getActivePanel().tabId) {
18380             return true;
18381         }
18382         
18383         if (false === cur.fireEvent('beforedeactivate')) {
18384             return false;
18385         }
18386         
18387         if(this.bullets > 0 && !Roo.isTouch){
18388             this.setActiveBullet(this.indexOfPanel(pan));
18389         }
18390         
18391         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18392             
18393             //class="carousel-item carousel-item-next carousel-item-left"
18394             
18395             this.transition = true;
18396             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18397             var lr = dir == 'next' ? 'left' : 'right';
18398             pan.el.addClass(dir); // or prev
18399             pan.el.addClass('carousel-item-' + dir); // or prev
18400             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18401             cur.el.addClass(lr); // or right
18402             pan.el.addClass(lr);
18403             cur.el.addClass('carousel-item-' +lr); // or right
18404             pan.el.addClass('carousel-item-' +lr);
18405             
18406             
18407             var _this = this;
18408             cur.el.on('transitionend', function() {
18409                 Roo.log("trans end?");
18410                 
18411                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
18412                 pan.setActive(true);
18413                 
18414                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
18415                 cur.setActive(false);
18416                 
18417                 _this.transition = false;
18418                 
18419             }, this, { single:  true } );
18420             
18421             return true;
18422         }
18423         
18424         cur.setActive(false);
18425         pan.setActive(true);
18426         
18427         return true;
18428         
18429     },
18430     showPanelNext : function()
18431     {
18432         var i = this.indexOfPanel(this.getActivePanel());
18433         
18434         if (i >= this.tabs.length - 1 && !this.autoslide) {
18435             return;
18436         }
18437         
18438         if (i >= this.tabs.length - 1 && this.autoslide) {
18439             i = -1;
18440         }
18441         
18442         this.showPanel(this.tabs[i+1]);
18443     },
18444     
18445     showPanelPrev : function()
18446     {
18447         var i = this.indexOfPanel(this.getActivePanel());
18448         
18449         if (i  < 1 && !this.autoslide) {
18450             return;
18451         }
18452         
18453         if (i < 1 && this.autoslide) {
18454             i = this.tabs.length;
18455         }
18456         
18457         this.showPanel(this.tabs[i-1]);
18458     },
18459     
18460     
18461     addBullet: function()
18462     {
18463         if(!this.bullets || Roo.isTouch){
18464             return;
18465         }
18466         var ctr = this.el.select('.carousel-bullets',true).first();
18467         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18468         var bullet = ctr.createChild({
18469             cls : 'bullet bullet-' + i
18470         },ctr.dom.lastChild);
18471         
18472         
18473         var _this = this;
18474         
18475         bullet.on('click', (function(e, el, o, ii, t){
18476
18477             e.preventDefault();
18478
18479             this.showPanel(ii);
18480
18481             if(this.autoslide && this.slideFn){
18482                 clearInterval(this.slideFn);
18483                 this.slideFn = window.setInterval(function() {
18484                     _this.showPanelNext();
18485                 }, this.timer);
18486             }
18487
18488         }).createDelegate(this, [i, bullet], true));
18489                 
18490         
18491     },
18492      
18493     setActiveBullet : function(i)
18494     {
18495         if(Roo.isTouch){
18496             return;
18497         }
18498         
18499         Roo.each(this.el.select('.bullet', true).elements, function(el){
18500             el.removeClass('selected');
18501         });
18502
18503         var bullet = this.el.select('.bullet-' + i, true).first();
18504         
18505         if(!bullet){
18506             return;
18507         }
18508         
18509         bullet.addClass('selected');
18510     }
18511     
18512     
18513   
18514 });
18515
18516  
18517
18518  
18519  
18520 Roo.apply(Roo.bootstrap.TabGroup, {
18521     
18522     groups: {},
18523      /**
18524     * register a Navigation Group
18525     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18526     */
18527     register : function(navgrp)
18528     {
18529         this.groups[navgrp.navId] = navgrp;
18530         
18531     },
18532     /**
18533     * fetch a Navigation Group based on the navigation ID
18534     * if one does not exist , it will get created.
18535     * @param {string} the navgroup to add
18536     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18537     */
18538     get: function(navId) {
18539         if (typeof(this.groups[navId]) == 'undefined') {
18540             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18541         }
18542         return this.groups[navId] ;
18543     }
18544     
18545     
18546     
18547 });
18548
18549  /*
18550  * - LGPL
18551  *
18552  * TabPanel
18553  * 
18554  */
18555
18556 /**
18557  * @class Roo.bootstrap.TabPanel
18558  * @extends Roo.bootstrap.Component
18559  * Bootstrap TabPanel class
18560  * @cfg {Boolean} active panel active
18561  * @cfg {String} html panel content
18562  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18563  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18564  * @cfg {String} href click to link..
18565  * 
18566  * 
18567  * @constructor
18568  * Create a new TabPanel
18569  * @param {Object} config The config object
18570  */
18571
18572 Roo.bootstrap.TabPanel = function(config){
18573     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18574     this.addEvents({
18575         /**
18576              * @event changed
18577              * Fires when the active status changes
18578              * @param {Roo.bootstrap.TabPanel} this
18579              * @param {Boolean} state the new state
18580             
18581          */
18582         'changed': true,
18583         /**
18584              * @event beforedeactivate
18585              * Fires before a tab is de-activated - can be used to do validation on a form.
18586              * @param {Roo.bootstrap.TabPanel} this
18587              * @return {Boolean} false if there is an error
18588             
18589          */
18590         'beforedeactivate': true
18591      });
18592     
18593     this.tabId = this.tabId || Roo.id();
18594   
18595 };
18596
18597 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18598     
18599     active: false,
18600     html: false,
18601     tabId: false,
18602     navId : false,
18603     href : '',
18604     
18605     getAutoCreate : function(){
18606         
18607         
18608         var cfg = {
18609             tag: 'div',
18610             // item is needed for carousel - not sure if it has any effect otherwise
18611             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18612             html: this.html || ''
18613         };
18614         
18615         if(this.active){
18616             cfg.cls += ' active';
18617         }
18618         
18619         if(this.tabId){
18620             cfg.tabId = this.tabId;
18621         }
18622         
18623         
18624         
18625         return cfg;
18626     },
18627     
18628     initEvents:  function()
18629     {
18630         var p = this.parent();
18631         
18632         this.navId = this.navId || p.navId;
18633         
18634         if (typeof(this.navId) != 'undefined') {
18635             // not really needed.. but just in case.. parent should be a NavGroup.
18636             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18637             
18638             tg.register(this);
18639             
18640             var i = tg.tabs.length - 1;
18641             
18642             if(this.active && tg.bullets > 0 && i < tg.bullets){
18643                 tg.setActiveBullet(i);
18644             }
18645         }
18646         
18647         this.el.on('click', this.onClick, this);
18648         
18649         if(Roo.isTouch){
18650             this.el.on("touchstart", this.onTouchStart, this);
18651             this.el.on("touchmove", this.onTouchMove, this);
18652             this.el.on("touchend", this.onTouchEnd, this);
18653         }
18654         
18655     },
18656     
18657     onRender : function(ct, position)
18658     {
18659         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18660     },
18661     
18662     setActive : function(state)
18663     {
18664         Roo.log("panel - set active " + this.tabId + "=" + state);
18665         
18666         this.active = state;
18667         if (!state) {
18668             this.el.removeClass('active');
18669             
18670         } else  if (!this.el.hasClass('active')) {
18671             this.el.addClass('active');
18672         }
18673         
18674         this.fireEvent('changed', this, state);
18675     },
18676     
18677     onClick : function(e)
18678     {
18679         e.preventDefault();
18680         
18681         if(!this.href.length){
18682             return;
18683         }
18684         
18685         window.location.href = this.href;
18686     },
18687     
18688     startX : 0,
18689     startY : 0,
18690     endX : 0,
18691     endY : 0,
18692     swiping : false,
18693     
18694     onTouchStart : function(e)
18695     {
18696         this.swiping = false;
18697         
18698         this.startX = e.browserEvent.touches[0].clientX;
18699         this.startY = e.browserEvent.touches[0].clientY;
18700     },
18701     
18702     onTouchMove : function(e)
18703     {
18704         this.swiping = true;
18705         
18706         this.endX = e.browserEvent.touches[0].clientX;
18707         this.endY = e.browserEvent.touches[0].clientY;
18708     },
18709     
18710     onTouchEnd : function(e)
18711     {
18712         if(!this.swiping){
18713             this.onClick(e);
18714             return;
18715         }
18716         
18717         var tabGroup = this.parent();
18718         
18719         if(this.endX > this.startX){ // swiping right
18720             tabGroup.showPanelPrev();
18721             return;
18722         }
18723         
18724         if(this.startX > this.endX){ // swiping left
18725             tabGroup.showPanelNext();
18726             return;
18727         }
18728     }
18729     
18730     
18731 });
18732  
18733
18734  
18735
18736  /*
18737  * - LGPL
18738  *
18739  * DateField
18740  * 
18741  */
18742
18743 /**
18744  * @class Roo.bootstrap.DateField
18745  * @extends Roo.bootstrap.Input
18746  * Bootstrap DateField class
18747  * @cfg {Number} weekStart default 0
18748  * @cfg {String} viewMode default empty, (months|years)
18749  * @cfg {String} minViewMode default empty, (months|years)
18750  * @cfg {Number} startDate default -Infinity
18751  * @cfg {Number} endDate default Infinity
18752  * @cfg {Boolean} todayHighlight default false
18753  * @cfg {Boolean} todayBtn default false
18754  * @cfg {Boolean} calendarWeeks default false
18755  * @cfg {Object} daysOfWeekDisabled default empty
18756  * @cfg {Boolean} singleMode default false (true | false)
18757  * 
18758  * @cfg {Boolean} keyboardNavigation default true
18759  * @cfg {String} language default en
18760  * 
18761  * @constructor
18762  * Create a new DateField
18763  * @param {Object} config The config object
18764  */
18765
18766 Roo.bootstrap.DateField = function(config){
18767     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18768      this.addEvents({
18769             /**
18770              * @event show
18771              * Fires when this field show.
18772              * @param {Roo.bootstrap.DateField} this
18773              * @param {Mixed} date The date value
18774              */
18775             show : true,
18776             /**
18777              * @event show
18778              * Fires when this field hide.
18779              * @param {Roo.bootstrap.DateField} this
18780              * @param {Mixed} date The date value
18781              */
18782             hide : true,
18783             /**
18784              * @event select
18785              * Fires when select a date.
18786              * @param {Roo.bootstrap.DateField} this
18787              * @param {Mixed} date The date value
18788              */
18789             select : true,
18790             /**
18791              * @event beforeselect
18792              * Fires when before select a date.
18793              * @param {Roo.bootstrap.DateField} this
18794              * @param {Mixed} date The date value
18795              */
18796             beforeselect : true
18797         });
18798 };
18799
18800 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18801     
18802     /**
18803      * @cfg {String} format
18804      * The default date format string which can be overriden for localization support.  The format must be
18805      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18806      */
18807     format : "m/d/y",
18808     /**
18809      * @cfg {String} altFormats
18810      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18811      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18812      */
18813     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18814     
18815     weekStart : 0,
18816     
18817     viewMode : '',
18818     
18819     minViewMode : '',
18820     
18821     todayHighlight : false,
18822     
18823     todayBtn: false,
18824     
18825     language: 'en',
18826     
18827     keyboardNavigation: true,
18828     
18829     calendarWeeks: false,
18830     
18831     startDate: -Infinity,
18832     
18833     endDate: Infinity,
18834     
18835     daysOfWeekDisabled: [],
18836     
18837     _events: [],
18838     
18839     singleMode : false,
18840     
18841     UTCDate: function()
18842     {
18843         return new Date(Date.UTC.apply(Date, arguments));
18844     },
18845     
18846     UTCToday: function()
18847     {
18848         var today = new Date();
18849         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18850     },
18851     
18852     getDate: function() {
18853             var d = this.getUTCDate();
18854             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18855     },
18856     
18857     getUTCDate: function() {
18858             return this.date;
18859     },
18860     
18861     setDate: function(d) {
18862             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18863     },
18864     
18865     setUTCDate: function(d) {
18866             this.date = d;
18867             this.setValue(this.formatDate(this.date));
18868     },
18869         
18870     onRender: function(ct, position)
18871     {
18872         
18873         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18874         
18875         this.language = this.language || 'en';
18876         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18877         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18878         
18879         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18880         this.format = this.format || 'm/d/y';
18881         this.isInline = false;
18882         this.isInput = true;
18883         this.component = this.el.select('.add-on', true).first() || false;
18884         this.component = (this.component && this.component.length === 0) ? false : this.component;
18885         this.hasInput = this.component && this.inputEl().length;
18886         
18887         if (typeof(this.minViewMode === 'string')) {
18888             switch (this.minViewMode) {
18889                 case 'months':
18890                     this.minViewMode = 1;
18891                     break;
18892                 case 'years':
18893                     this.minViewMode = 2;
18894                     break;
18895                 default:
18896                     this.minViewMode = 0;
18897                     break;
18898             }
18899         }
18900         
18901         if (typeof(this.viewMode === 'string')) {
18902             switch (this.viewMode) {
18903                 case 'months':
18904                     this.viewMode = 1;
18905                     break;
18906                 case 'years':
18907                     this.viewMode = 2;
18908                     break;
18909                 default:
18910                     this.viewMode = 0;
18911                     break;
18912             }
18913         }
18914                 
18915         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18916         
18917 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18918         
18919         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18920         
18921         this.picker().on('mousedown', this.onMousedown, this);
18922         this.picker().on('click', this.onClick, this);
18923         
18924         this.picker().addClass('datepicker-dropdown');
18925         
18926         this.startViewMode = this.viewMode;
18927         
18928         if(this.singleMode){
18929             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18930                 v.setVisibilityMode(Roo.Element.DISPLAY);
18931                 v.hide();
18932             });
18933             
18934             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18935                 v.setStyle('width', '189px');
18936             });
18937         }
18938         
18939         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18940             if(!this.calendarWeeks){
18941                 v.remove();
18942                 return;
18943             }
18944             
18945             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18946             v.attr('colspan', function(i, val){
18947                 return parseInt(val) + 1;
18948             });
18949         });
18950                         
18951         
18952         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18953         
18954         this.setStartDate(this.startDate);
18955         this.setEndDate(this.endDate);
18956         
18957         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18958         
18959         this.fillDow();
18960         this.fillMonths();
18961         this.update();
18962         this.showMode();
18963         
18964         if(this.isInline) {
18965             this.showPopup();
18966         }
18967     },
18968     
18969     picker : function()
18970     {
18971         return this.pickerEl;
18972 //        return this.el.select('.datepicker', true).first();
18973     },
18974     
18975     fillDow: function()
18976     {
18977         var dowCnt = this.weekStart;
18978         
18979         var dow = {
18980             tag: 'tr',
18981             cn: [
18982                 
18983             ]
18984         };
18985         
18986         if(this.calendarWeeks){
18987             dow.cn.push({
18988                 tag: 'th',
18989                 cls: 'cw',
18990                 html: '&nbsp;'
18991             })
18992         }
18993         
18994         while (dowCnt < this.weekStart + 7) {
18995             dow.cn.push({
18996                 tag: 'th',
18997                 cls: 'dow',
18998                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18999             });
19000         }
19001         
19002         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
19003     },
19004     
19005     fillMonths: function()
19006     {    
19007         var i = 0;
19008         var months = this.picker().select('>.datepicker-months td', true).first();
19009         
19010         months.dom.innerHTML = '';
19011         
19012         while (i < 12) {
19013             var month = {
19014                 tag: 'span',
19015                 cls: 'month',
19016                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
19017             };
19018             
19019             months.createChild(month);
19020         }
19021         
19022     },
19023     
19024     update: function()
19025     {
19026         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;
19027         
19028         if (this.date < this.startDate) {
19029             this.viewDate = new Date(this.startDate);
19030         } else if (this.date > this.endDate) {
19031             this.viewDate = new Date(this.endDate);
19032         } else {
19033             this.viewDate = new Date(this.date);
19034         }
19035         
19036         this.fill();
19037     },
19038     
19039     fill: function() 
19040     {
19041         var d = new Date(this.viewDate),
19042                 year = d.getUTCFullYear(),
19043                 month = d.getUTCMonth(),
19044                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
19045                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
19046                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
19047                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
19048                 currentDate = this.date && this.date.valueOf(),
19049                 today = this.UTCToday();
19050         
19051         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
19052         
19053 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19054         
19055 //        this.picker.select('>tfoot th.today').
19056 //                                              .text(dates[this.language].today)
19057 //                                              .toggle(this.todayBtn !== false);
19058     
19059         this.updateNavArrows();
19060         this.fillMonths();
19061                                                 
19062         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
19063         
19064         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
19065          
19066         prevMonth.setUTCDate(day);
19067         
19068         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
19069         
19070         var nextMonth = new Date(prevMonth);
19071         
19072         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
19073         
19074         nextMonth = nextMonth.valueOf();
19075         
19076         var fillMonths = false;
19077         
19078         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
19079         
19080         while(prevMonth.valueOf() <= nextMonth) {
19081             var clsName = '';
19082             
19083             if (prevMonth.getUTCDay() === this.weekStart) {
19084                 if(fillMonths){
19085                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
19086                 }
19087                     
19088                 fillMonths = {
19089                     tag: 'tr',
19090                     cn: []
19091                 };
19092                 
19093                 if(this.calendarWeeks){
19094                     // ISO 8601: First week contains first thursday.
19095                     // ISO also states week starts on Monday, but we can be more abstract here.
19096                     var
19097                     // Start of current week: based on weekstart/current date
19098                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
19099                     // Thursday of this week
19100                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
19101                     // First Thursday of year, year from thursday
19102                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
19103                     // Calendar week: ms between thursdays, div ms per day, div 7 days
19104                     calWeek =  (th - yth) / 864e5 / 7 + 1;
19105                     
19106                     fillMonths.cn.push({
19107                         tag: 'td',
19108                         cls: 'cw',
19109                         html: calWeek
19110                     });
19111                 }
19112             }
19113             
19114             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
19115                 clsName += ' old';
19116             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19117                 clsName += ' new';
19118             }
19119             if (this.todayHighlight &&
19120                 prevMonth.getUTCFullYear() == today.getFullYear() &&
19121                 prevMonth.getUTCMonth() == today.getMonth() &&
19122                 prevMonth.getUTCDate() == today.getDate()) {
19123                 clsName += ' today';
19124             }
19125             
19126             if (currentDate && prevMonth.valueOf() === currentDate) {
19127                 clsName += ' active';
19128             }
19129             
19130             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19131                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19132                     clsName += ' disabled';
19133             }
19134             
19135             fillMonths.cn.push({
19136                 tag: 'td',
19137                 cls: 'day ' + clsName,
19138                 html: prevMonth.getDate()
19139             });
19140             
19141             prevMonth.setDate(prevMonth.getDate()+1);
19142         }
19143           
19144         var currentYear = this.date && this.date.getUTCFullYear();
19145         var currentMonth = this.date && this.date.getUTCMonth();
19146         
19147         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19148         
19149         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19150             v.removeClass('active');
19151             
19152             if(currentYear === year && k === currentMonth){
19153                 v.addClass('active');
19154             }
19155             
19156             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19157                 v.addClass('disabled');
19158             }
19159             
19160         });
19161         
19162         
19163         year = parseInt(year/10, 10) * 10;
19164         
19165         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19166         
19167         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19168         
19169         year -= 1;
19170         for (var i = -1; i < 11; i++) {
19171             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19172                 tag: 'span',
19173                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19174                 html: year
19175             });
19176             
19177             year += 1;
19178         }
19179     },
19180     
19181     showMode: function(dir) 
19182     {
19183         if (dir) {
19184             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19185         }
19186         
19187         Roo.each(this.picker().select('>div',true).elements, function(v){
19188             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19189             v.hide();
19190         });
19191         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19192     },
19193     
19194     place: function()
19195     {
19196         if(this.isInline) {
19197             return;
19198         }
19199         
19200         this.picker().removeClass(['bottom', 'top']);
19201         
19202         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19203             /*
19204              * place to the top of element!
19205              *
19206              */
19207             
19208             this.picker().addClass('top');
19209             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19210             
19211             return;
19212         }
19213         
19214         this.picker().addClass('bottom');
19215         
19216         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19217     },
19218     
19219     parseDate : function(value)
19220     {
19221         if(!value || value instanceof Date){
19222             return value;
19223         }
19224         var v = Date.parseDate(value, this.format);
19225         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19226             v = Date.parseDate(value, 'Y-m-d');
19227         }
19228         if(!v && this.altFormats){
19229             if(!this.altFormatsArray){
19230                 this.altFormatsArray = this.altFormats.split("|");
19231             }
19232             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19233                 v = Date.parseDate(value, this.altFormatsArray[i]);
19234             }
19235         }
19236         return v;
19237     },
19238     
19239     formatDate : function(date, fmt)
19240     {   
19241         return (!date || !(date instanceof Date)) ?
19242         date : date.dateFormat(fmt || this.format);
19243     },
19244     
19245     onFocus : function()
19246     {
19247         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19248         this.showPopup();
19249     },
19250     
19251     onBlur : function()
19252     {
19253         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19254         
19255         var d = this.inputEl().getValue();
19256         
19257         this.setValue(d);
19258                 
19259         this.hidePopup();
19260     },
19261     
19262     showPopup : function()
19263     {
19264         this.picker().show();
19265         this.update();
19266         this.place();
19267         
19268         this.fireEvent('showpopup', this, this.date);
19269     },
19270     
19271     hidePopup : function()
19272     {
19273         if(this.isInline) {
19274             return;
19275         }
19276         this.picker().hide();
19277         this.viewMode = this.startViewMode;
19278         this.showMode();
19279         
19280         this.fireEvent('hidepopup', this, this.date);
19281         
19282     },
19283     
19284     onMousedown: function(e)
19285     {
19286         e.stopPropagation();
19287         e.preventDefault();
19288     },
19289     
19290     keyup: function(e)
19291     {
19292         Roo.bootstrap.DateField.superclass.keyup.call(this);
19293         this.update();
19294     },
19295
19296     setValue: function(v)
19297     {
19298         if(this.fireEvent('beforeselect', this, v) !== false){
19299             var d = new Date(this.parseDate(v) ).clearTime();
19300         
19301             if(isNaN(d.getTime())){
19302                 this.date = this.viewDate = '';
19303                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19304                 return;
19305             }
19306
19307             v = this.formatDate(d);
19308
19309             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19310
19311             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19312
19313             this.update();
19314
19315             this.fireEvent('select', this, this.date);
19316         }
19317     },
19318     
19319     getValue: function()
19320     {
19321         return this.formatDate(this.date);
19322     },
19323     
19324     fireKey: function(e)
19325     {
19326         if (!this.picker().isVisible()){
19327             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19328                 this.showPopup();
19329             }
19330             return;
19331         }
19332         
19333         var dateChanged = false,
19334         dir, day, month,
19335         newDate, newViewDate;
19336         
19337         switch(e.keyCode){
19338             case 27: // escape
19339                 this.hidePopup();
19340                 e.preventDefault();
19341                 break;
19342             case 37: // left
19343             case 39: // right
19344                 if (!this.keyboardNavigation) {
19345                     break;
19346                 }
19347                 dir = e.keyCode == 37 ? -1 : 1;
19348                 
19349                 if (e.ctrlKey){
19350                     newDate = this.moveYear(this.date, dir);
19351                     newViewDate = this.moveYear(this.viewDate, dir);
19352                 } else if (e.shiftKey){
19353                     newDate = this.moveMonth(this.date, dir);
19354                     newViewDate = this.moveMonth(this.viewDate, dir);
19355                 } else {
19356                     newDate = new Date(this.date);
19357                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19358                     newViewDate = new Date(this.viewDate);
19359                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19360                 }
19361                 if (this.dateWithinRange(newDate)){
19362                     this.date = newDate;
19363                     this.viewDate = newViewDate;
19364                     this.setValue(this.formatDate(this.date));
19365 //                    this.update();
19366                     e.preventDefault();
19367                     dateChanged = true;
19368                 }
19369                 break;
19370             case 38: // up
19371             case 40: // down
19372                 if (!this.keyboardNavigation) {
19373                     break;
19374                 }
19375                 dir = e.keyCode == 38 ? -1 : 1;
19376                 if (e.ctrlKey){
19377                     newDate = this.moveYear(this.date, dir);
19378                     newViewDate = this.moveYear(this.viewDate, dir);
19379                 } else if (e.shiftKey){
19380                     newDate = this.moveMonth(this.date, dir);
19381                     newViewDate = this.moveMonth(this.viewDate, dir);
19382                 } else {
19383                     newDate = new Date(this.date);
19384                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19385                     newViewDate = new Date(this.viewDate);
19386                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19387                 }
19388                 if (this.dateWithinRange(newDate)){
19389                     this.date = newDate;
19390                     this.viewDate = newViewDate;
19391                     this.setValue(this.formatDate(this.date));
19392 //                    this.update();
19393                     e.preventDefault();
19394                     dateChanged = true;
19395                 }
19396                 break;
19397             case 13: // enter
19398                 this.setValue(this.formatDate(this.date));
19399                 this.hidePopup();
19400                 e.preventDefault();
19401                 break;
19402             case 9: // tab
19403                 this.setValue(this.formatDate(this.date));
19404                 this.hidePopup();
19405                 break;
19406             case 16: // shift
19407             case 17: // ctrl
19408             case 18: // alt
19409                 break;
19410             default :
19411                 this.hidePopup();
19412                 
19413         }
19414     },
19415     
19416     
19417     onClick: function(e) 
19418     {
19419         e.stopPropagation();
19420         e.preventDefault();
19421         
19422         var target = e.getTarget();
19423         
19424         if(target.nodeName.toLowerCase() === 'i'){
19425             target = Roo.get(target).dom.parentNode;
19426         }
19427         
19428         var nodeName = target.nodeName;
19429         var className = target.className;
19430         var html = target.innerHTML;
19431         //Roo.log(nodeName);
19432         
19433         switch(nodeName.toLowerCase()) {
19434             case 'th':
19435                 switch(className) {
19436                     case 'switch':
19437                         this.showMode(1);
19438                         break;
19439                     case 'prev':
19440                     case 'next':
19441                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19442                         switch(this.viewMode){
19443                                 case 0:
19444                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19445                                         break;
19446                                 case 1:
19447                                 case 2:
19448                                         this.viewDate = this.moveYear(this.viewDate, dir);
19449                                         break;
19450                         }
19451                         this.fill();
19452                         break;
19453                     case 'today':
19454                         var date = new Date();
19455                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19456 //                        this.fill()
19457                         this.setValue(this.formatDate(this.date));
19458                         
19459                         this.hidePopup();
19460                         break;
19461                 }
19462                 break;
19463             case 'span':
19464                 if (className.indexOf('disabled') < 0) {
19465                     this.viewDate.setUTCDate(1);
19466                     if (className.indexOf('month') > -1) {
19467                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19468                     } else {
19469                         var year = parseInt(html, 10) || 0;
19470                         this.viewDate.setUTCFullYear(year);
19471                         
19472                     }
19473                     
19474                     if(this.singleMode){
19475                         this.setValue(this.formatDate(this.viewDate));
19476                         this.hidePopup();
19477                         return;
19478                     }
19479                     
19480                     this.showMode(-1);
19481                     this.fill();
19482                 }
19483                 break;
19484                 
19485             case 'td':
19486                 //Roo.log(className);
19487                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19488                     var day = parseInt(html, 10) || 1;
19489                     var year = this.viewDate.getUTCFullYear(),
19490                         month = this.viewDate.getUTCMonth();
19491
19492                     if (className.indexOf('old') > -1) {
19493                         if(month === 0 ){
19494                             month = 11;
19495                             year -= 1;
19496                         }else{
19497                             month -= 1;
19498                         }
19499                     } else if (className.indexOf('new') > -1) {
19500                         if (month == 11) {
19501                             month = 0;
19502                             year += 1;
19503                         } else {
19504                             month += 1;
19505                         }
19506                     }
19507                     //Roo.log([year,month,day]);
19508                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19509                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19510 //                    this.fill();
19511                     //Roo.log(this.formatDate(this.date));
19512                     this.setValue(this.formatDate(this.date));
19513                     this.hidePopup();
19514                 }
19515                 break;
19516         }
19517     },
19518     
19519     setStartDate: function(startDate)
19520     {
19521         this.startDate = startDate || -Infinity;
19522         if (this.startDate !== -Infinity) {
19523             this.startDate = this.parseDate(this.startDate);
19524         }
19525         this.update();
19526         this.updateNavArrows();
19527     },
19528
19529     setEndDate: function(endDate)
19530     {
19531         this.endDate = endDate || Infinity;
19532         if (this.endDate !== Infinity) {
19533             this.endDate = this.parseDate(this.endDate);
19534         }
19535         this.update();
19536         this.updateNavArrows();
19537     },
19538     
19539     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19540     {
19541         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19542         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19543             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19544         }
19545         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19546             return parseInt(d, 10);
19547         });
19548         this.update();
19549         this.updateNavArrows();
19550     },
19551     
19552     updateNavArrows: function() 
19553     {
19554         if(this.singleMode){
19555             return;
19556         }
19557         
19558         var d = new Date(this.viewDate),
19559         year = d.getUTCFullYear(),
19560         month = d.getUTCMonth();
19561         
19562         Roo.each(this.picker().select('.prev', true).elements, function(v){
19563             v.show();
19564             switch (this.viewMode) {
19565                 case 0:
19566
19567                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19568                         v.hide();
19569                     }
19570                     break;
19571                 case 1:
19572                 case 2:
19573                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19574                         v.hide();
19575                     }
19576                     break;
19577             }
19578         });
19579         
19580         Roo.each(this.picker().select('.next', true).elements, function(v){
19581             v.show();
19582             switch (this.viewMode) {
19583                 case 0:
19584
19585                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19586                         v.hide();
19587                     }
19588                     break;
19589                 case 1:
19590                 case 2:
19591                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19592                         v.hide();
19593                     }
19594                     break;
19595             }
19596         })
19597     },
19598     
19599     moveMonth: function(date, dir)
19600     {
19601         if (!dir) {
19602             return date;
19603         }
19604         var new_date = new Date(date.valueOf()),
19605         day = new_date.getUTCDate(),
19606         month = new_date.getUTCMonth(),
19607         mag = Math.abs(dir),
19608         new_month, test;
19609         dir = dir > 0 ? 1 : -1;
19610         if (mag == 1){
19611             test = dir == -1
19612             // If going back one month, make sure month is not current month
19613             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19614             ? function(){
19615                 return new_date.getUTCMonth() == month;
19616             }
19617             // If going forward one month, make sure month is as expected
19618             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19619             : function(){
19620                 return new_date.getUTCMonth() != new_month;
19621             };
19622             new_month = month + dir;
19623             new_date.setUTCMonth(new_month);
19624             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19625             if (new_month < 0 || new_month > 11) {
19626                 new_month = (new_month + 12) % 12;
19627             }
19628         } else {
19629             // For magnitudes >1, move one month at a time...
19630             for (var i=0; i<mag; i++) {
19631                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19632                 new_date = this.moveMonth(new_date, dir);
19633             }
19634             // ...then reset the day, keeping it in the new month
19635             new_month = new_date.getUTCMonth();
19636             new_date.setUTCDate(day);
19637             test = function(){
19638                 return new_month != new_date.getUTCMonth();
19639             };
19640         }
19641         // Common date-resetting loop -- if date is beyond end of month, make it
19642         // end of month
19643         while (test()){
19644             new_date.setUTCDate(--day);
19645             new_date.setUTCMonth(new_month);
19646         }
19647         return new_date;
19648     },
19649
19650     moveYear: function(date, dir)
19651     {
19652         return this.moveMonth(date, dir*12);
19653     },
19654
19655     dateWithinRange: function(date)
19656     {
19657         return date >= this.startDate && date <= this.endDate;
19658     },
19659
19660     
19661     remove: function() 
19662     {
19663         this.picker().remove();
19664     },
19665     
19666     validateValue : function(value)
19667     {
19668         if(this.getVisibilityEl().hasClass('hidden')){
19669             return true;
19670         }
19671         
19672         if(value.length < 1)  {
19673             if(this.allowBlank){
19674                 return true;
19675             }
19676             return false;
19677         }
19678         
19679         if(value.length < this.minLength){
19680             return false;
19681         }
19682         if(value.length > this.maxLength){
19683             return false;
19684         }
19685         if(this.vtype){
19686             var vt = Roo.form.VTypes;
19687             if(!vt[this.vtype](value, this)){
19688                 return false;
19689             }
19690         }
19691         if(typeof this.validator == "function"){
19692             var msg = this.validator(value);
19693             if(msg !== true){
19694                 return false;
19695             }
19696         }
19697         
19698         if(this.regex && !this.regex.test(value)){
19699             return false;
19700         }
19701         
19702         if(typeof(this.parseDate(value)) == 'undefined'){
19703             return false;
19704         }
19705         
19706         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19707             return false;
19708         }      
19709         
19710         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19711             return false;
19712         } 
19713         
19714         
19715         return true;
19716     },
19717     
19718     reset : function()
19719     {
19720         this.date = this.viewDate = '';
19721         
19722         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19723     }
19724    
19725 });
19726
19727 Roo.apply(Roo.bootstrap.DateField,  {
19728     
19729     head : {
19730         tag: 'thead',
19731         cn: [
19732         {
19733             tag: 'tr',
19734             cn: [
19735             {
19736                 tag: 'th',
19737                 cls: 'prev',
19738                 html: '<i class="fa fa-arrow-left"/>'
19739             },
19740             {
19741                 tag: 'th',
19742                 cls: 'switch',
19743                 colspan: '5'
19744             },
19745             {
19746                 tag: 'th',
19747                 cls: 'next',
19748                 html: '<i class="fa fa-arrow-right"/>'
19749             }
19750
19751             ]
19752         }
19753         ]
19754     },
19755     
19756     content : {
19757         tag: 'tbody',
19758         cn: [
19759         {
19760             tag: 'tr',
19761             cn: [
19762             {
19763                 tag: 'td',
19764                 colspan: '7'
19765             }
19766             ]
19767         }
19768         ]
19769     },
19770     
19771     footer : {
19772         tag: 'tfoot',
19773         cn: [
19774         {
19775             tag: 'tr',
19776             cn: [
19777             {
19778                 tag: 'th',
19779                 colspan: '7',
19780                 cls: 'today'
19781             }
19782                     
19783             ]
19784         }
19785         ]
19786     },
19787     
19788     dates:{
19789         en: {
19790             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19791             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19792             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19793             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19794             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19795             today: "Today"
19796         }
19797     },
19798     
19799     modes: [
19800     {
19801         clsName: 'days',
19802         navFnc: 'Month',
19803         navStep: 1
19804     },
19805     {
19806         clsName: 'months',
19807         navFnc: 'FullYear',
19808         navStep: 1
19809     },
19810     {
19811         clsName: 'years',
19812         navFnc: 'FullYear',
19813         navStep: 10
19814     }]
19815 });
19816
19817 Roo.apply(Roo.bootstrap.DateField,  {
19818   
19819     template : {
19820         tag: 'div',
19821         cls: 'datepicker dropdown-menu roo-dynamic',
19822         cn: [
19823         {
19824             tag: 'div',
19825             cls: 'datepicker-days',
19826             cn: [
19827             {
19828                 tag: 'table',
19829                 cls: 'table-condensed',
19830                 cn:[
19831                 Roo.bootstrap.DateField.head,
19832                 {
19833                     tag: 'tbody'
19834                 },
19835                 Roo.bootstrap.DateField.footer
19836                 ]
19837             }
19838             ]
19839         },
19840         {
19841             tag: 'div',
19842             cls: 'datepicker-months',
19843             cn: [
19844             {
19845                 tag: 'table',
19846                 cls: 'table-condensed',
19847                 cn:[
19848                 Roo.bootstrap.DateField.head,
19849                 Roo.bootstrap.DateField.content,
19850                 Roo.bootstrap.DateField.footer
19851                 ]
19852             }
19853             ]
19854         },
19855         {
19856             tag: 'div',
19857             cls: 'datepicker-years',
19858             cn: [
19859             {
19860                 tag: 'table',
19861                 cls: 'table-condensed',
19862                 cn:[
19863                 Roo.bootstrap.DateField.head,
19864                 Roo.bootstrap.DateField.content,
19865                 Roo.bootstrap.DateField.footer
19866                 ]
19867             }
19868             ]
19869         }
19870         ]
19871     }
19872 });
19873
19874  
19875
19876  /*
19877  * - LGPL
19878  *
19879  * TimeField
19880  * 
19881  */
19882
19883 /**
19884  * @class Roo.bootstrap.TimeField
19885  * @extends Roo.bootstrap.Input
19886  * Bootstrap DateField class
19887  * 
19888  * 
19889  * @constructor
19890  * Create a new TimeField
19891  * @param {Object} config The config object
19892  */
19893
19894 Roo.bootstrap.TimeField = function(config){
19895     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19896     this.addEvents({
19897             /**
19898              * @event show
19899              * Fires when this field show.
19900              * @param {Roo.bootstrap.DateField} thisthis
19901              * @param {Mixed} date The date value
19902              */
19903             show : true,
19904             /**
19905              * @event show
19906              * Fires when this field hide.
19907              * @param {Roo.bootstrap.DateField} this
19908              * @param {Mixed} date The date value
19909              */
19910             hide : true,
19911             /**
19912              * @event select
19913              * Fires when select a date.
19914              * @param {Roo.bootstrap.DateField} this
19915              * @param {Mixed} date The date value
19916              */
19917             select : true
19918         });
19919 };
19920
19921 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19922     
19923     /**
19924      * @cfg {String} format
19925      * The default time format string which can be overriden for localization support.  The format must be
19926      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19927      */
19928     format : "H:i",
19929        
19930     onRender: function(ct, position)
19931     {
19932         
19933         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19934                 
19935         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19936         
19937         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19938         
19939         this.pop = this.picker().select('>.datepicker-time',true).first();
19940         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19941         
19942         this.picker().on('mousedown', this.onMousedown, this);
19943         this.picker().on('click', this.onClick, this);
19944         
19945         this.picker().addClass('datepicker-dropdown');
19946     
19947         this.fillTime();
19948         this.update();
19949             
19950         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19951         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19952         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19953         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19954         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19955         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19956
19957     },
19958     
19959     fireKey: function(e){
19960         if (!this.picker().isVisible()){
19961             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19962                 this.show();
19963             }
19964             return;
19965         }
19966
19967         e.preventDefault();
19968         
19969         switch(e.keyCode){
19970             case 27: // escape
19971                 this.hide();
19972                 break;
19973             case 37: // left
19974             case 39: // right
19975                 this.onTogglePeriod();
19976                 break;
19977             case 38: // up
19978                 this.onIncrementMinutes();
19979                 break;
19980             case 40: // down
19981                 this.onDecrementMinutes();
19982                 break;
19983             case 13: // enter
19984             case 9: // tab
19985                 this.setTime();
19986                 break;
19987         }
19988     },
19989     
19990     onClick: function(e) {
19991         e.stopPropagation();
19992         e.preventDefault();
19993     },
19994     
19995     picker : function()
19996     {
19997         return this.el.select('.datepicker', true).first();
19998     },
19999     
20000     fillTime: function()
20001     {    
20002         var time = this.pop.select('tbody', true).first();
20003         
20004         time.dom.innerHTML = '';
20005         
20006         time.createChild({
20007             tag: 'tr',
20008             cn: [
20009                 {
20010                     tag: 'td',
20011                     cn: [
20012                         {
20013                             tag: 'a',
20014                             href: '#',
20015                             cls: 'btn',
20016                             cn: [
20017                                 {
20018                                     tag: 'span',
20019                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
20020                                 }
20021                             ]
20022                         } 
20023                     ]
20024                 },
20025                 {
20026                     tag: 'td',
20027                     cls: 'separator'
20028                 },
20029                 {
20030                     tag: 'td',
20031                     cn: [
20032                         {
20033                             tag: 'a',
20034                             href: '#',
20035                             cls: 'btn',
20036                             cn: [
20037                                 {
20038                                     tag: 'span',
20039                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
20040                                 }
20041                             ]
20042                         }
20043                     ]
20044                 },
20045                 {
20046                     tag: 'td',
20047                     cls: 'separator'
20048                 }
20049             ]
20050         });
20051         
20052         time.createChild({
20053             tag: 'tr',
20054             cn: [
20055                 {
20056                     tag: 'td',
20057                     cn: [
20058                         {
20059                             tag: 'span',
20060                             cls: 'timepicker-hour',
20061                             html: '00'
20062                         }  
20063                     ]
20064                 },
20065                 {
20066                     tag: 'td',
20067                     cls: 'separator',
20068                     html: ':'
20069                 },
20070                 {
20071                     tag: 'td',
20072                     cn: [
20073                         {
20074                             tag: 'span',
20075                             cls: 'timepicker-minute',
20076                             html: '00'
20077                         }  
20078                     ]
20079                 },
20080                 {
20081                     tag: 'td',
20082                     cls: 'separator'
20083                 },
20084                 {
20085                     tag: 'td',
20086                     cn: [
20087                         {
20088                             tag: 'button',
20089                             type: 'button',
20090                             cls: 'btn btn-primary period',
20091                             html: 'AM'
20092                             
20093                         }
20094                     ]
20095                 }
20096             ]
20097         });
20098         
20099         time.createChild({
20100             tag: 'tr',
20101             cn: [
20102                 {
20103                     tag: 'td',
20104                     cn: [
20105                         {
20106                             tag: 'a',
20107                             href: '#',
20108                             cls: 'btn',
20109                             cn: [
20110                                 {
20111                                     tag: 'span',
20112                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
20113                                 }
20114                             ]
20115                         }
20116                     ]
20117                 },
20118                 {
20119                     tag: 'td',
20120                     cls: 'separator'
20121                 },
20122                 {
20123                     tag: 'td',
20124                     cn: [
20125                         {
20126                             tag: 'a',
20127                             href: '#',
20128                             cls: 'btn',
20129                             cn: [
20130                                 {
20131                                     tag: 'span',
20132                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
20133                                 }
20134                             ]
20135                         }
20136                     ]
20137                 },
20138                 {
20139                     tag: 'td',
20140                     cls: 'separator'
20141                 }
20142             ]
20143         });
20144         
20145     },
20146     
20147     update: function()
20148     {
20149         
20150         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20151         
20152         this.fill();
20153     },
20154     
20155     fill: function() 
20156     {
20157         var hours = this.time.getHours();
20158         var minutes = this.time.getMinutes();
20159         var period = 'AM';
20160         
20161         if(hours > 11){
20162             period = 'PM';
20163         }
20164         
20165         if(hours == 0){
20166             hours = 12;
20167         }
20168         
20169         
20170         if(hours > 12){
20171             hours = hours - 12;
20172         }
20173         
20174         if(hours < 10){
20175             hours = '0' + hours;
20176         }
20177         
20178         if(minutes < 10){
20179             minutes = '0' + minutes;
20180         }
20181         
20182         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20183         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20184         this.pop.select('button', true).first().dom.innerHTML = period;
20185         
20186     },
20187     
20188     place: function()
20189     {   
20190         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20191         
20192         var cls = ['bottom'];
20193         
20194         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20195             cls.pop();
20196             cls.push('top');
20197         }
20198         
20199         cls.push('right');
20200         
20201         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20202             cls.pop();
20203             cls.push('left');
20204         }
20205         
20206         this.picker().addClass(cls.join('-'));
20207         
20208         var _this = this;
20209         
20210         Roo.each(cls, function(c){
20211             if(c == 'bottom'){
20212                 _this.picker().setTop(_this.inputEl().getHeight());
20213                 return;
20214             }
20215             if(c == 'top'){
20216                 _this.picker().setTop(0 - _this.picker().getHeight());
20217                 return;
20218             }
20219             
20220             if(c == 'left'){
20221                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20222                 return;
20223             }
20224             if(c == 'right'){
20225                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20226                 return;
20227             }
20228         });
20229         
20230     },
20231   
20232     onFocus : function()
20233     {
20234         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20235         this.show();
20236     },
20237     
20238     onBlur : function()
20239     {
20240         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20241         this.hide();
20242     },
20243     
20244     show : function()
20245     {
20246         this.picker().show();
20247         this.pop.show();
20248         this.update();
20249         this.place();
20250         
20251         this.fireEvent('show', this, this.date);
20252     },
20253     
20254     hide : function()
20255     {
20256         this.picker().hide();
20257         this.pop.hide();
20258         
20259         this.fireEvent('hide', this, this.date);
20260     },
20261     
20262     setTime : function()
20263     {
20264         this.hide();
20265         this.setValue(this.time.format(this.format));
20266         
20267         this.fireEvent('select', this, this.date);
20268         
20269         
20270     },
20271     
20272     onMousedown: function(e){
20273         e.stopPropagation();
20274         e.preventDefault();
20275     },
20276     
20277     onIncrementHours: function()
20278     {
20279         Roo.log('onIncrementHours');
20280         this.time = this.time.add(Date.HOUR, 1);
20281         this.update();
20282         
20283     },
20284     
20285     onDecrementHours: function()
20286     {
20287         Roo.log('onDecrementHours');
20288         this.time = this.time.add(Date.HOUR, -1);
20289         this.update();
20290     },
20291     
20292     onIncrementMinutes: function()
20293     {
20294         Roo.log('onIncrementMinutes');
20295         this.time = this.time.add(Date.MINUTE, 1);
20296         this.update();
20297     },
20298     
20299     onDecrementMinutes: function()
20300     {
20301         Roo.log('onDecrementMinutes');
20302         this.time = this.time.add(Date.MINUTE, -1);
20303         this.update();
20304     },
20305     
20306     onTogglePeriod: function()
20307     {
20308         Roo.log('onTogglePeriod');
20309         this.time = this.time.add(Date.HOUR, 12);
20310         this.update();
20311     }
20312     
20313    
20314 });
20315
20316 Roo.apply(Roo.bootstrap.TimeField,  {
20317     
20318     content : {
20319         tag: 'tbody',
20320         cn: [
20321             {
20322                 tag: 'tr',
20323                 cn: [
20324                 {
20325                     tag: 'td',
20326                     colspan: '7'
20327                 }
20328                 ]
20329             }
20330         ]
20331     },
20332     
20333     footer : {
20334         tag: 'tfoot',
20335         cn: [
20336             {
20337                 tag: 'tr',
20338                 cn: [
20339                 {
20340                     tag: 'th',
20341                     colspan: '7',
20342                     cls: '',
20343                     cn: [
20344                         {
20345                             tag: 'button',
20346                             cls: 'btn btn-info ok',
20347                             html: 'OK'
20348                         }
20349                     ]
20350                 }
20351
20352                 ]
20353             }
20354         ]
20355     }
20356 });
20357
20358 Roo.apply(Roo.bootstrap.TimeField,  {
20359   
20360     template : {
20361         tag: 'div',
20362         cls: 'datepicker dropdown-menu',
20363         cn: [
20364             {
20365                 tag: 'div',
20366                 cls: 'datepicker-time',
20367                 cn: [
20368                 {
20369                     tag: 'table',
20370                     cls: 'table-condensed',
20371                     cn:[
20372                     Roo.bootstrap.TimeField.content,
20373                     Roo.bootstrap.TimeField.footer
20374                     ]
20375                 }
20376                 ]
20377             }
20378         ]
20379     }
20380 });
20381
20382  
20383
20384  /*
20385  * - LGPL
20386  *
20387  * MonthField
20388  * 
20389  */
20390
20391 /**
20392  * @class Roo.bootstrap.MonthField
20393  * @extends Roo.bootstrap.Input
20394  * Bootstrap MonthField class
20395  * 
20396  * @cfg {String} language default en
20397  * 
20398  * @constructor
20399  * Create a new MonthField
20400  * @param {Object} config The config object
20401  */
20402
20403 Roo.bootstrap.MonthField = function(config){
20404     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20405     
20406     this.addEvents({
20407         /**
20408          * @event show
20409          * Fires when this field show.
20410          * @param {Roo.bootstrap.MonthField} this
20411          * @param {Mixed} date The date value
20412          */
20413         show : true,
20414         /**
20415          * @event show
20416          * Fires when this field hide.
20417          * @param {Roo.bootstrap.MonthField} this
20418          * @param {Mixed} date The date value
20419          */
20420         hide : true,
20421         /**
20422          * @event select
20423          * Fires when select a date.
20424          * @param {Roo.bootstrap.MonthField} this
20425          * @param {String} oldvalue The old value
20426          * @param {String} newvalue The new value
20427          */
20428         select : true
20429     });
20430 };
20431
20432 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20433     
20434     onRender: function(ct, position)
20435     {
20436         
20437         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20438         
20439         this.language = this.language || 'en';
20440         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20441         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20442         
20443         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20444         this.isInline = false;
20445         this.isInput = true;
20446         this.component = this.el.select('.add-on', true).first() || false;
20447         this.component = (this.component && this.component.length === 0) ? false : this.component;
20448         this.hasInput = this.component && this.inputEL().length;
20449         
20450         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20451         
20452         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20453         
20454         this.picker().on('mousedown', this.onMousedown, this);
20455         this.picker().on('click', this.onClick, this);
20456         
20457         this.picker().addClass('datepicker-dropdown');
20458         
20459         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20460             v.setStyle('width', '189px');
20461         });
20462         
20463         this.fillMonths();
20464         
20465         this.update();
20466         
20467         if(this.isInline) {
20468             this.show();
20469         }
20470         
20471     },
20472     
20473     setValue: function(v, suppressEvent)
20474     {   
20475         var o = this.getValue();
20476         
20477         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20478         
20479         this.update();
20480
20481         if(suppressEvent !== true){
20482             this.fireEvent('select', this, o, v);
20483         }
20484         
20485     },
20486     
20487     getValue: function()
20488     {
20489         return this.value;
20490     },
20491     
20492     onClick: function(e) 
20493     {
20494         e.stopPropagation();
20495         e.preventDefault();
20496         
20497         var target = e.getTarget();
20498         
20499         if(target.nodeName.toLowerCase() === 'i'){
20500             target = Roo.get(target).dom.parentNode;
20501         }
20502         
20503         var nodeName = target.nodeName;
20504         var className = target.className;
20505         var html = target.innerHTML;
20506         
20507         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20508             return;
20509         }
20510         
20511         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20512         
20513         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20514         
20515         this.hide();
20516                         
20517     },
20518     
20519     picker : function()
20520     {
20521         return this.pickerEl;
20522     },
20523     
20524     fillMonths: function()
20525     {    
20526         var i = 0;
20527         var months = this.picker().select('>.datepicker-months td', true).first();
20528         
20529         months.dom.innerHTML = '';
20530         
20531         while (i < 12) {
20532             var month = {
20533                 tag: 'span',
20534                 cls: 'month',
20535                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20536             };
20537             
20538             months.createChild(month);
20539         }
20540         
20541     },
20542     
20543     update: function()
20544     {
20545         var _this = this;
20546         
20547         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20548             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20549         }
20550         
20551         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20552             e.removeClass('active');
20553             
20554             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20555                 e.addClass('active');
20556             }
20557         })
20558     },
20559     
20560     place: function()
20561     {
20562         if(this.isInline) {
20563             return;
20564         }
20565         
20566         this.picker().removeClass(['bottom', 'top']);
20567         
20568         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20569             /*
20570              * place to the top of element!
20571              *
20572              */
20573             
20574             this.picker().addClass('top');
20575             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20576             
20577             return;
20578         }
20579         
20580         this.picker().addClass('bottom');
20581         
20582         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20583     },
20584     
20585     onFocus : function()
20586     {
20587         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20588         this.show();
20589     },
20590     
20591     onBlur : function()
20592     {
20593         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20594         
20595         var d = this.inputEl().getValue();
20596         
20597         this.setValue(d);
20598                 
20599         this.hide();
20600     },
20601     
20602     show : function()
20603     {
20604         this.picker().show();
20605         this.picker().select('>.datepicker-months', true).first().show();
20606         this.update();
20607         this.place();
20608         
20609         this.fireEvent('show', this, this.date);
20610     },
20611     
20612     hide : function()
20613     {
20614         if(this.isInline) {
20615             return;
20616         }
20617         this.picker().hide();
20618         this.fireEvent('hide', this, this.date);
20619         
20620     },
20621     
20622     onMousedown: function(e)
20623     {
20624         e.stopPropagation();
20625         e.preventDefault();
20626     },
20627     
20628     keyup: function(e)
20629     {
20630         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20631         this.update();
20632     },
20633
20634     fireKey: function(e)
20635     {
20636         if (!this.picker().isVisible()){
20637             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20638                 this.show();
20639             }
20640             return;
20641         }
20642         
20643         var dir;
20644         
20645         switch(e.keyCode){
20646             case 27: // escape
20647                 this.hide();
20648                 e.preventDefault();
20649                 break;
20650             case 37: // left
20651             case 39: // right
20652                 dir = e.keyCode == 37 ? -1 : 1;
20653                 
20654                 this.vIndex = this.vIndex + dir;
20655                 
20656                 if(this.vIndex < 0){
20657                     this.vIndex = 0;
20658                 }
20659                 
20660                 if(this.vIndex > 11){
20661                     this.vIndex = 11;
20662                 }
20663                 
20664                 if(isNaN(this.vIndex)){
20665                     this.vIndex = 0;
20666                 }
20667                 
20668                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20669                 
20670                 break;
20671             case 38: // up
20672             case 40: // down
20673                 
20674                 dir = e.keyCode == 38 ? -1 : 1;
20675                 
20676                 this.vIndex = this.vIndex + dir * 4;
20677                 
20678                 if(this.vIndex < 0){
20679                     this.vIndex = 0;
20680                 }
20681                 
20682                 if(this.vIndex > 11){
20683                     this.vIndex = 11;
20684                 }
20685                 
20686                 if(isNaN(this.vIndex)){
20687                     this.vIndex = 0;
20688                 }
20689                 
20690                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20691                 break;
20692                 
20693             case 13: // enter
20694                 
20695                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20696                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20697                 }
20698                 
20699                 this.hide();
20700                 e.preventDefault();
20701                 break;
20702             case 9: // tab
20703                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20704                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20705                 }
20706                 this.hide();
20707                 break;
20708             case 16: // shift
20709             case 17: // ctrl
20710             case 18: // alt
20711                 break;
20712             default :
20713                 this.hide();
20714                 
20715         }
20716     },
20717     
20718     remove: function() 
20719     {
20720         this.picker().remove();
20721     }
20722    
20723 });
20724
20725 Roo.apply(Roo.bootstrap.MonthField,  {
20726     
20727     content : {
20728         tag: 'tbody',
20729         cn: [
20730         {
20731             tag: 'tr',
20732             cn: [
20733             {
20734                 tag: 'td',
20735                 colspan: '7'
20736             }
20737             ]
20738         }
20739         ]
20740     },
20741     
20742     dates:{
20743         en: {
20744             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20745             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20746         }
20747     }
20748 });
20749
20750 Roo.apply(Roo.bootstrap.MonthField,  {
20751   
20752     template : {
20753         tag: 'div',
20754         cls: 'datepicker dropdown-menu roo-dynamic',
20755         cn: [
20756             {
20757                 tag: 'div',
20758                 cls: 'datepicker-months',
20759                 cn: [
20760                 {
20761                     tag: 'table',
20762                     cls: 'table-condensed',
20763                     cn:[
20764                         Roo.bootstrap.DateField.content
20765                     ]
20766                 }
20767                 ]
20768             }
20769         ]
20770     }
20771 });
20772
20773  
20774
20775  
20776  /*
20777  * - LGPL
20778  *
20779  * CheckBox
20780  * 
20781  */
20782
20783 /**
20784  * @class Roo.bootstrap.CheckBox
20785  * @extends Roo.bootstrap.Input
20786  * Bootstrap CheckBox class
20787  * 
20788  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20789  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20790  * @cfg {String} boxLabel The text that appears beside the checkbox
20791  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20792  * @cfg {Boolean} checked initnal the element
20793  * @cfg {Boolean} inline inline the element (default false)
20794  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20795  * @cfg {String} tooltip label tooltip
20796  * 
20797  * @constructor
20798  * Create a new CheckBox
20799  * @param {Object} config The config object
20800  */
20801
20802 Roo.bootstrap.CheckBox = function(config){
20803     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20804    
20805     this.addEvents({
20806         /**
20807         * @event check
20808         * Fires when the element is checked or unchecked.
20809         * @param {Roo.bootstrap.CheckBox} this This input
20810         * @param {Boolean} checked The new checked value
20811         */
20812        check : true,
20813        /**
20814         * @event click
20815         * Fires when the element is click.
20816         * @param {Roo.bootstrap.CheckBox} this This input
20817         */
20818        click : true
20819     });
20820     
20821 };
20822
20823 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20824   
20825     inputType: 'checkbox',
20826     inputValue: 1,
20827     valueOff: 0,
20828     boxLabel: false,
20829     checked: false,
20830     weight : false,
20831     inline: false,
20832     tooltip : '',
20833     
20834     getAutoCreate : function()
20835     {
20836         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20837         
20838         var id = Roo.id();
20839         
20840         var cfg = {};
20841         
20842         cfg.cls = 'form-group ' + this.inputType; //input-group
20843         
20844         if(this.inline){
20845             cfg.cls += ' ' + this.inputType + '-inline';
20846         }
20847         
20848         var input =  {
20849             tag: 'input',
20850             id : id,
20851             type : this.inputType,
20852             value : this.inputValue,
20853             cls : 'roo-' + this.inputType, //'form-box',
20854             placeholder : this.placeholder || ''
20855             
20856         };
20857         
20858         if(this.inputType != 'radio'){
20859             var hidden =  {
20860                 tag: 'input',
20861                 type : 'hidden',
20862                 cls : 'roo-hidden-value',
20863                 value : this.checked ? this.inputValue : this.valueOff
20864             };
20865         }
20866         
20867             
20868         if (this.weight) { // Validity check?
20869             cfg.cls += " " + this.inputType + "-" + this.weight;
20870         }
20871         
20872         if (this.disabled) {
20873             input.disabled=true;
20874         }
20875         
20876         if(this.checked){
20877             input.checked = this.checked;
20878         }
20879         
20880         if (this.name) {
20881             
20882             input.name = this.name;
20883             
20884             if(this.inputType != 'radio'){
20885                 hidden.name = this.name;
20886                 input.name = '_hidden_' + this.name;
20887             }
20888         }
20889         
20890         if (this.size) {
20891             input.cls += ' input-' + this.size;
20892         }
20893         
20894         var settings=this;
20895         
20896         ['xs','sm','md','lg'].map(function(size){
20897             if (settings[size]) {
20898                 cfg.cls += ' col-' + size + '-' + settings[size];
20899             }
20900         });
20901         
20902         var inputblock = input;
20903          
20904         if (this.before || this.after) {
20905             
20906             inputblock = {
20907                 cls : 'input-group',
20908                 cn :  [] 
20909             };
20910             
20911             if (this.before) {
20912                 inputblock.cn.push({
20913                     tag :'span',
20914                     cls : 'input-group-addon',
20915                     html : this.before
20916                 });
20917             }
20918             
20919             inputblock.cn.push(input);
20920             
20921             if(this.inputType != 'radio'){
20922                 inputblock.cn.push(hidden);
20923             }
20924             
20925             if (this.after) {
20926                 inputblock.cn.push({
20927                     tag :'span',
20928                     cls : 'input-group-addon',
20929                     html : this.after
20930                 });
20931             }
20932             
20933         }
20934         
20935         if (align ==='left' && this.fieldLabel.length) {
20936 //                Roo.log("left and has label");
20937             cfg.cn = [
20938                 {
20939                     tag: 'label',
20940                     'for' :  id,
20941                     cls : 'control-label',
20942                     html : this.fieldLabel
20943                 },
20944                 {
20945                     cls : "", 
20946                     cn: [
20947                         inputblock
20948                     ]
20949                 }
20950             ];
20951             
20952             if(this.labelWidth > 12){
20953                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20954             }
20955             
20956             if(this.labelWidth < 13 && this.labelmd == 0){
20957                 this.labelmd = this.labelWidth;
20958             }
20959             
20960             if(this.labellg > 0){
20961                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20962                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20963             }
20964             
20965             if(this.labelmd > 0){
20966                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20967                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20968             }
20969             
20970             if(this.labelsm > 0){
20971                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20972                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20973             }
20974             
20975             if(this.labelxs > 0){
20976                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20977                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20978             }
20979             
20980         } else if ( this.fieldLabel.length) {
20981 //                Roo.log(" label");
20982                 cfg.cn = [
20983                    
20984                     {
20985                         tag: this.boxLabel ? 'span' : 'label',
20986                         'for': id,
20987                         cls: 'control-label box-input-label',
20988                         //cls : 'input-group-addon',
20989                         html : this.fieldLabel
20990                     },
20991                     
20992                     inputblock
20993                     
20994                 ];
20995
20996         } else {
20997             
20998 //                Roo.log(" no label && no align");
20999                 cfg.cn = [  inputblock ] ;
21000                 
21001                 
21002         }
21003         
21004         if(this.boxLabel){
21005              var boxLabelCfg = {
21006                 tag: 'label',
21007                 //'for': id, // box label is handled by onclick - so no for...
21008                 cls: 'box-label',
21009                 html: this.boxLabel
21010             };
21011             
21012             if(this.tooltip){
21013                 boxLabelCfg.tooltip = this.tooltip;
21014             }
21015              
21016             cfg.cn.push(boxLabelCfg);
21017         }
21018         
21019         if(this.inputType != 'radio'){
21020             cfg.cn.push(hidden);
21021         }
21022         
21023         return cfg;
21024         
21025     },
21026     
21027     /**
21028      * return the real input element.
21029      */
21030     inputEl: function ()
21031     {
21032         return this.el.select('input.roo-' + this.inputType,true).first();
21033     },
21034     hiddenEl: function ()
21035     {
21036         return this.el.select('input.roo-hidden-value',true).first();
21037     },
21038     
21039     labelEl: function()
21040     {
21041         return this.el.select('label.control-label',true).first();
21042     },
21043     /* depricated... */
21044     
21045     label: function()
21046     {
21047         return this.labelEl();
21048     },
21049     
21050     boxLabelEl: function()
21051     {
21052         return this.el.select('label.box-label',true).first();
21053     },
21054     
21055     initEvents : function()
21056     {
21057 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
21058         
21059         this.inputEl().on('click', this.onClick,  this);
21060         
21061         if (this.boxLabel) { 
21062             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
21063         }
21064         
21065         this.startValue = this.getValue();
21066         
21067         if(this.groupId){
21068             Roo.bootstrap.CheckBox.register(this);
21069         }
21070     },
21071     
21072     onClick : function(e)
21073     {   
21074         if(this.fireEvent('click', this, e) !== false){
21075             this.setChecked(!this.checked);
21076         }
21077         
21078     },
21079     
21080     setChecked : function(state,suppressEvent)
21081     {
21082         this.startValue = this.getValue();
21083
21084         if(this.inputType == 'radio'){
21085             
21086             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21087                 e.dom.checked = false;
21088             });
21089             
21090             this.inputEl().dom.checked = true;
21091             
21092             this.inputEl().dom.value = this.inputValue;
21093             
21094             if(suppressEvent !== true){
21095                 this.fireEvent('check', this, true);
21096             }
21097             
21098             this.validate();
21099             
21100             return;
21101         }
21102         
21103         this.checked = state;
21104         
21105         this.inputEl().dom.checked = state;
21106         
21107         
21108         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
21109         
21110         if(suppressEvent !== true){
21111             this.fireEvent('check', this, state);
21112         }
21113         
21114         this.validate();
21115     },
21116     
21117     getValue : function()
21118     {
21119         if(this.inputType == 'radio'){
21120             return this.getGroupValue();
21121         }
21122         
21123         return this.hiddenEl().dom.value;
21124         
21125     },
21126     
21127     getGroupValue : function()
21128     {
21129         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21130             return '';
21131         }
21132         
21133         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21134     },
21135     
21136     setValue : function(v,suppressEvent)
21137     {
21138         if(this.inputType == 'radio'){
21139             this.setGroupValue(v, suppressEvent);
21140             return;
21141         }
21142         
21143         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21144         
21145         this.validate();
21146     },
21147     
21148     setGroupValue : function(v, suppressEvent)
21149     {
21150         this.startValue = this.getValue();
21151         
21152         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21153             e.dom.checked = false;
21154             
21155             if(e.dom.value == v){
21156                 e.dom.checked = true;
21157             }
21158         });
21159         
21160         if(suppressEvent !== true){
21161             this.fireEvent('check', this, true);
21162         }
21163
21164         this.validate();
21165         
21166         return;
21167     },
21168     
21169     validate : function()
21170     {
21171         if(this.getVisibilityEl().hasClass('hidden')){
21172             return true;
21173         }
21174         
21175         if(
21176                 this.disabled || 
21177                 (this.inputType == 'radio' && this.validateRadio()) ||
21178                 (this.inputType == 'checkbox' && this.validateCheckbox())
21179         ){
21180             this.markValid();
21181             return true;
21182         }
21183         
21184         this.markInvalid();
21185         return false;
21186     },
21187     
21188     validateRadio : function()
21189     {
21190         if(this.getVisibilityEl().hasClass('hidden')){
21191             return true;
21192         }
21193         
21194         if(this.allowBlank){
21195             return true;
21196         }
21197         
21198         var valid = false;
21199         
21200         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21201             if(!e.dom.checked){
21202                 return;
21203             }
21204             
21205             valid = true;
21206             
21207             return false;
21208         });
21209         
21210         return valid;
21211     },
21212     
21213     validateCheckbox : function()
21214     {
21215         if(!this.groupId){
21216             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21217             //return (this.getValue() == this.inputValue) ? true : false;
21218         }
21219         
21220         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21221         
21222         if(!group){
21223             return false;
21224         }
21225         
21226         var r = false;
21227         
21228         for(var i in group){
21229             if(group[i].el.isVisible(true)){
21230                 r = false;
21231                 break;
21232             }
21233             
21234             r = true;
21235         }
21236         
21237         for(var i in group){
21238             if(r){
21239                 break;
21240             }
21241             
21242             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21243         }
21244         
21245         return r;
21246     },
21247     
21248     /**
21249      * Mark this field as valid
21250      */
21251     markValid : function()
21252     {
21253         var _this = this;
21254         
21255         this.fireEvent('valid', this);
21256         
21257         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21258         
21259         if(this.groupId){
21260             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21261         }
21262         
21263         if(label){
21264             label.markValid();
21265         }
21266
21267         if(this.inputType == 'radio'){
21268             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21269                 var fg = e.findParent('.form-group', false, true);
21270                 if (Roo.bootstrap.version == 3) {
21271                     fg.removeClass([_this.invalidClass, _this.validClass]);
21272                     fg.addClass(_this.validClass);
21273                 } else {
21274                     fg.removeClass(['is-valid', 'is-invalid']);
21275                     fg.addClass('is-valid');
21276                 }
21277             });
21278             
21279             return;
21280         }
21281
21282         if(!this.groupId){
21283             var fg = this.el.findParent('.form-group', false, true);
21284             if (Roo.bootstrap.version == 3) {
21285                 fg.removeClass([this.invalidClass, this.validClass]);
21286                 fg.addClass(this.validClass);
21287             } else {
21288                 fg.removeClass(['is-valid', 'is-invalid']);
21289                 fg.addClass('is-valid');
21290             }
21291             return;
21292         }
21293         
21294         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21295         
21296         if(!group){
21297             return;
21298         }
21299         
21300         for(var i in group){
21301             var fg = group[i].el.findParent('.form-group', false, true);
21302             if (Roo.bootstrap.version == 3) {
21303                 fg.removeClass([this.invalidClass, this.validClass]);
21304                 fg.addClass(this.validClass);
21305             } else {
21306                 fg.removeClass(['is-valid', 'is-invalid']);
21307                 fg.addClass('is-valid');
21308             }
21309         }
21310     },
21311     
21312      /**
21313      * Mark this field as invalid
21314      * @param {String} msg The validation message
21315      */
21316     markInvalid : function(msg)
21317     {
21318         if(this.allowBlank){
21319             return;
21320         }
21321         
21322         var _this = this;
21323         
21324         this.fireEvent('invalid', this, msg);
21325         
21326         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21327         
21328         if(this.groupId){
21329             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21330         }
21331         
21332         if(label){
21333             label.markInvalid();
21334         }
21335             
21336         if(this.inputType == 'radio'){
21337             
21338             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21339                 var fg = e.findParent('.form-group', false, true);
21340                 if (Roo.bootstrap.version == 3) {
21341                     fg.removeClass([_this.invalidClass, _this.validClass]);
21342                     fg.addClass(_this.invalidClass);
21343                 } else {
21344                     fg.removeClass(['is-invalid', 'is-valid']);
21345                     fg.addClass('is-invalid');
21346                 }
21347             });
21348             
21349             return;
21350         }
21351         
21352         if(!this.groupId){
21353             var fg = this.el.findParent('.form-group', false, true);
21354             if (Roo.bootstrap.version == 3) {
21355                 fg.removeClass([_this.invalidClass, _this.validClass]);
21356                 fg.addClass(_this.invalidClass);
21357             } else {
21358                 fg.removeClass(['is-invalid', 'is-valid']);
21359                 fg.addClass('is-invalid');
21360             }
21361             return;
21362         }
21363         
21364         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21365         
21366         if(!group){
21367             return;
21368         }
21369         
21370         for(var i in group){
21371             var fg = group[i].el.findParent('.form-group', false, true);
21372             if (Roo.bootstrap.version == 3) {
21373                 fg.removeClass([_this.invalidClass, _this.validClass]);
21374                 fg.addClass(_this.invalidClass);
21375             } else {
21376                 fg.removeClass(['is-invalid', 'is-valid']);
21377                 fg.addClass('is-invalid');
21378             }
21379         }
21380         
21381     },
21382     
21383     clearInvalid : function()
21384     {
21385         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21386         
21387         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21388         
21389         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21390         
21391         if (label && label.iconEl) {
21392             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
21393             label.iconEl.removeClass(['is-invalid', 'is-valid']);
21394         }
21395     },
21396     
21397     disable : function()
21398     {
21399         if(this.inputType != 'radio'){
21400             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21401             return;
21402         }
21403         
21404         var _this = this;
21405         
21406         if(this.rendered){
21407             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21408                 _this.getActionEl().addClass(this.disabledClass);
21409                 e.dom.disabled = true;
21410             });
21411         }
21412         
21413         this.disabled = true;
21414         this.fireEvent("disable", this);
21415         return this;
21416     },
21417
21418     enable : function()
21419     {
21420         if(this.inputType != 'radio'){
21421             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21422             return;
21423         }
21424         
21425         var _this = this;
21426         
21427         if(this.rendered){
21428             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21429                 _this.getActionEl().removeClass(this.disabledClass);
21430                 e.dom.disabled = false;
21431             });
21432         }
21433         
21434         this.disabled = false;
21435         this.fireEvent("enable", this);
21436         return this;
21437     },
21438     
21439     setBoxLabel : function(v)
21440     {
21441         this.boxLabel = v;
21442         
21443         if(this.rendered){
21444             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21445         }
21446     }
21447
21448 });
21449
21450 Roo.apply(Roo.bootstrap.CheckBox, {
21451     
21452     groups: {},
21453     
21454      /**
21455     * register a CheckBox Group
21456     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21457     */
21458     register : function(checkbox)
21459     {
21460         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21461             this.groups[checkbox.groupId] = {};
21462         }
21463         
21464         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21465             return;
21466         }
21467         
21468         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21469         
21470     },
21471     /**
21472     * fetch a CheckBox Group based on the group ID
21473     * @param {string} the group ID
21474     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21475     */
21476     get: function(groupId) {
21477         if (typeof(this.groups[groupId]) == 'undefined') {
21478             return false;
21479         }
21480         
21481         return this.groups[groupId] ;
21482     }
21483     
21484     
21485 });
21486 /*
21487  * - LGPL
21488  *
21489  * RadioItem
21490  * 
21491  */
21492
21493 /**
21494  * @class Roo.bootstrap.Radio
21495  * @extends Roo.bootstrap.Component
21496  * Bootstrap Radio class
21497  * @cfg {String} boxLabel - the label associated
21498  * @cfg {String} value - the value of radio
21499  * 
21500  * @constructor
21501  * Create a new Radio
21502  * @param {Object} config The config object
21503  */
21504 Roo.bootstrap.Radio = function(config){
21505     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21506     
21507 };
21508
21509 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21510     
21511     boxLabel : '',
21512     
21513     value : '',
21514     
21515     getAutoCreate : function()
21516     {
21517         var cfg = {
21518             tag : 'div',
21519             cls : 'form-group radio',
21520             cn : [
21521                 {
21522                     tag : 'label',
21523                     cls : 'box-label',
21524                     html : this.boxLabel
21525                 }
21526             ]
21527         };
21528         
21529         return cfg;
21530     },
21531     
21532     initEvents : function() 
21533     {
21534         this.parent().register(this);
21535         
21536         this.el.on('click', this.onClick, this);
21537         
21538     },
21539     
21540     onClick : function(e)
21541     {
21542         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21543             this.setChecked(true);
21544         }
21545     },
21546     
21547     setChecked : function(state, suppressEvent)
21548     {
21549         this.parent().setValue(this.value, suppressEvent);
21550         
21551     },
21552     
21553     setBoxLabel : function(v)
21554     {
21555         this.boxLabel = v;
21556         
21557         if(this.rendered){
21558             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21559         }
21560     }
21561     
21562 });
21563  
21564
21565  /*
21566  * - LGPL
21567  *
21568  * Input
21569  * 
21570  */
21571
21572 /**
21573  * @class Roo.bootstrap.SecurePass
21574  * @extends Roo.bootstrap.Input
21575  * Bootstrap SecurePass class
21576  *
21577  * 
21578  * @constructor
21579  * Create a new SecurePass
21580  * @param {Object} config The config object
21581  */
21582  
21583 Roo.bootstrap.SecurePass = function (config) {
21584     // these go here, so the translation tool can replace them..
21585     this.errors = {
21586         PwdEmpty: "Please type a password, and then retype it to confirm.",
21587         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21588         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21589         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21590         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21591         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21592         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21593         TooWeak: "Your password is Too Weak."
21594     },
21595     this.meterLabel = "Password strength:";
21596     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21597     this.meterClass = [
21598         "roo-password-meter-tooweak", 
21599         "roo-password-meter-weak", 
21600         "roo-password-meter-medium", 
21601         "roo-password-meter-strong", 
21602         "roo-password-meter-grey"
21603     ];
21604     
21605     this.errors = {};
21606     
21607     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21608 }
21609
21610 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21611     /**
21612      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21613      * {
21614      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21615      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21616      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21617      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21618      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21619      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21620      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21621      * })
21622      */
21623     // private
21624     
21625     meterWidth: 300,
21626     errorMsg :'',    
21627     errors: false,
21628     imageRoot: '/',
21629     /**
21630      * @cfg {String/Object} Label for the strength meter (defaults to
21631      * 'Password strength:')
21632      */
21633     // private
21634     meterLabel: '',
21635     /**
21636      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21637      * ['Weak', 'Medium', 'Strong'])
21638      */
21639     // private    
21640     pwdStrengths: false,    
21641     // private
21642     strength: 0,
21643     // private
21644     _lastPwd: null,
21645     // private
21646     kCapitalLetter: 0,
21647     kSmallLetter: 1,
21648     kDigit: 2,
21649     kPunctuation: 3,
21650     
21651     insecure: false,
21652     // private
21653     initEvents: function ()
21654     {
21655         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21656
21657         if (this.el.is('input[type=password]') && Roo.isSafari) {
21658             this.el.on('keydown', this.SafariOnKeyDown, this);
21659         }
21660
21661         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21662     },
21663     // private
21664     onRender: function (ct, position)
21665     {
21666         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21667         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21668         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21669
21670         this.trigger.createChild({
21671                    cn: [
21672                     {
21673                     //id: 'PwdMeter',
21674                     tag: 'div',
21675                     cls: 'roo-password-meter-grey col-xs-12',
21676                     style: {
21677                         //width: 0,
21678                         //width: this.meterWidth + 'px'                                                
21679                         }
21680                     },
21681                     {                            
21682                          cls: 'roo-password-meter-text'                          
21683                     }
21684                 ]            
21685         });
21686
21687          
21688         if (this.hideTrigger) {
21689             this.trigger.setDisplayed(false);
21690         }
21691         this.setSize(this.width || '', this.height || '');
21692     },
21693     // private
21694     onDestroy: function ()
21695     {
21696         if (this.trigger) {
21697             this.trigger.removeAllListeners();
21698             this.trigger.remove();
21699         }
21700         if (this.wrap) {
21701             this.wrap.remove();
21702         }
21703         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21704     },
21705     // private
21706     checkStrength: function ()
21707     {
21708         var pwd = this.inputEl().getValue();
21709         if (pwd == this._lastPwd) {
21710             return;
21711         }
21712
21713         var strength;
21714         if (this.ClientSideStrongPassword(pwd)) {
21715             strength = 3;
21716         } else if (this.ClientSideMediumPassword(pwd)) {
21717             strength = 2;
21718         } else if (this.ClientSideWeakPassword(pwd)) {
21719             strength = 1;
21720         } else {
21721             strength = 0;
21722         }
21723         
21724         Roo.log('strength1: ' + strength);
21725         
21726         //var pm = this.trigger.child('div/div/div').dom;
21727         var pm = this.trigger.child('div/div');
21728         pm.removeClass(this.meterClass);
21729         pm.addClass(this.meterClass[strength]);
21730                 
21731         
21732         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21733                 
21734         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21735         
21736         this._lastPwd = pwd;
21737     },
21738     reset: function ()
21739     {
21740         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21741         
21742         this._lastPwd = '';
21743         
21744         var pm = this.trigger.child('div/div');
21745         pm.removeClass(this.meterClass);
21746         pm.addClass('roo-password-meter-grey');        
21747         
21748         
21749         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21750         
21751         pt.innerHTML = '';
21752         this.inputEl().dom.type='password';
21753     },
21754     // private
21755     validateValue: function (value)
21756     {
21757         
21758         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21759             return false;
21760         }
21761         if (value.length == 0) {
21762             if (this.allowBlank) {
21763                 this.clearInvalid();
21764                 return true;
21765             }
21766
21767             this.markInvalid(this.errors.PwdEmpty);
21768             this.errorMsg = this.errors.PwdEmpty;
21769             return false;
21770         }
21771         
21772         if(this.insecure){
21773             return true;
21774         }
21775         
21776         if ('[\x21-\x7e]*'.match(value)) {
21777             this.markInvalid(this.errors.PwdBadChar);
21778             this.errorMsg = this.errors.PwdBadChar;
21779             return false;
21780         }
21781         if (value.length < 6) {
21782             this.markInvalid(this.errors.PwdShort);
21783             this.errorMsg = this.errors.PwdShort;
21784             return false;
21785         }
21786         if (value.length > 16) {
21787             this.markInvalid(this.errors.PwdLong);
21788             this.errorMsg = this.errors.PwdLong;
21789             return false;
21790         }
21791         var strength;
21792         if (this.ClientSideStrongPassword(value)) {
21793             strength = 3;
21794         } else if (this.ClientSideMediumPassword(value)) {
21795             strength = 2;
21796         } else if (this.ClientSideWeakPassword(value)) {
21797             strength = 1;
21798         } else {
21799             strength = 0;
21800         }
21801
21802         
21803         if (strength < 2) {
21804             //this.markInvalid(this.errors.TooWeak);
21805             this.errorMsg = this.errors.TooWeak;
21806             //return false;
21807         }
21808         
21809         
21810         console.log('strength2: ' + strength);
21811         
21812         //var pm = this.trigger.child('div/div/div').dom;
21813         
21814         var pm = this.trigger.child('div/div');
21815         pm.removeClass(this.meterClass);
21816         pm.addClass(this.meterClass[strength]);
21817                 
21818         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21819                 
21820         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21821         
21822         this.errorMsg = ''; 
21823         return true;
21824     },
21825     // private
21826     CharacterSetChecks: function (type)
21827     {
21828         this.type = type;
21829         this.fResult = false;
21830     },
21831     // private
21832     isctype: function (character, type)
21833     {
21834         switch (type) {  
21835             case this.kCapitalLetter:
21836                 if (character >= 'A' && character <= 'Z') {
21837                     return true;
21838                 }
21839                 break;
21840             
21841             case this.kSmallLetter:
21842                 if (character >= 'a' && character <= 'z') {
21843                     return true;
21844                 }
21845                 break;
21846             
21847             case this.kDigit:
21848                 if (character >= '0' && character <= '9') {
21849                     return true;
21850                 }
21851                 break;
21852             
21853             case this.kPunctuation:
21854                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21855                     return true;
21856                 }
21857                 break;
21858             
21859             default:
21860                 return false;
21861         }
21862
21863     },
21864     // private
21865     IsLongEnough: function (pwd, size)
21866     {
21867         return !(pwd == null || isNaN(size) || pwd.length < size);
21868     },
21869     // private
21870     SpansEnoughCharacterSets: function (word, nb)
21871     {
21872         if (!this.IsLongEnough(word, nb))
21873         {
21874             return false;
21875         }
21876
21877         var characterSetChecks = new Array(
21878             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21879             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21880         );
21881         
21882         for (var index = 0; index < word.length; ++index) {
21883             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21884                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21885                     characterSetChecks[nCharSet].fResult = true;
21886                     break;
21887                 }
21888             }
21889         }
21890
21891         var nCharSets = 0;
21892         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21893             if (characterSetChecks[nCharSet].fResult) {
21894                 ++nCharSets;
21895             }
21896         }
21897
21898         if (nCharSets < nb) {
21899             return false;
21900         }
21901         return true;
21902     },
21903     // private
21904     ClientSideStrongPassword: function (pwd)
21905     {
21906         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21907     },
21908     // private
21909     ClientSideMediumPassword: function (pwd)
21910     {
21911         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21912     },
21913     // private
21914     ClientSideWeakPassword: function (pwd)
21915     {
21916         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21917     }
21918           
21919 })//<script type="text/javascript">
21920
21921 /*
21922  * Based  Ext JS Library 1.1.1
21923  * Copyright(c) 2006-2007, Ext JS, LLC.
21924  * LGPL
21925  *
21926  */
21927  
21928 /**
21929  * @class Roo.HtmlEditorCore
21930  * @extends Roo.Component
21931  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21932  *
21933  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21934  */
21935
21936 Roo.HtmlEditorCore = function(config){
21937     
21938     
21939     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21940     
21941     
21942     this.addEvents({
21943         /**
21944          * @event initialize
21945          * Fires when the editor is fully initialized (including the iframe)
21946          * @param {Roo.HtmlEditorCore} this
21947          */
21948         initialize: true,
21949         /**
21950          * @event activate
21951          * Fires when the editor is first receives the focus. Any insertion must wait
21952          * until after this event.
21953          * @param {Roo.HtmlEditorCore} this
21954          */
21955         activate: true,
21956          /**
21957          * @event beforesync
21958          * Fires before the textarea is updated with content from the editor iframe. Return false
21959          * to cancel the sync.
21960          * @param {Roo.HtmlEditorCore} this
21961          * @param {String} html
21962          */
21963         beforesync: true,
21964          /**
21965          * @event beforepush
21966          * Fires before the iframe editor is updated with content from the textarea. Return false
21967          * to cancel the push.
21968          * @param {Roo.HtmlEditorCore} this
21969          * @param {String} html
21970          */
21971         beforepush: true,
21972          /**
21973          * @event sync
21974          * Fires when the textarea is updated with content from the editor iframe.
21975          * @param {Roo.HtmlEditorCore} this
21976          * @param {String} html
21977          */
21978         sync: true,
21979          /**
21980          * @event push
21981          * Fires when the iframe editor is updated with content from the textarea.
21982          * @param {Roo.HtmlEditorCore} this
21983          * @param {String} html
21984          */
21985         push: true,
21986         
21987         /**
21988          * @event editorevent
21989          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21990          * @param {Roo.HtmlEditorCore} this
21991          */
21992         editorevent: true
21993         
21994     });
21995     
21996     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21997     
21998     // defaults : white / black...
21999     this.applyBlacklists();
22000     
22001     
22002     
22003 };
22004
22005
22006 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
22007
22008
22009      /**
22010      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
22011      */
22012     
22013     owner : false,
22014     
22015      /**
22016      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22017      *                        Roo.resizable.
22018      */
22019     resizable : false,
22020      /**
22021      * @cfg {Number} height (in pixels)
22022      */   
22023     height: 300,
22024    /**
22025      * @cfg {Number} width (in pixels)
22026      */   
22027     width: 500,
22028     
22029     /**
22030      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22031      * 
22032      */
22033     stylesheets: false,
22034     
22035     // id of frame..
22036     frameId: false,
22037     
22038     // private properties
22039     validationEvent : false,
22040     deferHeight: true,
22041     initialized : false,
22042     activated : false,
22043     sourceEditMode : false,
22044     onFocus : Roo.emptyFn,
22045     iframePad:3,
22046     hideMode:'offsets',
22047     
22048     clearUp: true,
22049     
22050     // blacklist + whitelisted elements..
22051     black: false,
22052     white: false,
22053      
22054     bodyCls : '',
22055
22056     /**
22057      * Protected method that will not generally be called directly. It
22058      * is called when the editor initializes the iframe with HTML contents. Override this method if you
22059      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
22060      */
22061     getDocMarkup : function(){
22062         // body styles..
22063         var st = '';
22064         
22065         // inherit styels from page...?? 
22066         if (this.stylesheets === false) {
22067             
22068             Roo.get(document.head).select('style').each(function(node) {
22069                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22070             });
22071             
22072             Roo.get(document.head).select('link').each(function(node) { 
22073                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22074             });
22075             
22076         } else if (!this.stylesheets.length) {
22077                 // simple..
22078                 st = '<style type="text/css">' +
22079                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22080                    '</style>';
22081         } else { 
22082             st = '<style type="text/css">' +
22083                     this.stylesheets +
22084                 '</style>';
22085         }
22086         
22087         st +=  '<style type="text/css">' +
22088             'IMG { cursor: pointer } ' +
22089         '</style>';
22090
22091         var cls = 'roo-htmleditor-body';
22092         
22093         if(this.bodyCls.length){
22094             cls += ' ' + this.bodyCls;
22095         }
22096         
22097         return '<html><head>' + st  +
22098             //<style type="text/css">' +
22099             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22100             //'</style>' +
22101             ' </head><body class="' +  cls + '"></body></html>';
22102     },
22103
22104     // private
22105     onRender : function(ct, position)
22106     {
22107         var _t = this;
22108         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
22109         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
22110         
22111         
22112         this.el.dom.style.border = '0 none';
22113         this.el.dom.setAttribute('tabIndex', -1);
22114         this.el.addClass('x-hidden hide');
22115         
22116         
22117         
22118         if(Roo.isIE){ // fix IE 1px bogus margin
22119             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
22120         }
22121        
22122         
22123         this.frameId = Roo.id();
22124         
22125          
22126         
22127         var iframe = this.owner.wrap.createChild({
22128             tag: 'iframe',
22129             cls: 'form-control', // bootstrap..
22130             id: this.frameId,
22131             name: this.frameId,
22132             frameBorder : 'no',
22133             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
22134         }, this.el
22135         );
22136         
22137         
22138         this.iframe = iframe.dom;
22139
22140          this.assignDocWin();
22141         
22142         this.doc.designMode = 'on';
22143        
22144         this.doc.open();
22145         this.doc.write(this.getDocMarkup());
22146         this.doc.close();
22147
22148         
22149         var task = { // must defer to wait for browser to be ready
22150             run : function(){
22151                 //console.log("run task?" + this.doc.readyState);
22152                 this.assignDocWin();
22153                 if(this.doc.body || this.doc.readyState == 'complete'){
22154                     try {
22155                         this.doc.designMode="on";
22156                     } catch (e) {
22157                         return;
22158                     }
22159                     Roo.TaskMgr.stop(task);
22160                     this.initEditor.defer(10, this);
22161                 }
22162             },
22163             interval : 10,
22164             duration: 10000,
22165             scope: this
22166         };
22167         Roo.TaskMgr.start(task);
22168
22169     },
22170
22171     // private
22172     onResize : function(w, h)
22173     {
22174          Roo.log('resize: ' +w + ',' + h );
22175         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22176         if(!this.iframe){
22177             return;
22178         }
22179         if(typeof w == 'number'){
22180             
22181             this.iframe.style.width = w + 'px';
22182         }
22183         if(typeof h == 'number'){
22184             
22185             this.iframe.style.height = h + 'px';
22186             if(this.doc){
22187                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22188             }
22189         }
22190         
22191     },
22192
22193     /**
22194      * Toggles the editor between standard and source edit mode.
22195      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22196      */
22197     toggleSourceEdit : function(sourceEditMode){
22198         
22199         this.sourceEditMode = sourceEditMode === true;
22200         
22201         if(this.sourceEditMode){
22202  
22203             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
22204             
22205         }else{
22206             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22207             //this.iframe.className = '';
22208             this.deferFocus();
22209         }
22210         //this.setSize(this.owner.wrap.getSize());
22211         //this.fireEvent('editmodechange', this, this.sourceEditMode);
22212     },
22213
22214     
22215   
22216
22217     /**
22218      * Protected method that will not generally be called directly. If you need/want
22219      * custom HTML cleanup, this is the method you should override.
22220      * @param {String} html The HTML to be cleaned
22221      * return {String} The cleaned HTML
22222      */
22223     cleanHtml : function(html){
22224         html = String(html);
22225         if(html.length > 5){
22226             if(Roo.isSafari){ // strip safari nonsense
22227                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22228             }
22229         }
22230         if(html == '&nbsp;'){
22231             html = '';
22232         }
22233         return html;
22234     },
22235
22236     /**
22237      * HTML Editor -> Textarea
22238      * Protected method that will not generally be called directly. Syncs the contents
22239      * of the editor iframe with the textarea.
22240      */
22241     syncValue : function(){
22242         if(this.initialized){
22243             var bd = (this.doc.body || this.doc.documentElement);
22244             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22245             var html = bd.innerHTML;
22246             if(Roo.isSafari){
22247                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22248                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22249                 if(m && m[1]){
22250                     html = '<div style="'+m[0]+'">' + html + '</div>';
22251                 }
22252             }
22253             html = this.cleanHtml(html);
22254             // fix up the special chars.. normaly like back quotes in word...
22255             // however we do not want to do this with chinese..
22256             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22257                 var cc = b.charCodeAt();
22258                 if (
22259                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22260                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22261                     (cc >= 0xf900 && cc < 0xfb00 )
22262                 ) {
22263                         return b;
22264                 }
22265                 return "&#"+cc+";" 
22266             });
22267             if(this.owner.fireEvent('beforesync', this, html) !== false){
22268                 this.el.dom.value = html;
22269                 this.owner.fireEvent('sync', this, html);
22270             }
22271         }
22272     },
22273
22274     /**
22275      * Protected method that will not generally be called directly. Pushes the value of the textarea
22276      * into the iframe editor.
22277      */
22278     pushValue : function(){
22279         if(this.initialized){
22280             var v = this.el.dom.value.trim();
22281             
22282 //            if(v.length < 1){
22283 //                v = '&#160;';
22284 //            }
22285             
22286             if(this.owner.fireEvent('beforepush', this, v) !== false){
22287                 var d = (this.doc.body || this.doc.documentElement);
22288                 d.innerHTML = v;
22289                 this.cleanUpPaste();
22290                 this.el.dom.value = d.innerHTML;
22291                 this.owner.fireEvent('push', this, v);
22292             }
22293         }
22294     },
22295
22296     // private
22297     deferFocus : function(){
22298         this.focus.defer(10, this);
22299     },
22300
22301     // doc'ed in Field
22302     focus : function(){
22303         if(this.win && !this.sourceEditMode){
22304             this.win.focus();
22305         }else{
22306             this.el.focus();
22307         }
22308     },
22309     
22310     assignDocWin: function()
22311     {
22312         var iframe = this.iframe;
22313         
22314          if(Roo.isIE){
22315             this.doc = iframe.contentWindow.document;
22316             this.win = iframe.contentWindow;
22317         } else {
22318 //            if (!Roo.get(this.frameId)) {
22319 //                return;
22320 //            }
22321 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22322 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22323             
22324             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22325                 return;
22326             }
22327             
22328             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22329             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22330         }
22331     },
22332     
22333     // private
22334     initEditor : function(){
22335         //console.log("INIT EDITOR");
22336         this.assignDocWin();
22337         
22338         
22339         
22340         this.doc.designMode="on";
22341         this.doc.open();
22342         this.doc.write(this.getDocMarkup());
22343         this.doc.close();
22344         
22345         var dbody = (this.doc.body || this.doc.documentElement);
22346         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22347         // this copies styles from the containing element into thsi one..
22348         // not sure why we need all of this..
22349         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22350         
22351         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22352         //ss['background-attachment'] = 'fixed'; // w3c
22353         dbody.bgProperties = 'fixed'; // ie
22354         //Roo.DomHelper.applyStyles(dbody, ss);
22355         Roo.EventManager.on(this.doc, {
22356             //'mousedown': this.onEditorEvent,
22357             'mouseup': this.onEditorEvent,
22358             'dblclick': this.onEditorEvent,
22359             'click': this.onEditorEvent,
22360             'keyup': this.onEditorEvent,
22361             buffer:100,
22362             scope: this
22363         });
22364         if(Roo.isGecko){
22365             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22366         }
22367         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22368             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22369         }
22370         this.initialized = true;
22371
22372         this.owner.fireEvent('initialize', this);
22373         this.pushValue();
22374     },
22375
22376     // private
22377     onDestroy : function(){
22378         
22379         
22380         
22381         if(this.rendered){
22382             
22383             //for (var i =0; i < this.toolbars.length;i++) {
22384             //    // fixme - ask toolbars for heights?
22385             //    this.toolbars[i].onDestroy();
22386            // }
22387             
22388             //this.wrap.dom.innerHTML = '';
22389             //this.wrap.remove();
22390         }
22391     },
22392
22393     // private
22394     onFirstFocus : function(){
22395         
22396         this.assignDocWin();
22397         
22398         
22399         this.activated = true;
22400          
22401     
22402         if(Roo.isGecko){ // prevent silly gecko errors
22403             this.win.focus();
22404             var s = this.win.getSelection();
22405             if(!s.focusNode || s.focusNode.nodeType != 3){
22406                 var r = s.getRangeAt(0);
22407                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22408                 r.collapse(true);
22409                 this.deferFocus();
22410             }
22411             try{
22412                 this.execCmd('useCSS', true);
22413                 this.execCmd('styleWithCSS', false);
22414             }catch(e){}
22415         }
22416         this.owner.fireEvent('activate', this);
22417     },
22418
22419     // private
22420     adjustFont: function(btn){
22421         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22422         //if(Roo.isSafari){ // safari
22423         //    adjust *= 2;
22424        // }
22425         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22426         if(Roo.isSafari){ // safari
22427             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22428             v =  (v < 10) ? 10 : v;
22429             v =  (v > 48) ? 48 : v;
22430             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22431             
22432         }
22433         
22434         
22435         v = Math.max(1, v+adjust);
22436         
22437         this.execCmd('FontSize', v  );
22438     },
22439
22440     onEditorEvent : function(e)
22441     {
22442         this.owner.fireEvent('editorevent', this, e);
22443       //  this.updateToolbar();
22444         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22445     },
22446
22447     insertTag : function(tg)
22448     {
22449         // could be a bit smarter... -> wrap the current selected tRoo..
22450         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22451             
22452             range = this.createRange(this.getSelection());
22453             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22454             wrappingNode.appendChild(range.extractContents());
22455             range.insertNode(wrappingNode);
22456
22457             return;
22458             
22459             
22460             
22461         }
22462         this.execCmd("formatblock",   tg);
22463         
22464     },
22465     
22466     insertText : function(txt)
22467     {
22468         
22469         
22470         var range = this.createRange();
22471         range.deleteContents();
22472                //alert(Sender.getAttribute('label'));
22473                
22474         range.insertNode(this.doc.createTextNode(txt));
22475     } ,
22476     
22477      
22478
22479     /**
22480      * Executes a Midas editor command on the editor document and performs necessary focus and
22481      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22482      * @param {String} cmd The Midas command
22483      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22484      */
22485     relayCmd : function(cmd, value){
22486         this.win.focus();
22487         this.execCmd(cmd, value);
22488         this.owner.fireEvent('editorevent', this);
22489         //this.updateToolbar();
22490         this.owner.deferFocus();
22491     },
22492
22493     /**
22494      * Executes a Midas editor command directly on the editor document.
22495      * For visual commands, you should use {@link #relayCmd} instead.
22496      * <b>This should only be called after the editor is initialized.</b>
22497      * @param {String} cmd The Midas command
22498      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22499      */
22500     execCmd : function(cmd, value){
22501         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22502         this.syncValue();
22503     },
22504  
22505  
22506    
22507     /**
22508      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22509      * to insert tRoo.
22510      * @param {String} text | dom node.. 
22511      */
22512     insertAtCursor : function(text)
22513     {
22514         
22515         if(!this.activated){
22516             return;
22517         }
22518         /*
22519         if(Roo.isIE){
22520             this.win.focus();
22521             var r = this.doc.selection.createRange();
22522             if(r){
22523                 r.collapse(true);
22524                 r.pasteHTML(text);
22525                 this.syncValue();
22526                 this.deferFocus();
22527             
22528             }
22529             return;
22530         }
22531         */
22532         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22533             this.win.focus();
22534             
22535             
22536             // from jquery ui (MIT licenced)
22537             var range, node;
22538             var win = this.win;
22539             
22540             if (win.getSelection && win.getSelection().getRangeAt) {
22541                 range = win.getSelection().getRangeAt(0);
22542                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22543                 range.insertNode(node);
22544             } else if (win.document.selection && win.document.selection.createRange) {
22545                 // no firefox support
22546                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22547                 win.document.selection.createRange().pasteHTML(txt);
22548             } else {
22549                 // no firefox support
22550                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22551                 this.execCmd('InsertHTML', txt);
22552             } 
22553             
22554             this.syncValue();
22555             
22556             this.deferFocus();
22557         }
22558     },
22559  // private
22560     mozKeyPress : function(e){
22561         if(e.ctrlKey){
22562             var c = e.getCharCode(), cmd;
22563           
22564             if(c > 0){
22565                 c = String.fromCharCode(c).toLowerCase();
22566                 switch(c){
22567                     case 'b':
22568                         cmd = 'bold';
22569                         break;
22570                     case 'i':
22571                         cmd = 'italic';
22572                         break;
22573                     
22574                     case 'u':
22575                         cmd = 'underline';
22576                         break;
22577                     
22578                     case 'v':
22579                         this.cleanUpPaste.defer(100, this);
22580                         return;
22581                         
22582                 }
22583                 if(cmd){
22584                     this.win.focus();
22585                     this.execCmd(cmd);
22586                     this.deferFocus();
22587                     e.preventDefault();
22588                 }
22589                 
22590             }
22591         }
22592     },
22593
22594     // private
22595     fixKeys : function(){ // load time branching for fastest keydown performance
22596         if(Roo.isIE){
22597             return function(e){
22598                 var k = e.getKey(), r;
22599                 if(k == e.TAB){
22600                     e.stopEvent();
22601                     r = this.doc.selection.createRange();
22602                     if(r){
22603                         r.collapse(true);
22604                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22605                         this.deferFocus();
22606                     }
22607                     return;
22608                 }
22609                 
22610                 if(k == e.ENTER){
22611                     r = this.doc.selection.createRange();
22612                     if(r){
22613                         var target = r.parentElement();
22614                         if(!target || target.tagName.toLowerCase() != 'li'){
22615                             e.stopEvent();
22616                             r.pasteHTML('<br />');
22617                             r.collapse(false);
22618                             r.select();
22619                         }
22620                     }
22621                 }
22622                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22623                     this.cleanUpPaste.defer(100, this);
22624                     return;
22625                 }
22626                 
22627                 
22628             };
22629         }else if(Roo.isOpera){
22630             return function(e){
22631                 var k = e.getKey();
22632                 if(k == e.TAB){
22633                     e.stopEvent();
22634                     this.win.focus();
22635                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22636                     this.deferFocus();
22637                 }
22638                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22639                     this.cleanUpPaste.defer(100, this);
22640                     return;
22641                 }
22642                 
22643             };
22644         }else if(Roo.isSafari){
22645             return function(e){
22646                 var k = e.getKey();
22647                 
22648                 if(k == e.TAB){
22649                     e.stopEvent();
22650                     this.execCmd('InsertText','\t');
22651                     this.deferFocus();
22652                     return;
22653                 }
22654                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22655                     this.cleanUpPaste.defer(100, this);
22656                     return;
22657                 }
22658                 
22659              };
22660         }
22661     }(),
22662     
22663     getAllAncestors: function()
22664     {
22665         var p = this.getSelectedNode();
22666         var a = [];
22667         if (!p) {
22668             a.push(p); // push blank onto stack..
22669             p = this.getParentElement();
22670         }
22671         
22672         
22673         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22674             a.push(p);
22675             p = p.parentNode;
22676         }
22677         a.push(this.doc.body);
22678         return a;
22679     },
22680     lastSel : false,
22681     lastSelNode : false,
22682     
22683     
22684     getSelection : function() 
22685     {
22686         this.assignDocWin();
22687         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22688     },
22689     
22690     getSelectedNode: function() 
22691     {
22692         // this may only work on Gecko!!!
22693         
22694         // should we cache this!!!!
22695         
22696         
22697         
22698          
22699         var range = this.createRange(this.getSelection()).cloneRange();
22700         
22701         if (Roo.isIE) {
22702             var parent = range.parentElement();
22703             while (true) {
22704                 var testRange = range.duplicate();
22705                 testRange.moveToElementText(parent);
22706                 if (testRange.inRange(range)) {
22707                     break;
22708                 }
22709                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22710                     break;
22711                 }
22712                 parent = parent.parentElement;
22713             }
22714             return parent;
22715         }
22716         
22717         // is ancestor a text element.
22718         var ac =  range.commonAncestorContainer;
22719         if (ac.nodeType == 3) {
22720             ac = ac.parentNode;
22721         }
22722         
22723         var ar = ac.childNodes;
22724          
22725         var nodes = [];
22726         var other_nodes = [];
22727         var has_other_nodes = false;
22728         for (var i=0;i<ar.length;i++) {
22729             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22730                 continue;
22731             }
22732             // fullly contained node.
22733             
22734             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22735                 nodes.push(ar[i]);
22736                 continue;
22737             }
22738             
22739             // probably selected..
22740             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22741                 other_nodes.push(ar[i]);
22742                 continue;
22743             }
22744             // outer..
22745             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22746                 continue;
22747             }
22748             
22749             
22750             has_other_nodes = true;
22751         }
22752         if (!nodes.length && other_nodes.length) {
22753             nodes= other_nodes;
22754         }
22755         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22756             return false;
22757         }
22758         
22759         return nodes[0];
22760     },
22761     createRange: function(sel)
22762     {
22763         // this has strange effects when using with 
22764         // top toolbar - not sure if it's a great idea.
22765         //this.editor.contentWindow.focus();
22766         if (typeof sel != "undefined") {
22767             try {
22768                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22769             } catch(e) {
22770                 return this.doc.createRange();
22771             }
22772         } else {
22773             return this.doc.createRange();
22774         }
22775     },
22776     getParentElement: function()
22777     {
22778         
22779         this.assignDocWin();
22780         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22781         
22782         var range = this.createRange(sel);
22783          
22784         try {
22785             var p = range.commonAncestorContainer;
22786             while (p.nodeType == 3) { // text node
22787                 p = p.parentNode;
22788             }
22789             return p;
22790         } catch (e) {
22791             return null;
22792         }
22793     
22794     },
22795     /***
22796      *
22797      * Range intersection.. the hard stuff...
22798      *  '-1' = before
22799      *  '0' = hits..
22800      *  '1' = after.
22801      *         [ -- selected range --- ]
22802      *   [fail]                        [fail]
22803      *
22804      *    basically..
22805      *      if end is before start or  hits it. fail.
22806      *      if start is after end or hits it fail.
22807      *
22808      *   if either hits (but other is outside. - then it's not 
22809      *   
22810      *    
22811      **/
22812     
22813     
22814     // @see http://www.thismuchiknow.co.uk/?p=64.
22815     rangeIntersectsNode : function(range, node)
22816     {
22817         var nodeRange = node.ownerDocument.createRange();
22818         try {
22819             nodeRange.selectNode(node);
22820         } catch (e) {
22821             nodeRange.selectNodeContents(node);
22822         }
22823     
22824         var rangeStartRange = range.cloneRange();
22825         rangeStartRange.collapse(true);
22826     
22827         var rangeEndRange = range.cloneRange();
22828         rangeEndRange.collapse(false);
22829     
22830         var nodeStartRange = nodeRange.cloneRange();
22831         nodeStartRange.collapse(true);
22832     
22833         var nodeEndRange = nodeRange.cloneRange();
22834         nodeEndRange.collapse(false);
22835     
22836         return rangeStartRange.compareBoundaryPoints(
22837                  Range.START_TO_START, nodeEndRange) == -1 &&
22838                rangeEndRange.compareBoundaryPoints(
22839                  Range.START_TO_START, nodeStartRange) == 1;
22840         
22841          
22842     },
22843     rangeCompareNode : function(range, node)
22844     {
22845         var nodeRange = node.ownerDocument.createRange();
22846         try {
22847             nodeRange.selectNode(node);
22848         } catch (e) {
22849             nodeRange.selectNodeContents(node);
22850         }
22851         
22852         
22853         range.collapse(true);
22854     
22855         nodeRange.collapse(true);
22856      
22857         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22858         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22859          
22860         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22861         
22862         var nodeIsBefore   =  ss == 1;
22863         var nodeIsAfter    = ee == -1;
22864         
22865         if (nodeIsBefore && nodeIsAfter) {
22866             return 0; // outer
22867         }
22868         if (!nodeIsBefore && nodeIsAfter) {
22869             return 1; //right trailed.
22870         }
22871         
22872         if (nodeIsBefore && !nodeIsAfter) {
22873             return 2;  // left trailed.
22874         }
22875         // fully contined.
22876         return 3;
22877     },
22878
22879     // private? - in a new class?
22880     cleanUpPaste :  function()
22881     {
22882         // cleans up the whole document..
22883         Roo.log('cleanuppaste');
22884         
22885         this.cleanUpChildren(this.doc.body);
22886         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22887         if (clean != this.doc.body.innerHTML) {
22888             this.doc.body.innerHTML = clean;
22889         }
22890         
22891     },
22892     
22893     cleanWordChars : function(input) {// change the chars to hex code
22894         var he = Roo.HtmlEditorCore;
22895         
22896         var output = input;
22897         Roo.each(he.swapCodes, function(sw) { 
22898             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22899             
22900             output = output.replace(swapper, sw[1]);
22901         });
22902         
22903         return output;
22904     },
22905     
22906     
22907     cleanUpChildren : function (n)
22908     {
22909         if (!n.childNodes.length) {
22910             return;
22911         }
22912         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22913            this.cleanUpChild(n.childNodes[i]);
22914         }
22915     },
22916     
22917     
22918         
22919     
22920     cleanUpChild : function (node)
22921     {
22922         var ed = this;
22923         //console.log(node);
22924         if (node.nodeName == "#text") {
22925             // clean up silly Windows -- stuff?
22926             return; 
22927         }
22928         if (node.nodeName == "#comment") {
22929             node.parentNode.removeChild(node);
22930             // clean up silly Windows -- stuff?
22931             return; 
22932         }
22933         var lcname = node.tagName.toLowerCase();
22934         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22935         // whitelist of tags..
22936         
22937         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22938             // remove node.
22939             node.parentNode.removeChild(node);
22940             return;
22941             
22942         }
22943         
22944         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22945         
22946         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22947         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22948         
22949         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22950         //    remove_keep_children = true;
22951         //}
22952         
22953         if (remove_keep_children) {
22954             this.cleanUpChildren(node);
22955             // inserts everything just before this node...
22956             while (node.childNodes.length) {
22957                 var cn = node.childNodes[0];
22958                 node.removeChild(cn);
22959                 node.parentNode.insertBefore(cn, node);
22960             }
22961             node.parentNode.removeChild(node);
22962             return;
22963         }
22964         
22965         if (!node.attributes || !node.attributes.length) {
22966             this.cleanUpChildren(node);
22967             return;
22968         }
22969         
22970         function cleanAttr(n,v)
22971         {
22972             
22973             if (v.match(/^\./) || v.match(/^\//)) {
22974                 return;
22975             }
22976             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22977                 return;
22978             }
22979             if (v.match(/^#/)) {
22980                 return;
22981             }
22982 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22983             node.removeAttribute(n);
22984             
22985         }
22986         
22987         var cwhite = this.cwhite;
22988         var cblack = this.cblack;
22989             
22990         function cleanStyle(n,v)
22991         {
22992             if (v.match(/expression/)) { //XSS?? should we even bother..
22993                 node.removeAttribute(n);
22994                 return;
22995             }
22996             
22997             var parts = v.split(/;/);
22998             var clean = [];
22999             
23000             Roo.each(parts, function(p) {
23001                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
23002                 if (!p.length) {
23003                     return true;
23004                 }
23005                 var l = p.split(':').shift().replace(/\s+/g,'');
23006                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
23007                 
23008                 if ( cwhite.length && cblack.indexOf(l) > -1) {
23009 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23010                     //node.removeAttribute(n);
23011                     return true;
23012                 }
23013                 //Roo.log()
23014                 // only allow 'c whitelisted system attributes'
23015                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
23016 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23017                     //node.removeAttribute(n);
23018                     return true;
23019                 }
23020                 
23021                 
23022                  
23023                 
23024                 clean.push(p);
23025                 return true;
23026             });
23027             if (clean.length) { 
23028                 node.setAttribute(n, clean.join(';'));
23029             } else {
23030                 node.removeAttribute(n);
23031             }
23032             
23033         }
23034         
23035         
23036         for (var i = node.attributes.length-1; i > -1 ; i--) {
23037             var a = node.attributes[i];
23038             //console.log(a);
23039             
23040             if (a.name.toLowerCase().substr(0,2)=='on')  {
23041                 node.removeAttribute(a.name);
23042                 continue;
23043             }
23044             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
23045                 node.removeAttribute(a.name);
23046                 continue;
23047             }
23048             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
23049                 cleanAttr(a.name,a.value); // fixme..
23050                 continue;
23051             }
23052             if (a.name == 'style') {
23053                 cleanStyle(a.name,a.value);
23054                 continue;
23055             }
23056             /// clean up MS crap..
23057             // tecnically this should be a list of valid class'es..
23058             
23059             
23060             if (a.name == 'class') {
23061                 if (a.value.match(/^Mso/)) {
23062                     node.className = '';
23063                 }
23064                 
23065                 if (a.value.match(/^body$/)) {
23066                     node.className = '';
23067                 }
23068                 continue;
23069             }
23070             
23071             // style cleanup!?
23072             // class cleanup?
23073             
23074         }
23075         
23076         
23077         this.cleanUpChildren(node);
23078         
23079         
23080     },
23081     
23082     /**
23083      * Clean up MS wordisms...
23084      */
23085     cleanWord : function(node)
23086     {
23087         
23088         
23089         if (!node) {
23090             this.cleanWord(this.doc.body);
23091             return;
23092         }
23093         if (node.nodeName == "#text") {
23094             // clean up silly Windows -- stuff?
23095             return; 
23096         }
23097         if (node.nodeName == "#comment") {
23098             node.parentNode.removeChild(node);
23099             // clean up silly Windows -- stuff?
23100             return; 
23101         }
23102         
23103         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
23104             node.parentNode.removeChild(node);
23105             return;
23106         }
23107         
23108         // remove - but keep children..
23109         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
23110             while (node.childNodes.length) {
23111                 var cn = node.childNodes[0];
23112                 node.removeChild(cn);
23113                 node.parentNode.insertBefore(cn, node);
23114             }
23115             node.parentNode.removeChild(node);
23116             this.iterateChildren(node, this.cleanWord);
23117             return;
23118         }
23119         // clean styles
23120         if (node.className.length) {
23121             
23122             var cn = node.className.split(/\W+/);
23123             var cna = [];
23124             Roo.each(cn, function(cls) {
23125                 if (cls.match(/Mso[a-zA-Z]+/)) {
23126                     return;
23127                 }
23128                 cna.push(cls);
23129             });
23130             node.className = cna.length ? cna.join(' ') : '';
23131             if (!cna.length) {
23132                 node.removeAttribute("class");
23133             }
23134         }
23135         
23136         if (node.hasAttribute("lang")) {
23137             node.removeAttribute("lang");
23138         }
23139         
23140         if (node.hasAttribute("style")) {
23141             
23142             var styles = node.getAttribute("style").split(";");
23143             var nstyle = [];
23144             Roo.each(styles, function(s) {
23145                 if (!s.match(/:/)) {
23146                     return;
23147                 }
23148                 var kv = s.split(":");
23149                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
23150                     return;
23151                 }
23152                 // what ever is left... we allow.
23153                 nstyle.push(s);
23154             });
23155             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23156             if (!nstyle.length) {
23157                 node.removeAttribute('style');
23158             }
23159         }
23160         this.iterateChildren(node, this.cleanWord);
23161         
23162         
23163         
23164     },
23165     /**
23166      * iterateChildren of a Node, calling fn each time, using this as the scole..
23167      * @param {DomNode} node node to iterate children of.
23168      * @param {Function} fn method of this class to call on each item.
23169      */
23170     iterateChildren : function(node, fn)
23171     {
23172         if (!node.childNodes.length) {
23173                 return;
23174         }
23175         for (var i = node.childNodes.length-1; i > -1 ; i--) {
23176            fn.call(this, node.childNodes[i])
23177         }
23178     },
23179     
23180     
23181     /**
23182      * cleanTableWidths.
23183      *
23184      * Quite often pasting from word etc.. results in tables with column and widths.
23185      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23186      *
23187      */
23188     cleanTableWidths : function(node)
23189     {
23190          
23191          
23192         if (!node) {
23193             this.cleanTableWidths(this.doc.body);
23194             return;
23195         }
23196         
23197         // ignore list...
23198         if (node.nodeName == "#text" || node.nodeName == "#comment") {
23199             return; 
23200         }
23201         Roo.log(node.tagName);
23202         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23203             this.iterateChildren(node, this.cleanTableWidths);
23204             return;
23205         }
23206         if (node.hasAttribute('width')) {
23207             node.removeAttribute('width');
23208         }
23209         
23210          
23211         if (node.hasAttribute("style")) {
23212             // pretty basic...
23213             
23214             var styles = node.getAttribute("style").split(";");
23215             var nstyle = [];
23216             Roo.each(styles, function(s) {
23217                 if (!s.match(/:/)) {
23218                     return;
23219                 }
23220                 var kv = s.split(":");
23221                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23222                     return;
23223                 }
23224                 // what ever is left... we allow.
23225                 nstyle.push(s);
23226             });
23227             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23228             if (!nstyle.length) {
23229                 node.removeAttribute('style');
23230             }
23231         }
23232         
23233         this.iterateChildren(node, this.cleanTableWidths);
23234         
23235         
23236     },
23237     
23238     
23239     
23240     
23241     domToHTML : function(currentElement, depth, nopadtext) {
23242         
23243         depth = depth || 0;
23244         nopadtext = nopadtext || false;
23245     
23246         if (!currentElement) {
23247             return this.domToHTML(this.doc.body);
23248         }
23249         
23250         //Roo.log(currentElement);
23251         var j;
23252         var allText = false;
23253         var nodeName = currentElement.nodeName;
23254         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23255         
23256         if  (nodeName == '#text') {
23257             
23258             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23259         }
23260         
23261         
23262         var ret = '';
23263         if (nodeName != 'BODY') {
23264              
23265             var i = 0;
23266             // Prints the node tagName, such as <A>, <IMG>, etc
23267             if (tagName) {
23268                 var attr = [];
23269                 for(i = 0; i < currentElement.attributes.length;i++) {
23270                     // quoting?
23271                     var aname = currentElement.attributes.item(i).name;
23272                     if (!currentElement.attributes.item(i).value.length) {
23273                         continue;
23274                     }
23275                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23276                 }
23277                 
23278                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23279             } 
23280             else {
23281                 
23282                 // eack
23283             }
23284         } else {
23285             tagName = false;
23286         }
23287         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23288             return ret;
23289         }
23290         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23291             nopadtext = true;
23292         }
23293         
23294         
23295         // Traverse the tree
23296         i = 0;
23297         var currentElementChild = currentElement.childNodes.item(i);
23298         var allText = true;
23299         var innerHTML  = '';
23300         lastnode = '';
23301         while (currentElementChild) {
23302             // Formatting code (indent the tree so it looks nice on the screen)
23303             var nopad = nopadtext;
23304             if (lastnode == 'SPAN') {
23305                 nopad  = true;
23306             }
23307             // text
23308             if  (currentElementChild.nodeName == '#text') {
23309                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23310                 toadd = nopadtext ? toadd : toadd.trim();
23311                 if (!nopad && toadd.length > 80) {
23312                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23313                 }
23314                 innerHTML  += toadd;
23315                 
23316                 i++;
23317                 currentElementChild = currentElement.childNodes.item(i);
23318                 lastNode = '';
23319                 continue;
23320             }
23321             allText = false;
23322             
23323             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23324                 
23325             // Recursively traverse the tree structure of the child node
23326             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23327             lastnode = currentElementChild.nodeName;
23328             i++;
23329             currentElementChild=currentElement.childNodes.item(i);
23330         }
23331         
23332         ret += innerHTML;
23333         
23334         if (!allText) {
23335                 // The remaining code is mostly for formatting the tree
23336             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23337         }
23338         
23339         
23340         if (tagName) {
23341             ret+= "</"+tagName+">";
23342         }
23343         return ret;
23344         
23345     },
23346         
23347     applyBlacklists : function()
23348     {
23349         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23350         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23351         
23352         this.white = [];
23353         this.black = [];
23354         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23355             if (b.indexOf(tag) > -1) {
23356                 return;
23357             }
23358             this.white.push(tag);
23359             
23360         }, this);
23361         
23362         Roo.each(w, function(tag) {
23363             if (b.indexOf(tag) > -1) {
23364                 return;
23365             }
23366             if (this.white.indexOf(tag) > -1) {
23367                 return;
23368             }
23369             this.white.push(tag);
23370             
23371         }, this);
23372         
23373         
23374         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23375             if (w.indexOf(tag) > -1) {
23376                 return;
23377             }
23378             this.black.push(tag);
23379             
23380         }, this);
23381         
23382         Roo.each(b, function(tag) {
23383             if (w.indexOf(tag) > -1) {
23384                 return;
23385             }
23386             if (this.black.indexOf(tag) > -1) {
23387                 return;
23388             }
23389             this.black.push(tag);
23390             
23391         }, this);
23392         
23393         
23394         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23395         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23396         
23397         this.cwhite = [];
23398         this.cblack = [];
23399         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23400             if (b.indexOf(tag) > -1) {
23401                 return;
23402             }
23403             this.cwhite.push(tag);
23404             
23405         }, this);
23406         
23407         Roo.each(w, function(tag) {
23408             if (b.indexOf(tag) > -1) {
23409                 return;
23410             }
23411             if (this.cwhite.indexOf(tag) > -1) {
23412                 return;
23413             }
23414             this.cwhite.push(tag);
23415             
23416         }, this);
23417         
23418         
23419         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23420             if (w.indexOf(tag) > -1) {
23421                 return;
23422             }
23423             this.cblack.push(tag);
23424             
23425         }, this);
23426         
23427         Roo.each(b, function(tag) {
23428             if (w.indexOf(tag) > -1) {
23429                 return;
23430             }
23431             if (this.cblack.indexOf(tag) > -1) {
23432                 return;
23433             }
23434             this.cblack.push(tag);
23435             
23436         }, this);
23437     },
23438     
23439     setStylesheets : function(stylesheets)
23440     {
23441         if(typeof(stylesheets) == 'string'){
23442             Roo.get(this.iframe.contentDocument.head).createChild({
23443                 tag : 'link',
23444                 rel : 'stylesheet',
23445                 type : 'text/css',
23446                 href : stylesheets
23447             });
23448             
23449             return;
23450         }
23451         var _this = this;
23452      
23453         Roo.each(stylesheets, function(s) {
23454             if(!s.length){
23455                 return;
23456             }
23457             
23458             Roo.get(_this.iframe.contentDocument.head).createChild({
23459                 tag : 'link',
23460                 rel : 'stylesheet',
23461                 type : 'text/css',
23462                 href : s
23463             });
23464         });
23465
23466         
23467     },
23468     
23469     removeStylesheets : function()
23470     {
23471         var _this = this;
23472         
23473         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23474             s.remove();
23475         });
23476     },
23477     
23478     setStyle : function(style)
23479     {
23480         Roo.get(this.iframe.contentDocument.head).createChild({
23481             tag : 'style',
23482             type : 'text/css',
23483             html : style
23484         });
23485
23486         return;
23487     }
23488     
23489     // hide stuff that is not compatible
23490     /**
23491      * @event blur
23492      * @hide
23493      */
23494     /**
23495      * @event change
23496      * @hide
23497      */
23498     /**
23499      * @event focus
23500      * @hide
23501      */
23502     /**
23503      * @event specialkey
23504      * @hide
23505      */
23506     /**
23507      * @cfg {String} fieldClass @hide
23508      */
23509     /**
23510      * @cfg {String} focusClass @hide
23511      */
23512     /**
23513      * @cfg {String} autoCreate @hide
23514      */
23515     /**
23516      * @cfg {String} inputType @hide
23517      */
23518     /**
23519      * @cfg {String} invalidClass @hide
23520      */
23521     /**
23522      * @cfg {String} invalidText @hide
23523      */
23524     /**
23525      * @cfg {String} msgFx @hide
23526      */
23527     /**
23528      * @cfg {String} validateOnBlur @hide
23529      */
23530 });
23531
23532 Roo.HtmlEditorCore.white = [
23533         'area', 'br', 'img', 'input', 'hr', 'wbr',
23534         
23535        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23536        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23537        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23538        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23539        'table',   'ul',         'xmp', 
23540        
23541        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23542       'thead',   'tr', 
23543      
23544       'dir', 'menu', 'ol', 'ul', 'dl',
23545        
23546       'embed',  'object'
23547 ];
23548
23549
23550 Roo.HtmlEditorCore.black = [
23551     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23552         'applet', // 
23553         'base',   'basefont', 'bgsound', 'blink',  'body', 
23554         'frame',  'frameset', 'head',    'html',   'ilayer', 
23555         'iframe', 'layer',  'link',     'meta',    'object',   
23556         'script', 'style' ,'title',  'xml' // clean later..
23557 ];
23558 Roo.HtmlEditorCore.clean = [
23559     'script', 'style', 'title', 'xml'
23560 ];
23561 Roo.HtmlEditorCore.remove = [
23562     'font'
23563 ];
23564 // attributes..
23565
23566 Roo.HtmlEditorCore.ablack = [
23567     'on'
23568 ];
23569     
23570 Roo.HtmlEditorCore.aclean = [ 
23571     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23572 ];
23573
23574 // protocols..
23575 Roo.HtmlEditorCore.pwhite= [
23576         'http',  'https',  'mailto'
23577 ];
23578
23579 // white listed style attributes.
23580 Roo.HtmlEditorCore.cwhite= [
23581       //  'text-align', /// default is to allow most things..
23582       
23583          
23584 //        'font-size'//??
23585 ];
23586
23587 // black listed style attributes.
23588 Roo.HtmlEditorCore.cblack= [
23589       //  'font-size' -- this can be set by the project 
23590 ];
23591
23592
23593 Roo.HtmlEditorCore.swapCodes   =[ 
23594     [    8211, "--" ], 
23595     [    8212, "--" ], 
23596     [    8216,  "'" ],  
23597     [    8217, "'" ],  
23598     [    8220, '"' ],  
23599     [    8221, '"' ],  
23600     [    8226, "*" ],  
23601     [    8230, "..." ]
23602 ]; 
23603
23604     /*
23605  * - LGPL
23606  *
23607  * HtmlEditor
23608  * 
23609  */
23610
23611 /**
23612  * @class Roo.bootstrap.HtmlEditor
23613  * @extends Roo.bootstrap.TextArea
23614  * Bootstrap HtmlEditor class
23615
23616  * @constructor
23617  * Create a new HtmlEditor
23618  * @param {Object} config The config object
23619  */
23620
23621 Roo.bootstrap.HtmlEditor = function(config){
23622     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23623     if (!this.toolbars) {
23624         this.toolbars = [];
23625     }
23626     
23627     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23628     this.addEvents({
23629             /**
23630              * @event initialize
23631              * Fires when the editor is fully initialized (including the iframe)
23632              * @param {HtmlEditor} this
23633              */
23634             initialize: true,
23635             /**
23636              * @event activate
23637              * Fires when the editor is first receives the focus. Any insertion must wait
23638              * until after this event.
23639              * @param {HtmlEditor} this
23640              */
23641             activate: true,
23642              /**
23643              * @event beforesync
23644              * Fires before the textarea is updated with content from the editor iframe. Return false
23645              * to cancel the sync.
23646              * @param {HtmlEditor} this
23647              * @param {String} html
23648              */
23649             beforesync: true,
23650              /**
23651              * @event beforepush
23652              * Fires before the iframe editor is updated with content from the textarea. Return false
23653              * to cancel the push.
23654              * @param {HtmlEditor} this
23655              * @param {String} html
23656              */
23657             beforepush: true,
23658              /**
23659              * @event sync
23660              * Fires when the textarea is updated with content from the editor iframe.
23661              * @param {HtmlEditor} this
23662              * @param {String} html
23663              */
23664             sync: true,
23665              /**
23666              * @event push
23667              * Fires when the iframe editor is updated with content from the textarea.
23668              * @param {HtmlEditor} this
23669              * @param {String} html
23670              */
23671             push: true,
23672              /**
23673              * @event editmodechange
23674              * Fires when the editor switches edit modes
23675              * @param {HtmlEditor} this
23676              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23677              */
23678             editmodechange: true,
23679             /**
23680              * @event editorevent
23681              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23682              * @param {HtmlEditor} this
23683              */
23684             editorevent: true,
23685             /**
23686              * @event firstfocus
23687              * Fires when on first focus - needed by toolbars..
23688              * @param {HtmlEditor} this
23689              */
23690             firstfocus: true,
23691             /**
23692              * @event autosave
23693              * Auto save the htmlEditor value as a file into Events
23694              * @param {HtmlEditor} this
23695              */
23696             autosave: true,
23697             /**
23698              * @event savedpreview
23699              * preview the saved version of htmlEditor
23700              * @param {HtmlEditor} this
23701              */
23702             savedpreview: true
23703         });
23704 };
23705
23706
23707 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23708     
23709     
23710       /**
23711      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23712      */
23713     toolbars : false,
23714     
23715      /**
23716     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23717     */
23718     btns : [],
23719    
23720      /**
23721      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23722      *                        Roo.resizable.
23723      */
23724     resizable : false,
23725      /**
23726      * @cfg {Number} height (in pixels)
23727      */   
23728     height: 300,
23729    /**
23730      * @cfg {Number} width (in pixels)
23731      */   
23732     width: false,
23733     
23734     /**
23735      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23736      * 
23737      */
23738     stylesheets: false,
23739     
23740     // id of frame..
23741     frameId: false,
23742     
23743     // private properties
23744     validationEvent : false,
23745     deferHeight: true,
23746     initialized : false,
23747     activated : false,
23748     
23749     onFocus : Roo.emptyFn,
23750     iframePad:3,
23751     hideMode:'offsets',
23752     
23753     tbContainer : false,
23754     
23755     bodyCls : '',
23756     
23757     toolbarContainer :function() {
23758         return this.wrap.select('.x-html-editor-tb',true).first();
23759     },
23760
23761     /**
23762      * Protected method that will not generally be called directly. It
23763      * is called when the editor creates its toolbar. Override this method if you need to
23764      * add custom toolbar buttons.
23765      * @param {HtmlEditor} editor
23766      */
23767     createToolbar : function(){
23768         Roo.log('renewing');
23769         Roo.log("create toolbars");
23770         
23771         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23772         this.toolbars[0].render(this.toolbarContainer());
23773         
23774         return;
23775         
23776 //        if (!editor.toolbars || !editor.toolbars.length) {
23777 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23778 //        }
23779 //        
23780 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23781 //            editor.toolbars[i] = Roo.factory(
23782 //                    typeof(editor.toolbars[i]) == 'string' ?
23783 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23784 //                Roo.bootstrap.HtmlEditor);
23785 //            editor.toolbars[i].init(editor);
23786 //        }
23787     },
23788
23789      
23790     // private
23791     onRender : function(ct, position)
23792     {
23793        // Roo.log("Call onRender: " + this.xtype);
23794         var _t = this;
23795         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23796       
23797         this.wrap = this.inputEl().wrap({
23798             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23799         });
23800         
23801         this.editorcore.onRender(ct, position);
23802          
23803         if (this.resizable) {
23804             this.resizeEl = new Roo.Resizable(this.wrap, {
23805                 pinned : true,
23806                 wrap: true,
23807                 dynamic : true,
23808                 minHeight : this.height,
23809                 height: this.height,
23810                 handles : this.resizable,
23811                 width: this.width,
23812                 listeners : {
23813                     resize : function(r, w, h) {
23814                         _t.onResize(w,h); // -something
23815                     }
23816                 }
23817             });
23818             
23819         }
23820         this.createToolbar(this);
23821        
23822         
23823         if(!this.width && this.resizable){
23824             this.setSize(this.wrap.getSize());
23825         }
23826         if (this.resizeEl) {
23827             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23828             // should trigger onReize..
23829         }
23830         
23831     },
23832
23833     // private
23834     onResize : function(w, h)
23835     {
23836         Roo.log('resize: ' +w + ',' + h );
23837         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23838         var ew = false;
23839         var eh = false;
23840         
23841         if(this.inputEl() ){
23842             if(typeof w == 'number'){
23843                 var aw = w - this.wrap.getFrameWidth('lr');
23844                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23845                 ew = aw;
23846             }
23847             if(typeof h == 'number'){
23848                  var tbh = -11;  // fixme it needs to tool bar size!
23849                 for (var i =0; i < this.toolbars.length;i++) {
23850                     // fixme - ask toolbars for heights?
23851                     tbh += this.toolbars[i].el.getHeight();
23852                     //if (this.toolbars[i].footer) {
23853                     //    tbh += this.toolbars[i].footer.el.getHeight();
23854                     //}
23855                 }
23856               
23857                 
23858                 
23859                 
23860                 
23861                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23862                 ah -= 5; // knock a few pixes off for look..
23863                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23864                 var eh = ah;
23865             }
23866         }
23867         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23868         this.editorcore.onResize(ew,eh);
23869         
23870     },
23871
23872     /**
23873      * Toggles the editor between standard and source edit mode.
23874      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23875      */
23876     toggleSourceEdit : function(sourceEditMode)
23877     {
23878         this.editorcore.toggleSourceEdit(sourceEditMode);
23879         
23880         if(this.editorcore.sourceEditMode){
23881             Roo.log('editor - showing textarea');
23882             
23883 //            Roo.log('in');
23884 //            Roo.log(this.syncValue());
23885             this.syncValue();
23886             this.inputEl().removeClass(['hide', 'x-hidden']);
23887             this.inputEl().dom.removeAttribute('tabIndex');
23888             this.inputEl().focus();
23889         }else{
23890             Roo.log('editor - hiding textarea');
23891 //            Roo.log('out')
23892 //            Roo.log(this.pushValue()); 
23893             this.pushValue();
23894             
23895             this.inputEl().addClass(['hide', 'x-hidden']);
23896             this.inputEl().dom.setAttribute('tabIndex', -1);
23897             //this.deferFocus();
23898         }
23899          
23900         if(this.resizable){
23901             this.setSize(this.wrap.getSize());
23902         }
23903         
23904         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23905     },
23906  
23907     // private (for BoxComponent)
23908     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23909
23910     // private (for BoxComponent)
23911     getResizeEl : function(){
23912         return this.wrap;
23913     },
23914
23915     // private (for BoxComponent)
23916     getPositionEl : function(){
23917         return this.wrap;
23918     },
23919
23920     // private
23921     initEvents : function(){
23922         this.originalValue = this.getValue();
23923     },
23924
23925 //    /**
23926 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23927 //     * @method
23928 //     */
23929 //    markInvalid : Roo.emptyFn,
23930 //    /**
23931 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23932 //     * @method
23933 //     */
23934 //    clearInvalid : Roo.emptyFn,
23935
23936     setValue : function(v){
23937         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23938         this.editorcore.pushValue();
23939     },
23940
23941      
23942     // private
23943     deferFocus : function(){
23944         this.focus.defer(10, this);
23945     },
23946
23947     // doc'ed in Field
23948     focus : function(){
23949         this.editorcore.focus();
23950         
23951     },
23952       
23953
23954     // private
23955     onDestroy : function(){
23956         
23957         
23958         
23959         if(this.rendered){
23960             
23961             for (var i =0; i < this.toolbars.length;i++) {
23962                 // fixme - ask toolbars for heights?
23963                 this.toolbars[i].onDestroy();
23964             }
23965             
23966             this.wrap.dom.innerHTML = '';
23967             this.wrap.remove();
23968         }
23969     },
23970
23971     // private
23972     onFirstFocus : function(){
23973         //Roo.log("onFirstFocus");
23974         this.editorcore.onFirstFocus();
23975          for (var i =0; i < this.toolbars.length;i++) {
23976             this.toolbars[i].onFirstFocus();
23977         }
23978         
23979     },
23980     
23981     // private
23982     syncValue : function()
23983     {   
23984         this.editorcore.syncValue();
23985     },
23986     
23987     pushValue : function()
23988     {   
23989         this.editorcore.pushValue();
23990     }
23991      
23992     
23993     // hide stuff that is not compatible
23994     /**
23995      * @event blur
23996      * @hide
23997      */
23998     /**
23999      * @event change
24000      * @hide
24001      */
24002     /**
24003      * @event focus
24004      * @hide
24005      */
24006     /**
24007      * @event specialkey
24008      * @hide
24009      */
24010     /**
24011      * @cfg {String} fieldClass @hide
24012      */
24013     /**
24014      * @cfg {String} focusClass @hide
24015      */
24016     /**
24017      * @cfg {String} autoCreate @hide
24018      */
24019     /**
24020      * @cfg {String} inputType @hide
24021      */
24022      
24023     /**
24024      * @cfg {String} invalidText @hide
24025      */
24026     /**
24027      * @cfg {String} msgFx @hide
24028      */
24029     /**
24030      * @cfg {String} validateOnBlur @hide
24031      */
24032 });
24033  
24034     
24035    
24036    
24037    
24038       
24039 Roo.namespace('Roo.bootstrap.htmleditor');
24040 /**
24041  * @class Roo.bootstrap.HtmlEditorToolbar1
24042  * Basic Toolbar
24043  * 
24044  * Usage:
24045  *
24046  new Roo.bootstrap.HtmlEditor({
24047     ....
24048     toolbars : [
24049         new Roo.bootstrap.HtmlEditorToolbar1({
24050             disable : { fonts: 1 , format: 1, ..., ... , ...],
24051             btns : [ .... ]
24052         })
24053     }
24054      
24055  * 
24056  * @cfg {Object} disable List of elements to disable..
24057  * @cfg {Array} btns List of additional buttons.
24058  * 
24059  * 
24060  * NEEDS Extra CSS? 
24061  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24062  */
24063  
24064 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
24065 {
24066     
24067     Roo.apply(this, config);
24068     
24069     // default disabled, based on 'good practice'..
24070     this.disable = this.disable || {};
24071     Roo.applyIf(this.disable, {
24072         fontSize : true,
24073         colors : true,
24074         specialElements : true
24075     });
24076     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
24077     
24078     this.editor = config.editor;
24079     this.editorcore = config.editor.editorcore;
24080     
24081     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
24082     
24083     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24084     // dont call parent... till later.
24085 }
24086 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
24087      
24088     bar : true,
24089     
24090     editor : false,
24091     editorcore : false,
24092     
24093     
24094     formats : [
24095         "p" ,  
24096         "h1","h2","h3","h4","h5","h6", 
24097         "pre", "code", 
24098         "abbr", "acronym", "address", "cite", "samp", "var",
24099         'div','span'
24100     ],
24101     
24102     onRender : function(ct, position)
24103     {
24104        // Roo.log("Call onRender: " + this.xtype);
24105         
24106        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
24107        Roo.log(this.el);
24108        this.el.dom.style.marginBottom = '0';
24109        var _this = this;
24110        var editorcore = this.editorcore;
24111        var editor= this.editor;
24112        
24113        var children = [];
24114        var btn = function(id,cmd , toggle, handler, html){
24115        
24116             var  event = toggle ? 'toggle' : 'click';
24117        
24118             var a = {
24119                 size : 'sm',
24120                 xtype: 'Button',
24121                 xns: Roo.bootstrap,
24122                 //glyphicon : id,
24123                 fa: id,
24124                 cmd : id || cmd,
24125                 enableToggle:toggle !== false,
24126                 html : html || '',
24127                 pressed : toggle ? false : null,
24128                 listeners : {}
24129             };
24130             a.listeners[toggle ? 'toggle' : 'click'] = function() {
24131                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
24132             };
24133             children.push(a);
24134             return a;
24135        }
24136        
24137     //    var cb_box = function...
24138         
24139         var style = {
24140                 xtype: 'Button',
24141                 size : 'sm',
24142                 xns: Roo.bootstrap,
24143                 fa : 'font',
24144                 //html : 'submit'
24145                 menu : {
24146                     xtype: 'Menu',
24147                     xns: Roo.bootstrap,
24148                     items:  []
24149                 }
24150         };
24151         Roo.each(this.formats, function(f) {
24152             style.menu.items.push({
24153                 xtype :'MenuItem',
24154                 xns: Roo.bootstrap,
24155                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24156                 tagname : f,
24157                 listeners : {
24158                     click : function()
24159                     {
24160                         editorcore.insertTag(this.tagname);
24161                         editor.focus();
24162                     }
24163                 }
24164                 
24165             });
24166         });
24167         children.push(style);   
24168         
24169         btn('bold',false,true);
24170         btn('italic',false,true);
24171         btn('align-left', 'justifyleft',true);
24172         btn('align-center', 'justifycenter',true);
24173         btn('align-right' , 'justifyright',true);
24174         btn('link', false, false, function(btn) {
24175             //Roo.log("create link?");
24176             var url = prompt(this.createLinkText, this.defaultLinkValue);
24177             if(url && url != 'http:/'+'/'){
24178                 this.editorcore.relayCmd('createlink', url);
24179             }
24180         }),
24181         btn('list','insertunorderedlist',true);
24182         btn('pencil', false,true, function(btn){
24183                 Roo.log(this);
24184                 this.toggleSourceEdit(btn.pressed);
24185         });
24186         
24187         if (this.editor.btns.length > 0) {
24188             for (var i = 0; i<this.editor.btns.length; i++) {
24189                 children.push(this.editor.btns[i]);
24190             }
24191         }
24192         
24193         /*
24194         var cog = {
24195                 xtype: 'Button',
24196                 size : 'sm',
24197                 xns: Roo.bootstrap,
24198                 glyphicon : 'cog',
24199                 //html : 'submit'
24200                 menu : {
24201                     xtype: 'Menu',
24202                     xns: Roo.bootstrap,
24203                     items:  []
24204                 }
24205         };
24206         
24207         cog.menu.items.push({
24208             xtype :'MenuItem',
24209             xns: Roo.bootstrap,
24210             html : Clean styles,
24211             tagname : f,
24212             listeners : {
24213                 click : function()
24214                 {
24215                     editorcore.insertTag(this.tagname);
24216                     editor.focus();
24217                 }
24218             }
24219             
24220         });
24221        */
24222         
24223          
24224        this.xtype = 'NavSimplebar';
24225         
24226         for(var i=0;i< children.length;i++) {
24227             
24228             this.buttons.add(this.addxtypeChild(children[i]));
24229             
24230         }
24231         
24232         editor.on('editorevent', this.updateToolbar, this);
24233     },
24234     onBtnClick : function(id)
24235     {
24236        this.editorcore.relayCmd(id);
24237        this.editorcore.focus();
24238     },
24239     
24240     /**
24241      * Protected method that will not generally be called directly. It triggers
24242      * a toolbar update by reading the markup state of the current selection in the editor.
24243      */
24244     updateToolbar: function(){
24245
24246         if(!this.editorcore.activated){
24247             this.editor.onFirstFocus(); // is this neeed?
24248             return;
24249         }
24250
24251         var btns = this.buttons; 
24252         var doc = this.editorcore.doc;
24253         btns.get('bold').setActive(doc.queryCommandState('bold'));
24254         btns.get('italic').setActive(doc.queryCommandState('italic'));
24255         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24256         
24257         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24258         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24259         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24260         
24261         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24262         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24263          /*
24264         
24265         var ans = this.editorcore.getAllAncestors();
24266         if (this.formatCombo) {
24267             
24268             
24269             var store = this.formatCombo.store;
24270             this.formatCombo.setValue("");
24271             for (var i =0; i < ans.length;i++) {
24272                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24273                     // select it..
24274                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24275                     break;
24276                 }
24277             }
24278         }
24279         
24280         
24281         
24282         // hides menus... - so this cant be on a menu...
24283         Roo.bootstrap.MenuMgr.hideAll();
24284         */
24285         Roo.bootstrap.MenuMgr.hideAll();
24286         //this.editorsyncValue();
24287     },
24288     onFirstFocus: function() {
24289         this.buttons.each(function(item){
24290            item.enable();
24291         });
24292     },
24293     toggleSourceEdit : function(sourceEditMode){
24294         
24295           
24296         if(sourceEditMode){
24297             Roo.log("disabling buttons");
24298            this.buttons.each( function(item){
24299                 if(item.cmd != 'pencil'){
24300                     item.disable();
24301                 }
24302             });
24303           
24304         }else{
24305             Roo.log("enabling buttons");
24306             if(this.editorcore.initialized){
24307                 this.buttons.each( function(item){
24308                     item.enable();
24309                 });
24310             }
24311             
24312         }
24313         Roo.log("calling toggole on editor");
24314         // tell the editor that it's been pressed..
24315         this.editor.toggleSourceEdit(sourceEditMode);
24316        
24317     }
24318 });
24319
24320
24321
24322
24323
24324 /**
24325  * @class Roo.bootstrap.Table.AbstractSelectionModel
24326  * @extends Roo.util.Observable
24327  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24328  * implemented by descendant classes.  This class should not be directly instantiated.
24329  * @constructor
24330  */
24331 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24332     this.locked = false;
24333     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24334 };
24335
24336
24337 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24338     /** @ignore Called by the grid automatically. Do not call directly. */
24339     init : function(grid){
24340         this.grid = grid;
24341         this.initEvents();
24342     },
24343
24344     /**
24345      * Locks the selections.
24346      */
24347     lock : function(){
24348         this.locked = true;
24349     },
24350
24351     /**
24352      * Unlocks the selections.
24353      */
24354     unlock : function(){
24355         this.locked = false;
24356     },
24357
24358     /**
24359      * Returns true if the selections are locked.
24360      * @return {Boolean}
24361      */
24362     isLocked : function(){
24363         return this.locked;
24364     }
24365 });
24366 /**
24367  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24368  * @class Roo.bootstrap.Table.RowSelectionModel
24369  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24370  * It supports multiple selections and keyboard selection/navigation. 
24371  * @constructor
24372  * @param {Object} config
24373  */
24374
24375 Roo.bootstrap.Table.RowSelectionModel = function(config){
24376     Roo.apply(this, config);
24377     this.selections = new Roo.util.MixedCollection(false, function(o){
24378         return o.id;
24379     });
24380
24381     this.last = false;
24382     this.lastActive = false;
24383
24384     this.addEvents({
24385         /**
24386              * @event selectionchange
24387              * Fires when the selection changes
24388              * @param {SelectionModel} this
24389              */
24390             "selectionchange" : true,
24391         /**
24392              * @event afterselectionchange
24393              * Fires after the selection changes (eg. by key press or clicking)
24394              * @param {SelectionModel} this
24395              */
24396             "afterselectionchange" : true,
24397         /**
24398              * @event beforerowselect
24399              * Fires when a row is selected being selected, return false to cancel.
24400              * @param {SelectionModel} this
24401              * @param {Number} rowIndex The selected index
24402              * @param {Boolean} keepExisting False if other selections will be cleared
24403              */
24404             "beforerowselect" : true,
24405         /**
24406              * @event rowselect
24407              * Fires when a row is selected.
24408              * @param {SelectionModel} this
24409              * @param {Number} rowIndex The selected index
24410              * @param {Roo.data.Record} r The record
24411              */
24412             "rowselect" : true,
24413         /**
24414              * @event rowdeselect
24415              * Fires when a row is deselected.
24416              * @param {SelectionModel} this
24417              * @param {Number} rowIndex The selected index
24418              */
24419         "rowdeselect" : true
24420     });
24421     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24422     this.locked = false;
24423  };
24424
24425 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24426     /**
24427      * @cfg {Boolean} singleSelect
24428      * True to allow selection of only one row at a time (defaults to false)
24429      */
24430     singleSelect : false,
24431
24432     // private
24433     initEvents : function()
24434     {
24435
24436         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24437         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24438         //}else{ // allow click to work like normal
24439          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24440         //}
24441         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24442         this.grid.on("rowclick", this.handleMouseDown, this);
24443         
24444         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24445             "up" : function(e){
24446                 if(!e.shiftKey){
24447                     this.selectPrevious(e.shiftKey);
24448                 }else if(this.last !== false && this.lastActive !== false){
24449                     var last = this.last;
24450                     this.selectRange(this.last,  this.lastActive-1);
24451                     this.grid.getView().focusRow(this.lastActive);
24452                     if(last !== false){
24453                         this.last = last;
24454                     }
24455                 }else{
24456                     this.selectFirstRow();
24457                 }
24458                 this.fireEvent("afterselectionchange", this);
24459             },
24460             "down" : function(e){
24461                 if(!e.shiftKey){
24462                     this.selectNext(e.shiftKey);
24463                 }else if(this.last !== false && this.lastActive !== false){
24464                     var last = this.last;
24465                     this.selectRange(this.last,  this.lastActive+1);
24466                     this.grid.getView().focusRow(this.lastActive);
24467                     if(last !== false){
24468                         this.last = last;
24469                     }
24470                 }else{
24471                     this.selectFirstRow();
24472                 }
24473                 this.fireEvent("afterselectionchange", this);
24474             },
24475             scope: this
24476         });
24477         this.grid.store.on('load', function(){
24478             this.selections.clear();
24479         },this);
24480         /*
24481         var view = this.grid.view;
24482         view.on("refresh", this.onRefresh, this);
24483         view.on("rowupdated", this.onRowUpdated, this);
24484         view.on("rowremoved", this.onRemove, this);
24485         */
24486     },
24487
24488     // private
24489     onRefresh : function()
24490     {
24491         var ds = this.grid.store, i, v = this.grid.view;
24492         var s = this.selections;
24493         s.each(function(r){
24494             if((i = ds.indexOfId(r.id)) != -1){
24495                 v.onRowSelect(i);
24496             }else{
24497                 s.remove(r);
24498             }
24499         });
24500     },
24501
24502     // private
24503     onRemove : function(v, index, r){
24504         this.selections.remove(r);
24505     },
24506
24507     // private
24508     onRowUpdated : function(v, index, r){
24509         if(this.isSelected(r)){
24510             v.onRowSelect(index);
24511         }
24512     },
24513
24514     /**
24515      * Select records.
24516      * @param {Array} records The records to select
24517      * @param {Boolean} keepExisting (optional) True to keep existing selections
24518      */
24519     selectRecords : function(records, keepExisting)
24520     {
24521         if(!keepExisting){
24522             this.clearSelections();
24523         }
24524             var ds = this.grid.store;
24525         for(var i = 0, len = records.length; i < len; i++){
24526             this.selectRow(ds.indexOf(records[i]), true);
24527         }
24528     },
24529
24530     /**
24531      * Gets the number of selected rows.
24532      * @return {Number}
24533      */
24534     getCount : function(){
24535         return this.selections.length;
24536     },
24537
24538     /**
24539      * Selects the first row in the grid.
24540      */
24541     selectFirstRow : function(){
24542         this.selectRow(0);
24543     },
24544
24545     /**
24546      * Select the last row.
24547      * @param {Boolean} keepExisting (optional) True to keep existing selections
24548      */
24549     selectLastRow : function(keepExisting){
24550         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24551         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24552     },
24553
24554     /**
24555      * Selects the row immediately following the last selected row.
24556      * @param {Boolean} keepExisting (optional) True to keep existing selections
24557      */
24558     selectNext : function(keepExisting)
24559     {
24560             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24561             this.selectRow(this.last+1, keepExisting);
24562             this.grid.getView().focusRow(this.last);
24563         }
24564     },
24565
24566     /**
24567      * Selects the row that precedes the last selected row.
24568      * @param {Boolean} keepExisting (optional) True to keep existing selections
24569      */
24570     selectPrevious : function(keepExisting){
24571         if(this.last){
24572             this.selectRow(this.last-1, keepExisting);
24573             this.grid.getView().focusRow(this.last);
24574         }
24575     },
24576
24577     /**
24578      * Returns the selected records
24579      * @return {Array} Array of selected records
24580      */
24581     getSelections : function(){
24582         return [].concat(this.selections.items);
24583     },
24584
24585     /**
24586      * Returns the first selected record.
24587      * @return {Record}
24588      */
24589     getSelected : function(){
24590         return this.selections.itemAt(0);
24591     },
24592
24593
24594     /**
24595      * Clears all selections.
24596      */
24597     clearSelections : function(fast)
24598     {
24599         if(this.locked) {
24600             return;
24601         }
24602         if(fast !== true){
24603                 var ds = this.grid.store;
24604             var s = this.selections;
24605             s.each(function(r){
24606                 this.deselectRow(ds.indexOfId(r.id));
24607             }, this);
24608             s.clear();
24609         }else{
24610             this.selections.clear();
24611         }
24612         this.last = false;
24613     },
24614
24615
24616     /**
24617      * Selects all rows.
24618      */
24619     selectAll : function(){
24620         if(this.locked) {
24621             return;
24622         }
24623         this.selections.clear();
24624         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24625             this.selectRow(i, true);
24626         }
24627     },
24628
24629     /**
24630      * Returns True if there is a selection.
24631      * @return {Boolean}
24632      */
24633     hasSelection : function(){
24634         return this.selections.length > 0;
24635     },
24636
24637     /**
24638      * Returns True if the specified row is selected.
24639      * @param {Number/Record} record The record or index of the record to check
24640      * @return {Boolean}
24641      */
24642     isSelected : function(index){
24643             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24644         return (r && this.selections.key(r.id) ? true : false);
24645     },
24646
24647     /**
24648      * Returns True if the specified record id is selected.
24649      * @param {String} id The id of record to check
24650      * @return {Boolean}
24651      */
24652     isIdSelected : function(id){
24653         return (this.selections.key(id) ? true : false);
24654     },
24655
24656
24657     // private
24658     handleMouseDBClick : function(e, t){
24659         
24660     },
24661     // private
24662     handleMouseDown : function(e, t)
24663     {
24664             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24665         if(this.isLocked() || rowIndex < 0 ){
24666             return;
24667         };
24668         if(e.shiftKey && this.last !== false){
24669             var last = this.last;
24670             this.selectRange(last, rowIndex, e.ctrlKey);
24671             this.last = last; // reset the last
24672             t.focus();
24673     
24674         }else{
24675             var isSelected = this.isSelected(rowIndex);
24676             //Roo.log("select row:" + rowIndex);
24677             if(isSelected){
24678                 this.deselectRow(rowIndex);
24679             } else {
24680                         this.selectRow(rowIndex, true);
24681             }
24682     
24683             /*
24684                 if(e.button !== 0 && isSelected){
24685                 alert('rowIndex 2: ' + rowIndex);
24686                     view.focusRow(rowIndex);
24687                 }else if(e.ctrlKey && isSelected){
24688                     this.deselectRow(rowIndex);
24689                 }else if(!isSelected){
24690                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24691                     view.focusRow(rowIndex);
24692                 }
24693             */
24694         }
24695         this.fireEvent("afterselectionchange", this);
24696     },
24697     // private
24698     handleDragableRowClick :  function(grid, rowIndex, e) 
24699     {
24700         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24701             this.selectRow(rowIndex, false);
24702             grid.view.focusRow(rowIndex);
24703              this.fireEvent("afterselectionchange", this);
24704         }
24705     },
24706     
24707     /**
24708      * Selects multiple rows.
24709      * @param {Array} rows Array of the indexes of the row to select
24710      * @param {Boolean} keepExisting (optional) True to keep existing selections
24711      */
24712     selectRows : function(rows, keepExisting){
24713         if(!keepExisting){
24714             this.clearSelections();
24715         }
24716         for(var i = 0, len = rows.length; i < len; i++){
24717             this.selectRow(rows[i], true);
24718         }
24719     },
24720
24721     /**
24722      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24723      * @param {Number} startRow The index of the first row in the range
24724      * @param {Number} endRow The index of the last row in the range
24725      * @param {Boolean} keepExisting (optional) True to retain existing selections
24726      */
24727     selectRange : function(startRow, endRow, keepExisting){
24728         if(this.locked) {
24729             return;
24730         }
24731         if(!keepExisting){
24732             this.clearSelections();
24733         }
24734         if(startRow <= endRow){
24735             for(var i = startRow; i <= endRow; i++){
24736                 this.selectRow(i, true);
24737             }
24738         }else{
24739             for(var i = startRow; i >= endRow; i--){
24740                 this.selectRow(i, true);
24741             }
24742         }
24743     },
24744
24745     /**
24746      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24747      * @param {Number} startRow The index of the first row in the range
24748      * @param {Number} endRow The index of the last row in the range
24749      */
24750     deselectRange : function(startRow, endRow, preventViewNotify){
24751         if(this.locked) {
24752             return;
24753         }
24754         for(var i = startRow; i <= endRow; i++){
24755             this.deselectRow(i, preventViewNotify);
24756         }
24757     },
24758
24759     /**
24760      * Selects a row.
24761      * @param {Number} row The index of the row to select
24762      * @param {Boolean} keepExisting (optional) True to keep existing selections
24763      */
24764     selectRow : function(index, keepExisting, preventViewNotify)
24765     {
24766             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24767             return;
24768         }
24769         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24770             if(!keepExisting || this.singleSelect){
24771                 this.clearSelections();
24772             }
24773             
24774             var r = this.grid.store.getAt(index);
24775             //console.log('selectRow - record id :' + r.id);
24776             
24777             this.selections.add(r);
24778             this.last = this.lastActive = index;
24779             if(!preventViewNotify){
24780                 var proxy = new Roo.Element(
24781                                 this.grid.getRowDom(index)
24782                 );
24783                 proxy.addClass('bg-info info');
24784             }
24785             this.fireEvent("rowselect", this, index, r);
24786             this.fireEvent("selectionchange", this);
24787         }
24788     },
24789
24790     /**
24791      * Deselects a row.
24792      * @param {Number} row The index of the row to deselect
24793      */
24794     deselectRow : function(index, preventViewNotify)
24795     {
24796         if(this.locked) {
24797             return;
24798         }
24799         if(this.last == index){
24800             this.last = false;
24801         }
24802         if(this.lastActive == index){
24803             this.lastActive = false;
24804         }
24805         
24806         var r = this.grid.store.getAt(index);
24807         if (!r) {
24808             return;
24809         }
24810         
24811         this.selections.remove(r);
24812         //.console.log('deselectRow - record id :' + r.id);
24813         if(!preventViewNotify){
24814         
24815             var proxy = new Roo.Element(
24816                 this.grid.getRowDom(index)
24817             );
24818             proxy.removeClass('bg-info info');
24819         }
24820         this.fireEvent("rowdeselect", this, index);
24821         this.fireEvent("selectionchange", this);
24822     },
24823
24824     // private
24825     restoreLast : function(){
24826         if(this._last){
24827             this.last = this._last;
24828         }
24829     },
24830
24831     // private
24832     acceptsNav : function(row, col, cm){
24833         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24834     },
24835
24836     // private
24837     onEditorKey : function(field, e){
24838         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24839         if(k == e.TAB){
24840             e.stopEvent();
24841             ed.completeEdit();
24842             if(e.shiftKey){
24843                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24844             }else{
24845                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24846             }
24847         }else if(k == e.ENTER && !e.ctrlKey){
24848             e.stopEvent();
24849             ed.completeEdit();
24850             if(e.shiftKey){
24851                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24852             }else{
24853                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24854             }
24855         }else if(k == e.ESC){
24856             ed.cancelEdit();
24857         }
24858         if(newCell){
24859             g.startEditing(newCell[0], newCell[1]);
24860         }
24861     }
24862 });
24863 /*
24864  * Based on:
24865  * Ext JS Library 1.1.1
24866  * Copyright(c) 2006-2007, Ext JS, LLC.
24867  *
24868  * Originally Released Under LGPL - original licence link has changed is not relivant.
24869  *
24870  * Fork - LGPL
24871  * <script type="text/javascript">
24872  */
24873  
24874 /**
24875  * @class Roo.bootstrap.PagingToolbar
24876  * @extends Roo.bootstrap.NavSimplebar
24877  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24878  * @constructor
24879  * Create a new PagingToolbar
24880  * @param {Object} config The config object
24881  * @param {Roo.data.Store} store
24882  */
24883 Roo.bootstrap.PagingToolbar = function(config)
24884 {
24885     // old args format still supported... - xtype is prefered..
24886         // created from xtype...
24887     
24888     this.ds = config.dataSource;
24889     
24890     if (config.store && !this.ds) {
24891         this.store= Roo.factory(config.store, Roo.data);
24892         this.ds = this.store;
24893         this.ds.xmodule = this.xmodule || false;
24894     }
24895     
24896     this.toolbarItems = [];
24897     if (config.items) {
24898         this.toolbarItems = config.items;
24899     }
24900     
24901     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24902     
24903     this.cursor = 0;
24904     
24905     if (this.ds) { 
24906         this.bind(this.ds);
24907     }
24908     
24909     if (Roo.bootstrap.version == 4) {
24910         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24911     } else {
24912         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24913     }
24914     
24915 };
24916
24917 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24918     /**
24919      * @cfg {Roo.data.Store} dataSource
24920      * The underlying data store providing the paged data
24921      */
24922     /**
24923      * @cfg {String/HTMLElement/Element} container
24924      * container The id or element that will contain the toolbar
24925      */
24926     /**
24927      * @cfg {Boolean} displayInfo
24928      * True to display the displayMsg (defaults to false)
24929      */
24930     /**
24931      * @cfg {Number} pageSize
24932      * The number of records to display per page (defaults to 20)
24933      */
24934     pageSize: 20,
24935     /**
24936      * @cfg {String} displayMsg
24937      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24938      */
24939     displayMsg : 'Displaying {0} - {1} of {2}',
24940     /**
24941      * @cfg {String} emptyMsg
24942      * The message to display when no records are found (defaults to "No data to display")
24943      */
24944     emptyMsg : 'No data to display',
24945     /**
24946      * Customizable piece of the default paging text (defaults to "Page")
24947      * @type String
24948      */
24949     beforePageText : "Page",
24950     /**
24951      * Customizable piece of the default paging text (defaults to "of %0")
24952      * @type String
24953      */
24954     afterPageText : "of {0}",
24955     /**
24956      * Customizable piece of the default paging text (defaults to "First Page")
24957      * @type String
24958      */
24959     firstText : "First Page",
24960     /**
24961      * Customizable piece of the default paging text (defaults to "Previous Page")
24962      * @type String
24963      */
24964     prevText : "Previous Page",
24965     /**
24966      * Customizable piece of the default paging text (defaults to "Next Page")
24967      * @type String
24968      */
24969     nextText : "Next Page",
24970     /**
24971      * Customizable piece of the default paging text (defaults to "Last Page")
24972      * @type String
24973      */
24974     lastText : "Last Page",
24975     /**
24976      * Customizable piece of the default paging text (defaults to "Refresh")
24977      * @type String
24978      */
24979     refreshText : "Refresh",
24980
24981     buttons : false,
24982     // private
24983     onRender : function(ct, position) 
24984     {
24985         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24986         this.navgroup.parentId = this.id;
24987         this.navgroup.onRender(this.el, null);
24988         // add the buttons to the navgroup
24989         
24990         if(this.displayInfo){
24991             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24992             this.displayEl = this.el.select('.x-paging-info', true).first();
24993 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24994 //            this.displayEl = navel.el.select('span',true).first();
24995         }
24996         
24997         var _this = this;
24998         
24999         if(this.buttons){
25000             Roo.each(_this.buttons, function(e){ // this might need to use render????
25001                Roo.factory(e).render(_this.el);
25002             });
25003         }
25004             
25005         Roo.each(_this.toolbarItems, function(e) {
25006             _this.navgroup.addItem(e);
25007         });
25008         
25009         
25010         this.first = this.navgroup.addItem({
25011             tooltip: this.firstText,
25012             cls: "prev btn-outline-secondary",
25013             html : ' <i class="fa fa-step-backward"></i>',
25014             disabled: true,
25015             preventDefault: true,
25016             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
25017         });
25018         
25019         this.prev =  this.navgroup.addItem({
25020             tooltip: this.prevText,
25021             cls: "prev btn-outline-secondary",
25022             html : ' <i class="fa fa-backward"></i>',
25023             disabled: true,
25024             preventDefault: true,
25025             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
25026         });
25027     //this.addSeparator();
25028         
25029         
25030         var field = this.navgroup.addItem( {
25031             tagtype : 'span',
25032             cls : 'x-paging-position  btn-outline-secondary',
25033              disabled: true,
25034             html : this.beforePageText  +
25035                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
25036                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
25037          } ); //?? escaped?
25038         
25039         this.field = field.el.select('input', true).first();
25040         this.field.on("keydown", this.onPagingKeydown, this);
25041         this.field.on("focus", function(){this.dom.select();});
25042     
25043     
25044         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
25045         //this.field.setHeight(18);
25046         //this.addSeparator();
25047         this.next = this.navgroup.addItem({
25048             tooltip: this.nextText,
25049             cls: "next btn-outline-secondary",
25050             html : ' <i class="fa fa-forward"></i>',
25051             disabled: true,
25052             preventDefault: true,
25053             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
25054         });
25055         this.last = this.navgroup.addItem({
25056             tooltip: this.lastText,
25057             html : ' <i class="fa fa-step-forward"></i>',
25058             cls: "next btn-outline-secondary",
25059             disabled: true,
25060             preventDefault: true,
25061             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
25062         });
25063     //this.addSeparator();
25064         this.loading = this.navgroup.addItem({
25065             tooltip: this.refreshText,
25066             cls: "btn-outline-secondary",
25067             html : ' <i class="fa fa-refresh"></i>',
25068             preventDefault: true,
25069             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
25070         });
25071         
25072     },
25073
25074     // private
25075     updateInfo : function(){
25076         if(this.displayEl){
25077             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
25078             var msg = count == 0 ?
25079                 this.emptyMsg :
25080                 String.format(
25081                     this.displayMsg,
25082                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
25083                 );
25084             this.displayEl.update(msg);
25085         }
25086     },
25087
25088     // private
25089     onLoad : function(ds, r, o)
25090     {
25091         this.cursor = o.params.start ? o.params.start : 0;
25092         
25093         var d = this.getPageData(),
25094             ap = d.activePage,
25095             ps = d.pages;
25096         
25097         
25098         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
25099         this.field.dom.value = ap;
25100         this.first.setDisabled(ap == 1);
25101         this.prev.setDisabled(ap == 1);
25102         this.next.setDisabled(ap == ps);
25103         this.last.setDisabled(ap == ps);
25104         this.loading.enable();
25105         this.updateInfo();
25106     },
25107
25108     // private
25109     getPageData : function(){
25110         var total = this.ds.getTotalCount();
25111         return {
25112             total : total,
25113             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
25114             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
25115         };
25116     },
25117
25118     // private
25119     onLoadError : function(){
25120         this.loading.enable();
25121     },
25122
25123     // private
25124     onPagingKeydown : function(e){
25125         var k = e.getKey();
25126         var d = this.getPageData();
25127         if(k == e.RETURN){
25128             var v = this.field.dom.value, pageNum;
25129             if(!v || isNaN(pageNum = parseInt(v, 10))){
25130                 this.field.dom.value = d.activePage;
25131                 return;
25132             }
25133             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
25134             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25135             e.stopEvent();
25136         }
25137         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))
25138         {
25139           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
25140           this.field.dom.value = pageNum;
25141           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
25142           e.stopEvent();
25143         }
25144         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
25145         {
25146           var v = this.field.dom.value, pageNum; 
25147           var increment = (e.shiftKey) ? 10 : 1;
25148           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
25149                 increment *= -1;
25150           }
25151           if(!v || isNaN(pageNum = parseInt(v, 10))) {
25152             this.field.dom.value = d.activePage;
25153             return;
25154           }
25155           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25156           {
25157             this.field.dom.value = parseInt(v, 10) + increment;
25158             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25159             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25160           }
25161           e.stopEvent();
25162         }
25163     },
25164
25165     // private
25166     beforeLoad : function(){
25167         if(this.loading){
25168             this.loading.disable();
25169         }
25170     },
25171
25172     // private
25173     onClick : function(which){
25174         
25175         var ds = this.ds;
25176         if (!ds) {
25177             return;
25178         }
25179         
25180         switch(which){
25181             case "first":
25182                 ds.load({params:{start: 0, limit: this.pageSize}});
25183             break;
25184             case "prev":
25185                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25186             break;
25187             case "next":
25188                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25189             break;
25190             case "last":
25191                 var total = ds.getTotalCount();
25192                 var extra = total % this.pageSize;
25193                 var lastStart = extra ? (total - extra) : total-this.pageSize;
25194                 ds.load({params:{start: lastStart, limit: this.pageSize}});
25195             break;
25196             case "refresh":
25197                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25198             break;
25199         }
25200     },
25201
25202     /**
25203      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25204      * @param {Roo.data.Store} store The data store to unbind
25205      */
25206     unbind : function(ds){
25207         ds.un("beforeload", this.beforeLoad, this);
25208         ds.un("load", this.onLoad, this);
25209         ds.un("loadexception", this.onLoadError, this);
25210         ds.un("remove", this.updateInfo, this);
25211         ds.un("add", this.updateInfo, this);
25212         this.ds = undefined;
25213     },
25214
25215     /**
25216      * Binds the paging toolbar to the specified {@link Roo.data.Store}
25217      * @param {Roo.data.Store} store The data store to bind
25218      */
25219     bind : function(ds){
25220         ds.on("beforeload", this.beforeLoad, this);
25221         ds.on("load", this.onLoad, this);
25222         ds.on("loadexception", this.onLoadError, this);
25223         ds.on("remove", this.updateInfo, this);
25224         ds.on("add", this.updateInfo, this);
25225         this.ds = ds;
25226     }
25227 });/*
25228  * - LGPL
25229  *
25230  * element
25231  * 
25232  */
25233
25234 /**
25235  * @class Roo.bootstrap.MessageBar
25236  * @extends Roo.bootstrap.Component
25237  * Bootstrap MessageBar class
25238  * @cfg {String} html contents of the MessageBar
25239  * @cfg {String} weight (info | success | warning | danger) default info
25240  * @cfg {String} beforeClass insert the bar before the given class
25241  * @cfg {Boolean} closable (true | false) default false
25242  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25243  * 
25244  * @constructor
25245  * Create a new Element
25246  * @param {Object} config The config object
25247  */
25248
25249 Roo.bootstrap.MessageBar = function(config){
25250     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25251 };
25252
25253 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25254     
25255     html: '',
25256     weight: 'info',
25257     closable: false,
25258     fixed: false,
25259     beforeClass: 'bootstrap-sticky-wrap',
25260     
25261     getAutoCreate : function(){
25262         
25263         var cfg = {
25264             tag: 'div',
25265             cls: 'alert alert-dismissable alert-' + this.weight,
25266             cn: [
25267                 {
25268                     tag: 'span',
25269                     cls: 'message',
25270                     html: this.html || ''
25271                 }
25272             ]
25273         };
25274         
25275         if(this.fixed){
25276             cfg.cls += ' alert-messages-fixed';
25277         }
25278         
25279         if(this.closable){
25280             cfg.cn.push({
25281                 tag: 'button',
25282                 cls: 'close',
25283                 html: 'x'
25284             });
25285         }
25286         
25287         return cfg;
25288     },
25289     
25290     onRender : function(ct, position)
25291     {
25292         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25293         
25294         if(!this.el){
25295             var cfg = Roo.apply({},  this.getAutoCreate());
25296             cfg.id = Roo.id();
25297             
25298             if (this.cls) {
25299                 cfg.cls += ' ' + this.cls;
25300             }
25301             if (this.style) {
25302                 cfg.style = this.style;
25303             }
25304             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25305             
25306             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25307         }
25308         
25309         this.el.select('>button.close').on('click', this.hide, this);
25310         
25311     },
25312     
25313     show : function()
25314     {
25315         if (!this.rendered) {
25316             this.render();
25317         }
25318         
25319         this.el.show();
25320         
25321         this.fireEvent('show', this);
25322         
25323     },
25324     
25325     hide : function()
25326     {
25327         if (!this.rendered) {
25328             this.render();
25329         }
25330         
25331         this.el.hide();
25332         
25333         this.fireEvent('hide', this);
25334     },
25335     
25336     update : function()
25337     {
25338 //        var e = this.el.dom.firstChild;
25339 //        
25340 //        if(this.closable){
25341 //            e = e.nextSibling;
25342 //        }
25343 //        
25344 //        e.data = this.html || '';
25345
25346         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25347     }
25348    
25349 });
25350
25351  
25352
25353      /*
25354  * - LGPL
25355  *
25356  * Graph
25357  * 
25358  */
25359
25360
25361 /**
25362  * @class Roo.bootstrap.Graph
25363  * @extends Roo.bootstrap.Component
25364  * Bootstrap Graph class
25365 > Prameters
25366  -sm {number} sm 4
25367  -md {number} md 5
25368  @cfg {String} graphtype  bar | vbar | pie
25369  @cfg {number} g_x coodinator | centre x (pie)
25370  @cfg {number} g_y coodinator | centre y (pie)
25371  @cfg {number} g_r radius (pie)
25372  @cfg {number} g_height height of the chart (respected by all elements in the set)
25373  @cfg {number} g_width width of the chart (respected by all elements in the set)
25374  @cfg {Object} title The title of the chart
25375     
25376  -{Array}  values
25377  -opts (object) options for the chart 
25378      o {
25379      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25380      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25381      o vgutter (number)
25382      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.
25383      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25384      o to
25385      o stretch (boolean)
25386      o }
25387  -opts (object) options for the pie
25388      o{
25389      o cut
25390      o startAngle (number)
25391      o endAngle (number)
25392      } 
25393  *
25394  * @constructor
25395  * Create a new Input
25396  * @param {Object} config The config object
25397  */
25398
25399 Roo.bootstrap.Graph = function(config){
25400     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25401     
25402     this.addEvents({
25403         // img events
25404         /**
25405          * @event click
25406          * The img click event for the img.
25407          * @param {Roo.EventObject} e
25408          */
25409         "click" : true
25410     });
25411 };
25412
25413 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25414     
25415     sm: 4,
25416     md: 5,
25417     graphtype: 'bar',
25418     g_height: 250,
25419     g_width: 400,
25420     g_x: 50,
25421     g_y: 50,
25422     g_r: 30,
25423     opts:{
25424         //g_colors: this.colors,
25425         g_type: 'soft',
25426         g_gutter: '20%'
25427
25428     },
25429     title : false,
25430
25431     getAutoCreate : function(){
25432         
25433         var cfg = {
25434             tag: 'div',
25435             html : null
25436         };
25437         
25438         
25439         return  cfg;
25440     },
25441
25442     onRender : function(ct,position){
25443         
25444         
25445         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25446         
25447         if (typeof(Raphael) == 'undefined') {
25448             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25449             return;
25450         }
25451         
25452         this.raphael = Raphael(this.el.dom);
25453         
25454                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25455                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25456                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25457                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25458                 /*
25459                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25460                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25461                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25462                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25463                 
25464                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25465                 r.barchart(330, 10, 300, 220, data1);
25466                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25467                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25468                 */
25469                 
25470                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25471                 // r.barchart(30, 30, 560, 250,  xdata, {
25472                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25473                 //     axis : "0 0 1 1",
25474                 //     axisxlabels :  xdata
25475                 //     //yvalues : cols,
25476                    
25477                 // });
25478 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25479 //        
25480 //        this.load(null,xdata,{
25481 //                axis : "0 0 1 1",
25482 //                axisxlabels :  xdata
25483 //                });
25484
25485     },
25486
25487     load : function(graphtype,xdata,opts)
25488     {
25489         this.raphael.clear();
25490         if(!graphtype) {
25491             graphtype = this.graphtype;
25492         }
25493         if(!opts){
25494             opts = this.opts;
25495         }
25496         var r = this.raphael,
25497             fin = function () {
25498                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25499             },
25500             fout = function () {
25501                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25502             },
25503             pfin = function() {
25504                 this.sector.stop();
25505                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25506
25507                 if (this.label) {
25508                     this.label[0].stop();
25509                     this.label[0].attr({ r: 7.5 });
25510                     this.label[1].attr({ "font-weight": 800 });
25511                 }
25512             },
25513             pfout = function() {
25514                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25515
25516                 if (this.label) {
25517                     this.label[0].animate({ r: 5 }, 500, "bounce");
25518                     this.label[1].attr({ "font-weight": 400 });
25519                 }
25520             };
25521
25522         switch(graphtype){
25523             case 'bar':
25524                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25525                 break;
25526             case 'hbar':
25527                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25528                 break;
25529             case 'pie':
25530 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25531 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25532 //            
25533                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25534                 
25535                 break;
25536
25537         }
25538         
25539         if(this.title){
25540             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25541         }
25542         
25543     },
25544     
25545     setTitle: function(o)
25546     {
25547         this.title = o;
25548     },
25549     
25550     initEvents: function() {
25551         
25552         if(!this.href){
25553             this.el.on('click', this.onClick, this);
25554         }
25555     },
25556     
25557     onClick : function(e)
25558     {
25559         Roo.log('img onclick');
25560         this.fireEvent('click', this, e);
25561     }
25562    
25563 });
25564
25565  
25566 /*
25567  * - LGPL
25568  *
25569  * numberBox
25570  * 
25571  */
25572 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25573
25574 /**
25575  * @class Roo.bootstrap.dash.NumberBox
25576  * @extends Roo.bootstrap.Component
25577  * Bootstrap NumberBox class
25578  * @cfg {String} headline Box headline
25579  * @cfg {String} content Box content
25580  * @cfg {String} icon Box icon
25581  * @cfg {String} footer Footer text
25582  * @cfg {String} fhref Footer href
25583  * 
25584  * @constructor
25585  * Create a new NumberBox
25586  * @param {Object} config The config object
25587  */
25588
25589
25590 Roo.bootstrap.dash.NumberBox = function(config){
25591     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25592     
25593 };
25594
25595 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25596     
25597     headline : '',
25598     content : '',
25599     icon : '',
25600     footer : '',
25601     fhref : '',
25602     ficon : '',
25603     
25604     getAutoCreate : function(){
25605         
25606         var cfg = {
25607             tag : 'div',
25608             cls : 'small-box ',
25609             cn : [
25610                 {
25611                     tag : 'div',
25612                     cls : 'inner',
25613                     cn :[
25614                         {
25615                             tag : 'h3',
25616                             cls : 'roo-headline',
25617                             html : this.headline
25618                         },
25619                         {
25620                             tag : 'p',
25621                             cls : 'roo-content',
25622                             html : this.content
25623                         }
25624                     ]
25625                 }
25626             ]
25627         };
25628         
25629         if(this.icon){
25630             cfg.cn.push({
25631                 tag : 'div',
25632                 cls : 'icon',
25633                 cn :[
25634                     {
25635                         tag : 'i',
25636                         cls : 'ion ' + this.icon
25637                     }
25638                 ]
25639             });
25640         }
25641         
25642         if(this.footer){
25643             var footer = {
25644                 tag : 'a',
25645                 cls : 'small-box-footer',
25646                 href : this.fhref || '#',
25647                 html : this.footer
25648             };
25649             
25650             cfg.cn.push(footer);
25651             
25652         }
25653         
25654         return  cfg;
25655     },
25656
25657     onRender : function(ct,position){
25658         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25659
25660
25661        
25662                 
25663     },
25664
25665     setHeadline: function (value)
25666     {
25667         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25668     },
25669     
25670     setFooter: function (value, href)
25671     {
25672         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25673         
25674         if(href){
25675             this.el.select('a.small-box-footer',true).first().attr('href', href);
25676         }
25677         
25678     },
25679
25680     setContent: function (value)
25681     {
25682         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25683     },
25684
25685     initEvents: function() 
25686     {   
25687         
25688     }
25689     
25690 });
25691
25692  
25693 /*
25694  * - LGPL
25695  *
25696  * TabBox
25697  * 
25698  */
25699 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25700
25701 /**
25702  * @class Roo.bootstrap.dash.TabBox
25703  * @extends Roo.bootstrap.Component
25704  * Bootstrap TabBox class
25705  * @cfg {String} title Title of the TabBox
25706  * @cfg {String} icon Icon of the TabBox
25707  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25708  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25709  * 
25710  * @constructor
25711  * Create a new TabBox
25712  * @param {Object} config The config object
25713  */
25714
25715
25716 Roo.bootstrap.dash.TabBox = function(config){
25717     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25718     this.addEvents({
25719         // raw events
25720         /**
25721          * @event addpane
25722          * When a pane is added
25723          * @param {Roo.bootstrap.dash.TabPane} pane
25724          */
25725         "addpane" : true,
25726         /**
25727          * @event activatepane
25728          * When a pane is activated
25729          * @param {Roo.bootstrap.dash.TabPane} pane
25730          */
25731         "activatepane" : true
25732         
25733          
25734     });
25735     
25736     this.panes = [];
25737 };
25738
25739 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25740
25741     title : '',
25742     icon : false,
25743     showtabs : true,
25744     tabScrollable : false,
25745     
25746     getChildContainer : function()
25747     {
25748         return this.el.select('.tab-content', true).first();
25749     },
25750     
25751     getAutoCreate : function(){
25752         
25753         var header = {
25754             tag: 'li',
25755             cls: 'pull-left header',
25756             html: this.title,
25757             cn : []
25758         };
25759         
25760         if(this.icon){
25761             header.cn.push({
25762                 tag: 'i',
25763                 cls: 'fa ' + this.icon
25764             });
25765         }
25766         
25767         var h = {
25768             tag: 'ul',
25769             cls: 'nav nav-tabs pull-right',
25770             cn: [
25771                 header
25772             ]
25773         };
25774         
25775         if(this.tabScrollable){
25776             h = {
25777                 tag: 'div',
25778                 cls: 'tab-header',
25779                 cn: [
25780                     {
25781                         tag: 'ul',
25782                         cls: 'nav nav-tabs pull-right',
25783                         cn: [
25784                             header
25785                         ]
25786                     }
25787                 ]
25788             };
25789         }
25790         
25791         var cfg = {
25792             tag: 'div',
25793             cls: 'nav-tabs-custom',
25794             cn: [
25795                 h,
25796                 {
25797                     tag: 'div',
25798                     cls: 'tab-content no-padding',
25799                     cn: []
25800                 }
25801             ]
25802         };
25803
25804         return  cfg;
25805     },
25806     initEvents : function()
25807     {
25808         //Roo.log('add add pane handler');
25809         this.on('addpane', this.onAddPane, this);
25810     },
25811      /**
25812      * Updates the box title
25813      * @param {String} html to set the title to.
25814      */
25815     setTitle : function(value)
25816     {
25817         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25818     },
25819     onAddPane : function(pane)
25820     {
25821         this.panes.push(pane);
25822         //Roo.log('addpane');
25823         //Roo.log(pane);
25824         // tabs are rendere left to right..
25825         if(!this.showtabs){
25826             return;
25827         }
25828         
25829         var ctr = this.el.select('.nav-tabs', true).first();
25830          
25831          
25832         var existing = ctr.select('.nav-tab',true);
25833         var qty = existing.getCount();;
25834         
25835         
25836         var tab = ctr.createChild({
25837             tag : 'li',
25838             cls : 'nav-tab' + (qty ? '' : ' active'),
25839             cn : [
25840                 {
25841                     tag : 'a',
25842                     href:'#',
25843                     html : pane.title
25844                 }
25845             ]
25846         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25847         pane.tab = tab;
25848         
25849         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25850         if (!qty) {
25851             pane.el.addClass('active');
25852         }
25853         
25854                 
25855     },
25856     onTabClick : function(ev,un,ob,pane)
25857     {
25858         //Roo.log('tab - prev default');
25859         ev.preventDefault();
25860         
25861         
25862         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25863         pane.tab.addClass('active');
25864         //Roo.log(pane.title);
25865         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25866         // technically we should have a deactivate event.. but maybe add later.
25867         // and it should not de-activate the selected tab...
25868         this.fireEvent('activatepane', pane);
25869         pane.el.addClass('active');
25870         pane.fireEvent('activate');
25871         
25872         
25873     },
25874     
25875     getActivePane : function()
25876     {
25877         var r = false;
25878         Roo.each(this.panes, function(p) {
25879             if(p.el.hasClass('active')){
25880                 r = p;
25881                 return false;
25882             }
25883             
25884             return;
25885         });
25886         
25887         return r;
25888     }
25889     
25890     
25891 });
25892
25893  
25894 /*
25895  * - LGPL
25896  *
25897  * Tab pane
25898  * 
25899  */
25900 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25901 /**
25902  * @class Roo.bootstrap.TabPane
25903  * @extends Roo.bootstrap.Component
25904  * Bootstrap TabPane class
25905  * @cfg {Boolean} active (false | true) Default false
25906  * @cfg {String} title title of panel
25907
25908  * 
25909  * @constructor
25910  * Create a new TabPane
25911  * @param {Object} config The config object
25912  */
25913
25914 Roo.bootstrap.dash.TabPane = function(config){
25915     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25916     
25917     this.addEvents({
25918         // raw events
25919         /**
25920          * @event activate
25921          * When a pane is activated
25922          * @param {Roo.bootstrap.dash.TabPane} pane
25923          */
25924         "activate" : true
25925          
25926     });
25927 };
25928
25929 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25930     
25931     active : false,
25932     title : '',
25933     
25934     // the tabBox that this is attached to.
25935     tab : false,
25936      
25937     getAutoCreate : function() 
25938     {
25939         var cfg = {
25940             tag: 'div',
25941             cls: 'tab-pane'
25942         };
25943         
25944         if(this.active){
25945             cfg.cls += ' active';
25946         }
25947         
25948         return cfg;
25949     },
25950     initEvents  : function()
25951     {
25952         //Roo.log('trigger add pane handler');
25953         this.parent().fireEvent('addpane', this)
25954     },
25955     
25956      /**
25957      * Updates the tab title 
25958      * @param {String} html to set the title to.
25959      */
25960     setTitle: function(str)
25961     {
25962         if (!this.tab) {
25963             return;
25964         }
25965         this.title = str;
25966         this.tab.select('a', true).first().dom.innerHTML = str;
25967         
25968     }
25969     
25970     
25971     
25972 });
25973
25974  
25975
25976
25977  /*
25978  * - LGPL
25979  *
25980  * menu
25981  * 
25982  */
25983 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25984
25985 /**
25986  * @class Roo.bootstrap.menu.Menu
25987  * @extends Roo.bootstrap.Component
25988  * Bootstrap Menu class - container for Menu
25989  * @cfg {String} html Text of the menu
25990  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25991  * @cfg {String} icon Font awesome icon
25992  * @cfg {String} pos Menu align to (top | bottom) default bottom
25993  * 
25994  * 
25995  * @constructor
25996  * Create a new Menu
25997  * @param {Object} config The config object
25998  */
25999
26000
26001 Roo.bootstrap.menu.Menu = function(config){
26002     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
26003     
26004     this.addEvents({
26005         /**
26006          * @event beforeshow
26007          * Fires before this menu is displayed
26008          * @param {Roo.bootstrap.menu.Menu} this
26009          */
26010         beforeshow : true,
26011         /**
26012          * @event beforehide
26013          * Fires before this menu is hidden
26014          * @param {Roo.bootstrap.menu.Menu} this
26015          */
26016         beforehide : true,
26017         /**
26018          * @event show
26019          * Fires after this menu is displayed
26020          * @param {Roo.bootstrap.menu.Menu} this
26021          */
26022         show : true,
26023         /**
26024          * @event hide
26025          * Fires after this menu is hidden
26026          * @param {Roo.bootstrap.menu.Menu} this
26027          */
26028         hide : true,
26029         /**
26030          * @event click
26031          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
26032          * @param {Roo.bootstrap.menu.Menu} this
26033          * @param {Roo.EventObject} e
26034          */
26035         click : true
26036     });
26037     
26038 };
26039
26040 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
26041     
26042     submenu : false,
26043     html : '',
26044     weight : 'default',
26045     icon : false,
26046     pos : 'bottom',
26047     
26048     
26049     getChildContainer : function() {
26050         if(this.isSubMenu){
26051             return this.el;
26052         }
26053         
26054         return this.el.select('ul.dropdown-menu', true).first();  
26055     },
26056     
26057     getAutoCreate : function()
26058     {
26059         var text = [
26060             {
26061                 tag : 'span',
26062                 cls : 'roo-menu-text',
26063                 html : this.html
26064             }
26065         ];
26066         
26067         if(this.icon){
26068             text.unshift({
26069                 tag : 'i',
26070                 cls : 'fa ' + this.icon
26071             })
26072         }
26073         
26074         
26075         var cfg = {
26076             tag : 'div',
26077             cls : 'btn-group',
26078             cn : [
26079                 {
26080                     tag : 'button',
26081                     cls : 'dropdown-button btn btn-' + this.weight,
26082                     cn : text
26083                 },
26084                 {
26085                     tag : 'button',
26086                     cls : 'dropdown-toggle btn btn-' + this.weight,
26087                     cn : [
26088                         {
26089                             tag : 'span',
26090                             cls : 'caret'
26091                         }
26092                     ]
26093                 },
26094                 {
26095                     tag : 'ul',
26096                     cls : 'dropdown-menu'
26097                 }
26098             ]
26099             
26100         };
26101         
26102         if(this.pos == 'top'){
26103             cfg.cls += ' dropup';
26104         }
26105         
26106         if(this.isSubMenu){
26107             cfg = {
26108                 tag : 'ul',
26109                 cls : 'dropdown-menu'
26110             }
26111         }
26112         
26113         return cfg;
26114     },
26115     
26116     onRender : function(ct, position)
26117     {
26118         this.isSubMenu = ct.hasClass('dropdown-submenu');
26119         
26120         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
26121     },
26122     
26123     initEvents : function() 
26124     {
26125         if(this.isSubMenu){
26126             return;
26127         }
26128         
26129         this.hidden = true;
26130         
26131         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
26132         this.triggerEl.on('click', this.onTriggerPress, this);
26133         
26134         this.buttonEl = this.el.select('button.dropdown-button', true).first();
26135         this.buttonEl.on('click', this.onClick, this);
26136         
26137     },
26138     
26139     list : function()
26140     {
26141         if(this.isSubMenu){
26142             return this.el;
26143         }
26144         
26145         return this.el.select('ul.dropdown-menu', true).first();
26146     },
26147     
26148     onClick : function(e)
26149     {
26150         this.fireEvent("click", this, e);
26151     },
26152     
26153     onTriggerPress  : function(e)
26154     {   
26155         if (this.isVisible()) {
26156             this.hide();
26157         } else {
26158             this.show();
26159         }
26160     },
26161     
26162     isVisible : function(){
26163         return !this.hidden;
26164     },
26165     
26166     show : function()
26167     {
26168         this.fireEvent("beforeshow", this);
26169         
26170         this.hidden = false;
26171         this.el.addClass('open');
26172         
26173         Roo.get(document).on("mouseup", this.onMouseUp, this);
26174         
26175         this.fireEvent("show", this);
26176         
26177         
26178     },
26179     
26180     hide : function()
26181     {
26182         this.fireEvent("beforehide", this);
26183         
26184         this.hidden = true;
26185         this.el.removeClass('open');
26186         
26187         Roo.get(document).un("mouseup", this.onMouseUp);
26188         
26189         this.fireEvent("hide", this);
26190     },
26191     
26192     onMouseUp : function()
26193     {
26194         this.hide();
26195     }
26196     
26197 });
26198
26199  
26200  /*
26201  * - LGPL
26202  *
26203  * menu item
26204  * 
26205  */
26206 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26207
26208 /**
26209  * @class Roo.bootstrap.menu.Item
26210  * @extends Roo.bootstrap.Component
26211  * Bootstrap MenuItem class
26212  * @cfg {Boolean} submenu (true | false) default false
26213  * @cfg {String} html text of the item
26214  * @cfg {String} href the link
26215  * @cfg {Boolean} disable (true | false) default false
26216  * @cfg {Boolean} preventDefault (true | false) default true
26217  * @cfg {String} icon Font awesome icon
26218  * @cfg {String} pos Submenu align to (left | right) default right 
26219  * 
26220  * 
26221  * @constructor
26222  * Create a new Item
26223  * @param {Object} config The config object
26224  */
26225
26226
26227 Roo.bootstrap.menu.Item = function(config){
26228     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26229     this.addEvents({
26230         /**
26231          * @event mouseover
26232          * Fires when the mouse is hovering over this menu
26233          * @param {Roo.bootstrap.menu.Item} this
26234          * @param {Roo.EventObject} e
26235          */
26236         mouseover : true,
26237         /**
26238          * @event mouseout
26239          * Fires when the mouse exits this menu
26240          * @param {Roo.bootstrap.menu.Item} this
26241          * @param {Roo.EventObject} e
26242          */
26243         mouseout : true,
26244         // raw events
26245         /**
26246          * @event click
26247          * The raw click event for the entire grid.
26248          * @param {Roo.EventObject} e
26249          */
26250         click : true
26251     });
26252 };
26253
26254 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26255     
26256     submenu : false,
26257     href : '',
26258     html : '',
26259     preventDefault: true,
26260     disable : false,
26261     icon : false,
26262     pos : 'right',
26263     
26264     getAutoCreate : function()
26265     {
26266         var text = [
26267             {
26268                 tag : 'span',
26269                 cls : 'roo-menu-item-text',
26270                 html : this.html
26271             }
26272         ];
26273         
26274         if(this.icon){
26275             text.unshift({
26276                 tag : 'i',
26277                 cls : 'fa ' + this.icon
26278             })
26279         }
26280         
26281         var cfg = {
26282             tag : 'li',
26283             cn : [
26284                 {
26285                     tag : 'a',
26286                     href : this.href || '#',
26287                     cn : text
26288                 }
26289             ]
26290         };
26291         
26292         if(this.disable){
26293             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26294         }
26295         
26296         if(this.submenu){
26297             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26298             
26299             if(this.pos == 'left'){
26300                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26301             }
26302         }
26303         
26304         return cfg;
26305     },
26306     
26307     initEvents : function() 
26308     {
26309         this.el.on('mouseover', this.onMouseOver, this);
26310         this.el.on('mouseout', this.onMouseOut, this);
26311         
26312         this.el.select('a', true).first().on('click', this.onClick, this);
26313         
26314     },
26315     
26316     onClick : function(e)
26317     {
26318         if(this.preventDefault){
26319             e.preventDefault();
26320         }
26321         
26322         this.fireEvent("click", this, e);
26323     },
26324     
26325     onMouseOver : function(e)
26326     {
26327         if(this.submenu && this.pos == 'left'){
26328             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26329         }
26330         
26331         this.fireEvent("mouseover", this, e);
26332     },
26333     
26334     onMouseOut : function(e)
26335     {
26336         this.fireEvent("mouseout", this, e);
26337     }
26338 });
26339
26340  
26341
26342  /*
26343  * - LGPL
26344  *
26345  * menu separator
26346  * 
26347  */
26348 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26349
26350 /**
26351  * @class Roo.bootstrap.menu.Separator
26352  * @extends Roo.bootstrap.Component
26353  * Bootstrap Separator class
26354  * 
26355  * @constructor
26356  * Create a new Separator
26357  * @param {Object} config The config object
26358  */
26359
26360
26361 Roo.bootstrap.menu.Separator = function(config){
26362     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26363 };
26364
26365 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26366     
26367     getAutoCreate : function(){
26368         var cfg = {
26369             tag : 'li',
26370             cls: 'divider'
26371         };
26372         
26373         return cfg;
26374     }
26375    
26376 });
26377
26378  
26379
26380  /*
26381  * - LGPL
26382  *
26383  * Tooltip
26384  * 
26385  */
26386
26387 /**
26388  * @class Roo.bootstrap.Tooltip
26389  * Bootstrap Tooltip class
26390  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26391  * to determine which dom element triggers the tooltip.
26392  * 
26393  * It needs to add support for additional attributes like tooltip-position
26394  * 
26395  * @constructor
26396  * Create a new Toolti
26397  * @param {Object} config The config object
26398  */
26399
26400 Roo.bootstrap.Tooltip = function(config){
26401     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26402     
26403     this.alignment = Roo.bootstrap.Tooltip.alignment;
26404     
26405     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26406         this.alignment = config.alignment;
26407     }
26408     
26409 };
26410
26411 Roo.apply(Roo.bootstrap.Tooltip, {
26412     /**
26413      * @function init initialize tooltip monitoring.
26414      * @static
26415      */
26416     currentEl : false,
26417     currentTip : false,
26418     currentRegion : false,
26419     
26420     //  init : delay?
26421     
26422     init : function()
26423     {
26424         Roo.get(document).on('mouseover', this.enter ,this);
26425         Roo.get(document).on('mouseout', this.leave, this);
26426          
26427         
26428         this.currentTip = new Roo.bootstrap.Tooltip();
26429     },
26430     
26431     enter : function(ev)
26432     {
26433         var dom = ev.getTarget();
26434         
26435         //Roo.log(['enter',dom]);
26436         var el = Roo.fly(dom);
26437         if (this.currentEl) {
26438             //Roo.log(dom);
26439             //Roo.log(this.currentEl);
26440             //Roo.log(this.currentEl.contains(dom));
26441             if (this.currentEl == el) {
26442                 return;
26443             }
26444             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26445                 return;
26446             }
26447
26448         }
26449         
26450         if (this.currentTip.el) {
26451             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26452         }    
26453         //Roo.log(ev);
26454         
26455         if(!el || el.dom == document){
26456             return;
26457         }
26458         
26459         var bindEl = el;
26460         
26461         // you can not look for children, as if el is the body.. then everythign is the child..
26462         if (!el.attr('tooltip')) { //
26463             if (!el.select("[tooltip]").elements.length) {
26464                 return;
26465             }
26466             // is the mouse over this child...?
26467             bindEl = el.select("[tooltip]").first();
26468             var xy = ev.getXY();
26469             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26470                 //Roo.log("not in region.");
26471                 return;
26472             }
26473             //Roo.log("child element over..");
26474             
26475         }
26476         this.currentEl = bindEl;
26477         this.currentTip.bind(bindEl);
26478         this.currentRegion = Roo.lib.Region.getRegion(dom);
26479         this.currentTip.enter();
26480         
26481     },
26482     leave : function(ev)
26483     {
26484         var dom = ev.getTarget();
26485         //Roo.log(['leave',dom]);
26486         if (!this.currentEl) {
26487             return;
26488         }
26489         
26490         
26491         if (dom != this.currentEl.dom) {
26492             return;
26493         }
26494         var xy = ev.getXY();
26495         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26496             return;
26497         }
26498         // only activate leave if mouse cursor is outside... bounding box..
26499         
26500         
26501         
26502         
26503         if (this.currentTip) {
26504             this.currentTip.leave();
26505         }
26506         //Roo.log('clear currentEl');
26507         this.currentEl = false;
26508         
26509         
26510     },
26511     alignment : {
26512         'left' : ['r-l', [-2,0], 'right'],
26513         'right' : ['l-r', [2,0], 'left'],
26514         'bottom' : ['t-b', [0,2], 'top'],
26515         'top' : [ 'b-t', [0,-2], 'bottom']
26516     }
26517     
26518 });
26519
26520
26521 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26522     
26523     
26524     bindEl : false,
26525     
26526     delay : null, // can be { show : 300 , hide: 500}
26527     
26528     timeout : null,
26529     
26530     hoverState : null, //???
26531     
26532     placement : 'bottom', 
26533     
26534     alignment : false,
26535     
26536     getAutoCreate : function(){
26537     
26538         var cfg = {
26539            cls : 'tooltip',
26540            role : 'tooltip',
26541            cn : [
26542                 {
26543                     cls : 'tooltip-arrow'
26544                 },
26545                 {
26546                     cls : 'tooltip-inner'
26547                 }
26548            ]
26549         };
26550         
26551         return cfg;
26552     },
26553     bind : function(el)
26554     {
26555         this.bindEl = el;
26556     },
26557       
26558     
26559     enter : function () {
26560        
26561         if (this.timeout != null) {
26562             clearTimeout(this.timeout);
26563         }
26564         
26565         this.hoverState = 'in';
26566          //Roo.log("enter - show");
26567         if (!this.delay || !this.delay.show) {
26568             this.show();
26569             return;
26570         }
26571         var _t = this;
26572         this.timeout = setTimeout(function () {
26573             if (_t.hoverState == 'in') {
26574                 _t.show();
26575             }
26576         }, this.delay.show);
26577     },
26578     leave : function()
26579     {
26580         clearTimeout(this.timeout);
26581     
26582         this.hoverState = 'out';
26583          if (!this.delay || !this.delay.hide) {
26584             this.hide();
26585             return;
26586         }
26587        
26588         var _t = this;
26589         this.timeout = setTimeout(function () {
26590             //Roo.log("leave - timeout");
26591             
26592             if (_t.hoverState == 'out') {
26593                 _t.hide();
26594                 Roo.bootstrap.Tooltip.currentEl = false;
26595             }
26596         }, delay);
26597     },
26598     
26599     show : function (msg)
26600     {
26601         if (!this.el) {
26602             this.render(document.body);
26603         }
26604         // set content.
26605         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26606         
26607         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26608         
26609         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26610         
26611         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26612         
26613         var placement = typeof this.placement == 'function' ?
26614             this.placement.call(this, this.el, on_el) :
26615             this.placement;
26616             
26617         var autoToken = /\s?auto?\s?/i;
26618         var autoPlace = autoToken.test(placement);
26619         if (autoPlace) {
26620             placement = placement.replace(autoToken, '') || 'top';
26621         }
26622         
26623         //this.el.detach()
26624         //this.el.setXY([0,0]);
26625         this.el.show();
26626         //this.el.dom.style.display='block';
26627         
26628         //this.el.appendTo(on_el);
26629         
26630         var p = this.getPosition();
26631         var box = this.el.getBox();
26632         
26633         if (autoPlace) {
26634             // fixme..
26635         }
26636         
26637         var align = this.alignment[placement];
26638         
26639         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26640         
26641         if(placement == 'top' || placement == 'bottom'){
26642             if(xy[0] < 0){
26643                 placement = 'right';
26644             }
26645             
26646             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26647                 placement = 'left';
26648             }
26649             
26650             var scroll = Roo.select('body', true).first().getScroll();
26651             
26652             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26653                 placement = 'top';
26654             }
26655             
26656             align = this.alignment[placement];
26657         }
26658         
26659         this.el.alignTo(this.bindEl, align[0],align[1]);
26660         //var arrow = this.el.select('.arrow',true).first();
26661         //arrow.set(align[2], 
26662         
26663         this.el.addClass(placement);
26664         
26665         this.el.addClass('in fade');
26666         
26667         this.hoverState = null;
26668         
26669         if (this.el.hasClass('fade')) {
26670             // fade it?
26671         }
26672         
26673     },
26674     hide : function()
26675     {
26676          
26677         if (!this.el) {
26678             return;
26679         }
26680         //this.el.setXY([0,0]);
26681         this.el.removeClass('in');
26682         //this.el.hide();
26683         
26684     }
26685     
26686 });
26687  
26688
26689  /*
26690  * - LGPL
26691  *
26692  * Location Picker
26693  * 
26694  */
26695
26696 /**
26697  * @class Roo.bootstrap.LocationPicker
26698  * @extends Roo.bootstrap.Component
26699  * Bootstrap LocationPicker class
26700  * @cfg {Number} latitude Position when init default 0
26701  * @cfg {Number} longitude Position when init default 0
26702  * @cfg {Number} zoom default 15
26703  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26704  * @cfg {Boolean} mapTypeControl default false
26705  * @cfg {Boolean} disableDoubleClickZoom default false
26706  * @cfg {Boolean} scrollwheel default true
26707  * @cfg {Boolean} streetViewControl default false
26708  * @cfg {Number} radius default 0
26709  * @cfg {String} locationName
26710  * @cfg {Boolean} draggable default true
26711  * @cfg {Boolean} enableAutocomplete default false
26712  * @cfg {Boolean} enableReverseGeocode default true
26713  * @cfg {String} markerTitle
26714  * 
26715  * @constructor
26716  * Create a new LocationPicker
26717  * @param {Object} config The config object
26718  */
26719
26720
26721 Roo.bootstrap.LocationPicker = function(config){
26722     
26723     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26724     
26725     this.addEvents({
26726         /**
26727          * @event initial
26728          * Fires when the picker initialized.
26729          * @param {Roo.bootstrap.LocationPicker} this
26730          * @param {Google Location} location
26731          */
26732         initial : true,
26733         /**
26734          * @event positionchanged
26735          * Fires when the picker position changed.
26736          * @param {Roo.bootstrap.LocationPicker} this
26737          * @param {Google Location} location
26738          */
26739         positionchanged : true,
26740         /**
26741          * @event resize
26742          * Fires when the map resize.
26743          * @param {Roo.bootstrap.LocationPicker} this
26744          */
26745         resize : true,
26746         /**
26747          * @event show
26748          * Fires when the map show.
26749          * @param {Roo.bootstrap.LocationPicker} this
26750          */
26751         show : true,
26752         /**
26753          * @event hide
26754          * Fires when the map hide.
26755          * @param {Roo.bootstrap.LocationPicker} this
26756          */
26757         hide : true,
26758         /**
26759          * @event mapClick
26760          * Fires when click the map.
26761          * @param {Roo.bootstrap.LocationPicker} this
26762          * @param {Map event} e
26763          */
26764         mapClick : true,
26765         /**
26766          * @event mapRightClick
26767          * Fires when right click the map.
26768          * @param {Roo.bootstrap.LocationPicker} this
26769          * @param {Map event} e
26770          */
26771         mapRightClick : true,
26772         /**
26773          * @event markerClick
26774          * Fires when click the marker.
26775          * @param {Roo.bootstrap.LocationPicker} this
26776          * @param {Map event} e
26777          */
26778         markerClick : true,
26779         /**
26780          * @event markerRightClick
26781          * Fires when right click the marker.
26782          * @param {Roo.bootstrap.LocationPicker} this
26783          * @param {Map event} e
26784          */
26785         markerRightClick : true,
26786         /**
26787          * @event OverlayViewDraw
26788          * Fires when OverlayView Draw
26789          * @param {Roo.bootstrap.LocationPicker} this
26790          */
26791         OverlayViewDraw : true,
26792         /**
26793          * @event OverlayViewOnAdd
26794          * Fires when OverlayView Draw
26795          * @param {Roo.bootstrap.LocationPicker} this
26796          */
26797         OverlayViewOnAdd : true,
26798         /**
26799          * @event OverlayViewOnRemove
26800          * Fires when OverlayView Draw
26801          * @param {Roo.bootstrap.LocationPicker} this
26802          */
26803         OverlayViewOnRemove : true,
26804         /**
26805          * @event OverlayViewShow
26806          * Fires when OverlayView Draw
26807          * @param {Roo.bootstrap.LocationPicker} this
26808          * @param {Pixel} cpx
26809          */
26810         OverlayViewShow : true,
26811         /**
26812          * @event OverlayViewHide
26813          * Fires when OverlayView Draw
26814          * @param {Roo.bootstrap.LocationPicker} this
26815          */
26816         OverlayViewHide : true,
26817         /**
26818          * @event loadexception
26819          * Fires when load google lib failed.
26820          * @param {Roo.bootstrap.LocationPicker} this
26821          */
26822         loadexception : true
26823     });
26824         
26825 };
26826
26827 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26828     
26829     gMapContext: false,
26830     
26831     latitude: 0,
26832     longitude: 0,
26833     zoom: 15,
26834     mapTypeId: false,
26835     mapTypeControl: false,
26836     disableDoubleClickZoom: false,
26837     scrollwheel: true,
26838     streetViewControl: false,
26839     radius: 0,
26840     locationName: '',
26841     draggable: true,
26842     enableAutocomplete: false,
26843     enableReverseGeocode: true,
26844     markerTitle: '',
26845     
26846     getAutoCreate: function()
26847     {
26848
26849         var cfg = {
26850             tag: 'div',
26851             cls: 'roo-location-picker'
26852         };
26853         
26854         return cfg
26855     },
26856     
26857     initEvents: function(ct, position)
26858     {       
26859         if(!this.el.getWidth() || this.isApplied()){
26860             return;
26861         }
26862         
26863         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26864         
26865         this.initial();
26866     },
26867     
26868     initial: function()
26869     {
26870         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26871             this.fireEvent('loadexception', this);
26872             return;
26873         }
26874         
26875         if(!this.mapTypeId){
26876             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26877         }
26878         
26879         this.gMapContext = this.GMapContext();
26880         
26881         this.initOverlayView();
26882         
26883         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26884         
26885         var _this = this;
26886                 
26887         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26888             _this.setPosition(_this.gMapContext.marker.position);
26889         });
26890         
26891         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26892             _this.fireEvent('mapClick', this, event);
26893             
26894         });
26895
26896         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26897             _this.fireEvent('mapRightClick', this, event);
26898             
26899         });
26900         
26901         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26902             _this.fireEvent('markerClick', this, event);
26903             
26904         });
26905
26906         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26907             _this.fireEvent('markerRightClick', this, event);
26908             
26909         });
26910         
26911         this.setPosition(this.gMapContext.location);
26912         
26913         this.fireEvent('initial', this, this.gMapContext.location);
26914     },
26915     
26916     initOverlayView: function()
26917     {
26918         var _this = this;
26919         
26920         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26921             
26922             draw: function()
26923             {
26924                 _this.fireEvent('OverlayViewDraw', _this);
26925             },
26926             
26927             onAdd: function()
26928             {
26929                 _this.fireEvent('OverlayViewOnAdd', _this);
26930             },
26931             
26932             onRemove: function()
26933             {
26934                 _this.fireEvent('OverlayViewOnRemove', _this);
26935             },
26936             
26937             show: function(cpx)
26938             {
26939                 _this.fireEvent('OverlayViewShow', _this, cpx);
26940             },
26941             
26942             hide: function()
26943             {
26944                 _this.fireEvent('OverlayViewHide', _this);
26945             }
26946             
26947         });
26948     },
26949     
26950     fromLatLngToContainerPixel: function(event)
26951     {
26952         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26953     },
26954     
26955     isApplied: function() 
26956     {
26957         return this.getGmapContext() == false ? false : true;
26958     },
26959     
26960     getGmapContext: function() 
26961     {
26962         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26963     },
26964     
26965     GMapContext: function() 
26966     {
26967         var position = new google.maps.LatLng(this.latitude, this.longitude);
26968         
26969         var _map = new google.maps.Map(this.el.dom, {
26970             center: position,
26971             zoom: this.zoom,
26972             mapTypeId: this.mapTypeId,
26973             mapTypeControl: this.mapTypeControl,
26974             disableDoubleClickZoom: this.disableDoubleClickZoom,
26975             scrollwheel: this.scrollwheel,
26976             streetViewControl: this.streetViewControl,
26977             locationName: this.locationName,
26978             draggable: this.draggable,
26979             enableAutocomplete: this.enableAutocomplete,
26980             enableReverseGeocode: this.enableReverseGeocode
26981         });
26982         
26983         var _marker = new google.maps.Marker({
26984             position: position,
26985             map: _map,
26986             title: this.markerTitle,
26987             draggable: this.draggable
26988         });
26989         
26990         return {
26991             map: _map,
26992             marker: _marker,
26993             circle: null,
26994             location: position,
26995             radius: this.radius,
26996             locationName: this.locationName,
26997             addressComponents: {
26998                 formatted_address: null,
26999                 addressLine1: null,
27000                 addressLine2: null,
27001                 streetName: null,
27002                 streetNumber: null,
27003                 city: null,
27004                 district: null,
27005                 state: null,
27006                 stateOrProvince: null
27007             },
27008             settings: this,
27009             domContainer: this.el.dom,
27010             geodecoder: new google.maps.Geocoder()
27011         };
27012     },
27013     
27014     drawCircle: function(center, radius, options) 
27015     {
27016         if (this.gMapContext.circle != null) {
27017             this.gMapContext.circle.setMap(null);
27018         }
27019         if (radius > 0) {
27020             radius *= 1;
27021             options = Roo.apply({}, options, {
27022                 strokeColor: "#0000FF",
27023                 strokeOpacity: .35,
27024                 strokeWeight: 2,
27025                 fillColor: "#0000FF",
27026                 fillOpacity: .2
27027             });
27028             
27029             options.map = this.gMapContext.map;
27030             options.radius = radius;
27031             options.center = center;
27032             this.gMapContext.circle = new google.maps.Circle(options);
27033             return this.gMapContext.circle;
27034         }
27035         
27036         return null;
27037     },
27038     
27039     setPosition: function(location) 
27040     {
27041         this.gMapContext.location = location;
27042         this.gMapContext.marker.setPosition(location);
27043         this.gMapContext.map.panTo(location);
27044         this.drawCircle(location, this.gMapContext.radius, {});
27045         
27046         var _this = this;
27047         
27048         if (this.gMapContext.settings.enableReverseGeocode) {
27049             this.gMapContext.geodecoder.geocode({
27050                 latLng: this.gMapContext.location
27051             }, function(results, status) {
27052                 
27053                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
27054                     _this.gMapContext.locationName = results[0].formatted_address;
27055                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
27056                     
27057                     _this.fireEvent('positionchanged', this, location);
27058                 }
27059             });
27060             
27061             return;
27062         }
27063         
27064         this.fireEvent('positionchanged', this, location);
27065     },
27066     
27067     resize: function()
27068     {
27069         google.maps.event.trigger(this.gMapContext.map, "resize");
27070         
27071         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
27072         
27073         this.fireEvent('resize', this);
27074     },
27075     
27076     setPositionByLatLng: function(latitude, longitude)
27077     {
27078         this.setPosition(new google.maps.LatLng(latitude, longitude));
27079     },
27080     
27081     getCurrentPosition: function() 
27082     {
27083         return {
27084             latitude: this.gMapContext.location.lat(),
27085             longitude: this.gMapContext.location.lng()
27086         };
27087     },
27088     
27089     getAddressName: function() 
27090     {
27091         return this.gMapContext.locationName;
27092     },
27093     
27094     getAddressComponents: function() 
27095     {
27096         return this.gMapContext.addressComponents;
27097     },
27098     
27099     address_component_from_google_geocode: function(address_components) 
27100     {
27101         var result = {};
27102         
27103         for (var i = 0; i < address_components.length; i++) {
27104             var component = address_components[i];
27105             if (component.types.indexOf("postal_code") >= 0) {
27106                 result.postalCode = component.short_name;
27107             } else if (component.types.indexOf("street_number") >= 0) {
27108                 result.streetNumber = component.short_name;
27109             } else if (component.types.indexOf("route") >= 0) {
27110                 result.streetName = component.short_name;
27111             } else if (component.types.indexOf("neighborhood") >= 0) {
27112                 result.city = component.short_name;
27113             } else if (component.types.indexOf("locality") >= 0) {
27114                 result.city = component.short_name;
27115             } else if (component.types.indexOf("sublocality") >= 0) {
27116                 result.district = component.short_name;
27117             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
27118                 result.stateOrProvince = component.short_name;
27119             } else if (component.types.indexOf("country") >= 0) {
27120                 result.country = component.short_name;
27121             }
27122         }
27123         
27124         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
27125         result.addressLine2 = "";
27126         return result;
27127     },
27128     
27129     setZoomLevel: function(zoom)
27130     {
27131         this.gMapContext.map.setZoom(zoom);
27132     },
27133     
27134     show: function()
27135     {
27136         if(!this.el){
27137             return;
27138         }
27139         
27140         this.el.show();
27141         
27142         this.resize();
27143         
27144         this.fireEvent('show', this);
27145     },
27146     
27147     hide: function()
27148     {
27149         if(!this.el){
27150             return;
27151         }
27152         
27153         this.el.hide();
27154         
27155         this.fireEvent('hide', this);
27156     }
27157     
27158 });
27159
27160 Roo.apply(Roo.bootstrap.LocationPicker, {
27161     
27162     OverlayView : function(map, options)
27163     {
27164         options = options || {};
27165         
27166         this.setMap(map);
27167     }
27168     
27169     
27170 });/*
27171  * - LGPL
27172  *
27173  * Alert
27174  * 
27175  */
27176
27177 /**
27178  * @class Roo.bootstrap.Alert
27179  * @extends Roo.bootstrap.Component
27180  * Bootstrap Alert class
27181  * @cfg {String} title The title of alert
27182  * @cfg {String} html The content of alert
27183  * @cfg {String} weight (  success | info | warning | danger )
27184  * @cfg {String} faicon font-awesomeicon
27185  * 
27186  * @constructor
27187  * Create a new alert
27188  * @param {Object} config The config object
27189  */
27190
27191
27192 Roo.bootstrap.Alert = function(config){
27193     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27194     
27195 };
27196
27197 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
27198     
27199     title: '',
27200     html: '',
27201     weight: false,
27202     faicon: false,
27203     
27204     getAutoCreate : function()
27205     {
27206         
27207         var cfg = {
27208             tag : 'div',
27209             cls : 'alert',
27210             cn : [
27211                 {
27212                     tag : 'i',
27213                     cls : 'roo-alert-icon'
27214                     
27215                 },
27216                 {
27217                     tag : 'b',
27218                     cls : 'roo-alert-title',
27219                     html : this.title
27220                 },
27221                 {
27222                     tag : 'span',
27223                     cls : 'roo-alert-text',
27224                     html : this.html
27225                 }
27226             ]
27227         };
27228         
27229         if(this.faicon){
27230             cfg.cn[0].cls += ' fa ' + this.faicon;
27231         }
27232         
27233         if(this.weight){
27234             cfg.cls += ' alert-' + this.weight;
27235         }
27236         
27237         return cfg;
27238     },
27239     
27240     initEvents: function() 
27241     {
27242         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27243     },
27244     
27245     setTitle : function(str)
27246     {
27247         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27248     },
27249     
27250     setText : function(str)
27251     {
27252         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27253     },
27254     
27255     setWeight : function(weight)
27256     {
27257         if(this.weight){
27258             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27259         }
27260         
27261         this.weight = weight;
27262         
27263         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27264     },
27265     
27266     setIcon : function(icon)
27267     {
27268         if(this.faicon){
27269             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27270         }
27271         
27272         this.faicon = icon;
27273         
27274         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27275     },
27276     
27277     hide: function() 
27278     {
27279         this.el.hide();   
27280     },
27281     
27282     show: function() 
27283     {  
27284         this.el.show();   
27285     }
27286     
27287 });
27288
27289  
27290 /*
27291 * Licence: LGPL
27292 */
27293
27294 /**
27295  * @class Roo.bootstrap.UploadCropbox
27296  * @extends Roo.bootstrap.Component
27297  * Bootstrap UploadCropbox class
27298  * @cfg {String} emptyText show when image has been loaded
27299  * @cfg {String} rotateNotify show when image too small to rotate
27300  * @cfg {Number} errorTimeout default 3000
27301  * @cfg {Number} minWidth default 300
27302  * @cfg {Number} minHeight default 300
27303  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27304  * @cfg {Boolean} isDocument (true|false) default false
27305  * @cfg {String} url action url
27306  * @cfg {String} paramName default 'imageUpload'
27307  * @cfg {String} method default POST
27308  * @cfg {Boolean} loadMask (true|false) default true
27309  * @cfg {Boolean} loadingText default 'Loading...'
27310  * 
27311  * @constructor
27312  * Create a new UploadCropbox
27313  * @param {Object} config The config object
27314  */
27315
27316 Roo.bootstrap.UploadCropbox = function(config){
27317     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27318     
27319     this.addEvents({
27320         /**
27321          * @event beforeselectfile
27322          * Fire before select file
27323          * @param {Roo.bootstrap.UploadCropbox} this
27324          */
27325         "beforeselectfile" : true,
27326         /**
27327          * @event initial
27328          * Fire after initEvent
27329          * @param {Roo.bootstrap.UploadCropbox} this
27330          */
27331         "initial" : true,
27332         /**
27333          * @event crop
27334          * Fire after initEvent
27335          * @param {Roo.bootstrap.UploadCropbox} this
27336          * @param {String} data
27337          */
27338         "crop" : true,
27339         /**
27340          * @event prepare
27341          * Fire when preparing the file data
27342          * @param {Roo.bootstrap.UploadCropbox} this
27343          * @param {Object} file
27344          */
27345         "prepare" : true,
27346         /**
27347          * @event exception
27348          * Fire when get exception
27349          * @param {Roo.bootstrap.UploadCropbox} this
27350          * @param {XMLHttpRequest} xhr
27351          */
27352         "exception" : true,
27353         /**
27354          * @event beforeloadcanvas
27355          * Fire before load the canvas
27356          * @param {Roo.bootstrap.UploadCropbox} this
27357          * @param {String} src
27358          */
27359         "beforeloadcanvas" : true,
27360         /**
27361          * @event trash
27362          * Fire when trash image
27363          * @param {Roo.bootstrap.UploadCropbox} this
27364          */
27365         "trash" : true,
27366         /**
27367          * @event download
27368          * Fire when download the image
27369          * @param {Roo.bootstrap.UploadCropbox} this
27370          */
27371         "download" : true,
27372         /**
27373          * @event footerbuttonclick
27374          * Fire when footerbuttonclick
27375          * @param {Roo.bootstrap.UploadCropbox} this
27376          * @param {String} type
27377          */
27378         "footerbuttonclick" : true,
27379         /**
27380          * @event resize
27381          * Fire when resize
27382          * @param {Roo.bootstrap.UploadCropbox} this
27383          */
27384         "resize" : true,
27385         /**
27386          * @event rotate
27387          * Fire when rotate the image
27388          * @param {Roo.bootstrap.UploadCropbox} this
27389          * @param {String} pos
27390          */
27391         "rotate" : true,
27392         /**
27393          * @event inspect
27394          * Fire when inspect the file
27395          * @param {Roo.bootstrap.UploadCropbox} this
27396          * @param {Object} file
27397          */
27398         "inspect" : true,
27399         /**
27400          * @event upload
27401          * Fire when xhr upload the file
27402          * @param {Roo.bootstrap.UploadCropbox} this
27403          * @param {Object} data
27404          */
27405         "upload" : true,
27406         /**
27407          * @event arrange
27408          * Fire when arrange the file data
27409          * @param {Roo.bootstrap.UploadCropbox} this
27410          * @param {Object} formData
27411          */
27412         "arrange" : true
27413     });
27414     
27415     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27416 };
27417
27418 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27419     
27420     emptyText : 'Click to upload image',
27421     rotateNotify : 'Image is too small to rotate',
27422     errorTimeout : 3000,
27423     scale : 0,
27424     baseScale : 1,
27425     rotate : 0,
27426     dragable : false,
27427     pinching : false,
27428     mouseX : 0,
27429     mouseY : 0,
27430     cropData : false,
27431     minWidth : 300,
27432     minHeight : 300,
27433     file : false,
27434     exif : {},
27435     baseRotate : 1,
27436     cropType : 'image/jpeg',
27437     buttons : false,
27438     canvasLoaded : false,
27439     isDocument : false,
27440     method : 'POST',
27441     paramName : 'imageUpload',
27442     loadMask : true,
27443     loadingText : 'Loading...',
27444     maskEl : false,
27445     
27446     getAutoCreate : function()
27447     {
27448         var cfg = {
27449             tag : 'div',
27450             cls : 'roo-upload-cropbox',
27451             cn : [
27452                 {
27453                     tag : 'input',
27454                     cls : 'roo-upload-cropbox-selector',
27455                     type : 'file'
27456                 },
27457                 {
27458                     tag : 'div',
27459                     cls : 'roo-upload-cropbox-body',
27460                     style : 'cursor:pointer',
27461                     cn : [
27462                         {
27463                             tag : 'div',
27464                             cls : 'roo-upload-cropbox-preview'
27465                         },
27466                         {
27467                             tag : 'div',
27468                             cls : 'roo-upload-cropbox-thumb'
27469                         },
27470                         {
27471                             tag : 'div',
27472                             cls : 'roo-upload-cropbox-empty-notify',
27473                             html : this.emptyText
27474                         },
27475                         {
27476                             tag : 'div',
27477                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27478                             html : this.rotateNotify
27479                         }
27480                     ]
27481                 },
27482                 {
27483                     tag : 'div',
27484                     cls : 'roo-upload-cropbox-footer',
27485                     cn : {
27486                         tag : 'div',
27487                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27488                         cn : []
27489                     }
27490                 }
27491             ]
27492         };
27493         
27494         return cfg;
27495     },
27496     
27497     onRender : function(ct, position)
27498     {
27499         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27500         
27501         if (this.buttons.length) {
27502             
27503             Roo.each(this.buttons, function(bb) {
27504                 
27505                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27506                 
27507                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27508                 
27509             }, this);
27510         }
27511         
27512         if(this.loadMask){
27513             this.maskEl = this.el;
27514         }
27515     },
27516     
27517     initEvents : function()
27518     {
27519         this.urlAPI = (window.createObjectURL && window) || 
27520                                 (window.URL && URL.revokeObjectURL && URL) || 
27521                                 (window.webkitURL && webkitURL);
27522                         
27523         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27524         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27525         
27526         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27527         this.selectorEl.hide();
27528         
27529         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27530         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27531         
27532         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27533         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27534         this.thumbEl.hide();
27535         
27536         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27537         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27538         
27539         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27540         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27541         this.errorEl.hide();
27542         
27543         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27544         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27545         this.footerEl.hide();
27546         
27547         this.setThumbBoxSize();
27548         
27549         this.bind();
27550         
27551         this.resize();
27552         
27553         this.fireEvent('initial', this);
27554     },
27555
27556     bind : function()
27557     {
27558         var _this = this;
27559         
27560         window.addEventListener("resize", function() { _this.resize(); } );
27561         
27562         this.bodyEl.on('click', this.beforeSelectFile, this);
27563         
27564         if(Roo.isTouch){
27565             this.bodyEl.on('touchstart', this.onTouchStart, this);
27566             this.bodyEl.on('touchmove', this.onTouchMove, this);
27567             this.bodyEl.on('touchend', this.onTouchEnd, this);
27568         }
27569         
27570         if(!Roo.isTouch){
27571             this.bodyEl.on('mousedown', this.onMouseDown, this);
27572             this.bodyEl.on('mousemove', this.onMouseMove, this);
27573             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27574             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27575             Roo.get(document).on('mouseup', this.onMouseUp, this);
27576         }
27577         
27578         this.selectorEl.on('change', this.onFileSelected, this);
27579     },
27580     
27581     reset : function()
27582     {    
27583         this.scale = 0;
27584         this.baseScale = 1;
27585         this.rotate = 0;
27586         this.baseRotate = 1;
27587         this.dragable = false;
27588         this.pinching = false;
27589         this.mouseX = 0;
27590         this.mouseY = 0;
27591         this.cropData = false;
27592         this.notifyEl.dom.innerHTML = this.emptyText;
27593         
27594         this.selectorEl.dom.value = '';
27595         
27596     },
27597     
27598     resize : function()
27599     {
27600         if(this.fireEvent('resize', this) != false){
27601             this.setThumbBoxPosition();
27602             this.setCanvasPosition();
27603         }
27604     },
27605     
27606     onFooterButtonClick : function(e, el, o, type)
27607     {
27608         switch (type) {
27609             case 'rotate-left' :
27610                 this.onRotateLeft(e);
27611                 break;
27612             case 'rotate-right' :
27613                 this.onRotateRight(e);
27614                 break;
27615             case 'picture' :
27616                 this.beforeSelectFile(e);
27617                 break;
27618             case 'trash' :
27619                 this.trash(e);
27620                 break;
27621             case 'crop' :
27622                 this.crop(e);
27623                 break;
27624             case 'download' :
27625                 this.download(e);
27626                 break;
27627             default :
27628                 break;
27629         }
27630         
27631         this.fireEvent('footerbuttonclick', this, type);
27632     },
27633     
27634     beforeSelectFile : function(e)
27635     {
27636         e.preventDefault();
27637         
27638         if(this.fireEvent('beforeselectfile', this) != false){
27639             this.selectorEl.dom.click();
27640         }
27641     },
27642     
27643     onFileSelected : function(e)
27644     {
27645         e.preventDefault();
27646         
27647         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27648             return;
27649         }
27650         
27651         var file = this.selectorEl.dom.files[0];
27652         
27653         if(this.fireEvent('inspect', this, file) != false){
27654             this.prepare(file);
27655         }
27656         
27657     },
27658     
27659     trash : function(e)
27660     {
27661         this.fireEvent('trash', this);
27662     },
27663     
27664     download : function(e)
27665     {
27666         this.fireEvent('download', this);
27667     },
27668     
27669     loadCanvas : function(src)
27670     {   
27671         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27672             
27673             this.reset();
27674             
27675             this.imageEl = document.createElement('img');
27676             
27677             var _this = this;
27678             
27679             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27680             
27681             this.imageEl.src = src;
27682         }
27683     },
27684     
27685     onLoadCanvas : function()
27686     {   
27687         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27688         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27689         
27690         this.bodyEl.un('click', this.beforeSelectFile, this);
27691         
27692         this.notifyEl.hide();
27693         this.thumbEl.show();
27694         this.footerEl.show();
27695         
27696         this.baseRotateLevel();
27697         
27698         if(this.isDocument){
27699             this.setThumbBoxSize();
27700         }
27701         
27702         this.setThumbBoxPosition();
27703         
27704         this.baseScaleLevel();
27705         
27706         this.draw();
27707         
27708         this.resize();
27709         
27710         this.canvasLoaded = true;
27711         
27712         if(this.loadMask){
27713             this.maskEl.unmask();
27714         }
27715         
27716     },
27717     
27718     setCanvasPosition : function()
27719     {   
27720         if(!this.canvasEl){
27721             return;
27722         }
27723         
27724         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27725         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27726         
27727         this.previewEl.setLeft(pw);
27728         this.previewEl.setTop(ph);
27729         
27730     },
27731     
27732     onMouseDown : function(e)
27733     {   
27734         e.stopEvent();
27735         
27736         this.dragable = true;
27737         this.pinching = false;
27738         
27739         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27740             this.dragable = false;
27741             return;
27742         }
27743         
27744         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27745         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27746         
27747     },
27748     
27749     onMouseMove : function(e)
27750     {   
27751         e.stopEvent();
27752         
27753         if(!this.canvasLoaded){
27754             return;
27755         }
27756         
27757         if (!this.dragable){
27758             return;
27759         }
27760         
27761         var minX = Math.ceil(this.thumbEl.getLeft(true));
27762         var minY = Math.ceil(this.thumbEl.getTop(true));
27763         
27764         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27765         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27766         
27767         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27768         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27769         
27770         x = x - this.mouseX;
27771         y = y - this.mouseY;
27772         
27773         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27774         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27775         
27776         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27777         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27778         
27779         this.previewEl.setLeft(bgX);
27780         this.previewEl.setTop(bgY);
27781         
27782         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27783         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27784     },
27785     
27786     onMouseUp : function(e)
27787     {   
27788         e.stopEvent();
27789         
27790         this.dragable = false;
27791     },
27792     
27793     onMouseWheel : function(e)
27794     {   
27795         e.stopEvent();
27796         
27797         this.startScale = this.scale;
27798         
27799         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27800         
27801         if(!this.zoomable()){
27802             this.scale = this.startScale;
27803             return;
27804         }
27805         
27806         this.draw();
27807         
27808         return;
27809     },
27810     
27811     zoomable : function()
27812     {
27813         var minScale = this.thumbEl.getWidth() / this.minWidth;
27814         
27815         if(this.minWidth < this.minHeight){
27816             minScale = this.thumbEl.getHeight() / this.minHeight;
27817         }
27818         
27819         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27820         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27821         
27822         if(
27823                 this.isDocument &&
27824                 (this.rotate == 0 || this.rotate == 180) && 
27825                 (
27826                     width > this.imageEl.OriginWidth || 
27827                     height > this.imageEl.OriginHeight ||
27828                     (width < this.minWidth && height < this.minHeight)
27829                 )
27830         ){
27831             return false;
27832         }
27833         
27834         if(
27835                 this.isDocument &&
27836                 (this.rotate == 90 || this.rotate == 270) && 
27837                 (
27838                     width > this.imageEl.OriginWidth || 
27839                     height > this.imageEl.OriginHeight ||
27840                     (width < this.minHeight && height < this.minWidth)
27841                 )
27842         ){
27843             return false;
27844         }
27845         
27846         if(
27847                 !this.isDocument &&
27848                 (this.rotate == 0 || this.rotate == 180) && 
27849                 (
27850                     width < this.minWidth || 
27851                     width > this.imageEl.OriginWidth || 
27852                     height < this.minHeight || 
27853                     height > this.imageEl.OriginHeight
27854                 )
27855         ){
27856             return false;
27857         }
27858         
27859         if(
27860                 !this.isDocument &&
27861                 (this.rotate == 90 || this.rotate == 270) && 
27862                 (
27863                     width < this.minHeight || 
27864                     width > this.imageEl.OriginWidth || 
27865                     height < this.minWidth || 
27866                     height > this.imageEl.OriginHeight
27867                 )
27868         ){
27869             return false;
27870         }
27871         
27872         return true;
27873         
27874     },
27875     
27876     onRotateLeft : function(e)
27877     {   
27878         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27879             
27880             var minScale = this.thumbEl.getWidth() / this.minWidth;
27881             
27882             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27883             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27884             
27885             this.startScale = this.scale;
27886             
27887             while (this.getScaleLevel() < minScale){
27888             
27889                 this.scale = this.scale + 1;
27890                 
27891                 if(!this.zoomable()){
27892                     break;
27893                 }
27894                 
27895                 if(
27896                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27897                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27898                 ){
27899                     continue;
27900                 }
27901                 
27902                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27903
27904                 this.draw();
27905                 
27906                 return;
27907             }
27908             
27909             this.scale = this.startScale;
27910             
27911             this.onRotateFail();
27912             
27913             return false;
27914         }
27915         
27916         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27917
27918         if(this.isDocument){
27919             this.setThumbBoxSize();
27920             this.setThumbBoxPosition();
27921             this.setCanvasPosition();
27922         }
27923         
27924         this.draw();
27925         
27926         this.fireEvent('rotate', this, 'left');
27927         
27928     },
27929     
27930     onRotateRight : function(e)
27931     {
27932         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27933             
27934             var minScale = this.thumbEl.getWidth() / this.minWidth;
27935         
27936             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27937             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27938             
27939             this.startScale = this.scale;
27940             
27941             while (this.getScaleLevel() < minScale){
27942             
27943                 this.scale = this.scale + 1;
27944                 
27945                 if(!this.zoomable()){
27946                     break;
27947                 }
27948                 
27949                 if(
27950                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27951                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27952                 ){
27953                     continue;
27954                 }
27955                 
27956                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27957
27958                 this.draw();
27959                 
27960                 return;
27961             }
27962             
27963             this.scale = this.startScale;
27964             
27965             this.onRotateFail();
27966             
27967             return false;
27968         }
27969         
27970         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27971
27972         if(this.isDocument){
27973             this.setThumbBoxSize();
27974             this.setThumbBoxPosition();
27975             this.setCanvasPosition();
27976         }
27977         
27978         this.draw();
27979         
27980         this.fireEvent('rotate', this, 'right');
27981     },
27982     
27983     onRotateFail : function()
27984     {
27985         this.errorEl.show(true);
27986         
27987         var _this = this;
27988         
27989         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27990     },
27991     
27992     draw : function()
27993     {
27994         this.previewEl.dom.innerHTML = '';
27995         
27996         var canvasEl = document.createElement("canvas");
27997         
27998         var contextEl = canvasEl.getContext("2d");
27999         
28000         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28001         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28002         var center = this.imageEl.OriginWidth / 2;
28003         
28004         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
28005             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28006             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28007             center = this.imageEl.OriginHeight / 2;
28008         }
28009         
28010         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
28011         
28012         contextEl.translate(center, center);
28013         contextEl.rotate(this.rotate * Math.PI / 180);
28014
28015         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28016         
28017         this.canvasEl = document.createElement("canvas");
28018         
28019         this.contextEl = this.canvasEl.getContext("2d");
28020         
28021         switch (this.rotate) {
28022             case 0 :
28023                 
28024                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28025                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28026                 
28027                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28028                 
28029                 break;
28030             case 90 : 
28031                 
28032                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28033                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28034                 
28035                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28036                     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);
28037                     break;
28038                 }
28039                 
28040                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28041                 
28042                 break;
28043             case 180 :
28044                 
28045                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28046                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28047                 
28048                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28049                     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);
28050                     break;
28051                 }
28052                 
28053                 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);
28054                 
28055                 break;
28056             case 270 :
28057                 
28058                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28059                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28060         
28061                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28062                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28063                     break;
28064                 }
28065                 
28066                 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);
28067                 
28068                 break;
28069             default : 
28070                 break;
28071         }
28072         
28073         this.previewEl.appendChild(this.canvasEl);
28074         
28075         this.setCanvasPosition();
28076     },
28077     
28078     crop : function()
28079     {
28080         if(!this.canvasLoaded){
28081             return;
28082         }
28083         
28084         var imageCanvas = document.createElement("canvas");
28085         
28086         var imageContext = imageCanvas.getContext("2d");
28087         
28088         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28089         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28090         
28091         var center = imageCanvas.width / 2;
28092         
28093         imageContext.translate(center, center);
28094         
28095         imageContext.rotate(this.rotate * Math.PI / 180);
28096         
28097         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28098         
28099         var canvas = document.createElement("canvas");
28100         
28101         var context = canvas.getContext("2d");
28102                 
28103         canvas.width = this.minWidth;
28104         canvas.height = this.minHeight;
28105
28106         switch (this.rotate) {
28107             case 0 :
28108                 
28109                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28110                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28111                 
28112                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28113                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28114                 
28115                 var targetWidth = this.minWidth - 2 * x;
28116                 var targetHeight = this.minHeight - 2 * y;
28117                 
28118                 var scale = 1;
28119                 
28120                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28121                     scale = targetWidth / width;
28122                 }
28123                 
28124                 if(x > 0 && y == 0){
28125                     scale = targetHeight / height;
28126                 }
28127                 
28128                 if(x > 0 && y > 0){
28129                     scale = targetWidth / width;
28130                     
28131                     if(width < height){
28132                         scale = targetHeight / height;
28133                     }
28134                 }
28135                 
28136                 context.scale(scale, scale);
28137                 
28138                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28139                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28140
28141                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28142                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28143
28144                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28145                 
28146                 break;
28147             case 90 : 
28148                 
28149                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28150                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28151                 
28152                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28153                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28154                 
28155                 var targetWidth = this.minWidth - 2 * x;
28156                 var targetHeight = this.minHeight - 2 * y;
28157                 
28158                 var scale = 1;
28159                 
28160                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28161                     scale = targetWidth / width;
28162                 }
28163                 
28164                 if(x > 0 && y == 0){
28165                     scale = targetHeight / height;
28166                 }
28167                 
28168                 if(x > 0 && y > 0){
28169                     scale = targetWidth / width;
28170                     
28171                     if(width < height){
28172                         scale = targetHeight / height;
28173                     }
28174                 }
28175                 
28176                 context.scale(scale, scale);
28177                 
28178                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28179                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28180
28181                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28182                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28183                 
28184                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28185                 
28186                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28187                 
28188                 break;
28189             case 180 :
28190                 
28191                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28192                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28193                 
28194                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28195                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28196                 
28197                 var targetWidth = this.minWidth - 2 * x;
28198                 var targetHeight = this.minHeight - 2 * y;
28199                 
28200                 var scale = 1;
28201                 
28202                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28203                     scale = targetWidth / width;
28204                 }
28205                 
28206                 if(x > 0 && y == 0){
28207                     scale = targetHeight / height;
28208                 }
28209                 
28210                 if(x > 0 && y > 0){
28211                     scale = targetWidth / width;
28212                     
28213                     if(width < height){
28214                         scale = targetHeight / height;
28215                     }
28216                 }
28217                 
28218                 context.scale(scale, scale);
28219                 
28220                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28221                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28222
28223                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28224                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28225
28226                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28227                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28228                 
28229                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28230                 
28231                 break;
28232             case 270 :
28233                 
28234                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28235                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28236                 
28237                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28238                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28239                 
28240                 var targetWidth = this.minWidth - 2 * x;
28241                 var targetHeight = this.minHeight - 2 * y;
28242                 
28243                 var scale = 1;
28244                 
28245                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28246                     scale = targetWidth / width;
28247                 }
28248                 
28249                 if(x > 0 && y == 0){
28250                     scale = targetHeight / height;
28251                 }
28252                 
28253                 if(x > 0 && y > 0){
28254                     scale = targetWidth / width;
28255                     
28256                     if(width < height){
28257                         scale = targetHeight / height;
28258                     }
28259                 }
28260                 
28261                 context.scale(scale, scale);
28262                 
28263                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28264                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28265
28266                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28267                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28268                 
28269                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28270                 
28271                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28272                 
28273                 break;
28274             default : 
28275                 break;
28276         }
28277         
28278         this.cropData = canvas.toDataURL(this.cropType);
28279         
28280         if(this.fireEvent('crop', this, this.cropData) !== false){
28281             this.process(this.file, this.cropData);
28282         }
28283         
28284         return;
28285         
28286     },
28287     
28288     setThumbBoxSize : function()
28289     {
28290         var width, height;
28291         
28292         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28293             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28294             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28295             
28296             this.minWidth = width;
28297             this.minHeight = height;
28298             
28299             if(this.rotate == 90 || this.rotate == 270){
28300                 this.minWidth = height;
28301                 this.minHeight = width;
28302             }
28303         }
28304         
28305         height = 300;
28306         width = Math.ceil(this.minWidth * height / this.minHeight);
28307         
28308         if(this.minWidth > this.minHeight){
28309             width = 300;
28310             height = Math.ceil(this.minHeight * width / this.minWidth);
28311         }
28312         
28313         this.thumbEl.setStyle({
28314             width : width + 'px',
28315             height : height + 'px'
28316         });
28317
28318         return;
28319             
28320     },
28321     
28322     setThumbBoxPosition : function()
28323     {
28324         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28325         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28326         
28327         this.thumbEl.setLeft(x);
28328         this.thumbEl.setTop(y);
28329         
28330     },
28331     
28332     baseRotateLevel : function()
28333     {
28334         this.baseRotate = 1;
28335         
28336         if(
28337                 typeof(this.exif) != 'undefined' &&
28338                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28339                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28340         ){
28341             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28342         }
28343         
28344         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28345         
28346     },
28347     
28348     baseScaleLevel : function()
28349     {
28350         var width, height;
28351         
28352         if(this.isDocument){
28353             
28354             if(this.baseRotate == 6 || this.baseRotate == 8){
28355             
28356                 height = this.thumbEl.getHeight();
28357                 this.baseScale = height / this.imageEl.OriginWidth;
28358
28359                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28360                     width = this.thumbEl.getWidth();
28361                     this.baseScale = width / this.imageEl.OriginHeight;
28362                 }
28363
28364                 return;
28365             }
28366
28367             height = this.thumbEl.getHeight();
28368             this.baseScale = height / this.imageEl.OriginHeight;
28369
28370             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28371                 width = this.thumbEl.getWidth();
28372                 this.baseScale = width / this.imageEl.OriginWidth;
28373             }
28374
28375             return;
28376         }
28377         
28378         if(this.baseRotate == 6 || this.baseRotate == 8){
28379             
28380             width = this.thumbEl.getHeight();
28381             this.baseScale = width / this.imageEl.OriginHeight;
28382             
28383             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28384                 height = this.thumbEl.getWidth();
28385                 this.baseScale = height / this.imageEl.OriginHeight;
28386             }
28387             
28388             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28389                 height = this.thumbEl.getWidth();
28390                 this.baseScale = height / this.imageEl.OriginHeight;
28391                 
28392                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28393                     width = this.thumbEl.getHeight();
28394                     this.baseScale = width / this.imageEl.OriginWidth;
28395                 }
28396             }
28397             
28398             return;
28399         }
28400         
28401         width = this.thumbEl.getWidth();
28402         this.baseScale = width / this.imageEl.OriginWidth;
28403         
28404         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28405             height = this.thumbEl.getHeight();
28406             this.baseScale = height / this.imageEl.OriginHeight;
28407         }
28408         
28409         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28410             
28411             height = this.thumbEl.getHeight();
28412             this.baseScale = height / this.imageEl.OriginHeight;
28413             
28414             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28415                 width = this.thumbEl.getWidth();
28416                 this.baseScale = width / this.imageEl.OriginWidth;
28417             }
28418             
28419         }
28420         
28421         return;
28422     },
28423     
28424     getScaleLevel : function()
28425     {
28426         return this.baseScale * Math.pow(1.1, this.scale);
28427     },
28428     
28429     onTouchStart : function(e)
28430     {
28431         if(!this.canvasLoaded){
28432             this.beforeSelectFile(e);
28433             return;
28434         }
28435         
28436         var touches = e.browserEvent.touches;
28437         
28438         if(!touches){
28439             return;
28440         }
28441         
28442         if(touches.length == 1){
28443             this.onMouseDown(e);
28444             return;
28445         }
28446         
28447         if(touches.length != 2){
28448             return;
28449         }
28450         
28451         var coords = [];
28452         
28453         for(var i = 0, finger; finger = touches[i]; i++){
28454             coords.push(finger.pageX, finger.pageY);
28455         }
28456         
28457         var x = Math.pow(coords[0] - coords[2], 2);
28458         var y = Math.pow(coords[1] - coords[3], 2);
28459         
28460         this.startDistance = Math.sqrt(x + y);
28461         
28462         this.startScale = this.scale;
28463         
28464         this.pinching = true;
28465         this.dragable = false;
28466         
28467     },
28468     
28469     onTouchMove : function(e)
28470     {
28471         if(!this.pinching && !this.dragable){
28472             return;
28473         }
28474         
28475         var touches = e.browserEvent.touches;
28476         
28477         if(!touches){
28478             return;
28479         }
28480         
28481         if(this.dragable){
28482             this.onMouseMove(e);
28483             return;
28484         }
28485         
28486         var coords = [];
28487         
28488         for(var i = 0, finger; finger = touches[i]; i++){
28489             coords.push(finger.pageX, finger.pageY);
28490         }
28491         
28492         var x = Math.pow(coords[0] - coords[2], 2);
28493         var y = Math.pow(coords[1] - coords[3], 2);
28494         
28495         this.endDistance = Math.sqrt(x + y);
28496         
28497         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28498         
28499         if(!this.zoomable()){
28500             this.scale = this.startScale;
28501             return;
28502         }
28503         
28504         this.draw();
28505         
28506     },
28507     
28508     onTouchEnd : function(e)
28509     {
28510         this.pinching = false;
28511         this.dragable = false;
28512         
28513     },
28514     
28515     process : function(file, crop)
28516     {
28517         if(this.loadMask){
28518             this.maskEl.mask(this.loadingText);
28519         }
28520         
28521         this.xhr = new XMLHttpRequest();
28522         
28523         file.xhr = this.xhr;
28524
28525         this.xhr.open(this.method, this.url, true);
28526         
28527         var headers = {
28528             "Accept": "application/json",
28529             "Cache-Control": "no-cache",
28530             "X-Requested-With": "XMLHttpRequest"
28531         };
28532         
28533         for (var headerName in headers) {
28534             var headerValue = headers[headerName];
28535             if (headerValue) {
28536                 this.xhr.setRequestHeader(headerName, headerValue);
28537             }
28538         }
28539         
28540         var _this = this;
28541         
28542         this.xhr.onload = function()
28543         {
28544             _this.xhrOnLoad(_this.xhr);
28545         }
28546         
28547         this.xhr.onerror = function()
28548         {
28549             _this.xhrOnError(_this.xhr);
28550         }
28551         
28552         var formData = new FormData();
28553
28554         formData.append('returnHTML', 'NO');
28555         
28556         if(crop){
28557             formData.append('crop', crop);
28558         }
28559         
28560         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28561             formData.append(this.paramName, file, file.name);
28562         }
28563         
28564         if(typeof(file.filename) != 'undefined'){
28565             formData.append('filename', file.filename);
28566         }
28567         
28568         if(typeof(file.mimetype) != 'undefined'){
28569             formData.append('mimetype', file.mimetype);
28570         }
28571         
28572         if(this.fireEvent('arrange', this, formData) != false){
28573             this.xhr.send(formData);
28574         };
28575     },
28576     
28577     xhrOnLoad : function(xhr)
28578     {
28579         if(this.loadMask){
28580             this.maskEl.unmask();
28581         }
28582         
28583         if (xhr.readyState !== 4) {
28584             this.fireEvent('exception', this, xhr);
28585             return;
28586         }
28587
28588         var response = Roo.decode(xhr.responseText);
28589         
28590         if(!response.success){
28591             this.fireEvent('exception', this, xhr);
28592             return;
28593         }
28594         
28595         var response = Roo.decode(xhr.responseText);
28596         
28597         this.fireEvent('upload', this, response);
28598         
28599     },
28600     
28601     xhrOnError : function()
28602     {
28603         if(this.loadMask){
28604             this.maskEl.unmask();
28605         }
28606         
28607         Roo.log('xhr on error');
28608         
28609         var response = Roo.decode(xhr.responseText);
28610           
28611         Roo.log(response);
28612         
28613     },
28614     
28615     prepare : function(file)
28616     {   
28617         if(this.loadMask){
28618             this.maskEl.mask(this.loadingText);
28619         }
28620         
28621         this.file = false;
28622         this.exif = {};
28623         
28624         if(typeof(file) === 'string'){
28625             this.loadCanvas(file);
28626             return;
28627         }
28628         
28629         if(!file || !this.urlAPI){
28630             return;
28631         }
28632         
28633         this.file = file;
28634         this.cropType = file.type;
28635         
28636         var _this = this;
28637         
28638         if(this.fireEvent('prepare', this, this.file) != false){
28639             
28640             var reader = new FileReader();
28641             
28642             reader.onload = function (e) {
28643                 if (e.target.error) {
28644                     Roo.log(e.target.error);
28645                     return;
28646                 }
28647                 
28648                 var buffer = e.target.result,
28649                     dataView = new DataView(buffer),
28650                     offset = 2,
28651                     maxOffset = dataView.byteLength - 4,
28652                     markerBytes,
28653                     markerLength;
28654                 
28655                 if (dataView.getUint16(0) === 0xffd8) {
28656                     while (offset < maxOffset) {
28657                         markerBytes = dataView.getUint16(offset);
28658                         
28659                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28660                             markerLength = dataView.getUint16(offset + 2) + 2;
28661                             if (offset + markerLength > dataView.byteLength) {
28662                                 Roo.log('Invalid meta data: Invalid segment size.');
28663                                 break;
28664                             }
28665                             
28666                             if(markerBytes == 0xffe1){
28667                                 _this.parseExifData(
28668                                     dataView,
28669                                     offset,
28670                                     markerLength
28671                                 );
28672                             }
28673                             
28674                             offset += markerLength;
28675                             
28676                             continue;
28677                         }
28678                         
28679                         break;
28680                     }
28681                     
28682                 }
28683                 
28684                 var url = _this.urlAPI.createObjectURL(_this.file);
28685                 
28686                 _this.loadCanvas(url);
28687                 
28688                 return;
28689             }
28690             
28691             reader.readAsArrayBuffer(this.file);
28692             
28693         }
28694         
28695     },
28696     
28697     parseExifData : function(dataView, offset, length)
28698     {
28699         var tiffOffset = offset + 10,
28700             littleEndian,
28701             dirOffset;
28702     
28703         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28704             // No Exif data, might be XMP data instead
28705             return;
28706         }
28707         
28708         // Check for the ASCII code for "Exif" (0x45786966):
28709         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28710             // No Exif data, might be XMP data instead
28711             return;
28712         }
28713         if (tiffOffset + 8 > dataView.byteLength) {
28714             Roo.log('Invalid Exif data: Invalid segment size.');
28715             return;
28716         }
28717         // Check for the two null bytes:
28718         if (dataView.getUint16(offset + 8) !== 0x0000) {
28719             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28720             return;
28721         }
28722         // Check the byte alignment:
28723         switch (dataView.getUint16(tiffOffset)) {
28724         case 0x4949:
28725             littleEndian = true;
28726             break;
28727         case 0x4D4D:
28728             littleEndian = false;
28729             break;
28730         default:
28731             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28732             return;
28733         }
28734         // Check for the TIFF tag marker (0x002A):
28735         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28736             Roo.log('Invalid Exif data: Missing TIFF marker.');
28737             return;
28738         }
28739         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28740         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28741         
28742         this.parseExifTags(
28743             dataView,
28744             tiffOffset,
28745             tiffOffset + dirOffset,
28746             littleEndian
28747         );
28748     },
28749     
28750     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28751     {
28752         var tagsNumber,
28753             dirEndOffset,
28754             i;
28755         if (dirOffset + 6 > dataView.byteLength) {
28756             Roo.log('Invalid Exif data: Invalid directory offset.');
28757             return;
28758         }
28759         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28760         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28761         if (dirEndOffset + 4 > dataView.byteLength) {
28762             Roo.log('Invalid Exif data: Invalid directory size.');
28763             return;
28764         }
28765         for (i = 0; i < tagsNumber; i += 1) {
28766             this.parseExifTag(
28767                 dataView,
28768                 tiffOffset,
28769                 dirOffset + 2 + 12 * i, // tag offset
28770                 littleEndian
28771             );
28772         }
28773         // Return the offset to the next directory:
28774         return dataView.getUint32(dirEndOffset, littleEndian);
28775     },
28776     
28777     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28778     {
28779         var tag = dataView.getUint16(offset, littleEndian);
28780         
28781         this.exif[tag] = this.getExifValue(
28782             dataView,
28783             tiffOffset,
28784             offset,
28785             dataView.getUint16(offset + 2, littleEndian), // tag type
28786             dataView.getUint32(offset + 4, littleEndian), // tag length
28787             littleEndian
28788         );
28789     },
28790     
28791     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28792     {
28793         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28794             tagSize,
28795             dataOffset,
28796             values,
28797             i,
28798             str,
28799             c;
28800     
28801         if (!tagType) {
28802             Roo.log('Invalid Exif data: Invalid tag type.');
28803             return;
28804         }
28805         
28806         tagSize = tagType.size * length;
28807         // Determine if the value is contained in the dataOffset bytes,
28808         // or if the value at the dataOffset is a pointer to the actual data:
28809         dataOffset = tagSize > 4 ?
28810                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28811         if (dataOffset + tagSize > dataView.byteLength) {
28812             Roo.log('Invalid Exif data: Invalid data offset.');
28813             return;
28814         }
28815         if (length === 1) {
28816             return tagType.getValue(dataView, dataOffset, littleEndian);
28817         }
28818         values = [];
28819         for (i = 0; i < length; i += 1) {
28820             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28821         }
28822         
28823         if (tagType.ascii) {
28824             str = '';
28825             // Concatenate the chars:
28826             for (i = 0; i < values.length; i += 1) {
28827                 c = values[i];
28828                 // Ignore the terminating NULL byte(s):
28829                 if (c === '\u0000') {
28830                     break;
28831                 }
28832                 str += c;
28833             }
28834             return str;
28835         }
28836         return values;
28837     }
28838     
28839 });
28840
28841 Roo.apply(Roo.bootstrap.UploadCropbox, {
28842     tags : {
28843         'Orientation': 0x0112
28844     },
28845     
28846     Orientation: {
28847             1: 0, //'top-left',
28848 //            2: 'top-right',
28849             3: 180, //'bottom-right',
28850 //            4: 'bottom-left',
28851 //            5: 'left-top',
28852             6: 90, //'right-top',
28853 //            7: 'right-bottom',
28854             8: 270 //'left-bottom'
28855     },
28856     
28857     exifTagTypes : {
28858         // byte, 8-bit unsigned int:
28859         1: {
28860             getValue: function (dataView, dataOffset) {
28861                 return dataView.getUint8(dataOffset);
28862             },
28863             size: 1
28864         },
28865         // ascii, 8-bit byte:
28866         2: {
28867             getValue: function (dataView, dataOffset) {
28868                 return String.fromCharCode(dataView.getUint8(dataOffset));
28869             },
28870             size: 1,
28871             ascii: true
28872         },
28873         // short, 16 bit int:
28874         3: {
28875             getValue: function (dataView, dataOffset, littleEndian) {
28876                 return dataView.getUint16(dataOffset, littleEndian);
28877             },
28878             size: 2
28879         },
28880         // long, 32 bit int:
28881         4: {
28882             getValue: function (dataView, dataOffset, littleEndian) {
28883                 return dataView.getUint32(dataOffset, littleEndian);
28884             },
28885             size: 4
28886         },
28887         // rational = two long values, first is numerator, second is denominator:
28888         5: {
28889             getValue: function (dataView, dataOffset, littleEndian) {
28890                 return dataView.getUint32(dataOffset, littleEndian) /
28891                     dataView.getUint32(dataOffset + 4, littleEndian);
28892             },
28893             size: 8
28894         },
28895         // slong, 32 bit signed int:
28896         9: {
28897             getValue: function (dataView, dataOffset, littleEndian) {
28898                 return dataView.getInt32(dataOffset, littleEndian);
28899             },
28900             size: 4
28901         },
28902         // srational, two slongs, first is numerator, second is denominator:
28903         10: {
28904             getValue: function (dataView, dataOffset, littleEndian) {
28905                 return dataView.getInt32(dataOffset, littleEndian) /
28906                     dataView.getInt32(dataOffset + 4, littleEndian);
28907             },
28908             size: 8
28909         }
28910     },
28911     
28912     footer : {
28913         STANDARD : [
28914             {
28915                 tag : 'div',
28916                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28917                 action : 'rotate-left',
28918                 cn : [
28919                     {
28920                         tag : 'button',
28921                         cls : 'btn btn-default',
28922                         html : '<i class="fa fa-undo"></i>'
28923                     }
28924                 ]
28925             },
28926             {
28927                 tag : 'div',
28928                 cls : 'btn-group roo-upload-cropbox-picture',
28929                 action : 'picture',
28930                 cn : [
28931                     {
28932                         tag : 'button',
28933                         cls : 'btn btn-default',
28934                         html : '<i class="fa fa-picture-o"></i>'
28935                     }
28936                 ]
28937             },
28938             {
28939                 tag : 'div',
28940                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28941                 action : 'rotate-right',
28942                 cn : [
28943                     {
28944                         tag : 'button',
28945                         cls : 'btn btn-default',
28946                         html : '<i class="fa fa-repeat"></i>'
28947                     }
28948                 ]
28949             }
28950         ],
28951         DOCUMENT : [
28952             {
28953                 tag : 'div',
28954                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28955                 action : 'rotate-left',
28956                 cn : [
28957                     {
28958                         tag : 'button',
28959                         cls : 'btn btn-default',
28960                         html : '<i class="fa fa-undo"></i>'
28961                     }
28962                 ]
28963             },
28964             {
28965                 tag : 'div',
28966                 cls : 'btn-group roo-upload-cropbox-download',
28967                 action : 'download',
28968                 cn : [
28969                     {
28970                         tag : 'button',
28971                         cls : 'btn btn-default',
28972                         html : '<i class="fa fa-download"></i>'
28973                     }
28974                 ]
28975             },
28976             {
28977                 tag : 'div',
28978                 cls : 'btn-group roo-upload-cropbox-crop',
28979                 action : 'crop',
28980                 cn : [
28981                     {
28982                         tag : 'button',
28983                         cls : 'btn btn-default',
28984                         html : '<i class="fa fa-crop"></i>'
28985                     }
28986                 ]
28987             },
28988             {
28989                 tag : 'div',
28990                 cls : 'btn-group roo-upload-cropbox-trash',
28991                 action : 'trash',
28992                 cn : [
28993                     {
28994                         tag : 'button',
28995                         cls : 'btn btn-default',
28996                         html : '<i class="fa fa-trash"></i>'
28997                     }
28998                 ]
28999             },
29000             {
29001                 tag : 'div',
29002                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29003                 action : 'rotate-right',
29004                 cn : [
29005                     {
29006                         tag : 'button',
29007                         cls : 'btn btn-default',
29008                         html : '<i class="fa fa-repeat"></i>'
29009                     }
29010                 ]
29011             }
29012         ],
29013         ROTATOR : [
29014             {
29015                 tag : 'div',
29016                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29017                 action : 'rotate-left',
29018                 cn : [
29019                     {
29020                         tag : 'button',
29021                         cls : 'btn btn-default',
29022                         html : '<i class="fa fa-undo"></i>'
29023                     }
29024                 ]
29025             },
29026             {
29027                 tag : 'div',
29028                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29029                 action : 'rotate-right',
29030                 cn : [
29031                     {
29032                         tag : 'button',
29033                         cls : 'btn btn-default',
29034                         html : '<i class="fa fa-repeat"></i>'
29035                     }
29036                 ]
29037             }
29038         ]
29039     }
29040 });
29041
29042 /*
29043 * Licence: LGPL
29044 */
29045
29046 /**
29047  * @class Roo.bootstrap.DocumentManager
29048  * @extends Roo.bootstrap.Component
29049  * Bootstrap DocumentManager class
29050  * @cfg {String} paramName default 'imageUpload'
29051  * @cfg {String} toolTipName default 'filename'
29052  * @cfg {String} method default POST
29053  * @cfg {String} url action url
29054  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
29055  * @cfg {Boolean} multiple multiple upload default true
29056  * @cfg {Number} thumbSize default 300
29057  * @cfg {String} fieldLabel
29058  * @cfg {Number} labelWidth default 4
29059  * @cfg {String} labelAlign (left|top) default left
29060  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
29061 * @cfg {Number} labellg set the width of label (1-12)
29062  * @cfg {Number} labelmd set the width of label (1-12)
29063  * @cfg {Number} labelsm set the width of label (1-12)
29064  * @cfg {Number} labelxs set the width of label (1-12)
29065  * 
29066  * @constructor
29067  * Create a new DocumentManager
29068  * @param {Object} config The config object
29069  */
29070
29071 Roo.bootstrap.DocumentManager = function(config){
29072     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
29073     
29074     this.files = [];
29075     this.delegates = [];
29076     
29077     this.addEvents({
29078         /**
29079          * @event initial
29080          * Fire when initial the DocumentManager
29081          * @param {Roo.bootstrap.DocumentManager} this
29082          */
29083         "initial" : true,
29084         /**
29085          * @event inspect
29086          * inspect selected file
29087          * @param {Roo.bootstrap.DocumentManager} this
29088          * @param {File} file
29089          */
29090         "inspect" : true,
29091         /**
29092          * @event exception
29093          * Fire when xhr load exception
29094          * @param {Roo.bootstrap.DocumentManager} this
29095          * @param {XMLHttpRequest} xhr
29096          */
29097         "exception" : true,
29098         /**
29099          * @event afterupload
29100          * Fire when xhr load exception
29101          * @param {Roo.bootstrap.DocumentManager} this
29102          * @param {XMLHttpRequest} xhr
29103          */
29104         "afterupload" : true,
29105         /**
29106          * @event prepare
29107          * prepare the form data
29108          * @param {Roo.bootstrap.DocumentManager} this
29109          * @param {Object} formData
29110          */
29111         "prepare" : true,
29112         /**
29113          * @event remove
29114          * Fire when remove the file
29115          * @param {Roo.bootstrap.DocumentManager} this
29116          * @param {Object} file
29117          */
29118         "remove" : true,
29119         /**
29120          * @event refresh
29121          * Fire after refresh the file
29122          * @param {Roo.bootstrap.DocumentManager} this
29123          */
29124         "refresh" : true,
29125         /**
29126          * @event click
29127          * Fire after click the image
29128          * @param {Roo.bootstrap.DocumentManager} this
29129          * @param {Object} file
29130          */
29131         "click" : true,
29132         /**
29133          * @event edit
29134          * Fire when upload a image and editable set to true
29135          * @param {Roo.bootstrap.DocumentManager} this
29136          * @param {Object} file
29137          */
29138         "edit" : true,
29139         /**
29140          * @event beforeselectfile
29141          * Fire before select file
29142          * @param {Roo.bootstrap.DocumentManager} this
29143          */
29144         "beforeselectfile" : true,
29145         /**
29146          * @event process
29147          * Fire before process file
29148          * @param {Roo.bootstrap.DocumentManager} this
29149          * @param {Object} file
29150          */
29151         "process" : true,
29152         /**
29153          * @event previewrendered
29154          * Fire when preview rendered
29155          * @param {Roo.bootstrap.DocumentManager} this
29156          * @param {Object} file
29157          */
29158         "previewrendered" : true,
29159         /**
29160          */
29161         "previewResize" : true
29162         
29163     });
29164 };
29165
29166 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
29167     
29168     boxes : 0,
29169     inputName : '',
29170     thumbSize : 300,
29171     multiple : true,
29172     files : false,
29173     method : 'POST',
29174     url : '',
29175     paramName : 'imageUpload',
29176     toolTipName : 'filename',
29177     fieldLabel : '',
29178     labelWidth : 4,
29179     labelAlign : 'left',
29180     editable : true,
29181     delegates : false,
29182     xhr : false, 
29183     
29184     labellg : 0,
29185     labelmd : 0,
29186     labelsm : 0,
29187     labelxs : 0,
29188     
29189     getAutoCreate : function()
29190     {   
29191         var managerWidget = {
29192             tag : 'div',
29193             cls : 'roo-document-manager',
29194             cn : [
29195                 {
29196                     tag : 'input',
29197                     cls : 'roo-document-manager-selector',
29198                     type : 'file'
29199                 },
29200                 {
29201                     tag : 'div',
29202                     cls : 'roo-document-manager-uploader',
29203                     cn : [
29204                         {
29205                             tag : 'div',
29206                             cls : 'roo-document-manager-upload-btn',
29207                             html : '<i class="fa fa-plus"></i>'
29208                         }
29209                     ]
29210                     
29211                 }
29212             ]
29213         };
29214         
29215         var content = [
29216             {
29217                 tag : 'div',
29218                 cls : 'column col-md-12',
29219                 cn : managerWidget
29220             }
29221         ];
29222         
29223         if(this.fieldLabel.length){
29224             
29225             content = [
29226                 {
29227                     tag : 'div',
29228                     cls : 'column col-md-12',
29229                     html : this.fieldLabel
29230                 },
29231                 {
29232                     tag : 'div',
29233                     cls : 'column col-md-12',
29234                     cn : managerWidget
29235                 }
29236             ];
29237
29238             if(this.labelAlign == 'left'){
29239                 content = [
29240                     {
29241                         tag : 'div',
29242                         cls : 'column',
29243                         html : this.fieldLabel
29244                     },
29245                     {
29246                         tag : 'div',
29247                         cls : 'column',
29248                         cn : managerWidget
29249                     }
29250                 ];
29251                 
29252                 if(this.labelWidth > 12){
29253                     content[0].style = "width: " + this.labelWidth + 'px';
29254                 }
29255
29256                 if(this.labelWidth < 13 && this.labelmd == 0){
29257                     this.labelmd = this.labelWidth;
29258                 }
29259
29260                 if(this.labellg > 0){
29261                     content[0].cls += ' col-lg-' + this.labellg;
29262                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29263                 }
29264
29265                 if(this.labelmd > 0){
29266                     content[0].cls += ' col-md-' + this.labelmd;
29267                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29268                 }
29269
29270                 if(this.labelsm > 0){
29271                     content[0].cls += ' col-sm-' + this.labelsm;
29272                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29273                 }
29274
29275                 if(this.labelxs > 0){
29276                     content[0].cls += ' col-xs-' + this.labelxs;
29277                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29278                 }
29279                 
29280             }
29281         }
29282         
29283         var cfg = {
29284             tag : 'div',
29285             cls : 'row clearfix',
29286             cn : content
29287         };
29288         
29289         return cfg;
29290         
29291     },
29292     
29293     initEvents : function()
29294     {
29295         this.managerEl = this.el.select('.roo-document-manager', true).first();
29296         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29297         
29298         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29299         this.selectorEl.hide();
29300         
29301         if(this.multiple){
29302             this.selectorEl.attr('multiple', 'multiple');
29303         }
29304         
29305         this.selectorEl.on('change', this.onFileSelected, this);
29306         
29307         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29308         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29309         
29310         this.uploader.on('click', this.onUploaderClick, this);
29311         
29312         this.renderProgressDialog();
29313         
29314         var _this = this;
29315         
29316         window.addEventListener("resize", function() { _this.refresh(); } );
29317         
29318         this.fireEvent('initial', this);
29319     },
29320     
29321     renderProgressDialog : function()
29322     {
29323         var _this = this;
29324         
29325         this.progressDialog = new Roo.bootstrap.Modal({
29326             cls : 'roo-document-manager-progress-dialog',
29327             allow_close : false,
29328             animate : false,
29329             title : '',
29330             buttons : [
29331                 {
29332                     name  :'cancel',
29333                     weight : 'danger',
29334                     html : 'Cancel'
29335                 }
29336             ], 
29337             listeners : { 
29338                 btnclick : function() {
29339                     _this.uploadCancel();
29340                     this.hide();
29341                 }
29342             }
29343         });
29344          
29345         this.progressDialog.render(Roo.get(document.body));
29346          
29347         this.progress = new Roo.bootstrap.Progress({
29348             cls : 'roo-document-manager-progress',
29349             active : true,
29350             striped : true
29351         });
29352         
29353         this.progress.render(this.progressDialog.getChildContainer());
29354         
29355         this.progressBar = new Roo.bootstrap.ProgressBar({
29356             cls : 'roo-document-manager-progress-bar',
29357             aria_valuenow : 0,
29358             aria_valuemin : 0,
29359             aria_valuemax : 12,
29360             panel : 'success'
29361         });
29362         
29363         this.progressBar.render(this.progress.getChildContainer());
29364     },
29365     
29366     onUploaderClick : function(e)
29367     {
29368         e.preventDefault();
29369      
29370         if(this.fireEvent('beforeselectfile', this) != false){
29371             this.selectorEl.dom.click();
29372         }
29373         
29374     },
29375     
29376     onFileSelected : function(e)
29377     {
29378         e.preventDefault();
29379         
29380         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29381             return;
29382         }
29383         
29384         Roo.each(this.selectorEl.dom.files, function(file){
29385             if(this.fireEvent('inspect', this, file) != false){
29386                 this.files.push(file);
29387             }
29388         }, this);
29389         
29390         this.queue();
29391         
29392     },
29393     
29394     queue : function()
29395     {
29396         this.selectorEl.dom.value = '';
29397         
29398         if(!this.files || !this.files.length){
29399             return;
29400         }
29401         
29402         if(this.boxes > 0 && this.files.length > this.boxes){
29403             this.files = this.files.slice(0, this.boxes);
29404         }
29405         
29406         this.uploader.show();
29407         
29408         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29409             this.uploader.hide();
29410         }
29411         
29412         var _this = this;
29413         
29414         var files = [];
29415         
29416         var docs = [];
29417         
29418         Roo.each(this.files, function(file){
29419             
29420             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29421                 var f = this.renderPreview(file);
29422                 files.push(f);
29423                 return;
29424             }
29425             
29426             if(file.type.indexOf('image') != -1){
29427                 this.delegates.push(
29428                     (function(){
29429                         _this.process(file);
29430                     }).createDelegate(this)
29431                 );
29432         
29433                 return;
29434             }
29435             
29436             docs.push(
29437                 (function(){
29438                     _this.process(file);
29439                 }).createDelegate(this)
29440             );
29441             
29442         }, this);
29443         
29444         this.files = files;
29445         
29446         this.delegates = this.delegates.concat(docs);
29447         
29448         if(!this.delegates.length){
29449             this.refresh();
29450             return;
29451         }
29452         
29453         this.progressBar.aria_valuemax = this.delegates.length;
29454         
29455         this.arrange();
29456         
29457         return;
29458     },
29459     
29460     arrange : function()
29461     {
29462         if(!this.delegates.length){
29463             this.progressDialog.hide();
29464             this.refresh();
29465             return;
29466         }
29467         
29468         var delegate = this.delegates.shift();
29469         
29470         this.progressDialog.show();
29471         
29472         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29473         
29474         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29475         
29476         delegate();
29477     },
29478     
29479     refresh : function()
29480     {
29481         this.uploader.show();
29482         
29483         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29484             this.uploader.hide();
29485         }
29486         
29487         Roo.isTouch ? this.closable(false) : this.closable(true);
29488         
29489         this.fireEvent('refresh', this);
29490     },
29491     
29492     onRemove : function(e, el, o)
29493     {
29494         e.preventDefault();
29495         
29496         this.fireEvent('remove', this, o);
29497         
29498     },
29499     
29500     remove : function(o)
29501     {
29502         var files = [];
29503         
29504         Roo.each(this.files, function(file){
29505             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29506                 files.push(file);
29507                 return;
29508             }
29509
29510             o.target.remove();
29511
29512         }, this);
29513         
29514         this.files = files;
29515         
29516         this.refresh();
29517     },
29518     
29519     clear : function()
29520     {
29521         Roo.each(this.files, function(file){
29522             if(!file.target){
29523                 return;
29524             }
29525             
29526             file.target.remove();
29527
29528         }, this);
29529         
29530         this.files = [];
29531         
29532         this.refresh();
29533     },
29534     
29535     onClick : function(e, el, o)
29536     {
29537         e.preventDefault();
29538         
29539         this.fireEvent('click', this, o);
29540         
29541     },
29542     
29543     closable : function(closable)
29544     {
29545         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29546             
29547             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29548             
29549             if(closable){
29550                 el.show();
29551                 return;
29552             }
29553             
29554             el.hide();
29555             
29556         }, this);
29557     },
29558     
29559     xhrOnLoad : function(xhr)
29560     {
29561         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29562             el.remove();
29563         }, this);
29564         
29565         if (xhr.readyState !== 4) {
29566             this.arrange();
29567             this.fireEvent('exception', this, xhr);
29568             return;
29569         }
29570
29571         var response = Roo.decode(xhr.responseText);
29572         
29573         if(!response.success){
29574             this.arrange();
29575             this.fireEvent('exception', this, xhr);
29576             return;
29577         }
29578         
29579         var file = this.renderPreview(response.data);
29580         
29581         this.files.push(file);
29582         
29583         this.arrange();
29584         
29585         this.fireEvent('afterupload', this, xhr);
29586         
29587     },
29588     
29589     xhrOnError : function(xhr)
29590     {
29591         Roo.log('xhr on error');
29592         
29593         var response = Roo.decode(xhr.responseText);
29594           
29595         Roo.log(response);
29596         
29597         this.arrange();
29598     },
29599     
29600     process : function(file)
29601     {
29602         if(this.fireEvent('process', this, file) !== false){
29603             if(this.editable && file.type.indexOf('image') != -1){
29604                 this.fireEvent('edit', this, file);
29605                 return;
29606             }
29607
29608             this.uploadStart(file, false);
29609
29610             return;
29611         }
29612         
29613     },
29614     
29615     uploadStart : function(file, crop)
29616     {
29617         this.xhr = new XMLHttpRequest();
29618         
29619         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29620             this.arrange();
29621             return;
29622         }
29623         
29624         file.xhr = this.xhr;
29625             
29626         this.managerEl.createChild({
29627             tag : 'div',
29628             cls : 'roo-document-manager-loading',
29629             cn : [
29630                 {
29631                     tag : 'div',
29632                     tooltip : file.name,
29633                     cls : 'roo-document-manager-thumb',
29634                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29635                 }
29636             ]
29637
29638         });
29639
29640         this.xhr.open(this.method, this.url, true);
29641         
29642         var headers = {
29643             "Accept": "application/json",
29644             "Cache-Control": "no-cache",
29645             "X-Requested-With": "XMLHttpRequest"
29646         };
29647         
29648         for (var headerName in headers) {
29649             var headerValue = headers[headerName];
29650             if (headerValue) {
29651                 this.xhr.setRequestHeader(headerName, headerValue);
29652             }
29653         }
29654         
29655         var _this = this;
29656         
29657         this.xhr.onload = function()
29658         {
29659             _this.xhrOnLoad(_this.xhr);
29660         }
29661         
29662         this.xhr.onerror = function()
29663         {
29664             _this.xhrOnError(_this.xhr);
29665         }
29666         
29667         var formData = new FormData();
29668
29669         formData.append('returnHTML', 'NO');
29670         
29671         if(crop){
29672             formData.append('crop', crop);
29673         }
29674         
29675         formData.append(this.paramName, file, file.name);
29676         
29677         var options = {
29678             file : file, 
29679             manually : false
29680         };
29681         
29682         if(this.fireEvent('prepare', this, formData, options) != false){
29683             
29684             if(options.manually){
29685                 return;
29686             }
29687             
29688             this.xhr.send(formData);
29689             return;
29690         };
29691         
29692         this.uploadCancel();
29693     },
29694     
29695     uploadCancel : function()
29696     {
29697         if (this.xhr) {
29698             this.xhr.abort();
29699         }
29700         
29701         this.delegates = [];
29702         
29703         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29704             el.remove();
29705         }, this);
29706         
29707         this.arrange();
29708     },
29709     
29710     renderPreview : function(file)
29711     {
29712         if(typeof(file.target) != 'undefined' && file.target){
29713             return file;
29714         }
29715         
29716         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29717         
29718         var previewEl = this.managerEl.createChild({
29719             tag : 'div',
29720             cls : 'roo-document-manager-preview',
29721             cn : [
29722                 {
29723                     tag : 'div',
29724                     tooltip : file[this.toolTipName],
29725                     cls : 'roo-document-manager-thumb',
29726                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29727                 },
29728                 {
29729                     tag : 'button',
29730                     cls : 'close',
29731                     html : '<i class="fa fa-times-circle"></i>'
29732                 }
29733             ]
29734         });
29735
29736         var close = previewEl.select('button.close', true).first();
29737
29738         close.on('click', this.onRemove, this, file);
29739
29740         file.target = previewEl;
29741
29742         var image = previewEl.select('img', true).first();
29743         
29744         var _this = this;
29745         
29746         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29747         
29748         image.on('click', this.onClick, this, file);
29749         
29750         this.fireEvent('previewrendered', this, file);
29751         
29752         return file;
29753         
29754     },
29755     
29756     onPreviewLoad : function(file, image)
29757     {
29758         if(typeof(file.target) == 'undefined' || !file.target){
29759             return;
29760         }
29761         
29762         var width = image.dom.naturalWidth || image.dom.width;
29763         var height = image.dom.naturalHeight || image.dom.height;
29764         
29765         if(!this.previewResize) {
29766             return;
29767         }
29768         
29769         if(width > height){
29770             file.target.addClass('wide');
29771             return;
29772         }
29773         
29774         file.target.addClass('tall');
29775         return;
29776         
29777     },
29778     
29779     uploadFromSource : function(file, crop)
29780     {
29781         this.xhr = new XMLHttpRequest();
29782         
29783         this.managerEl.createChild({
29784             tag : 'div',
29785             cls : 'roo-document-manager-loading',
29786             cn : [
29787                 {
29788                     tag : 'div',
29789                     tooltip : file.name,
29790                     cls : 'roo-document-manager-thumb',
29791                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29792                 }
29793             ]
29794
29795         });
29796
29797         this.xhr.open(this.method, this.url, true);
29798         
29799         var headers = {
29800             "Accept": "application/json",
29801             "Cache-Control": "no-cache",
29802             "X-Requested-With": "XMLHttpRequest"
29803         };
29804         
29805         for (var headerName in headers) {
29806             var headerValue = headers[headerName];
29807             if (headerValue) {
29808                 this.xhr.setRequestHeader(headerName, headerValue);
29809             }
29810         }
29811         
29812         var _this = this;
29813         
29814         this.xhr.onload = function()
29815         {
29816             _this.xhrOnLoad(_this.xhr);
29817         }
29818         
29819         this.xhr.onerror = function()
29820         {
29821             _this.xhrOnError(_this.xhr);
29822         }
29823         
29824         var formData = new FormData();
29825
29826         formData.append('returnHTML', 'NO');
29827         
29828         formData.append('crop', crop);
29829         
29830         if(typeof(file.filename) != 'undefined'){
29831             formData.append('filename', file.filename);
29832         }
29833         
29834         if(typeof(file.mimetype) != 'undefined'){
29835             formData.append('mimetype', file.mimetype);
29836         }
29837         
29838         Roo.log(formData);
29839         
29840         if(this.fireEvent('prepare', this, formData) != false){
29841             this.xhr.send(formData);
29842         };
29843     }
29844 });
29845
29846 /*
29847 * Licence: LGPL
29848 */
29849
29850 /**
29851  * @class Roo.bootstrap.DocumentViewer
29852  * @extends Roo.bootstrap.Component
29853  * Bootstrap DocumentViewer class
29854  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29855  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29856  * 
29857  * @constructor
29858  * Create a new DocumentViewer
29859  * @param {Object} config The config object
29860  */
29861
29862 Roo.bootstrap.DocumentViewer = function(config){
29863     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29864     
29865     this.addEvents({
29866         /**
29867          * @event initial
29868          * Fire after initEvent
29869          * @param {Roo.bootstrap.DocumentViewer} this
29870          */
29871         "initial" : true,
29872         /**
29873          * @event click
29874          * Fire after click
29875          * @param {Roo.bootstrap.DocumentViewer} this
29876          */
29877         "click" : true,
29878         /**
29879          * @event download
29880          * Fire after download button
29881          * @param {Roo.bootstrap.DocumentViewer} this
29882          */
29883         "download" : true,
29884         /**
29885          * @event trash
29886          * Fire after trash button
29887          * @param {Roo.bootstrap.DocumentViewer} this
29888          */
29889         "trash" : true
29890         
29891     });
29892 };
29893
29894 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29895     
29896     showDownload : true,
29897     
29898     showTrash : true,
29899     
29900     getAutoCreate : function()
29901     {
29902         var cfg = {
29903             tag : 'div',
29904             cls : 'roo-document-viewer',
29905             cn : [
29906                 {
29907                     tag : 'div',
29908                     cls : 'roo-document-viewer-body',
29909                     cn : [
29910                         {
29911                             tag : 'div',
29912                             cls : 'roo-document-viewer-thumb',
29913                             cn : [
29914                                 {
29915                                     tag : 'img',
29916                                     cls : 'roo-document-viewer-image'
29917                                 }
29918                             ]
29919                         }
29920                     ]
29921                 },
29922                 {
29923                     tag : 'div',
29924                     cls : 'roo-document-viewer-footer',
29925                     cn : {
29926                         tag : 'div',
29927                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29928                         cn : [
29929                             {
29930                                 tag : 'div',
29931                                 cls : 'btn-group roo-document-viewer-download',
29932                                 cn : [
29933                                     {
29934                                         tag : 'button',
29935                                         cls : 'btn btn-default',
29936                                         html : '<i class="fa fa-download"></i>'
29937                                     }
29938                                 ]
29939                             },
29940                             {
29941                                 tag : 'div',
29942                                 cls : 'btn-group roo-document-viewer-trash',
29943                                 cn : [
29944                                     {
29945                                         tag : 'button',
29946                                         cls : 'btn btn-default',
29947                                         html : '<i class="fa fa-trash"></i>'
29948                                     }
29949                                 ]
29950                             }
29951                         ]
29952                     }
29953                 }
29954             ]
29955         };
29956         
29957         return cfg;
29958     },
29959     
29960     initEvents : function()
29961     {
29962         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29963         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29964         
29965         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29966         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29967         
29968         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29969         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29970         
29971         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29972         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29973         
29974         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29975         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29976         
29977         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29978         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29979         
29980         this.bodyEl.on('click', this.onClick, this);
29981         this.downloadBtn.on('click', this.onDownload, this);
29982         this.trashBtn.on('click', this.onTrash, this);
29983         
29984         this.downloadBtn.hide();
29985         this.trashBtn.hide();
29986         
29987         if(this.showDownload){
29988             this.downloadBtn.show();
29989         }
29990         
29991         if(this.showTrash){
29992             this.trashBtn.show();
29993         }
29994         
29995         if(!this.showDownload && !this.showTrash) {
29996             this.footerEl.hide();
29997         }
29998         
29999     },
30000     
30001     initial : function()
30002     {
30003         this.fireEvent('initial', this);
30004         
30005     },
30006     
30007     onClick : function(e)
30008     {
30009         e.preventDefault();
30010         
30011         this.fireEvent('click', this);
30012     },
30013     
30014     onDownload : function(e)
30015     {
30016         e.preventDefault();
30017         
30018         this.fireEvent('download', this);
30019     },
30020     
30021     onTrash : function(e)
30022     {
30023         e.preventDefault();
30024         
30025         this.fireEvent('trash', this);
30026     }
30027     
30028 });
30029 /*
30030  * - LGPL
30031  *
30032  * nav progress bar
30033  * 
30034  */
30035
30036 /**
30037  * @class Roo.bootstrap.NavProgressBar
30038  * @extends Roo.bootstrap.Component
30039  * Bootstrap NavProgressBar class
30040  * 
30041  * @constructor
30042  * Create a new nav progress bar
30043  * @param {Object} config The config object
30044  */
30045
30046 Roo.bootstrap.NavProgressBar = function(config){
30047     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
30048
30049     this.bullets = this.bullets || [];
30050    
30051 //    Roo.bootstrap.NavProgressBar.register(this);
30052      this.addEvents({
30053         /**
30054              * @event changed
30055              * Fires when the active item changes
30056              * @param {Roo.bootstrap.NavProgressBar} this
30057              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
30058              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
30059          */
30060         'changed': true
30061      });
30062     
30063 };
30064
30065 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
30066     
30067     bullets : [],
30068     barItems : [],
30069     
30070     getAutoCreate : function()
30071     {
30072         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
30073         
30074         cfg = {
30075             tag : 'div',
30076             cls : 'roo-navigation-bar-group',
30077             cn : [
30078                 {
30079                     tag : 'div',
30080                     cls : 'roo-navigation-top-bar'
30081                 },
30082                 {
30083                     tag : 'div',
30084                     cls : 'roo-navigation-bullets-bar',
30085                     cn : [
30086                         {
30087                             tag : 'ul',
30088                             cls : 'roo-navigation-bar'
30089                         }
30090                     ]
30091                 },
30092                 
30093                 {
30094                     tag : 'div',
30095                     cls : 'roo-navigation-bottom-bar'
30096                 }
30097             ]
30098             
30099         };
30100         
30101         return cfg;
30102         
30103     },
30104     
30105     initEvents: function() 
30106     {
30107         
30108     },
30109     
30110     onRender : function(ct, position) 
30111     {
30112         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30113         
30114         if(this.bullets.length){
30115             Roo.each(this.bullets, function(b){
30116                this.addItem(b);
30117             }, this);
30118         }
30119         
30120         this.format();
30121         
30122     },
30123     
30124     addItem : function(cfg)
30125     {
30126         var item = new Roo.bootstrap.NavProgressItem(cfg);
30127         
30128         item.parentId = this.id;
30129         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
30130         
30131         if(cfg.html){
30132             var top = new Roo.bootstrap.Element({
30133                 tag : 'div',
30134                 cls : 'roo-navigation-bar-text'
30135             });
30136             
30137             var bottom = new Roo.bootstrap.Element({
30138                 tag : 'div',
30139                 cls : 'roo-navigation-bar-text'
30140             });
30141             
30142             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
30143             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
30144             
30145             var topText = new Roo.bootstrap.Element({
30146                 tag : 'span',
30147                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
30148             });
30149             
30150             var bottomText = new Roo.bootstrap.Element({
30151                 tag : 'span',
30152                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30153             });
30154             
30155             topText.onRender(top.el, null);
30156             bottomText.onRender(bottom.el, null);
30157             
30158             item.topEl = top;
30159             item.bottomEl = bottom;
30160         }
30161         
30162         this.barItems.push(item);
30163         
30164         return item;
30165     },
30166     
30167     getActive : function()
30168     {
30169         var active = false;
30170         
30171         Roo.each(this.barItems, function(v){
30172             
30173             if (!v.isActive()) {
30174                 return;
30175             }
30176             
30177             active = v;
30178             return false;
30179             
30180         });
30181         
30182         return active;
30183     },
30184     
30185     setActiveItem : function(item)
30186     {
30187         var prev = false;
30188         
30189         Roo.each(this.barItems, function(v){
30190             if (v.rid == item.rid) {
30191                 return ;
30192             }
30193             
30194             if (v.isActive()) {
30195                 v.setActive(false);
30196                 prev = v;
30197             }
30198         });
30199
30200         item.setActive(true);
30201         
30202         this.fireEvent('changed', this, item, prev);
30203     },
30204     
30205     getBarItem: function(rid)
30206     {
30207         var ret = false;
30208         
30209         Roo.each(this.barItems, function(e) {
30210             if (e.rid != rid) {
30211                 return;
30212             }
30213             
30214             ret =  e;
30215             return false;
30216         });
30217         
30218         return ret;
30219     },
30220     
30221     indexOfItem : function(item)
30222     {
30223         var index = false;
30224         
30225         Roo.each(this.barItems, function(v, i){
30226             
30227             if (v.rid != item.rid) {
30228                 return;
30229             }
30230             
30231             index = i;
30232             return false
30233         });
30234         
30235         return index;
30236     },
30237     
30238     setActiveNext : function()
30239     {
30240         var i = this.indexOfItem(this.getActive());
30241         
30242         if (i > this.barItems.length) {
30243             return;
30244         }
30245         
30246         this.setActiveItem(this.barItems[i+1]);
30247     },
30248     
30249     setActivePrev : function()
30250     {
30251         var i = this.indexOfItem(this.getActive());
30252         
30253         if (i  < 1) {
30254             return;
30255         }
30256         
30257         this.setActiveItem(this.barItems[i-1]);
30258     },
30259     
30260     format : function()
30261     {
30262         if(!this.barItems.length){
30263             return;
30264         }
30265      
30266         var width = 100 / this.barItems.length;
30267         
30268         Roo.each(this.barItems, function(i){
30269             i.el.setStyle('width', width + '%');
30270             i.topEl.el.setStyle('width', width + '%');
30271             i.bottomEl.el.setStyle('width', width + '%');
30272         }, this);
30273         
30274     }
30275     
30276 });
30277 /*
30278  * - LGPL
30279  *
30280  * Nav Progress Item
30281  * 
30282  */
30283
30284 /**
30285  * @class Roo.bootstrap.NavProgressItem
30286  * @extends Roo.bootstrap.Component
30287  * Bootstrap NavProgressItem class
30288  * @cfg {String} rid the reference id
30289  * @cfg {Boolean} active (true|false) Is item active default false
30290  * @cfg {Boolean} disabled (true|false) Is item active default false
30291  * @cfg {String} html
30292  * @cfg {String} position (top|bottom) text position default bottom
30293  * @cfg {String} icon show icon instead of number
30294  * 
30295  * @constructor
30296  * Create a new NavProgressItem
30297  * @param {Object} config The config object
30298  */
30299 Roo.bootstrap.NavProgressItem = function(config){
30300     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30301     this.addEvents({
30302         // raw events
30303         /**
30304          * @event click
30305          * The raw click event for the entire grid.
30306          * @param {Roo.bootstrap.NavProgressItem} this
30307          * @param {Roo.EventObject} e
30308          */
30309         "click" : true
30310     });
30311    
30312 };
30313
30314 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30315     
30316     rid : '',
30317     active : false,
30318     disabled : false,
30319     html : '',
30320     position : 'bottom',
30321     icon : false,
30322     
30323     getAutoCreate : function()
30324     {
30325         var iconCls = 'roo-navigation-bar-item-icon';
30326         
30327         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30328         
30329         var cfg = {
30330             tag: 'li',
30331             cls: 'roo-navigation-bar-item',
30332             cn : [
30333                 {
30334                     tag : 'i',
30335                     cls : iconCls
30336                 }
30337             ]
30338         };
30339         
30340         if(this.active){
30341             cfg.cls += ' active';
30342         }
30343         if(this.disabled){
30344             cfg.cls += ' disabled';
30345         }
30346         
30347         return cfg;
30348     },
30349     
30350     disable : function()
30351     {
30352         this.setDisabled(true);
30353     },
30354     
30355     enable : function()
30356     {
30357         this.setDisabled(false);
30358     },
30359     
30360     initEvents: function() 
30361     {
30362         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30363         
30364         this.iconEl.on('click', this.onClick, this);
30365     },
30366     
30367     onClick : function(e)
30368     {
30369         e.preventDefault();
30370         
30371         if(this.disabled){
30372             return;
30373         }
30374         
30375         if(this.fireEvent('click', this, e) === false){
30376             return;
30377         };
30378         
30379         this.parent().setActiveItem(this);
30380     },
30381     
30382     isActive: function () 
30383     {
30384         return this.active;
30385     },
30386     
30387     setActive : function(state)
30388     {
30389         if(this.active == state){
30390             return;
30391         }
30392         
30393         this.active = state;
30394         
30395         if (state) {
30396             this.el.addClass('active');
30397             return;
30398         }
30399         
30400         this.el.removeClass('active');
30401         
30402         return;
30403     },
30404     
30405     setDisabled : function(state)
30406     {
30407         if(this.disabled == state){
30408             return;
30409         }
30410         
30411         this.disabled = state;
30412         
30413         if (state) {
30414             this.el.addClass('disabled');
30415             return;
30416         }
30417         
30418         this.el.removeClass('disabled');
30419     },
30420     
30421     tooltipEl : function()
30422     {
30423         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30424     }
30425 });
30426  
30427
30428  /*
30429  * - LGPL
30430  *
30431  * FieldLabel
30432  * 
30433  */
30434
30435 /**
30436  * @class Roo.bootstrap.FieldLabel
30437  * @extends Roo.bootstrap.Component
30438  * Bootstrap FieldLabel class
30439  * @cfg {String} html contents of the element
30440  * @cfg {String} tag tag of the element default label
30441  * @cfg {String} cls class of the element
30442  * @cfg {String} target label target 
30443  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30444  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
30445  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
30446  * @cfg {String} iconTooltip default "This field is required"
30447  * @cfg {String} indicatorpos (left|right) default left
30448  * 
30449  * @constructor
30450  * Create a new FieldLabel
30451  * @param {Object} config The config object
30452  */
30453
30454 Roo.bootstrap.FieldLabel = function(config){
30455     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30456     
30457     this.addEvents({
30458             /**
30459              * @event invalid
30460              * Fires after the field has been marked as invalid.
30461              * @param {Roo.form.FieldLabel} this
30462              * @param {String} msg The validation message
30463              */
30464             invalid : true,
30465             /**
30466              * @event valid
30467              * Fires after the field has been validated with no errors.
30468              * @param {Roo.form.FieldLabel} this
30469              */
30470             valid : true
30471         });
30472 };
30473
30474 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30475     
30476     tag: 'label',
30477     cls: '',
30478     html: '',
30479     target: '',
30480     allowBlank : true,
30481     invalidClass : 'has-warning',
30482     validClass : 'has-success',
30483     iconTooltip : 'This field is required',
30484     indicatorpos : 'left',
30485     
30486     getAutoCreate : function(){
30487         
30488         var cls = "";
30489         if (!this.allowBlank) {
30490             cls  = "visible";
30491         }
30492         
30493         var cfg = {
30494             tag : this.tag,
30495             cls : 'roo-bootstrap-field-label ' + this.cls,
30496             for : this.target,
30497             cn : [
30498                 {
30499                     tag : 'i',
30500                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30501                     tooltip : this.iconTooltip
30502                 },
30503                 {
30504                     tag : 'span',
30505                     html : this.html
30506                 }
30507             ] 
30508         };
30509         
30510         if(this.indicatorpos == 'right'){
30511             var cfg = {
30512                 tag : this.tag,
30513                 cls : 'roo-bootstrap-field-label ' + this.cls,
30514                 for : this.target,
30515                 cn : [
30516                     {
30517                         tag : 'span',
30518                         html : this.html
30519                     },
30520                     {
30521                         tag : 'i',
30522                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30523                         tooltip : this.iconTooltip
30524                     }
30525                 ] 
30526             };
30527         }
30528         
30529         return cfg;
30530     },
30531     
30532     initEvents: function() 
30533     {
30534         Roo.bootstrap.Element.superclass.initEvents.call(this);
30535         
30536         this.indicator = this.indicatorEl();
30537         
30538         if(this.indicator){
30539             this.indicator.removeClass('visible');
30540             this.indicator.addClass('invisible');
30541         }
30542         
30543         Roo.bootstrap.FieldLabel.register(this);
30544     },
30545     
30546     indicatorEl : function()
30547     {
30548         var indicator = this.el.select('i.roo-required-indicator',true).first();
30549         
30550         if(!indicator){
30551             return false;
30552         }
30553         
30554         return indicator;
30555         
30556     },
30557     
30558     /**
30559      * Mark this field as valid
30560      */
30561     markValid : function()
30562     {
30563         if(this.indicator){
30564             this.indicator.removeClass('visible');
30565             this.indicator.addClass('invisible');
30566         }
30567         if (Roo.bootstrap.version == 3) {
30568             this.el.removeClass(this.invalidClass);
30569             this.el.addClass(this.validClass);
30570         } else {
30571             this.el.removeClass('is-invalid');
30572             this.el.addClass('is-valid');
30573         }
30574         
30575         
30576         this.fireEvent('valid', this);
30577     },
30578     
30579     /**
30580      * Mark this field as invalid
30581      * @param {String} msg The validation message
30582      */
30583     markInvalid : function(msg)
30584     {
30585         if(this.indicator){
30586             this.indicator.removeClass('invisible');
30587             this.indicator.addClass('visible');
30588         }
30589           if (Roo.bootstrap.version == 3) {
30590             this.el.removeClass(this.validClass);
30591             this.el.addClass(this.invalidClass);
30592         } else {
30593             this.el.removeClass('is-valid');
30594             this.el.addClass('is-invalid');
30595         }
30596         
30597         
30598         this.fireEvent('invalid', this, msg);
30599     }
30600     
30601    
30602 });
30603
30604 Roo.apply(Roo.bootstrap.FieldLabel, {
30605     
30606     groups: {},
30607     
30608      /**
30609     * register a FieldLabel Group
30610     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30611     */
30612     register : function(label)
30613     {
30614         if(this.groups.hasOwnProperty(label.target)){
30615             return;
30616         }
30617      
30618         this.groups[label.target] = label;
30619         
30620     },
30621     /**
30622     * fetch a FieldLabel Group based on the target
30623     * @param {string} target
30624     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30625     */
30626     get: function(target) {
30627         if (typeof(this.groups[target]) == 'undefined') {
30628             return false;
30629         }
30630         
30631         return this.groups[target] ;
30632     }
30633 });
30634
30635  
30636
30637  /*
30638  * - LGPL
30639  *
30640  * page DateSplitField.
30641  * 
30642  */
30643
30644
30645 /**
30646  * @class Roo.bootstrap.DateSplitField
30647  * @extends Roo.bootstrap.Component
30648  * Bootstrap DateSplitField class
30649  * @cfg {string} fieldLabel - the label associated
30650  * @cfg {Number} labelWidth set the width of label (0-12)
30651  * @cfg {String} labelAlign (top|left)
30652  * @cfg {Boolean} dayAllowBlank (true|false) default false
30653  * @cfg {Boolean} monthAllowBlank (true|false) default false
30654  * @cfg {Boolean} yearAllowBlank (true|false) default false
30655  * @cfg {string} dayPlaceholder 
30656  * @cfg {string} monthPlaceholder
30657  * @cfg {string} yearPlaceholder
30658  * @cfg {string} dayFormat default 'd'
30659  * @cfg {string} monthFormat default 'm'
30660  * @cfg {string} yearFormat default 'Y'
30661  * @cfg {Number} labellg set the width of label (1-12)
30662  * @cfg {Number} labelmd set the width of label (1-12)
30663  * @cfg {Number} labelsm set the width of label (1-12)
30664  * @cfg {Number} labelxs set the width of label (1-12)
30665
30666  *     
30667  * @constructor
30668  * Create a new DateSplitField
30669  * @param {Object} config The config object
30670  */
30671
30672 Roo.bootstrap.DateSplitField = function(config){
30673     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30674     
30675     this.addEvents({
30676         // raw events
30677          /**
30678          * @event years
30679          * getting the data of years
30680          * @param {Roo.bootstrap.DateSplitField} this
30681          * @param {Object} years
30682          */
30683         "years" : true,
30684         /**
30685          * @event days
30686          * getting the data of days
30687          * @param {Roo.bootstrap.DateSplitField} this
30688          * @param {Object} days
30689          */
30690         "days" : true,
30691         /**
30692          * @event invalid
30693          * Fires after the field has been marked as invalid.
30694          * @param {Roo.form.Field} this
30695          * @param {String} msg The validation message
30696          */
30697         invalid : true,
30698        /**
30699          * @event valid
30700          * Fires after the field has been validated with no errors.
30701          * @param {Roo.form.Field} this
30702          */
30703         valid : true
30704     });
30705 };
30706
30707 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30708     
30709     fieldLabel : '',
30710     labelAlign : 'top',
30711     labelWidth : 3,
30712     dayAllowBlank : false,
30713     monthAllowBlank : false,
30714     yearAllowBlank : false,
30715     dayPlaceholder : '',
30716     monthPlaceholder : '',
30717     yearPlaceholder : '',
30718     dayFormat : 'd',
30719     monthFormat : 'm',
30720     yearFormat : 'Y',
30721     isFormField : true,
30722     labellg : 0,
30723     labelmd : 0,
30724     labelsm : 0,
30725     labelxs : 0,
30726     
30727     getAutoCreate : function()
30728     {
30729         var cfg = {
30730             tag : 'div',
30731             cls : 'row roo-date-split-field-group',
30732             cn : [
30733                 {
30734                     tag : 'input',
30735                     type : 'hidden',
30736                     cls : 'form-hidden-field roo-date-split-field-group-value',
30737                     name : this.name
30738                 }
30739             ]
30740         };
30741         
30742         var labelCls = 'col-md-12';
30743         var contentCls = 'col-md-4';
30744         
30745         if(this.fieldLabel){
30746             
30747             var label = {
30748                 tag : 'div',
30749                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30750                 cn : [
30751                     {
30752                         tag : 'label',
30753                         html : this.fieldLabel
30754                     }
30755                 ]
30756             };
30757             
30758             if(this.labelAlign == 'left'){
30759             
30760                 if(this.labelWidth > 12){
30761                     label.style = "width: " + this.labelWidth + 'px';
30762                 }
30763
30764                 if(this.labelWidth < 13 && this.labelmd == 0){
30765                     this.labelmd = this.labelWidth;
30766                 }
30767
30768                 if(this.labellg > 0){
30769                     labelCls = ' col-lg-' + this.labellg;
30770                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30771                 }
30772
30773                 if(this.labelmd > 0){
30774                     labelCls = ' col-md-' + this.labelmd;
30775                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30776                 }
30777
30778                 if(this.labelsm > 0){
30779                     labelCls = ' col-sm-' + this.labelsm;
30780                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30781                 }
30782
30783                 if(this.labelxs > 0){
30784                     labelCls = ' col-xs-' + this.labelxs;
30785                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30786                 }
30787             }
30788             
30789             label.cls += ' ' + labelCls;
30790             
30791             cfg.cn.push(label);
30792         }
30793         
30794         Roo.each(['day', 'month', 'year'], function(t){
30795             cfg.cn.push({
30796                 tag : 'div',
30797                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30798             });
30799         }, this);
30800         
30801         return cfg;
30802     },
30803     
30804     inputEl: function ()
30805     {
30806         return this.el.select('.roo-date-split-field-group-value', true).first();
30807     },
30808     
30809     onRender : function(ct, position) 
30810     {
30811         var _this = this;
30812         
30813         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30814         
30815         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30816         
30817         this.dayField = new Roo.bootstrap.ComboBox({
30818             allowBlank : this.dayAllowBlank,
30819             alwaysQuery : true,
30820             displayField : 'value',
30821             editable : false,
30822             fieldLabel : '',
30823             forceSelection : true,
30824             mode : 'local',
30825             placeholder : this.dayPlaceholder,
30826             selectOnFocus : true,
30827             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30828             triggerAction : 'all',
30829             typeAhead : true,
30830             valueField : 'value',
30831             store : new Roo.data.SimpleStore({
30832                 data : (function() {    
30833                     var days = [];
30834                     _this.fireEvent('days', _this, days);
30835                     return days;
30836                 })(),
30837                 fields : [ 'value' ]
30838             }),
30839             listeners : {
30840                 select : function (_self, record, index)
30841                 {
30842                     _this.setValue(_this.getValue());
30843                 }
30844             }
30845         });
30846
30847         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30848         
30849         this.monthField = new Roo.bootstrap.MonthField({
30850             after : '<i class=\"fa fa-calendar\"></i>',
30851             allowBlank : this.monthAllowBlank,
30852             placeholder : this.monthPlaceholder,
30853             readOnly : true,
30854             listeners : {
30855                 render : function (_self)
30856                 {
30857                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30858                         e.preventDefault();
30859                         _self.focus();
30860                     });
30861                 },
30862                 select : function (_self, oldvalue, newvalue)
30863                 {
30864                     _this.setValue(_this.getValue());
30865                 }
30866             }
30867         });
30868         
30869         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30870         
30871         this.yearField = new Roo.bootstrap.ComboBox({
30872             allowBlank : this.yearAllowBlank,
30873             alwaysQuery : true,
30874             displayField : 'value',
30875             editable : false,
30876             fieldLabel : '',
30877             forceSelection : true,
30878             mode : 'local',
30879             placeholder : this.yearPlaceholder,
30880             selectOnFocus : true,
30881             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30882             triggerAction : 'all',
30883             typeAhead : true,
30884             valueField : 'value',
30885             store : new Roo.data.SimpleStore({
30886                 data : (function() {
30887                     var years = [];
30888                     _this.fireEvent('years', _this, years);
30889                     return years;
30890                 })(),
30891                 fields : [ 'value' ]
30892             }),
30893             listeners : {
30894                 select : function (_self, record, index)
30895                 {
30896                     _this.setValue(_this.getValue());
30897                 }
30898             }
30899         });
30900
30901         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30902     },
30903     
30904     setValue : function(v, format)
30905     {
30906         this.inputEl.dom.value = v;
30907         
30908         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30909         
30910         var d = Date.parseDate(v, f);
30911         
30912         if(!d){
30913             this.validate();
30914             return;
30915         }
30916         
30917         this.setDay(d.format(this.dayFormat));
30918         this.setMonth(d.format(this.monthFormat));
30919         this.setYear(d.format(this.yearFormat));
30920         
30921         this.validate();
30922         
30923         return;
30924     },
30925     
30926     setDay : function(v)
30927     {
30928         this.dayField.setValue(v);
30929         this.inputEl.dom.value = this.getValue();
30930         this.validate();
30931         return;
30932     },
30933     
30934     setMonth : function(v)
30935     {
30936         this.monthField.setValue(v, true);
30937         this.inputEl.dom.value = this.getValue();
30938         this.validate();
30939         return;
30940     },
30941     
30942     setYear : function(v)
30943     {
30944         this.yearField.setValue(v);
30945         this.inputEl.dom.value = this.getValue();
30946         this.validate();
30947         return;
30948     },
30949     
30950     getDay : function()
30951     {
30952         return this.dayField.getValue();
30953     },
30954     
30955     getMonth : function()
30956     {
30957         return this.monthField.getValue();
30958     },
30959     
30960     getYear : function()
30961     {
30962         return this.yearField.getValue();
30963     },
30964     
30965     getValue : function()
30966     {
30967         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30968         
30969         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30970         
30971         return date;
30972     },
30973     
30974     reset : function()
30975     {
30976         this.setDay('');
30977         this.setMonth('');
30978         this.setYear('');
30979         this.inputEl.dom.value = '';
30980         this.validate();
30981         return;
30982     },
30983     
30984     validate : function()
30985     {
30986         var d = this.dayField.validate();
30987         var m = this.monthField.validate();
30988         var y = this.yearField.validate();
30989         
30990         var valid = true;
30991         
30992         if(
30993                 (!this.dayAllowBlank && !d) ||
30994                 (!this.monthAllowBlank && !m) ||
30995                 (!this.yearAllowBlank && !y)
30996         ){
30997             valid = false;
30998         }
30999         
31000         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
31001             return valid;
31002         }
31003         
31004         if(valid){
31005             this.markValid();
31006             return valid;
31007         }
31008         
31009         this.markInvalid();
31010         
31011         return valid;
31012     },
31013     
31014     markValid : function()
31015     {
31016         
31017         var label = this.el.select('label', true).first();
31018         var icon = this.el.select('i.fa-star', true).first();
31019
31020         if(label && icon){
31021             icon.remove();
31022         }
31023         
31024         this.fireEvent('valid', this);
31025     },
31026     
31027      /**
31028      * Mark this field as invalid
31029      * @param {String} msg The validation message
31030      */
31031     markInvalid : function(msg)
31032     {
31033         
31034         var label = this.el.select('label', true).first();
31035         var icon = this.el.select('i.fa-star', true).first();
31036
31037         if(label && !icon){
31038             this.el.select('.roo-date-split-field-label', true).createChild({
31039                 tag : 'i',
31040                 cls : 'text-danger fa fa-lg fa-star',
31041                 tooltip : 'This field is required',
31042                 style : 'margin-right:5px;'
31043             }, label, true);
31044         }
31045         
31046         this.fireEvent('invalid', this, msg);
31047     },
31048     
31049     clearInvalid : function()
31050     {
31051         var label = this.el.select('label', true).first();
31052         var icon = this.el.select('i.fa-star', true).first();
31053
31054         if(label && icon){
31055             icon.remove();
31056         }
31057         
31058         this.fireEvent('valid', this);
31059     },
31060     
31061     getName: function()
31062     {
31063         return this.name;
31064     }
31065     
31066 });
31067
31068  /**
31069  *
31070  * This is based on 
31071  * http://masonry.desandro.com
31072  *
31073  * The idea is to render all the bricks based on vertical width...
31074  *
31075  * The original code extends 'outlayer' - we might need to use that....
31076  * 
31077  */
31078
31079
31080 /**
31081  * @class Roo.bootstrap.LayoutMasonry
31082  * @extends Roo.bootstrap.Component
31083  * Bootstrap Layout Masonry class
31084  * 
31085  * @constructor
31086  * Create a new Element
31087  * @param {Object} config The config object
31088  */
31089
31090 Roo.bootstrap.LayoutMasonry = function(config){
31091     
31092     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
31093     
31094     this.bricks = [];
31095     
31096     Roo.bootstrap.LayoutMasonry.register(this);
31097     
31098     this.addEvents({
31099         // raw events
31100         /**
31101          * @event layout
31102          * Fire after layout the items
31103          * @param {Roo.bootstrap.LayoutMasonry} this
31104          * @param {Roo.EventObject} e
31105          */
31106         "layout" : true
31107     });
31108     
31109 };
31110
31111 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
31112     
31113     /**
31114      * @cfg {Boolean} isLayoutInstant = no animation?
31115      */   
31116     isLayoutInstant : false, // needed?
31117    
31118     /**
31119      * @cfg {Number} boxWidth  width of the columns
31120      */   
31121     boxWidth : 450,
31122     
31123       /**
31124      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
31125      */   
31126     boxHeight : 0,
31127     
31128     /**
31129      * @cfg {Number} padWidth padding below box..
31130      */   
31131     padWidth : 10, 
31132     
31133     /**
31134      * @cfg {Number} gutter gutter width..
31135      */   
31136     gutter : 10,
31137     
31138      /**
31139      * @cfg {Number} maxCols maximum number of columns
31140      */   
31141     
31142     maxCols: 0,
31143     
31144     /**
31145      * @cfg {Boolean} isAutoInitial defalut true
31146      */   
31147     isAutoInitial : true, 
31148     
31149     containerWidth: 0,
31150     
31151     /**
31152      * @cfg {Boolean} isHorizontal defalut false
31153      */   
31154     isHorizontal : false, 
31155
31156     currentSize : null,
31157     
31158     tag: 'div',
31159     
31160     cls: '',
31161     
31162     bricks: null, //CompositeElement
31163     
31164     cols : 1,
31165     
31166     _isLayoutInited : false,
31167     
31168 //    isAlternative : false, // only use for vertical layout...
31169     
31170     /**
31171      * @cfg {Number} alternativePadWidth padding below box..
31172      */   
31173     alternativePadWidth : 50,
31174     
31175     selectedBrick : [],
31176     
31177     getAutoCreate : function(){
31178         
31179         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31180         
31181         var cfg = {
31182             tag: this.tag,
31183             cls: 'blog-masonary-wrapper ' + this.cls,
31184             cn : {
31185                 cls : 'mas-boxes masonary'
31186             }
31187         };
31188         
31189         return cfg;
31190     },
31191     
31192     getChildContainer: function( )
31193     {
31194         if (this.boxesEl) {
31195             return this.boxesEl;
31196         }
31197         
31198         this.boxesEl = this.el.select('.mas-boxes').first();
31199         
31200         return this.boxesEl;
31201     },
31202     
31203     
31204     initEvents : function()
31205     {
31206         var _this = this;
31207         
31208         if(this.isAutoInitial){
31209             Roo.log('hook children rendered');
31210             this.on('childrenrendered', function() {
31211                 Roo.log('children rendered');
31212                 _this.initial();
31213             } ,this);
31214         }
31215     },
31216     
31217     initial : function()
31218     {
31219         this.selectedBrick = [];
31220         
31221         this.currentSize = this.el.getBox(true);
31222         
31223         Roo.EventManager.onWindowResize(this.resize, this); 
31224
31225         if(!this.isAutoInitial){
31226             this.layout();
31227             return;
31228         }
31229         
31230         this.layout();
31231         
31232         return;
31233         //this.layout.defer(500,this);
31234         
31235     },
31236     
31237     resize : function()
31238     {
31239         var cs = this.el.getBox(true);
31240         
31241         if (
31242                 this.currentSize.width == cs.width && 
31243                 this.currentSize.x == cs.x && 
31244                 this.currentSize.height == cs.height && 
31245                 this.currentSize.y == cs.y 
31246         ) {
31247             Roo.log("no change in with or X or Y");
31248             return;
31249         }
31250         
31251         this.currentSize = cs;
31252         
31253         this.layout();
31254         
31255     },
31256     
31257     layout : function()
31258     {   
31259         this._resetLayout();
31260         
31261         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31262         
31263         this.layoutItems( isInstant );
31264       
31265         this._isLayoutInited = true;
31266         
31267         this.fireEvent('layout', this);
31268         
31269     },
31270     
31271     _resetLayout : function()
31272     {
31273         if(this.isHorizontal){
31274             this.horizontalMeasureColumns();
31275             return;
31276         }
31277         
31278         this.verticalMeasureColumns();
31279         
31280     },
31281     
31282     verticalMeasureColumns : function()
31283     {
31284         this.getContainerWidth();
31285         
31286 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31287 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31288 //            return;
31289 //        }
31290         
31291         var boxWidth = this.boxWidth + this.padWidth;
31292         
31293         if(this.containerWidth < this.boxWidth){
31294             boxWidth = this.containerWidth
31295         }
31296         
31297         var containerWidth = this.containerWidth;
31298         
31299         var cols = Math.floor(containerWidth / boxWidth);
31300         
31301         this.cols = Math.max( cols, 1 );
31302         
31303         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31304         
31305         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31306         
31307         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31308         
31309         this.colWidth = boxWidth + avail - this.padWidth;
31310         
31311         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31312         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31313     },
31314     
31315     horizontalMeasureColumns : function()
31316     {
31317         this.getContainerWidth();
31318         
31319         var boxWidth = this.boxWidth;
31320         
31321         if(this.containerWidth < boxWidth){
31322             boxWidth = this.containerWidth;
31323         }
31324         
31325         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31326         
31327         this.el.setHeight(boxWidth);
31328         
31329     },
31330     
31331     getContainerWidth : function()
31332     {
31333         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31334     },
31335     
31336     layoutItems : function( isInstant )
31337     {
31338         Roo.log(this.bricks);
31339         
31340         var items = Roo.apply([], this.bricks);
31341         
31342         if(this.isHorizontal){
31343             this._horizontalLayoutItems( items , isInstant );
31344             return;
31345         }
31346         
31347 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31348 //            this._verticalAlternativeLayoutItems( items , isInstant );
31349 //            return;
31350 //        }
31351         
31352         this._verticalLayoutItems( items , isInstant );
31353         
31354     },
31355     
31356     _verticalLayoutItems : function ( items , isInstant)
31357     {
31358         if ( !items || !items.length ) {
31359             return;
31360         }
31361         
31362         var standard = [
31363             ['xs', 'xs', 'xs', 'tall'],
31364             ['xs', 'xs', 'tall'],
31365             ['xs', 'xs', 'sm'],
31366             ['xs', 'xs', 'xs'],
31367             ['xs', 'tall'],
31368             ['xs', 'sm'],
31369             ['xs', 'xs'],
31370             ['xs'],
31371             
31372             ['sm', 'xs', 'xs'],
31373             ['sm', 'xs'],
31374             ['sm'],
31375             
31376             ['tall', 'xs', 'xs', 'xs'],
31377             ['tall', 'xs', 'xs'],
31378             ['tall', 'xs'],
31379             ['tall']
31380             
31381         ];
31382         
31383         var queue = [];
31384         
31385         var boxes = [];
31386         
31387         var box = [];
31388         
31389         Roo.each(items, function(item, k){
31390             
31391             switch (item.size) {
31392                 // these layouts take up a full box,
31393                 case 'md' :
31394                 case 'md-left' :
31395                 case 'md-right' :
31396                 case 'wide' :
31397                     
31398                     if(box.length){
31399                         boxes.push(box);
31400                         box = [];
31401                     }
31402                     
31403                     boxes.push([item]);
31404                     
31405                     break;
31406                     
31407                 case 'xs' :
31408                 case 'sm' :
31409                 case 'tall' :
31410                     
31411                     box.push(item);
31412                     
31413                     break;
31414                 default :
31415                     break;
31416                     
31417             }
31418             
31419         }, this);
31420         
31421         if(box.length){
31422             boxes.push(box);
31423             box = [];
31424         }
31425         
31426         var filterPattern = function(box, length)
31427         {
31428             if(!box.length){
31429                 return;
31430             }
31431             
31432             var match = false;
31433             
31434             var pattern = box.slice(0, length);
31435             
31436             var format = [];
31437             
31438             Roo.each(pattern, function(i){
31439                 format.push(i.size);
31440             }, this);
31441             
31442             Roo.each(standard, function(s){
31443                 
31444                 if(String(s) != String(format)){
31445                     return;
31446                 }
31447                 
31448                 match = true;
31449                 return false;
31450                 
31451             }, this);
31452             
31453             if(!match && length == 1){
31454                 return;
31455             }
31456             
31457             if(!match){
31458                 filterPattern(box, length - 1);
31459                 return;
31460             }
31461                 
31462             queue.push(pattern);
31463
31464             box = box.slice(length, box.length);
31465
31466             filterPattern(box, 4);
31467
31468             return;
31469             
31470         }
31471         
31472         Roo.each(boxes, function(box, k){
31473             
31474             if(!box.length){
31475                 return;
31476             }
31477             
31478             if(box.length == 1){
31479                 queue.push(box);
31480                 return;
31481             }
31482             
31483             filterPattern(box, 4);
31484             
31485         }, this);
31486         
31487         this._processVerticalLayoutQueue( queue, isInstant );
31488         
31489     },
31490     
31491 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31492 //    {
31493 //        if ( !items || !items.length ) {
31494 //            return;
31495 //        }
31496 //
31497 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31498 //        
31499 //    },
31500     
31501     _horizontalLayoutItems : function ( items , isInstant)
31502     {
31503         if ( !items || !items.length || items.length < 3) {
31504             return;
31505         }
31506         
31507         items.reverse();
31508         
31509         var eItems = items.slice(0, 3);
31510         
31511         items = items.slice(3, items.length);
31512         
31513         var standard = [
31514             ['xs', 'xs', 'xs', 'wide'],
31515             ['xs', 'xs', 'wide'],
31516             ['xs', 'xs', 'sm'],
31517             ['xs', 'xs', 'xs'],
31518             ['xs', 'wide'],
31519             ['xs', 'sm'],
31520             ['xs', 'xs'],
31521             ['xs'],
31522             
31523             ['sm', 'xs', 'xs'],
31524             ['sm', 'xs'],
31525             ['sm'],
31526             
31527             ['wide', 'xs', 'xs', 'xs'],
31528             ['wide', 'xs', 'xs'],
31529             ['wide', 'xs'],
31530             ['wide'],
31531             
31532             ['wide-thin']
31533         ];
31534         
31535         var queue = [];
31536         
31537         var boxes = [];
31538         
31539         var box = [];
31540         
31541         Roo.each(items, function(item, k){
31542             
31543             switch (item.size) {
31544                 case 'md' :
31545                 case 'md-left' :
31546                 case 'md-right' :
31547                 case 'tall' :
31548                     
31549                     if(box.length){
31550                         boxes.push(box);
31551                         box = [];
31552                     }
31553                     
31554                     boxes.push([item]);
31555                     
31556                     break;
31557                     
31558                 case 'xs' :
31559                 case 'sm' :
31560                 case 'wide' :
31561                 case 'wide-thin' :
31562                     
31563                     box.push(item);
31564                     
31565                     break;
31566                 default :
31567                     break;
31568                     
31569             }
31570             
31571         }, this);
31572         
31573         if(box.length){
31574             boxes.push(box);
31575             box = [];
31576         }
31577         
31578         var filterPattern = function(box, length)
31579         {
31580             if(!box.length){
31581                 return;
31582             }
31583             
31584             var match = false;
31585             
31586             var pattern = box.slice(0, length);
31587             
31588             var format = [];
31589             
31590             Roo.each(pattern, function(i){
31591                 format.push(i.size);
31592             }, this);
31593             
31594             Roo.each(standard, function(s){
31595                 
31596                 if(String(s) != String(format)){
31597                     return;
31598                 }
31599                 
31600                 match = true;
31601                 return false;
31602                 
31603             }, this);
31604             
31605             if(!match && length == 1){
31606                 return;
31607             }
31608             
31609             if(!match){
31610                 filterPattern(box, length - 1);
31611                 return;
31612             }
31613                 
31614             queue.push(pattern);
31615
31616             box = box.slice(length, box.length);
31617
31618             filterPattern(box, 4);
31619
31620             return;
31621             
31622         }
31623         
31624         Roo.each(boxes, function(box, k){
31625             
31626             if(!box.length){
31627                 return;
31628             }
31629             
31630             if(box.length == 1){
31631                 queue.push(box);
31632                 return;
31633             }
31634             
31635             filterPattern(box, 4);
31636             
31637         }, this);
31638         
31639         
31640         var prune = [];
31641         
31642         var pos = this.el.getBox(true);
31643         
31644         var minX = pos.x;
31645         
31646         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31647         
31648         var hit_end = false;
31649         
31650         Roo.each(queue, function(box){
31651             
31652             if(hit_end){
31653                 
31654                 Roo.each(box, function(b){
31655                 
31656                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31657                     b.el.hide();
31658
31659                 }, this);
31660
31661                 return;
31662             }
31663             
31664             var mx = 0;
31665             
31666             Roo.each(box, function(b){
31667                 
31668                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31669                 b.el.show();
31670
31671                 mx = Math.max(mx, b.x);
31672                 
31673             }, this);
31674             
31675             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31676             
31677             if(maxX < minX){
31678                 
31679                 Roo.each(box, function(b){
31680                 
31681                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31682                     b.el.hide();
31683                     
31684                 }, this);
31685                 
31686                 hit_end = true;
31687                 
31688                 return;
31689             }
31690             
31691             prune.push(box);
31692             
31693         }, this);
31694         
31695         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31696     },
31697     
31698     /** Sets position of item in DOM
31699     * @param {Element} item
31700     * @param {Number} x - horizontal position
31701     * @param {Number} y - vertical position
31702     * @param {Boolean} isInstant - disables transitions
31703     */
31704     _processVerticalLayoutQueue : function( queue, isInstant )
31705     {
31706         var pos = this.el.getBox(true);
31707         var x = pos.x;
31708         var y = pos.y;
31709         var maxY = [];
31710         
31711         for (var i = 0; i < this.cols; i++){
31712             maxY[i] = pos.y;
31713         }
31714         
31715         Roo.each(queue, function(box, k){
31716             
31717             var col = k % this.cols;
31718             
31719             Roo.each(box, function(b,kk){
31720                 
31721                 b.el.position('absolute');
31722                 
31723                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31724                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31725                 
31726                 if(b.size == 'md-left' || b.size == 'md-right'){
31727                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31728                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31729                 }
31730                 
31731                 b.el.setWidth(width);
31732                 b.el.setHeight(height);
31733                 // iframe?
31734                 b.el.select('iframe',true).setSize(width,height);
31735                 
31736             }, this);
31737             
31738             for (var i = 0; i < this.cols; i++){
31739                 
31740                 if(maxY[i] < maxY[col]){
31741                     col = i;
31742                     continue;
31743                 }
31744                 
31745                 col = Math.min(col, i);
31746                 
31747             }
31748             
31749             x = pos.x + col * (this.colWidth + this.padWidth);
31750             
31751             y = maxY[col];
31752             
31753             var positions = [];
31754             
31755             switch (box.length){
31756                 case 1 :
31757                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31758                     break;
31759                 case 2 :
31760                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31761                     break;
31762                 case 3 :
31763                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31764                     break;
31765                 case 4 :
31766                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31767                     break;
31768                 default :
31769                     break;
31770             }
31771             
31772             Roo.each(box, function(b,kk){
31773                 
31774                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31775                 
31776                 var sz = b.el.getSize();
31777                 
31778                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31779                 
31780             }, this);
31781             
31782         }, this);
31783         
31784         var mY = 0;
31785         
31786         for (var i = 0; i < this.cols; i++){
31787             mY = Math.max(mY, maxY[i]);
31788         }
31789         
31790         this.el.setHeight(mY - pos.y);
31791         
31792     },
31793     
31794 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31795 //    {
31796 //        var pos = this.el.getBox(true);
31797 //        var x = pos.x;
31798 //        var y = pos.y;
31799 //        var maxX = pos.right;
31800 //        
31801 //        var maxHeight = 0;
31802 //        
31803 //        Roo.each(items, function(item, k){
31804 //            
31805 //            var c = k % 2;
31806 //            
31807 //            item.el.position('absolute');
31808 //                
31809 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31810 //
31811 //            item.el.setWidth(width);
31812 //
31813 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31814 //
31815 //            item.el.setHeight(height);
31816 //            
31817 //            if(c == 0){
31818 //                item.el.setXY([x, y], isInstant ? false : true);
31819 //            } else {
31820 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31821 //            }
31822 //            
31823 //            y = y + height + this.alternativePadWidth;
31824 //            
31825 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31826 //            
31827 //        }, this);
31828 //        
31829 //        this.el.setHeight(maxHeight);
31830 //        
31831 //    },
31832     
31833     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31834     {
31835         var pos = this.el.getBox(true);
31836         
31837         var minX = pos.x;
31838         var minY = pos.y;
31839         
31840         var maxX = pos.right;
31841         
31842         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31843         
31844         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31845         
31846         Roo.each(queue, function(box, k){
31847             
31848             Roo.each(box, function(b, kk){
31849                 
31850                 b.el.position('absolute');
31851                 
31852                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31853                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31854                 
31855                 if(b.size == 'md-left' || b.size == 'md-right'){
31856                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31857                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31858                 }
31859                 
31860                 b.el.setWidth(width);
31861                 b.el.setHeight(height);
31862                 
31863             }, this);
31864             
31865             if(!box.length){
31866                 return;
31867             }
31868             
31869             var positions = [];
31870             
31871             switch (box.length){
31872                 case 1 :
31873                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31874                     break;
31875                 case 2 :
31876                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31877                     break;
31878                 case 3 :
31879                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31880                     break;
31881                 case 4 :
31882                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31883                     break;
31884                 default :
31885                     break;
31886             }
31887             
31888             Roo.each(box, function(b,kk){
31889                 
31890                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31891                 
31892                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31893                 
31894             }, this);
31895             
31896         }, this);
31897         
31898     },
31899     
31900     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31901     {
31902         Roo.each(eItems, function(b,k){
31903             
31904             b.size = (k == 0) ? 'sm' : 'xs';
31905             b.x = (k == 0) ? 2 : 1;
31906             b.y = (k == 0) ? 2 : 1;
31907             
31908             b.el.position('absolute');
31909             
31910             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31911                 
31912             b.el.setWidth(width);
31913             
31914             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31915             
31916             b.el.setHeight(height);
31917             
31918         }, this);
31919
31920         var positions = [];
31921         
31922         positions.push({
31923             x : maxX - this.unitWidth * 2 - this.gutter,
31924             y : minY
31925         });
31926         
31927         positions.push({
31928             x : maxX - this.unitWidth,
31929             y : minY + (this.unitWidth + this.gutter) * 2
31930         });
31931         
31932         positions.push({
31933             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31934             y : minY
31935         });
31936         
31937         Roo.each(eItems, function(b,k){
31938             
31939             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31940
31941         }, this);
31942         
31943     },
31944     
31945     getVerticalOneBoxColPositions : function(x, y, box)
31946     {
31947         var pos = [];
31948         
31949         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31950         
31951         if(box[0].size == 'md-left'){
31952             rand = 0;
31953         }
31954         
31955         if(box[0].size == 'md-right'){
31956             rand = 1;
31957         }
31958         
31959         pos.push({
31960             x : x + (this.unitWidth + this.gutter) * rand,
31961             y : y
31962         });
31963         
31964         return pos;
31965     },
31966     
31967     getVerticalTwoBoxColPositions : function(x, y, box)
31968     {
31969         var pos = [];
31970         
31971         if(box[0].size == 'xs'){
31972             
31973             pos.push({
31974                 x : x,
31975                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31976             });
31977
31978             pos.push({
31979                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31980                 y : y
31981             });
31982             
31983             return pos;
31984             
31985         }
31986         
31987         pos.push({
31988             x : x,
31989             y : y
31990         });
31991
31992         pos.push({
31993             x : x + (this.unitWidth + this.gutter) * 2,
31994             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31995         });
31996         
31997         return pos;
31998         
31999     },
32000     
32001     getVerticalThreeBoxColPositions : function(x, y, box)
32002     {
32003         var pos = [];
32004         
32005         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32006             
32007             pos.push({
32008                 x : x,
32009                 y : y
32010             });
32011
32012             pos.push({
32013                 x : x + (this.unitWidth + this.gutter) * 1,
32014                 y : y
32015             });
32016             
32017             pos.push({
32018                 x : x + (this.unitWidth + this.gutter) * 2,
32019                 y : y
32020             });
32021             
32022             return pos;
32023             
32024         }
32025         
32026         if(box[0].size == 'xs' && box[1].size == 'xs'){
32027             
32028             pos.push({
32029                 x : x,
32030                 y : y
32031             });
32032
32033             pos.push({
32034                 x : x,
32035                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
32036             });
32037             
32038             pos.push({
32039                 x : x + (this.unitWidth + this.gutter) * 1,
32040                 y : y
32041             });
32042             
32043             return pos;
32044             
32045         }
32046         
32047         pos.push({
32048             x : x,
32049             y : y
32050         });
32051
32052         pos.push({
32053             x : x + (this.unitWidth + this.gutter) * 2,
32054             y : y
32055         });
32056
32057         pos.push({
32058             x : x + (this.unitWidth + this.gutter) * 2,
32059             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
32060         });
32061             
32062         return pos;
32063         
32064     },
32065     
32066     getVerticalFourBoxColPositions : function(x, y, box)
32067     {
32068         var pos = [];
32069         
32070         if(box[0].size == 'xs'){
32071             
32072             pos.push({
32073                 x : x,
32074                 y : y
32075             });
32076
32077             pos.push({
32078                 x : x,
32079                 y : y + (this.unitHeight + this.gutter) * 1
32080             });
32081             
32082             pos.push({
32083                 x : x,
32084                 y : y + (this.unitHeight + this.gutter) * 2
32085             });
32086             
32087             pos.push({
32088                 x : x + (this.unitWidth + this.gutter) * 1,
32089                 y : y
32090             });
32091             
32092             return pos;
32093             
32094         }
32095         
32096         pos.push({
32097             x : x,
32098             y : y
32099         });
32100
32101         pos.push({
32102             x : x + (this.unitWidth + this.gutter) * 2,
32103             y : y
32104         });
32105
32106         pos.push({
32107             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
32108             y : y + (this.unitHeight + this.gutter) * 1
32109         });
32110
32111         pos.push({
32112             x : x + (this.unitWidth + this.gutter) * 2,
32113             y : y + (this.unitWidth + this.gutter) * 2
32114         });
32115
32116         return pos;
32117         
32118     },
32119     
32120     getHorizontalOneBoxColPositions : function(maxX, minY, box)
32121     {
32122         var pos = [];
32123         
32124         if(box[0].size == 'md-left'){
32125             pos.push({
32126                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32127                 y : minY
32128             });
32129             
32130             return pos;
32131         }
32132         
32133         if(box[0].size == 'md-right'){
32134             pos.push({
32135                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32136                 y : minY + (this.unitWidth + this.gutter) * 1
32137             });
32138             
32139             return pos;
32140         }
32141         
32142         var rand = Math.floor(Math.random() * (4 - box[0].y));
32143         
32144         pos.push({
32145             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32146             y : minY + (this.unitWidth + this.gutter) * rand
32147         });
32148         
32149         return pos;
32150         
32151     },
32152     
32153     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
32154     {
32155         var pos = [];
32156         
32157         if(box[0].size == 'xs'){
32158             
32159             pos.push({
32160                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32161                 y : minY
32162             });
32163
32164             pos.push({
32165                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32166                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32167             });
32168             
32169             return pos;
32170             
32171         }
32172         
32173         pos.push({
32174             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32175             y : minY
32176         });
32177
32178         pos.push({
32179             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32180             y : minY + (this.unitWidth + this.gutter) * 2
32181         });
32182         
32183         return pos;
32184         
32185     },
32186     
32187     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32188     {
32189         var pos = [];
32190         
32191         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32192             
32193             pos.push({
32194                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32195                 y : minY
32196             });
32197
32198             pos.push({
32199                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32200                 y : minY + (this.unitWidth + this.gutter) * 1
32201             });
32202             
32203             pos.push({
32204                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32205                 y : minY + (this.unitWidth + this.gutter) * 2
32206             });
32207             
32208             return pos;
32209             
32210         }
32211         
32212         if(box[0].size == 'xs' && box[1].size == 'xs'){
32213             
32214             pos.push({
32215                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32216                 y : minY
32217             });
32218
32219             pos.push({
32220                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32221                 y : minY
32222             });
32223             
32224             pos.push({
32225                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32226                 y : minY + (this.unitWidth + this.gutter) * 1
32227             });
32228             
32229             return pos;
32230             
32231         }
32232         
32233         pos.push({
32234             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32235             y : minY
32236         });
32237
32238         pos.push({
32239             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32240             y : minY + (this.unitWidth + this.gutter) * 2
32241         });
32242
32243         pos.push({
32244             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32245             y : minY + (this.unitWidth + this.gutter) * 2
32246         });
32247             
32248         return pos;
32249         
32250     },
32251     
32252     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32253     {
32254         var pos = [];
32255         
32256         if(box[0].size == 'xs'){
32257             
32258             pos.push({
32259                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32260                 y : minY
32261             });
32262
32263             pos.push({
32264                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32265                 y : minY
32266             });
32267             
32268             pos.push({
32269                 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),
32270                 y : minY
32271             });
32272             
32273             pos.push({
32274                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32275                 y : minY + (this.unitWidth + this.gutter) * 1
32276             });
32277             
32278             return pos;
32279             
32280         }
32281         
32282         pos.push({
32283             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32284             y : minY
32285         });
32286         
32287         pos.push({
32288             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32289             y : minY + (this.unitWidth + this.gutter) * 2
32290         });
32291         
32292         pos.push({
32293             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32294             y : minY + (this.unitWidth + this.gutter) * 2
32295         });
32296         
32297         pos.push({
32298             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),
32299             y : minY + (this.unitWidth + this.gutter) * 2
32300         });
32301
32302         return pos;
32303         
32304     },
32305     
32306     /**
32307     * remove a Masonry Brick
32308     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32309     */
32310     removeBrick : function(brick_id)
32311     {
32312         if (!brick_id) {
32313             return;
32314         }
32315         
32316         for (var i = 0; i<this.bricks.length; i++) {
32317             if (this.bricks[i].id == brick_id) {
32318                 this.bricks.splice(i,1);
32319                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32320                 this.initial();
32321             }
32322         }
32323     },
32324     
32325     /**
32326     * adds a Masonry Brick
32327     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32328     */
32329     addBrick : function(cfg)
32330     {
32331         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32332         //this.register(cn);
32333         cn.parentId = this.id;
32334         cn.render(this.el);
32335         return cn;
32336     },
32337     
32338     /**
32339     * register a Masonry Brick
32340     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32341     */
32342     
32343     register : function(brick)
32344     {
32345         this.bricks.push(brick);
32346         brick.masonryId = this.id;
32347     },
32348     
32349     /**
32350     * clear all the Masonry Brick
32351     */
32352     clearAll : function()
32353     {
32354         this.bricks = [];
32355         //this.getChildContainer().dom.innerHTML = "";
32356         this.el.dom.innerHTML = '';
32357     },
32358     
32359     getSelected : function()
32360     {
32361         if (!this.selectedBrick) {
32362             return false;
32363         }
32364         
32365         return this.selectedBrick;
32366     }
32367 });
32368
32369 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32370     
32371     groups: {},
32372      /**
32373     * register a Masonry Layout
32374     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32375     */
32376     
32377     register : function(layout)
32378     {
32379         this.groups[layout.id] = layout;
32380     },
32381     /**
32382     * fetch a  Masonry Layout based on the masonry layout ID
32383     * @param {string} the masonry layout to add
32384     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32385     */
32386     
32387     get: function(layout_id) {
32388         if (typeof(this.groups[layout_id]) == 'undefined') {
32389             return false;
32390         }
32391         return this.groups[layout_id] ;
32392     }
32393     
32394     
32395     
32396 });
32397
32398  
32399
32400  /**
32401  *
32402  * This is based on 
32403  * http://masonry.desandro.com
32404  *
32405  * The idea is to render all the bricks based on vertical width...
32406  *
32407  * The original code extends 'outlayer' - we might need to use that....
32408  * 
32409  */
32410
32411
32412 /**
32413  * @class Roo.bootstrap.LayoutMasonryAuto
32414  * @extends Roo.bootstrap.Component
32415  * Bootstrap Layout Masonry class
32416  * 
32417  * @constructor
32418  * Create a new Element
32419  * @param {Object} config The config object
32420  */
32421
32422 Roo.bootstrap.LayoutMasonryAuto = function(config){
32423     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32424 };
32425
32426 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32427     
32428       /**
32429      * @cfg {Boolean} isFitWidth  - resize the width..
32430      */   
32431     isFitWidth : false,  // options..
32432     /**
32433      * @cfg {Boolean} isOriginLeft = left align?
32434      */   
32435     isOriginLeft : true,
32436     /**
32437      * @cfg {Boolean} isOriginTop = top align?
32438      */   
32439     isOriginTop : false,
32440     /**
32441      * @cfg {Boolean} isLayoutInstant = no animation?
32442      */   
32443     isLayoutInstant : false, // needed?
32444     /**
32445      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32446      */   
32447     isResizingContainer : true,
32448     /**
32449      * @cfg {Number} columnWidth  width of the columns 
32450      */   
32451     
32452     columnWidth : 0,
32453     
32454     /**
32455      * @cfg {Number} maxCols maximum number of columns
32456      */   
32457     
32458     maxCols: 0,
32459     /**
32460      * @cfg {Number} padHeight padding below box..
32461      */   
32462     
32463     padHeight : 10, 
32464     
32465     /**
32466      * @cfg {Boolean} isAutoInitial defalut true
32467      */   
32468     
32469     isAutoInitial : true, 
32470     
32471     // private?
32472     gutter : 0,
32473     
32474     containerWidth: 0,
32475     initialColumnWidth : 0,
32476     currentSize : null,
32477     
32478     colYs : null, // array.
32479     maxY : 0,
32480     padWidth: 10,
32481     
32482     
32483     tag: 'div',
32484     cls: '',
32485     bricks: null, //CompositeElement
32486     cols : 0, // array?
32487     // element : null, // wrapped now this.el
32488     _isLayoutInited : null, 
32489     
32490     
32491     getAutoCreate : function(){
32492         
32493         var cfg = {
32494             tag: this.tag,
32495             cls: 'blog-masonary-wrapper ' + this.cls,
32496             cn : {
32497                 cls : 'mas-boxes masonary'
32498             }
32499         };
32500         
32501         return cfg;
32502     },
32503     
32504     getChildContainer: function( )
32505     {
32506         if (this.boxesEl) {
32507             return this.boxesEl;
32508         }
32509         
32510         this.boxesEl = this.el.select('.mas-boxes').first();
32511         
32512         return this.boxesEl;
32513     },
32514     
32515     
32516     initEvents : function()
32517     {
32518         var _this = this;
32519         
32520         if(this.isAutoInitial){
32521             Roo.log('hook children rendered');
32522             this.on('childrenrendered', function() {
32523                 Roo.log('children rendered');
32524                 _this.initial();
32525             } ,this);
32526         }
32527         
32528     },
32529     
32530     initial : function()
32531     {
32532         this.reloadItems();
32533
32534         this.currentSize = this.el.getBox(true);
32535
32536         /// was window resize... - let's see if this works..
32537         Roo.EventManager.onWindowResize(this.resize, this); 
32538
32539         if(!this.isAutoInitial){
32540             this.layout();
32541             return;
32542         }
32543         
32544         this.layout.defer(500,this);
32545     },
32546     
32547     reloadItems: function()
32548     {
32549         this.bricks = this.el.select('.masonry-brick', true);
32550         
32551         this.bricks.each(function(b) {
32552             //Roo.log(b.getSize());
32553             if (!b.attr('originalwidth')) {
32554                 b.attr('originalwidth',  b.getSize().width);
32555             }
32556             
32557         });
32558         
32559         Roo.log(this.bricks.elements.length);
32560     },
32561     
32562     resize : function()
32563     {
32564         Roo.log('resize');
32565         var cs = this.el.getBox(true);
32566         
32567         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32568             Roo.log("no change in with or X");
32569             return;
32570         }
32571         this.currentSize = cs;
32572         this.layout();
32573     },
32574     
32575     layout : function()
32576     {
32577          Roo.log('layout');
32578         this._resetLayout();
32579         //this._manageStamps();
32580       
32581         // don't animate first layout
32582         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32583         this.layoutItems( isInstant );
32584       
32585         // flag for initalized
32586         this._isLayoutInited = true;
32587     },
32588     
32589     layoutItems : function( isInstant )
32590     {
32591         //var items = this._getItemsForLayout( this.items );
32592         // original code supports filtering layout items.. we just ignore it..
32593         
32594         this._layoutItems( this.bricks , isInstant );
32595       
32596         this._postLayout();
32597     },
32598     _layoutItems : function ( items , isInstant)
32599     {
32600        //this.fireEvent( 'layout', this, items );
32601     
32602
32603         if ( !items || !items.elements.length ) {
32604           // no items, emit event with empty array
32605             return;
32606         }
32607
32608         var queue = [];
32609         items.each(function(item) {
32610             Roo.log("layout item");
32611             Roo.log(item);
32612             // get x/y object from method
32613             var position = this._getItemLayoutPosition( item );
32614             // enqueue
32615             position.item = item;
32616             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32617             queue.push( position );
32618         }, this);
32619       
32620         this._processLayoutQueue( queue );
32621     },
32622     /** Sets position of item in DOM
32623     * @param {Element} item
32624     * @param {Number} x - horizontal position
32625     * @param {Number} y - vertical position
32626     * @param {Boolean} isInstant - disables transitions
32627     */
32628     _processLayoutQueue : function( queue )
32629     {
32630         for ( var i=0, len = queue.length; i < len; i++ ) {
32631             var obj = queue[i];
32632             obj.item.position('absolute');
32633             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32634         }
32635     },
32636       
32637     
32638     /**
32639     * Any logic you want to do after each layout,
32640     * i.e. size the container
32641     */
32642     _postLayout : function()
32643     {
32644         this.resizeContainer();
32645     },
32646     
32647     resizeContainer : function()
32648     {
32649         if ( !this.isResizingContainer ) {
32650             return;
32651         }
32652         var size = this._getContainerSize();
32653         if ( size ) {
32654             this.el.setSize(size.width,size.height);
32655             this.boxesEl.setSize(size.width,size.height);
32656         }
32657     },
32658     
32659     
32660     
32661     _resetLayout : function()
32662     {
32663         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32664         this.colWidth = this.el.getWidth();
32665         //this.gutter = this.el.getWidth(); 
32666         
32667         this.measureColumns();
32668
32669         // reset column Y
32670         var i = this.cols;
32671         this.colYs = [];
32672         while (i--) {
32673             this.colYs.push( 0 );
32674         }
32675     
32676         this.maxY = 0;
32677     },
32678
32679     measureColumns : function()
32680     {
32681         this.getContainerWidth();
32682       // if columnWidth is 0, default to outerWidth of first item
32683         if ( !this.columnWidth ) {
32684             var firstItem = this.bricks.first();
32685             Roo.log(firstItem);
32686             this.columnWidth  = this.containerWidth;
32687             if (firstItem && firstItem.attr('originalwidth') ) {
32688                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32689             }
32690             // columnWidth fall back to item of first element
32691             Roo.log("set column width?");
32692                         this.initialColumnWidth = this.columnWidth  ;
32693
32694             // if first elem has no width, default to size of container
32695             
32696         }
32697         
32698         
32699         if (this.initialColumnWidth) {
32700             this.columnWidth = this.initialColumnWidth;
32701         }
32702         
32703         
32704             
32705         // column width is fixed at the top - however if container width get's smaller we should
32706         // reduce it...
32707         
32708         // this bit calcs how man columns..
32709             
32710         var columnWidth = this.columnWidth += this.gutter;
32711       
32712         // calculate columns
32713         var containerWidth = this.containerWidth + this.gutter;
32714         
32715         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32716         // fix rounding errors, typically with gutters
32717         var excess = columnWidth - containerWidth % columnWidth;
32718         
32719         
32720         // if overshoot is less than a pixel, round up, otherwise floor it
32721         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32722         cols = Math[ mathMethod ]( cols );
32723         this.cols = Math.max( cols, 1 );
32724         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32725         
32726          // padding positioning..
32727         var totalColWidth = this.cols * this.columnWidth;
32728         var padavail = this.containerWidth - totalColWidth;
32729         // so for 2 columns - we need 3 'pads'
32730         
32731         var padNeeded = (1+this.cols) * this.padWidth;
32732         
32733         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32734         
32735         this.columnWidth += padExtra
32736         //this.padWidth = Math.floor(padavail /  ( this.cols));
32737         
32738         // adjust colum width so that padding is fixed??
32739         
32740         // we have 3 columns ... total = width * 3
32741         // we have X left over... that should be used by 
32742         
32743         //if (this.expandC) {
32744             
32745         //}
32746         
32747         
32748         
32749     },
32750     
32751     getContainerWidth : function()
32752     {
32753        /* // container is parent if fit width
32754         var container = this.isFitWidth ? this.element.parentNode : this.element;
32755         // check that this.size and size are there
32756         // IE8 triggers resize on body size change, so they might not be
32757         
32758         var size = getSize( container );  //FIXME
32759         this.containerWidth = size && size.innerWidth; //FIXME
32760         */
32761          
32762         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32763         
32764     },
32765     
32766     _getItemLayoutPosition : function( item )  // what is item?
32767     {
32768         // we resize the item to our columnWidth..
32769       
32770         item.setWidth(this.columnWidth);
32771         item.autoBoxAdjust  = false;
32772         
32773         var sz = item.getSize();
32774  
32775         // how many columns does this brick span
32776         var remainder = this.containerWidth % this.columnWidth;
32777         
32778         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32779         // round if off by 1 pixel, otherwise use ceil
32780         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32781         colSpan = Math.min( colSpan, this.cols );
32782         
32783         // normally this should be '1' as we dont' currently allow multi width columns..
32784         
32785         var colGroup = this._getColGroup( colSpan );
32786         // get the minimum Y value from the columns
32787         var minimumY = Math.min.apply( Math, colGroup );
32788         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32789         
32790         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32791          
32792         // position the brick
32793         var position = {
32794             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32795             y: this.currentSize.y + minimumY + this.padHeight
32796         };
32797         
32798         Roo.log(position);
32799         // apply setHeight to necessary columns
32800         var setHeight = minimumY + sz.height + this.padHeight;
32801         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32802         
32803         var setSpan = this.cols + 1 - colGroup.length;
32804         for ( var i = 0; i < setSpan; i++ ) {
32805           this.colYs[ shortColIndex + i ] = setHeight ;
32806         }
32807       
32808         return position;
32809     },
32810     
32811     /**
32812      * @param {Number} colSpan - number of columns the element spans
32813      * @returns {Array} colGroup
32814      */
32815     _getColGroup : function( colSpan )
32816     {
32817         if ( colSpan < 2 ) {
32818           // if brick spans only one column, use all the column Ys
32819           return this.colYs;
32820         }
32821       
32822         var colGroup = [];
32823         // how many different places could this brick fit horizontally
32824         var groupCount = this.cols + 1 - colSpan;
32825         // for each group potential horizontal position
32826         for ( var i = 0; i < groupCount; i++ ) {
32827           // make an array of colY values for that one group
32828           var groupColYs = this.colYs.slice( i, i + colSpan );
32829           // and get the max value of the array
32830           colGroup[i] = Math.max.apply( Math, groupColYs );
32831         }
32832         return colGroup;
32833     },
32834     /*
32835     _manageStamp : function( stamp )
32836     {
32837         var stampSize =  stamp.getSize();
32838         var offset = stamp.getBox();
32839         // get the columns that this stamp affects
32840         var firstX = this.isOriginLeft ? offset.x : offset.right;
32841         var lastX = firstX + stampSize.width;
32842         var firstCol = Math.floor( firstX / this.columnWidth );
32843         firstCol = Math.max( 0, firstCol );
32844         
32845         var lastCol = Math.floor( lastX / this.columnWidth );
32846         // lastCol should not go over if multiple of columnWidth #425
32847         lastCol -= lastX % this.columnWidth ? 0 : 1;
32848         lastCol = Math.min( this.cols - 1, lastCol );
32849         
32850         // set colYs to bottom of the stamp
32851         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32852             stampSize.height;
32853             
32854         for ( var i = firstCol; i <= lastCol; i++ ) {
32855           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32856         }
32857     },
32858     */
32859     
32860     _getContainerSize : function()
32861     {
32862         this.maxY = Math.max.apply( Math, this.colYs );
32863         var size = {
32864             height: this.maxY
32865         };
32866       
32867         if ( this.isFitWidth ) {
32868             size.width = this._getContainerFitWidth();
32869         }
32870       
32871         return size;
32872     },
32873     
32874     _getContainerFitWidth : function()
32875     {
32876         var unusedCols = 0;
32877         // count unused columns
32878         var i = this.cols;
32879         while ( --i ) {
32880           if ( this.colYs[i] !== 0 ) {
32881             break;
32882           }
32883           unusedCols++;
32884         }
32885         // fit container to columns that have been used
32886         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32887     },
32888     
32889     needsResizeLayout : function()
32890     {
32891         var previousWidth = this.containerWidth;
32892         this.getContainerWidth();
32893         return previousWidth !== this.containerWidth;
32894     }
32895  
32896 });
32897
32898  
32899
32900  /*
32901  * - LGPL
32902  *
32903  * element
32904  * 
32905  */
32906
32907 /**
32908  * @class Roo.bootstrap.MasonryBrick
32909  * @extends Roo.bootstrap.Component
32910  * Bootstrap MasonryBrick class
32911  * 
32912  * @constructor
32913  * Create a new MasonryBrick
32914  * @param {Object} config The config object
32915  */
32916
32917 Roo.bootstrap.MasonryBrick = function(config){
32918     
32919     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32920     
32921     Roo.bootstrap.MasonryBrick.register(this);
32922     
32923     this.addEvents({
32924         // raw events
32925         /**
32926          * @event click
32927          * When a MasonryBrick is clcik
32928          * @param {Roo.bootstrap.MasonryBrick} this
32929          * @param {Roo.EventObject} e
32930          */
32931         "click" : true
32932     });
32933 };
32934
32935 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32936     
32937     /**
32938      * @cfg {String} title
32939      */   
32940     title : '',
32941     /**
32942      * @cfg {String} html
32943      */   
32944     html : '',
32945     /**
32946      * @cfg {String} bgimage
32947      */   
32948     bgimage : '',
32949     /**
32950      * @cfg {String} videourl
32951      */   
32952     videourl : '',
32953     /**
32954      * @cfg {String} cls
32955      */   
32956     cls : '',
32957     /**
32958      * @cfg {String} href
32959      */   
32960     href : '',
32961     /**
32962      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32963      */   
32964     size : 'xs',
32965     
32966     /**
32967      * @cfg {String} placetitle (center|bottom)
32968      */   
32969     placetitle : '',
32970     
32971     /**
32972      * @cfg {Boolean} isFitContainer defalut true
32973      */   
32974     isFitContainer : true, 
32975     
32976     /**
32977      * @cfg {Boolean} preventDefault defalut false
32978      */   
32979     preventDefault : false, 
32980     
32981     /**
32982      * @cfg {Boolean} inverse defalut false
32983      */   
32984     maskInverse : false, 
32985     
32986     getAutoCreate : function()
32987     {
32988         if(!this.isFitContainer){
32989             return this.getSplitAutoCreate();
32990         }
32991         
32992         var cls = 'masonry-brick masonry-brick-full';
32993         
32994         if(this.href.length){
32995             cls += ' masonry-brick-link';
32996         }
32997         
32998         if(this.bgimage.length){
32999             cls += ' masonry-brick-image';
33000         }
33001         
33002         if(this.maskInverse){
33003             cls += ' mask-inverse';
33004         }
33005         
33006         if(!this.html.length && !this.maskInverse && !this.videourl.length){
33007             cls += ' enable-mask';
33008         }
33009         
33010         if(this.size){
33011             cls += ' masonry-' + this.size + '-brick';
33012         }
33013         
33014         if(this.placetitle.length){
33015             
33016             switch (this.placetitle) {
33017                 case 'center' :
33018                     cls += ' masonry-center-title';
33019                     break;
33020                 case 'bottom' :
33021                     cls += ' masonry-bottom-title';
33022                     break;
33023                 default:
33024                     break;
33025             }
33026             
33027         } else {
33028             if(!this.html.length && !this.bgimage.length){
33029                 cls += ' masonry-center-title';
33030             }
33031
33032             if(!this.html.length && this.bgimage.length){
33033                 cls += ' masonry-bottom-title';
33034             }
33035         }
33036         
33037         if(this.cls){
33038             cls += ' ' + this.cls;
33039         }
33040         
33041         var cfg = {
33042             tag: (this.href.length) ? 'a' : 'div',
33043             cls: cls,
33044             cn: [
33045                 {
33046                     tag: 'div',
33047                     cls: 'masonry-brick-mask'
33048                 },
33049                 {
33050                     tag: 'div',
33051                     cls: 'masonry-brick-paragraph',
33052                     cn: []
33053                 }
33054             ]
33055         };
33056         
33057         if(this.href.length){
33058             cfg.href = this.href;
33059         }
33060         
33061         var cn = cfg.cn[1].cn;
33062         
33063         if(this.title.length){
33064             cn.push({
33065                 tag: 'h4',
33066                 cls: 'masonry-brick-title',
33067                 html: this.title
33068             });
33069         }
33070         
33071         if(this.html.length){
33072             cn.push({
33073                 tag: 'p',
33074                 cls: 'masonry-brick-text',
33075                 html: this.html
33076             });
33077         }
33078         
33079         if (!this.title.length && !this.html.length) {
33080             cfg.cn[1].cls += ' hide';
33081         }
33082         
33083         if(this.bgimage.length){
33084             cfg.cn.push({
33085                 tag: 'img',
33086                 cls: 'masonry-brick-image-view',
33087                 src: this.bgimage
33088             });
33089         }
33090         
33091         if(this.videourl.length){
33092             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33093             // youtube support only?
33094             cfg.cn.push({
33095                 tag: 'iframe',
33096                 cls: 'masonry-brick-image-view',
33097                 src: vurl,
33098                 frameborder : 0,
33099                 allowfullscreen : true
33100             });
33101         }
33102         
33103         return cfg;
33104         
33105     },
33106     
33107     getSplitAutoCreate : function()
33108     {
33109         var cls = 'masonry-brick masonry-brick-split';
33110         
33111         if(this.href.length){
33112             cls += ' masonry-brick-link';
33113         }
33114         
33115         if(this.bgimage.length){
33116             cls += ' masonry-brick-image';
33117         }
33118         
33119         if(this.size){
33120             cls += ' masonry-' + this.size + '-brick';
33121         }
33122         
33123         switch (this.placetitle) {
33124             case 'center' :
33125                 cls += ' masonry-center-title';
33126                 break;
33127             case 'bottom' :
33128                 cls += ' masonry-bottom-title';
33129                 break;
33130             default:
33131                 if(!this.bgimage.length){
33132                     cls += ' masonry-center-title';
33133                 }
33134
33135                 if(this.bgimage.length){
33136                     cls += ' masonry-bottom-title';
33137                 }
33138                 break;
33139         }
33140         
33141         if(this.cls){
33142             cls += ' ' + this.cls;
33143         }
33144         
33145         var cfg = {
33146             tag: (this.href.length) ? 'a' : 'div',
33147             cls: cls,
33148             cn: [
33149                 {
33150                     tag: 'div',
33151                     cls: 'masonry-brick-split-head',
33152                     cn: [
33153                         {
33154                             tag: 'div',
33155                             cls: 'masonry-brick-paragraph',
33156                             cn: []
33157                         }
33158                     ]
33159                 },
33160                 {
33161                     tag: 'div',
33162                     cls: 'masonry-brick-split-body',
33163                     cn: []
33164                 }
33165             ]
33166         };
33167         
33168         if(this.href.length){
33169             cfg.href = this.href;
33170         }
33171         
33172         if(this.title.length){
33173             cfg.cn[0].cn[0].cn.push({
33174                 tag: 'h4',
33175                 cls: 'masonry-brick-title',
33176                 html: this.title
33177             });
33178         }
33179         
33180         if(this.html.length){
33181             cfg.cn[1].cn.push({
33182                 tag: 'p',
33183                 cls: 'masonry-brick-text',
33184                 html: this.html
33185             });
33186         }
33187
33188         if(this.bgimage.length){
33189             cfg.cn[0].cn.push({
33190                 tag: 'img',
33191                 cls: 'masonry-brick-image-view',
33192                 src: this.bgimage
33193             });
33194         }
33195         
33196         if(this.videourl.length){
33197             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33198             // youtube support only?
33199             cfg.cn[0].cn.cn.push({
33200                 tag: 'iframe',
33201                 cls: 'masonry-brick-image-view',
33202                 src: vurl,
33203                 frameborder : 0,
33204                 allowfullscreen : true
33205             });
33206         }
33207         
33208         return cfg;
33209     },
33210     
33211     initEvents: function() 
33212     {
33213         switch (this.size) {
33214             case 'xs' :
33215                 this.x = 1;
33216                 this.y = 1;
33217                 break;
33218             case 'sm' :
33219                 this.x = 2;
33220                 this.y = 2;
33221                 break;
33222             case 'md' :
33223             case 'md-left' :
33224             case 'md-right' :
33225                 this.x = 3;
33226                 this.y = 3;
33227                 break;
33228             case 'tall' :
33229                 this.x = 2;
33230                 this.y = 3;
33231                 break;
33232             case 'wide' :
33233                 this.x = 3;
33234                 this.y = 2;
33235                 break;
33236             case 'wide-thin' :
33237                 this.x = 3;
33238                 this.y = 1;
33239                 break;
33240                         
33241             default :
33242                 break;
33243         }
33244         
33245         if(Roo.isTouch){
33246             this.el.on('touchstart', this.onTouchStart, this);
33247             this.el.on('touchmove', this.onTouchMove, this);
33248             this.el.on('touchend', this.onTouchEnd, this);
33249             this.el.on('contextmenu', this.onContextMenu, this);
33250         } else {
33251             this.el.on('mouseenter'  ,this.enter, this);
33252             this.el.on('mouseleave', this.leave, this);
33253             this.el.on('click', this.onClick, this);
33254         }
33255         
33256         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33257             this.parent().bricks.push(this);   
33258         }
33259         
33260     },
33261     
33262     onClick: function(e, el)
33263     {
33264         var time = this.endTimer - this.startTimer;
33265         // Roo.log(e.preventDefault());
33266         if(Roo.isTouch){
33267             if(time > 1000){
33268                 e.preventDefault();
33269                 return;
33270             }
33271         }
33272         
33273         if(!this.preventDefault){
33274             return;
33275         }
33276         
33277         e.preventDefault();
33278         
33279         if (this.activeClass != '') {
33280             this.selectBrick();
33281         }
33282         
33283         this.fireEvent('click', this, e);
33284     },
33285     
33286     enter: function(e, el)
33287     {
33288         e.preventDefault();
33289         
33290         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33291             return;
33292         }
33293         
33294         if(this.bgimage.length && this.html.length){
33295             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33296         }
33297     },
33298     
33299     leave: function(e, el)
33300     {
33301         e.preventDefault();
33302         
33303         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33304             return;
33305         }
33306         
33307         if(this.bgimage.length && this.html.length){
33308             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33309         }
33310     },
33311     
33312     onTouchStart: function(e, el)
33313     {
33314 //        e.preventDefault();
33315         
33316         this.touchmoved = false;
33317         
33318         if(!this.isFitContainer){
33319             return;
33320         }
33321         
33322         if(!this.bgimage.length || !this.html.length){
33323             return;
33324         }
33325         
33326         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33327         
33328         this.timer = new Date().getTime();
33329         
33330     },
33331     
33332     onTouchMove: function(e, el)
33333     {
33334         this.touchmoved = true;
33335     },
33336     
33337     onContextMenu : function(e,el)
33338     {
33339         e.preventDefault();
33340         e.stopPropagation();
33341         return false;
33342     },
33343     
33344     onTouchEnd: function(e, el)
33345     {
33346 //        e.preventDefault();
33347         
33348         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33349         
33350             this.leave(e,el);
33351             
33352             return;
33353         }
33354         
33355         if(!this.bgimage.length || !this.html.length){
33356             
33357             if(this.href.length){
33358                 window.location.href = this.href;
33359             }
33360             
33361             return;
33362         }
33363         
33364         if(!this.isFitContainer){
33365             return;
33366         }
33367         
33368         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33369         
33370         window.location.href = this.href;
33371     },
33372     
33373     //selection on single brick only
33374     selectBrick : function() {
33375         
33376         if (!this.parentId) {
33377             return;
33378         }
33379         
33380         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33381         var index = m.selectedBrick.indexOf(this.id);
33382         
33383         if ( index > -1) {
33384             m.selectedBrick.splice(index,1);
33385             this.el.removeClass(this.activeClass);
33386             return;
33387         }
33388         
33389         for(var i = 0; i < m.selectedBrick.length; i++) {
33390             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33391             b.el.removeClass(b.activeClass);
33392         }
33393         
33394         m.selectedBrick = [];
33395         
33396         m.selectedBrick.push(this.id);
33397         this.el.addClass(this.activeClass);
33398         return;
33399     },
33400     
33401     isSelected : function(){
33402         return this.el.hasClass(this.activeClass);
33403         
33404     }
33405 });
33406
33407 Roo.apply(Roo.bootstrap.MasonryBrick, {
33408     
33409     //groups: {},
33410     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33411      /**
33412     * register a Masonry Brick
33413     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33414     */
33415     
33416     register : function(brick)
33417     {
33418         //this.groups[brick.id] = brick;
33419         this.groups.add(brick.id, brick);
33420     },
33421     /**
33422     * fetch a  masonry brick based on the masonry brick ID
33423     * @param {string} the masonry brick to add
33424     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33425     */
33426     
33427     get: function(brick_id) 
33428     {
33429         // if (typeof(this.groups[brick_id]) == 'undefined') {
33430         //     return false;
33431         // }
33432         // return this.groups[brick_id] ;
33433         
33434         if(this.groups.key(brick_id)) {
33435             return this.groups.key(brick_id);
33436         }
33437         
33438         return false;
33439     }
33440     
33441     
33442     
33443 });
33444
33445  /*
33446  * - LGPL
33447  *
33448  * element
33449  * 
33450  */
33451
33452 /**
33453  * @class Roo.bootstrap.Brick
33454  * @extends Roo.bootstrap.Component
33455  * Bootstrap Brick class
33456  * 
33457  * @constructor
33458  * Create a new Brick
33459  * @param {Object} config The config object
33460  */
33461
33462 Roo.bootstrap.Brick = function(config){
33463     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33464     
33465     this.addEvents({
33466         // raw events
33467         /**
33468          * @event click
33469          * When a Brick is click
33470          * @param {Roo.bootstrap.Brick} this
33471          * @param {Roo.EventObject} e
33472          */
33473         "click" : true
33474     });
33475 };
33476
33477 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33478     
33479     /**
33480      * @cfg {String} title
33481      */   
33482     title : '',
33483     /**
33484      * @cfg {String} html
33485      */   
33486     html : '',
33487     /**
33488      * @cfg {String} bgimage
33489      */   
33490     bgimage : '',
33491     /**
33492      * @cfg {String} cls
33493      */   
33494     cls : '',
33495     /**
33496      * @cfg {String} href
33497      */   
33498     href : '',
33499     /**
33500      * @cfg {String} video
33501      */   
33502     video : '',
33503     /**
33504      * @cfg {Boolean} square
33505      */   
33506     square : true,
33507     
33508     getAutoCreate : function()
33509     {
33510         var cls = 'roo-brick';
33511         
33512         if(this.href.length){
33513             cls += ' roo-brick-link';
33514         }
33515         
33516         if(this.bgimage.length){
33517             cls += ' roo-brick-image';
33518         }
33519         
33520         if(!this.html.length && !this.bgimage.length){
33521             cls += ' roo-brick-center-title';
33522         }
33523         
33524         if(!this.html.length && this.bgimage.length){
33525             cls += ' roo-brick-bottom-title';
33526         }
33527         
33528         if(this.cls){
33529             cls += ' ' + this.cls;
33530         }
33531         
33532         var cfg = {
33533             tag: (this.href.length) ? 'a' : 'div',
33534             cls: cls,
33535             cn: [
33536                 {
33537                     tag: 'div',
33538                     cls: 'roo-brick-paragraph',
33539                     cn: []
33540                 }
33541             ]
33542         };
33543         
33544         if(this.href.length){
33545             cfg.href = this.href;
33546         }
33547         
33548         var cn = cfg.cn[0].cn;
33549         
33550         if(this.title.length){
33551             cn.push({
33552                 tag: 'h4',
33553                 cls: 'roo-brick-title',
33554                 html: this.title
33555             });
33556         }
33557         
33558         if(this.html.length){
33559             cn.push({
33560                 tag: 'p',
33561                 cls: 'roo-brick-text',
33562                 html: this.html
33563             });
33564         } else {
33565             cn.cls += ' hide';
33566         }
33567         
33568         if(this.bgimage.length){
33569             cfg.cn.push({
33570                 tag: 'img',
33571                 cls: 'roo-brick-image-view',
33572                 src: this.bgimage
33573             });
33574         }
33575         
33576         return cfg;
33577     },
33578     
33579     initEvents: function() 
33580     {
33581         if(this.title.length || this.html.length){
33582             this.el.on('mouseenter'  ,this.enter, this);
33583             this.el.on('mouseleave', this.leave, this);
33584         }
33585         
33586         Roo.EventManager.onWindowResize(this.resize, this); 
33587         
33588         if(this.bgimage.length){
33589             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33590             this.imageEl.on('load', this.onImageLoad, this);
33591             return;
33592         }
33593         
33594         this.resize();
33595     },
33596     
33597     onImageLoad : function()
33598     {
33599         this.resize();
33600     },
33601     
33602     resize : function()
33603     {
33604         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33605         
33606         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33607         
33608         if(this.bgimage.length){
33609             var image = this.el.select('.roo-brick-image-view', true).first();
33610             
33611             image.setWidth(paragraph.getWidth());
33612             
33613             if(this.square){
33614                 image.setHeight(paragraph.getWidth());
33615             }
33616             
33617             this.el.setHeight(image.getHeight());
33618             paragraph.setHeight(image.getHeight());
33619             
33620         }
33621         
33622     },
33623     
33624     enter: function(e, el)
33625     {
33626         e.preventDefault();
33627         
33628         if(this.bgimage.length){
33629             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33630             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33631         }
33632     },
33633     
33634     leave: function(e, el)
33635     {
33636         e.preventDefault();
33637         
33638         if(this.bgimage.length){
33639             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33640             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33641         }
33642     }
33643     
33644 });
33645
33646  
33647
33648  /*
33649  * - LGPL
33650  *
33651  * Number field 
33652  */
33653
33654 /**
33655  * @class Roo.bootstrap.NumberField
33656  * @extends Roo.bootstrap.Input
33657  * Bootstrap NumberField class
33658  * 
33659  * 
33660  * 
33661  * 
33662  * @constructor
33663  * Create a new NumberField
33664  * @param {Object} config The config object
33665  */
33666
33667 Roo.bootstrap.NumberField = function(config){
33668     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33669 };
33670
33671 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33672     
33673     /**
33674      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33675      */
33676     allowDecimals : true,
33677     /**
33678      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33679      */
33680     decimalSeparator : ".",
33681     /**
33682      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33683      */
33684     decimalPrecision : 2,
33685     /**
33686      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33687      */
33688     allowNegative : true,
33689     
33690     /**
33691      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33692      */
33693     allowZero: true,
33694     /**
33695      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33696      */
33697     minValue : Number.NEGATIVE_INFINITY,
33698     /**
33699      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33700      */
33701     maxValue : Number.MAX_VALUE,
33702     /**
33703      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33704      */
33705     minText : "The minimum value for this field is {0}",
33706     /**
33707      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33708      */
33709     maxText : "The maximum value for this field is {0}",
33710     /**
33711      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33712      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33713      */
33714     nanText : "{0} is not a valid number",
33715     /**
33716      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33717      */
33718     thousandsDelimiter : false,
33719     /**
33720      * @cfg {String} valueAlign alignment of value
33721      */
33722     valueAlign : "left",
33723
33724     getAutoCreate : function()
33725     {
33726         var hiddenInput = {
33727             tag: 'input',
33728             type: 'hidden',
33729             id: Roo.id(),
33730             cls: 'hidden-number-input'
33731         };
33732         
33733         if (this.name) {
33734             hiddenInput.name = this.name;
33735         }
33736         
33737         this.name = '';
33738         
33739         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33740         
33741         this.name = hiddenInput.name;
33742         
33743         if(cfg.cn.length > 0) {
33744             cfg.cn.push(hiddenInput);
33745         }
33746         
33747         return cfg;
33748     },
33749
33750     // private
33751     initEvents : function()
33752     {   
33753         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33754         
33755         var allowed = "0123456789";
33756         
33757         if(this.allowDecimals){
33758             allowed += this.decimalSeparator;
33759         }
33760         
33761         if(this.allowNegative){
33762             allowed += "-";
33763         }
33764         
33765         if(this.thousandsDelimiter) {
33766             allowed += ",";
33767         }
33768         
33769         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33770         
33771         var keyPress = function(e){
33772             
33773             var k = e.getKey();
33774             
33775             var c = e.getCharCode();
33776             
33777             if(
33778                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33779                     allowed.indexOf(String.fromCharCode(c)) === -1
33780             ){
33781                 e.stopEvent();
33782                 return;
33783             }
33784             
33785             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33786                 return;
33787             }
33788             
33789             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33790                 e.stopEvent();
33791             }
33792         };
33793         
33794         this.el.on("keypress", keyPress, this);
33795     },
33796     
33797     validateValue : function(value)
33798     {
33799         
33800         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33801             return false;
33802         }
33803         
33804         var num = this.parseValue(value);
33805         
33806         if(isNaN(num)){
33807             this.markInvalid(String.format(this.nanText, value));
33808             return false;
33809         }
33810         
33811         if(num < this.minValue){
33812             this.markInvalid(String.format(this.minText, this.minValue));
33813             return false;
33814         }
33815         
33816         if(num > this.maxValue){
33817             this.markInvalid(String.format(this.maxText, this.maxValue));
33818             return false;
33819         }
33820         
33821         return true;
33822     },
33823
33824     getValue : function()
33825     {
33826         var v = this.hiddenEl().getValue();
33827         
33828         return this.fixPrecision(this.parseValue(v));
33829     },
33830
33831     parseValue : function(value)
33832     {
33833         if(this.thousandsDelimiter) {
33834             value += "";
33835             r = new RegExp(",", "g");
33836             value = value.replace(r, "");
33837         }
33838         
33839         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33840         return isNaN(value) ? '' : value;
33841     },
33842
33843     fixPrecision : function(value)
33844     {
33845         if(this.thousandsDelimiter) {
33846             value += "";
33847             r = new RegExp(",", "g");
33848             value = value.replace(r, "");
33849         }
33850         
33851         var nan = isNaN(value);
33852         
33853         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33854             return nan ? '' : value;
33855         }
33856         return parseFloat(value).toFixed(this.decimalPrecision);
33857     },
33858
33859     setValue : function(v)
33860     {
33861         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33862         
33863         this.value = v;
33864         
33865         if(this.rendered){
33866             
33867             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33868             
33869             this.inputEl().dom.value = (v == '') ? '' :
33870                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33871             
33872             if(!this.allowZero && v === '0') {
33873                 this.hiddenEl().dom.value = '';
33874                 this.inputEl().dom.value = '';
33875             }
33876             
33877             this.validate();
33878         }
33879     },
33880
33881     decimalPrecisionFcn : function(v)
33882     {
33883         return Math.floor(v);
33884     },
33885
33886     beforeBlur : function()
33887     {
33888         var v = this.parseValue(this.getRawValue());
33889         
33890         if(v || v === 0 || v === ''){
33891             this.setValue(v);
33892         }
33893     },
33894     
33895     hiddenEl : function()
33896     {
33897         return this.el.select('input.hidden-number-input',true).first();
33898     }
33899     
33900 });
33901
33902  
33903
33904 /*
33905 * Licence: LGPL
33906 */
33907
33908 /**
33909  * @class Roo.bootstrap.DocumentSlider
33910  * @extends Roo.bootstrap.Component
33911  * Bootstrap DocumentSlider class
33912  * 
33913  * @constructor
33914  * Create a new DocumentViewer
33915  * @param {Object} config The config object
33916  */
33917
33918 Roo.bootstrap.DocumentSlider = function(config){
33919     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33920     
33921     this.files = [];
33922     
33923     this.addEvents({
33924         /**
33925          * @event initial
33926          * Fire after initEvent
33927          * @param {Roo.bootstrap.DocumentSlider} this
33928          */
33929         "initial" : true,
33930         /**
33931          * @event update
33932          * Fire after update
33933          * @param {Roo.bootstrap.DocumentSlider} this
33934          */
33935         "update" : true,
33936         /**
33937          * @event click
33938          * Fire after click
33939          * @param {Roo.bootstrap.DocumentSlider} this
33940          */
33941         "click" : true
33942     });
33943 };
33944
33945 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33946     
33947     files : false,
33948     
33949     indicator : 0,
33950     
33951     getAutoCreate : function()
33952     {
33953         var cfg = {
33954             tag : 'div',
33955             cls : 'roo-document-slider',
33956             cn : [
33957                 {
33958                     tag : 'div',
33959                     cls : 'roo-document-slider-header',
33960                     cn : [
33961                         {
33962                             tag : 'div',
33963                             cls : 'roo-document-slider-header-title'
33964                         }
33965                     ]
33966                 },
33967                 {
33968                     tag : 'div',
33969                     cls : 'roo-document-slider-body',
33970                     cn : [
33971                         {
33972                             tag : 'div',
33973                             cls : 'roo-document-slider-prev',
33974                             cn : [
33975                                 {
33976                                     tag : 'i',
33977                                     cls : 'fa fa-chevron-left'
33978                                 }
33979                             ]
33980                         },
33981                         {
33982                             tag : 'div',
33983                             cls : 'roo-document-slider-thumb',
33984                             cn : [
33985                                 {
33986                                     tag : 'img',
33987                                     cls : 'roo-document-slider-image'
33988                                 }
33989                             ]
33990                         },
33991                         {
33992                             tag : 'div',
33993                             cls : 'roo-document-slider-next',
33994                             cn : [
33995                                 {
33996                                     tag : 'i',
33997                                     cls : 'fa fa-chevron-right'
33998                                 }
33999                             ]
34000                         }
34001                     ]
34002                 }
34003             ]
34004         };
34005         
34006         return cfg;
34007     },
34008     
34009     initEvents : function()
34010     {
34011         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
34012         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
34013         
34014         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
34015         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
34016         
34017         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
34018         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
34019         
34020         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
34021         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
34022         
34023         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
34024         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
34025         
34026         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
34027         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34028         
34029         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
34030         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34031         
34032         this.thumbEl.on('click', this.onClick, this);
34033         
34034         this.prevIndicator.on('click', this.prev, this);
34035         
34036         this.nextIndicator.on('click', this.next, this);
34037         
34038     },
34039     
34040     initial : function()
34041     {
34042         if(this.files.length){
34043             this.indicator = 1;
34044             this.update()
34045         }
34046         
34047         this.fireEvent('initial', this);
34048     },
34049     
34050     update : function()
34051     {
34052         this.imageEl.attr('src', this.files[this.indicator - 1]);
34053         
34054         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
34055         
34056         this.prevIndicator.show();
34057         
34058         if(this.indicator == 1){
34059             this.prevIndicator.hide();
34060         }
34061         
34062         this.nextIndicator.show();
34063         
34064         if(this.indicator == this.files.length){
34065             this.nextIndicator.hide();
34066         }
34067         
34068         this.thumbEl.scrollTo('top');
34069         
34070         this.fireEvent('update', this);
34071     },
34072     
34073     onClick : function(e)
34074     {
34075         e.preventDefault();
34076         
34077         this.fireEvent('click', this);
34078     },
34079     
34080     prev : function(e)
34081     {
34082         e.preventDefault();
34083         
34084         this.indicator = Math.max(1, this.indicator - 1);
34085         
34086         this.update();
34087     },
34088     
34089     next : function(e)
34090     {
34091         e.preventDefault();
34092         
34093         this.indicator = Math.min(this.files.length, this.indicator + 1);
34094         
34095         this.update();
34096     }
34097 });
34098 /*
34099  * - LGPL
34100  *
34101  * RadioSet
34102  *
34103  *
34104  */
34105
34106 /**
34107  * @class Roo.bootstrap.RadioSet
34108  * @extends Roo.bootstrap.Input
34109  * Bootstrap RadioSet class
34110  * @cfg {String} indicatorpos (left|right) default left
34111  * @cfg {Boolean} inline (true|false) inline the element (default true)
34112  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
34113  * @constructor
34114  * Create a new RadioSet
34115  * @param {Object} config The config object
34116  */
34117
34118 Roo.bootstrap.RadioSet = function(config){
34119     
34120     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
34121     
34122     this.radioes = [];
34123     
34124     Roo.bootstrap.RadioSet.register(this);
34125     
34126     this.addEvents({
34127         /**
34128         * @event check
34129         * Fires when the element is checked or unchecked.
34130         * @param {Roo.bootstrap.RadioSet} this This radio
34131         * @param {Roo.bootstrap.Radio} item The checked item
34132         */
34133        check : true,
34134        /**
34135         * @event click
34136         * Fires when the element is click.
34137         * @param {Roo.bootstrap.RadioSet} this This radio set
34138         * @param {Roo.bootstrap.Radio} item The checked item
34139         * @param {Roo.EventObject} e The event object
34140         */
34141        click : true
34142     });
34143     
34144 };
34145
34146 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
34147
34148     radioes : false,
34149     
34150     inline : true,
34151     
34152     weight : '',
34153     
34154     indicatorpos : 'left',
34155     
34156     getAutoCreate : function()
34157     {
34158         var label = {
34159             tag : 'label',
34160             cls : 'roo-radio-set-label',
34161             cn : [
34162                 {
34163                     tag : 'span',
34164                     html : this.fieldLabel
34165                 }
34166             ]
34167         };
34168         if (Roo.bootstrap.version == 3) {
34169             
34170             
34171             if(this.indicatorpos == 'left'){
34172                 label.cn.unshift({
34173                     tag : 'i',
34174                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34175                     tooltip : 'This field is required'
34176                 });
34177             } else {
34178                 label.cn.push({
34179                     tag : 'i',
34180                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34181                     tooltip : 'This field is required'
34182                 });
34183             }
34184         }
34185         var items = {
34186             tag : 'div',
34187             cls : 'roo-radio-set-items'
34188         };
34189         
34190         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34191         
34192         if (align === 'left' && this.fieldLabel.length) {
34193             
34194             items = {
34195                 cls : "roo-radio-set-right", 
34196                 cn: [
34197                     items
34198                 ]
34199             };
34200             
34201             if(this.labelWidth > 12){
34202                 label.style = "width: " + this.labelWidth + 'px';
34203             }
34204             
34205             if(this.labelWidth < 13 && this.labelmd == 0){
34206                 this.labelmd = this.labelWidth;
34207             }
34208             
34209             if(this.labellg > 0){
34210                 label.cls += ' col-lg-' + this.labellg;
34211                 items.cls += ' col-lg-' + (12 - this.labellg);
34212             }
34213             
34214             if(this.labelmd > 0){
34215                 label.cls += ' col-md-' + this.labelmd;
34216                 items.cls += ' col-md-' + (12 - this.labelmd);
34217             }
34218             
34219             if(this.labelsm > 0){
34220                 label.cls += ' col-sm-' + this.labelsm;
34221                 items.cls += ' col-sm-' + (12 - this.labelsm);
34222             }
34223             
34224             if(this.labelxs > 0){
34225                 label.cls += ' col-xs-' + this.labelxs;
34226                 items.cls += ' col-xs-' + (12 - this.labelxs);
34227             }
34228         }
34229         
34230         var cfg = {
34231             tag : 'div',
34232             cls : 'roo-radio-set',
34233             cn : [
34234                 {
34235                     tag : 'input',
34236                     cls : 'roo-radio-set-input',
34237                     type : 'hidden',
34238                     name : this.name,
34239                     value : this.value ? this.value :  ''
34240                 },
34241                 label,
34242                 items
34243             ]
34244         };
34245         
34246         if(this.weight.length){
34247             cfg.cls += ' roo-radio-' + this.weight;
34248         }
34249         
34250         if(this.inline) {
34251             cfg.cls += ' roo-radio-set-inline';
34252         }
34253         
34254         var settings=this;
34255         ['xs','sm','md','lg'].map(function(size){
34256             if (settings[size]) {
34257                 cfg.cls += ' col-' + size + '-' + settings[size];
34258             }
34259         });
34260         
34261         return cfg;
34262         
34263     },
34264
34265     initEvents : function()
34266     {
34267         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34268         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34269         
34270         if(!this.fieldLabel.length){
34271             this.labelEl.hide();
34272         }
34273         
34274         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34275         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34276         
34277         this.indicator = this.indicatorEl();
34278         
34279         if(this.indicator){
34280             this.indicator.addClass('invisible');
34281         }
34282         
34283         this.originalValue = this.getValue();
34284         
34285     },
34286     
34287     inputEl: function ()
34288     {
34289         return this.el.select('.roo-radio-set-input', true).first();
34290     },
34291     
34292     getChildContainer : function()
34293     {
34294         return this.itemsEl;
34295     },
34296     
34297     register : function(item)
34298     {
34299         this.radioes.push(item);
34300         
34301     },
34302     
34303     validate : function()
34304     {   
34305         if(this.getVisibilityEl().hasClass('hidden')){
34306             return true;
34307         }
34308         
34309         var valid = false;
34310         
34311         Roo.each(this.radioes, function(i){
34312             if(!i.checked){
34313                 return;
34314             }
34315             
34316             valid = true;
34317             return false;
34318         });
34319         
34320         if(this.allowBlank) {
34321             return true;
34322         }
34323         
34324         if(this.disabled || valid){
34325             this.markValid();
34326             return true;
34327         }
34328         
34329         this.markInvalid();
34330         return false;
34331         
34332     },
34333     
34334     markValid : function()
34335     {
34336         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34337             this.indicatorEl().removeClass('visible');
34338             this.indicatorEl().addClass('invisible');
34339         }
34340         
34341         
34342         if (Roo.bootstrap.version == 3) {
34343             this.el.removeClass([this.invalidClass, this.validClass]);
34344             this.el.addClass(this.validClass);
34345         } else {
34346             this.el.removeClass(['is-invalid','is-valid']);
34347             this.el.addClass(['is-valid']);
34348         }
34349         this.fireEvent('valid', this);
34350     },
34351     
34352     markInvalid : function(msg)
34353     {
34354         if(this.allowBlank || this.disabled){
34355             return;
34356         }
34357         
34358         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34359             this.indicatorEl().removeClass('invisible');
34360             this.indicatorEl().addClass('visible');
34361         }
34362         if (Roo.bootstrap.version == 3) {
34363             this.el.removeClass([this.invalidClass, this.validClass]);
34364             this.el.addClass(this.invalidClass);
34365         } else {
34366             this.el.removeClass(['is-invalid','is-valid']);
34367             this.el.addClass(['is-invalid']);
34368         }
34369         
34370         this.fireEvent('invalid', this, msg);
34371         
34372     },
34373     
34374     setValue : function(v, suppressEvent)
34375     {   
34376         if(this.value === v){
34377             return;
34378         }
34379         
34380         this.value = v;
34381         
34382         if(this.rendered){
34383             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34384         }
34385         
34386         Roo.each(this.radioes, function(i){
34387             i.checked = false;
34388             i.el.removeClass('checked');
34389         });
34390         
34391         Roo.each(this.radioes, function(i){
34392             
34393             if(i.value === v || i.value.toString() === v.toString()){
34394                 i.checked = true;
34395                 i.el.addClass('checked');
34396                 
34397                 if(suppressEvent !== true){
34398                     this.fireEvent('check', this, i);
34399                 }
34400                 
34401                 return false;
34402             }
34403             
34404         }, this);
34405         
34406         this.validate();
34407     },
34408     
34409     clearInvalid : function(){
34410         
34411         if(!this.el || this.preventMark){
34412             return;
34413         }
34414         
34415         this.el.removeClass([this.invalidClass]);
34416         
34417         this.fireEvent('valid', this);
34418     }
34419     
34420 });
34421
34422 Roo.apply(Roo.bootstrap.RadioSet, {
34423     
34424     groups: {},
34425     
34426     register : function(set)
34427     {
34428         this.groups[set.name] = set;
34429     },
34430     
34431     get: function(name) 
34432     {
34433         if (typeof(this.groups[name]) == 'undefined') {
34434             return false;
34435         }
34436         
34437         return this.groups[name] ;
34438     }
34439     
34440 });
34441 /*
34442  * Based on:
34443  * Ext JS Library 1.1.1
34444  * Copyright(c) 2006-2007, Ext JS, LLC.
34445  *
34446  * Originally Released Under LGPL - original licence link has changed is not relivant.
34447  *
34448  * Fork - LGPL
34449  * <script type="text/javascript">
34450  */
34451
34452
34453 /**
34454  * @class Roo.bootstrap.SplitBar
34455  * @extends Roo.util.Observable
34456  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34457  * <br><br>
34458  * Usage:
34459  * <pre><code>
34460 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34461                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34462 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34463 split.minSize = 100;
34464 split.maxSize = 600;
34465 split.animate = true;
34466 split.on('moved', splitterMoved);
34467 </code></pre>
34468  * @constructor
34469  * Create a new SplitBar
34470  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34471  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34472  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34473  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34474                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34475                         position of the SplitBar).
34476  */
34477 Roo.bootstrap.SplitBar = function(cfg){
34478     
34479     /** @private */
34480     
34481     //{
34482     //  dragElement : elm
34483     //  resizingElement: el,
34484         // optional..
34485     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34486     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34487         // existingProxy ???
34488     //}
34489     
34490     this.el = Roo.get(cfg.dragElement, true);
34491     this.el.dom.unselectable = "on";
34492     /** @private */
34493     this.resizingEl = Roo.get(cfg.resizingElement, true);
34494
34495     /**
34496      * @private
34497      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34498      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34499      * @type Number
34500      */
34501     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34502     
34503     /**
34504      * The minimum size of the resizing element. (Defaults to 0)
34505      * @type Number
34506      */
34507     this.minSize = 0;
34508     
34509     /**
34510      * The maximum size of the resizing element. (Defaults to 2000)
34511      * @type Number
34512      */
34513     this.maxSize = 2000;
34514     
34515     /**
34516      * Whether to animate the transition to the new size
34517      * @type Boolean
34518      */
34519     this.animate = false;
34520     
34521     /**
34522      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34523      * @type Boolean
34524      */
34525     this.useShim = false;
34526     
34527     /** @private */
34528     this.shim = null;
34529     
34530     if(!cfg.existingProxy){
34531         /** @private */
34532         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34533     }else{
34534         this.proxy = Roo.get(cfg.existingProxy).dom;
34535     }
34536     /** @private */
34537     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34538     
34539     /** @private */
34540     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34541     
34542     /** @private */
34543     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34544     
34545     /** @private */
34546     this.dragSpecs = {};
34547     
34548     /**
34549      * @private The adapter to use to positon and resize elements
34550      */
34551     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34552     this.adapter.init(this);
34553     
34554     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34555         /** @private */
34556         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34557         this.el.addClass("roo-splitbar-h");
34558     }else{
34559         /** @private */
34560         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34561         this.el.addClass("roo-splitbar-v");
34562     }
34563     
34564     this.addEvents({
34565         /**
34566          * @event resize
34567          * Fires when the splitter is moved (alias for {@link #event-moved})
34568          * @param {Roo.bootstrap.SplitBar} this
34569          * @param {Number} newSize the new width or height
34570          */
34571         "resize" : true,
34572         /**
34573          * @event moved
34574          * Fires when the splitter is moved
34575          * @param {Roo.bootstrap.SplitBar} this
34576          * @param {Number} newSize the new width or height
34577          */
34578         "moved" : true,
34579         /**
34580          * @event beforeresize
34581          * Fires before the splitter is dragged
34582          * @param {Roo.bootstrap.SplitBar} this
34583          */
34584         "beforeresize" : true,
34585
34586         "beforeapply" : true
34587     });
34588
34589     Roo.util.Observable.call(this);
34590 };
34591
34592 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34593     onStartProxyDrag : function(x, y){
34594         this.fireEvent("beforeresize", this);
34595         if(!this.overlay){
34596             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34597             o.unselectable();
34598             o.enableDisplayMode("block");
34599             // all splitbars share the same overlay
34600             Roo.bootstrap.SplitBar.prototype.overlay = o;
34601         }
34602         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34603         this.overlay.show();
34604         Roo.get(this.proxy).setDisplayed("block");
34605         var size = this.adapter.getElementSize(this);
34606         this.activeMinSize = this.getMinimumSize();;
34607         this.activeMaxSize = this.getMaximumSize();;
34608         var c1 = size - this.activeMinSize;
34609         var c2 = Math.max(this.activeMaxSize - size, 0);
34610         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34611             this.dd.resetConstraints();
34612             this.dd.setXConstraint(
34613                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34614                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34615             );
34616             this.dd.setYConstraint(0, 0);
34617         }else{
34618             this.dd.resetConstraints();
34619             this.dd.setXConstraint(0, 0);
34620             this.dd.setYConstraint(
34621                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34622                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34623             );
34624          }
34625         this.dragSpecs.startSize = size;
34626         this.dragSpecs.startPoint = [x, y];
34627         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34628     },
34629     
34630     /** 
34631      * @private Called after the drag operation by the DDProxy
34632      */
34633     onEndProxyDrag : function(e){
34634         Roo.get(this.proxy).setDisplayed(false);
34635         var endPoint = Roo.lib.Event.getXY(e);
34636         if(this.overlay){
34637             this.overlay.hide();
34638         }
34639         var newSize;
34640         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34641             newSize = this.dragSpecs.startSize + 
34642                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34643                     endPoint[0] - this.dragSpecs.startPoint[0] :
34644                     this.dragSpecs.startPoint[0] - endPoint[0]
34645                 );
34646         }else{
34647             newSize = this.dragSpecs.startSize + 
34648                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34649                     endPoint[1] - this.dragSpecs.startPoint[1] :
34650                     this.dragSpecs.startPoint[1] - endPoint[1]
34651                 );
34652         }
34653         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34654         if(newSize != this.dragSpecs.startSize){
34655             if(this.fireEvent('beforeapply', this, newSize) !== false){
34656                 this.adapter.setElementSize(this, newSize);
34657                 this.fireEvent("moved", this, newSize);
34658                 this.fireEvent("resize", this, newSize);
34659             }
34660         }
34661     },
34662     
34663     /**
34664      * Get the adapter this SplitBar uses
34665      * @return The adapter object
34666      */
34667     getAdapter : function(){
34668         return this.adapter;
34669     },
34670     
34671     /**
34672      * Set the adapter this SplitBar uses
34673      * @param {Object} adapter A SplitBar adapter object
34674      */
34675     setAdapter : function(adapter){
34676         this.adapter = adapter;
34677         this.adapter.init(this);
34678     },
34679     
34680     /**
34681      * Gets the minimum size for the resizing element
34682      * @return {Number} The minimum size
34683      */
34684     getMinimumSize : function(){
34685         return this.minSize;
34686     },
34687     
34688     /**
34689      * Sets the minimum size for the resizing element
34690      * @param {Number} minSize The minimum size
34691      */
34692     setMinimumSize : function(minSize){
34693         this.minSize = minSize;
34694     },
34695     
34696     /**
34697      * Gets the maximum size for the resizing element
34698      * @return {Number} The maximum size
34699      */
34700     getMaximumSize : function(){
34701         return this.maxSize;
34702     },
34703     
34704     /**
34705      * Sets the maximum size for the resizing element
34706      * @param {Number} maxSize The maximum size
34707      */
34708     setMaximumSize : function(maxSize){
34709         this.maxSize = maxSize;
34710     },
34711     
34712     /**
34713      * Sets the initialize size for the resizing element
34714      * @param {Number} size The initial size
34715      */
34716     setCurrentSize : function(size){
34717         var oldAnimate = this.animate;
34718         this.animate = false;
34719         this.adapter.setElementSize(this, size);
34720         this.animate = oldAnimate;
34721     },
34722     
34723     /**
34724      * Destroy this splitbar. 
34725      * @param {Boolean} removeEl True to remove the element
34726      */
34727     destroy : function(removeEl){
34728         if(this.shim){
34729             this.shim.remove();
34730         }
34731         this.dd.unreg();
34732         this.proxy.parentNode.removeChild(this.proxy);
34733         if(removeEl){
34734             this.el.remove();
34735         }
34736     }
34737 });
34738
34739 /**
34740  * @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.
34741  */
34742 Roo.bootstrap.SplitBar.createProxy = function(dir){
34743     var proxy = new Roo.Element(document.createElement("div"));
34744     proxy.unselectable();
34745     var cls = 'roo-splitbar-proxy';
34746     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34747     document.body.appendChild(proxy.dom);
34748     return proxy.dom;
34749 };
34750
34751 /** 
34752  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34753  * Default Adapter. It assumes the splitter and resizing element are not positioned
34754  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34755  */
34756 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34757 };
34758
34759 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34760     // do nothing for now
34761     init : function(s){
34762     
34763     },
34764     /**
34765      * Called before drag operations to get the current size of the resizing element. 
34766      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34767      */
34768      getElementSize : function(s){
34769         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34770             return s.resizingEl.getWidth();
34771         }else{
34772             return s.resizingEl.getHeight();
34773         }
34774     },
34775     
34776     /**
34777      * Called after drag operations to set the size of the resizing element.
34778      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34779      * @param {Number} newSize The new size to set
34780      * @param {Function} onComplete A function to be invoked when resizing is complete
34781      */
34782     setElementSize : function(s, newSize, onComplete){
34783         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34784             if(!s.animate){
34785                 s.resizingEl.setWidth(newSize);
34786                 if(onComplete){
34787                     onComplete(s, newSize);
34788                 }
34789             }else{
34790                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34791             }
34792         }else{
34793             
34794             if(!s.animate){
34795                 s.resizingEl.setHeight(newSize);
34796                 if(onComplete){
34797                     onComplete(s, newSize);
34798                 }
34799             }else{
34800                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34801             }
34802         }
34803     }
34804 };
34805
34806 /** 
34807  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34808  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34809  * Adapter that  moves the splitter element to align with the resized sizing element. 
34810  * Used with an absolute positioned SplitBar.
34811  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34812  * document.body, make sure you assign an id to the body element.
34813  */
34814 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34815     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34816     this.container = Roo.get(container);
34817 };
34818
34819 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34820     init : function(s){
34821         this.basic.init(s);
34822     },
34823     
34824     getElementSize : function(s){
34825         return this.basic.getElementSize(s);
34826     },
34827     
34828     setElementSize : function(s, newSize, onComplete){
34829         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34830     },
34831     
34832     moveSplitter : function(s){
34833         var yes = Roo.bootstrap.SplitBar;
34834         switch(s.placement){
34835             case yes.LEFT:
34836                 s.el.setX(s.resizingEl.getRight());
34837                 break;
34838             case yes.RIGHT:
34839                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34840                 break;
34841             case yes.TOP:
34842                 s.el.setY(s.resizingEl.getBottom());
34843                 break;
34844             case yes.BOTTOM:
34845                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34846                 break;
34847         }
34848     }
34849 };
34850
34851 /**
34852  * Orientation constant - Create a vertical SplitBar
34853  * @static
34854  * @type Number
34855  */
34856 Roo.bootstrap.SplitBar.VERTICAL = 1;
34857
34858 /**
34859  * Orientation constant - Create a horizontal SplitBar
34860  * @static
34861  * @type Number
34862  */
34863 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34864
34865 /**
34866  * Placement constant - The resizing element is to the left of the splitter element
34867  * @static
34868  * @type Number
34869  */
34870 Roo.bootstrap.SplitBar.LEFT = 1;
34871
34872 /**
34873  * Placement constant - The resizing element is to the right of the splitter element
34874  * @static
34875  * @type Number
34876  */
34877 Roo.bootstrap.SplitBar.RIGHT = 2;
34878
34879 /**
34880  * Placement constant - The resizing element is positioned above the splitter element
34881  * @static
34882  * @type Number
34883  */
34884 Roo.bootstrap.SplitBar.TOP = 3;
34885
34886 /**
34887  * Placement constant - The resizing element is positioned under splitter element
34888  * @static
34889  * @type Number
34890  */
34891 Roo.bootstrap.SplitBar.BOTTOM = 4;
34892 Roo.namespace("Roo.bootstrap.layout");/*
34893  * Based on:
34894  * Ext JS Library 1.1.1
34895  * Copyright(c) 2006-2007, Ext JS, LLC.
34896  *
34897  * Originally Released Under LGPL - original licence link has changed is not relivant.
34898  *
34899  * Fork - LGPL
34900  * <script type="text/javascript">
34901  */
34902
34903 /**
34904  * @class Roo.bootstrap.layout.Manager
34905  * @extends Roo.bootstrap.Component
34906  * Base class for layout managers.
34907  */
34908 Roo.bootstrap.layout.Manager = function(config)
34909 {
34910     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34911
34912
34913
34914
34915
34916     /** false to disable window resize monitoring @type Boolean */
34917     this.monitorWindowResize = true;
34918     this.regions = {};
34919     this.addEvents({
34920         /**
34921          * @event layout
34922          * Fires when a layout is performed.
34923          * @param {Roo.LayoutManager} this
34924          */
34925         "layout" : true,
34926         /**
34927          * @event regionresized
34928          * Fires when the user resizes a region.
34929          * @param {Roo.LayoutRegion} region The resized region
34930          * @param {Number} newSize The new size (width for east/west, height for north/south)
34931          */
34932         "regionresized" : true,
34933         /**
34934          * @event regioncollapsed
34935          * Fires when a region is collapsed.
34936          * @param {Roo.LayoutRegion} region The collapsed region
34937          */
34938         "regioncollapsed" : true,
34939         /**
34940          * @event regionexpanded
34941          * Fires when a region is expanded.
34942          * @param {Roo.LayoutRegion} region The expanded region
34943          */
34944         "regionexpanded" : true
34945     });
34946     this.updating = false;
34947
34948     if (config.el) {
34949         this.el = Roo.get(config.el);
34950         this.initEvents();
34951     }
34952
34953 };
34954
34955 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34956
34957
34958     regions : null,
34959
34960     monitorWindowResize : true,
34961
34962
34963     updating : false,
34964
34965
34966     onRender : function(ct, position)
34967     {
34968         if(!this.el){
34969             this.el = Roo.get(ct);
34970             this.initEvents();
34971         }
34972         //this.fireEvent('render',this);
34973     },
34974
34975
34976     initEvents: function()
34977     {
34978
34979
34980         // ie scrollbar fix
34981         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34982             document.body.scroll = "no";
34983         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34984             this.el.position('relative');
34985         }
34986         this.id = this.el.id;
34987         this.el.addClass("roo-layout-container");
34988         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34989         if(this.el.dom != document.body ) {
34990             this.el.on('resize', this.layout,this);
34991             this.el.on('show', this.layout,this);
34992         }
34993
34994     },
34995
34996     /**
34997      * Returns true if this layout is currently being updated
34998      * @return {Boolean}
34999      */
35000     isUpdating : function(){
35001         return this.updating;
35002     },
35003
35004     /**
35005      * Suspend the LayoutManager from doing auto-layouts while
35006      * making multiple add or remove calls
35007      */
35008     beginUpdate : function(){
35009         this.updating = true;
35010     },
35011
35012     /**
35013      * Restore auto-layouts and optionally disable the manager from performing a layout
35014      * @param {Boolean} noLayout true to disable a layout update
35015      */
35016     endUpdate : function(noLayout){
35017         this.updating = false;
35018         if(!noLayout){
35019             this.layout();
35020         }
35021     },
35022
35023     layout: function(){
35024         // abstract...
35025     },
35026
35027     onRegionResized : function(region, newSize){
35028         this.fireEvent("regionresized", region, newSize);
35029         this.layout();
35030     },
35031
35032     onRegionCollapsed : function(region){
35033         this.fireEvent("regioncollapsed", region);
35034     },
35035
35036     onRegionExpanded : function(region){
35037         this.fireEvent("regionexpanded", region);
35038     },
35039
35040     /**
35041      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
35042      * performs box-model adjustments.
35043      * @return {Object} The size as an object {width: (the width), height: (the height)}
35044      */
35045     getViewSize : function()
35046     {
35047         var size;
35048         if(this.el.dom != document.body){
35049             size = this.el.getSize();
35050         }else{
35051             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
35052         }
35053         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
35054         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35055         return size;
35056     },
35057
35058     /**
35059      * Returns the Element this layout is bound to.
35060      * @return {Roo.Element}
35061      */
35062     getEl : function(){
35063         return this.el;
35064     },
35065
35066     /**
35067      * Returns the specified region.
35068      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
35069      * @return {Roo.LayoutRegion}
35070      */
35071     getRegion : function(target){
35072         return this.regions[target.toLowerCase()];
35073     },
35074
35075     onWindowResize : function(){
35076         if(this.monitorWindowResize){
35077             this.layout();
35078         }
35079     }
35080 });
35081 /*
35082  * Based on:
35083  * Ext JS Library 1.1.1
35084  * Copyright(c) 2006-2007, Ext JS, LLC.
35085  *
35086  * Originally Released Under LGPL - original licence link has changed is not relivant.
35087  *
35088  * Fork - LGPL
35089  * <script type="text/javascript">
35090  */
35091 /**
35092  * @class Roo.bootstrap.layout.Border
35093  * @extends Roo.bootstrap.layout.Manager
35094  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
35095  * please see: examples/bootstrap/nested.html<br><br>
35096  
35097 <b>The container the layout is rendered into can be either the body element or any other element.
35098 If it is not the body element, the container needs to either be an absolute positioned element,
35099 or you will need to add "position:relative" to the css of the container.  You will also need to specify
35100 the container size if it is not the body element.</b>
35101
35102 * @constructor
35103 * Create a new Border
35104 * @param {Object} config Configuration options
35105  */
35106 Roo.bootstrap.layout.Border = function(config){
35107     config = config || {};
35108     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
35109     
35110     
35111     
35112     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35113         if(config[region]){
35114             config[region].region = region;
35115             this.addRegion(config[region]);
35116         }
35117     },this);
35118     
35119 };
35120
35121 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
35122
35123 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
35124     
35125     parent : false, // this might point to a 'nest' or a ???
35126     
35127     /**
35128      * Creates and adds a new region if it doesn't already exist.
35129      * @param {String} target The target region key (north, south, east, west or center).
35130      * @param {Object} config The regions config object
35131      * @return {BorderLayoutRegion} The new region
35132      */
35133     addRegion : function(config)
35134     {
35135         if(!this.regions[config.region]){
35136             var r = this.factory(config);
35137             this.bindRegion(r);
35138         }
35139         return this.regions[config.region];
35140     },
35141
35142     // private (kinda)
35143     bindRegion : function(r){
35144         this.regions[r.config.region] = r;
35145         
35146         r.on("visibilitychange",    this.layout, this);
35147         r.on("paneladded",          this.layout, this);
35148         r.on("panelremoved",        this.layout, this);
35149         r.on("invalidated",         this.layout, this);
35150         r.on("resized",             this.onRegionResized, this);
35151         r.on("collapsed",           this.onRegionCollapsed, this);
35152         r.on("expanded",            this.onRegionExpanded, this);
35153     },
35154
35155     /**
35156      * Performs a layout update.
35157      */
35158     layout : function()
35159     {
35160         if(this.updating) {
35161             return;
35162         }
35163         
35164         // render all the rebions if they have not been done alreayd?
35165         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35166             if(this.regions[region] && !this.regions[region].bodyEl){
35167                 this.regions[region].onRender(this.el)
35168             }
35169         },this);
35170         
35171         var size = this.getViewSize();
35172         var w = size.width;
35173         var h = size.height;
35174         var centerW = w;
35175         var centerH = h;
35176         var centerY = 0;
35177         var centerX = 0;
35178         //var x = 0, y = 0;
35179
35180         var rs = this.regions;
35181         var north = rs["north"];
35182         var south = rs["south"]; 
35183         var west = rs["west"];
35184         var east = rs["east"];
35185         var center = rs["center"];
35186         //if(this.hideOnLayout){ // not supported anymore
35187             //c.el.setStyle("display", "none");
35188         //}
35189         if(north && north.isVisible()){
35190             var b = north.getBox();
35191             var m = north.getMargins();
35192             b.width = w - (m.left+m.right);
35193             b.x = m.left;
35194             b.y = m.top;
35195             centerY = b.height + b.y + m.bottom;
35196             centerH -= centerY;
35197             north.updateBox(this.safeBox(b));
35198         }
35199         if(south && south.isVisible()){
35200             var b = south.getBox();
35201             var m = south.getMargins();
35202             b.width = w - (m.left+m.right);
35203             b.x = m.left;
35204             var totalHeight = (b.height + m.top + m.bottom);
35205             b.y = h - totalHeight + m.top;
35206             centerH -= totalHeight;
35207             south.updateBox(this.safeBox(b));
35208         }
35209         if(west && west.isVisible()){
35210             var b = west.getBox();
35211             var m = west.getMargins();
35212             b.height = centerH - (m.top+m.bottom);
35213             b.x = m.left;
35214             b.y = centerY + m.top;
35215             var totalWidth = (b.width + m.left + m.right);
35216             centerX += totalWidth;
35217             centerW -= totalWidth;
35218             west.updateBox(this.safeBox(b));
35219         }
35220         if(east && east.isVisible()){
35221             var b = east.getBox();
35222             var m = east.getMargins();
35223             b.height = centerH - (m.top+m.bottom);
35224             var totalWidth = (b.width + m.left + m.right);
35225             b.x = w - totalWidth + m.left;
35226             b.y = centerY + m.top;
35227             centerW -= totalWidth;
35228             east.updateBox(this.safeBox(b));
35229         }
35230         if(center){
35231             var m = center.getMargins();
35232             var centerBox = {
35233                 x: centerX + m.left,
35234                 y: centerY + m.top,
35235                 width: centerW - (m.left+m.right),
35236                 height: centerH - (m.top+m.bottom)
35237             };
35238             //if(this.hideOnLayout){
35239                 //center.el.setStyle("display", "block");
35240             //}
35241             center.updateBox(this.safeBox(centerBox));
35242         }
35243         this.el.repaint();
35244         this.fireEvent("layout", this);
35245     },
35246
35247     // private
35248     safeBox : function(box){
35249         box.width = Math.max(0, box.width);
35250         box.height = Math.max(0, box.height);
35251         return box;
35252     },
35253
35254     /**
35255      * Adds a ContentPanel (or subclass) to this layout.
35256      * @param {String} target The target region key (north, south, east, west or center).
35257      * @param {Roo.ContentPanel} panel The panel to add
35258      * @return {Roo.ContentPanel} The added panel
35259      */
35260     add : function(target, panel){
35261          
35262         target = target.toLowerCase();
35263         return this.regions[target].add(panel);
35264     },
35265
35266     /**
35267      * Remove a ContentPanel (or subclass) to this layout.
35268      * @param {String} target The target region key (north, south, east, west or center).
35269      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35270      * @return {Roo.ContentPanel} The removed panel
35271      */
35272     remove : function(target, panel){
35273         target = target.toLowerCase();
35274         return this.regions[target].remove(panel);
35275     },
35276
35277     /**
35278      * Searches all regions for a panel with the specified id
35279      * @param {String} panelId
35280      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35281      */
35282     findPanel : function(panelId){
35283         var rs = this.regions;
35284         for(var target in rs){
35285             if(typeof rs[target] != "function"){
35286                 var p = rs[target].getPanel(panelId);
35287                 if(p){
35288                     return p;
35289                 }
35290             }
35291         }
35292         return null;
35293     },
35294
35295     /**
35296      * Searches all regions for a panel with the specified id and activates (shows) it.
35297      * @param {String/ContentPanel} panelId The panels id or the panel itself
35298      * @return {Roo.ContentPanel} The shown panel or null
35299      */
35300     showPanel : function(panelId) {
35301       var rs = this.regions;
35302       for(var target in rs){
35303          var r = rs[target];
35304          if(typeof r != "function"){
35305             if(r.hasPanel(panelId)){
35306                return r.showPanel(panelId);
35307             }
35308          }
35309       }
35310       return null;
35311    },
35312
35313    /**
35314      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35315      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35316      */
35317    /*
35318     restoreState : function(provider){
35319         if(!provider){
35320             provider = Roo.state.Manager;
35321         }
35322         var sm = new Roo.LayoutStateManager();
35323         sm.init(this, provider);
35324     },
35325 */
35326  
35327  
35328     /**
35329      * Adds a xtype elements to the layout.
35330      * <pre><code>
35331
35332 layout.addxtype({
35333        xtype : 'ContentPanel',
35334        region: 'west',
35335        items: [ .... ]
35336    }
35337 );
35338
35339 layout.addxtype({
35340         xtype : 'NestedLayoutPanel',
35341         region: 'west',
35342         layout: {
35343            center: { },
35344            west: { }   
35345         },
35346         items : [ ... list of content panels or nested layout panels.. ]
35347    }
35348 );
35349 </code></pre>
35350      * @param {Object} cfg Xtype definition of item to add.
35351      */
35352     addxtype : function(cfg)
35353     {
35354         // basically accepts a pannel...
35355         // can accept a layout region..!?!?
35356         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35357         
35358         
35359         // theory?  children can only be panels??
35360         
35361         //if (!cfg.xtype.match(/Panel$/)) {
35362         //    return false;
35363         //}
35364         var ret = false;
35365         
35366         if (typeof(cfg.region) == 'undefined') {
35367             Roo.log("Failed to add Panel, region was not set");
35368             Roo.log(cfg);
35369             return false;
35370         }
35371         var region = cfg.region;
35372         delete cfg.region;
35373         
35374           
35375         var xitems = [];
35376         if (cfg.items) {
35377             xitems = cfg.items;
35378             delete cfg.items;
35379         }
35380         var nb = false;
35381         
35382         if ( region == 'center') {
35383             Roo.log("Center: " + cfg.title);
35384         }
35385         
35386         
35387         switch(cfg.xtype) 
35388         {
35389             case 'Content':  // ContentPanel (el, cfg)
35390             case 'Scroll':  // ContentPanel (el, cfg)
35391             case 'View': 
35392                 cfg.autoCreate = cfg.autoCreate || true;
35393                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35394                 //} else {
35395                 //    var el = this.el.createChild();
35396                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35397                 //}
35398                 
35399                 this.add(region, ret);
35400                 break;
35401             
35402             /*
35403             case 'TreePanel': // our new panel!
35404                 cfg.el = this.el.createChild();
35405                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35406                 this.add(region, ret);
35407                 break;
35408             */
35409             
35410             case 'Nest': 
35411                 // create a new Layout (which is  a Border Layout...
35412                 
35413                 var clayout = cfg.layout;
35414                 clayout.el  = this.el.createChild();
35415                 clayout.items   = clayout.items  || [];
35416                 
35417                 delete cfg.layout;
35418                 
35419                 // replace this exitems with the clayout ones..
35420                 xitems = clayout.items;
35421                  
35422                 // force background off if it's in center...
35423                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35424                     cfg.background = false;
35425                 }
35426                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35427                 
35428                 
35429                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35430                 //console.log('adding nested layout panel '  + cfg.toSource());
35431                 this.add(region, ret);
35432                 nb = {}; /// find first...
35433                 break;
35434             
35435             case 'Grid':
35436                 
35437                 // needs grid and region
35438                 
35439                 //var el = this.getRegion(region).el.createChild();
35440                 /*
35441                  *var el = this.el.createChild();
35442                 // create the grid first...
35443                 cfg.grid.container = el;
35444                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35445                 */
35446                 
35447                 if (region == 'center' && this.active ) {
35448                     cfg.background = false;
35449                 }
35450                 
35451                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35452                 
35453                 this.add(region, ret);
35454                 /*
35455                 if (cfg.background) {
35456                     // render grid on panel activation (if panel background)
35457                     ret.on('activate', function(gp) {
35458                         if (!gp.grid.rendered) {
35459                     //        gp.grid.render(el);
35460                         }
35461                     });
35462                 } else {
35463                   //  cfg.grid.render(el);
35464                 }
35465                 */
35466                 break;
35467            
35468            
35469             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35470                 // it was the old xcomponent building that caused this before.
35471                 // espeically if border is the top element in the tree.
35472                 ret = this;
35473                 break; 
35474                 
35475                     
35476                 
35477                 
35478                 
35479             default:
35480                 /*
35481                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35482                     
35483                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35484                     this.add(region, ret);
35485                 } else {
35486                 */
35487                     Roo.log(cfg);
35488                     throw "Can not add '" + cfg.xtype + "' to Border";
35489                     return null;
35490              
35491                                 
35492              
35493         }
35494         this.beginUpdate();
35495         // add children..
35496         var region = '';
35497         var abn = {};
35498         Roo.each(xitems, function(i)  {
35499             region = nb && i.region ? i.region : false;
35500             
35501             var add = ret.addxtype(i);
35502            
35503             if (region) {
35504                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35505                 if (!i.background) {
35506                     abn[region] = nb[region] ;
35507                 }
35508             }
35509             
35510         });
35511         this.endUpdate();
35512
35513         // make the last non-background panel active..
35514         //if (nb) { Roo.log(abn); }
35515         if (nb) {
35516             
35517             for(var r in abn) {
35518                 region = this.getRegion(r);
35519                 if (region) {
35520                     // tried using nb[r], but it does not work..
35521                      
35522                     region.showPanel(abn[r]);
35523                    
35524                 }
35525             }
35526         }
35527         return ret;
35528         
35529     },
35530     
35531     
35532 // private
35533     factory : function(cfg)
35534     {
35535         
35536         var validRegions = Roo.bootstrap.layout.Border.regions;
35537
35538         var target = cfg.region;
35539         cfg.mgr = this;
35540         
35541         var r = Roo.bootstrap.layout;
35542         Roo.log(target);
35543         switch(target){
35544             case "north":
35545                 return new r.North(cfg);
35546             case "south":
35547                 return new r.South(cfg);
35548             case "east":
35549                 return new r.East(cfg);
35550             case "west":
35551                 return new r.West(cfg);
35552             case "center":
35553                 return new r.Center(cfg);
35554         }
35555         throw 'Layout region "'+target+'" not supported.';
35556     }
35557     
35558     
35559 });
35560  /*
35561  * Based on:
35562  * Ext JS Library 1.1.1
35563  * Copyright(c) 2006-2007, Ext JS, LLC.
35564  *
35565  * Originally Released Under LGPL - original licence link has changed is not relivant.
35566  *
35567  * Fork - LGPL
35568  * <script type="text/javascript">
35569  */
35570  
35571 /**
35572  * @class Roo.bootstrap.layout.Basic
35573  * @extends Roo.util.Observable
35574  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35575  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35576  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35577  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35578  * @cfg {string}   region  the region that it inhabits..
35579  * @cfg {bool}   skipConfig skip config?
35580  * 
35581
35582  */
35583 Roo.bootstrap.layout.Basic = function(config){
35584     
35585     this.mgr = config.mgr;
35586     
35587     this.position = config.region;
35588     
35589     var skipConfig = config.skipConfig;
35590     
35591     this.events = {
35592         /**
35593          * @scope Roo.BasicLayoutRegion
35594          */
35595         
35596         /**
35597          * @event beforeremove
35598          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35599          * @param {Roo.LayoutRegion} this
35600          * @param {Roo.ContentPanel} panel The panel
35601          * @param {Object} e The cancel event object
35602          */
35603         "beforeremove" : true,
35604         /**
35605          * @event invalidated
35606          * Fires when the layout for this region is changed.
35607          * @param {Roo.LayoutRegion} this
35608          */
35609         "invalidated" : true,
35610         /**
35611          * @event visibilitychange
35612          * Fires when this region is shown or hidden 
35613          * @param {Roo.LayoutRegion} this
35614          * @param {Boolean} visibility true or false
35615          */
35616         "visibilitychange" : true,
35617         /**
35618          * @event paneladded
35619          * Fires when a panel is added. 
35620          * @param {Roo.LayoutRegion} this
35621          * @param {Roo.ContentPanel} panel The panel
35622          */
35623         "paneladded" : true,
35624         /**
35625          * @event panelremoved
35626          * Fires when a panel is removed. 
35627          * @param {Roo.LayoutRegion} this
35628          * @param {Roo.ContentPanel} panel The panel
35629          */
35630         "panelremoved" : true,
35631         /**
35632          * @event beforecollapse
35633          * Fires when this region before collapse.
35634          * @param {Roo.LayoutRegion} this
35635          */
35636         "beforecollapse" : true,
35637         /**
35638          * @event collapsed
35639          * Fires when this region is collapsed.
35640          * @param {Roo.LayoutRegion} this
35641          */
35642         "collapsed" : true,
35643         /**
35644          * @event expanded
35645          * Fires when this region is expanded.
35646          * @param {Roo.LayoutRegion} this
35647          */
35648         "expanded" : true,
35649         /**
35650          * @event slideshow
35651          * Fires when this region is slid into view.
35652          * @param {Roo.LayoutRegion} this
35653          */
35654         "slideshow" : true,
35655         /**
35656          * @event slidehide
35657          * Fires when this region slides out of view. 
35658          * @param {Roo.LayoutRegion} this
35659          */
35660         "slidehide" : true,
35661         /**
35662          * @event panelactivated
35663          * Fires when a panel is activated. 
35664          * @param {Roo.LayoutRegion} this
35665          * @param {Roo.ContentPanel} panel The activated panel
35666          */
35667         "panelactivated" : true,
35668         /**
35669          * @event resized
35670          * Fires when the user resizes this region. 
35671          * @param {Roo.LayoutRegion} this
35672          * @param {Number} newSize The new size (width for east/west, height for north/south)
35673          */
35674         "resized" : true
35675     };
35676     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35677     this.panels = new Roo.util.MixedCollection();
35678     this.panels.getKey = this.getPanelId.createDelegate(this);
35679     this.box = null;
35680     this.activePanel = null;
35681     // ensure listeners are added...
35682     
35683     if (config.listeners || config.events) {
35684         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35685             listeners : config.listeners || {},
35686             events : config.events || {}
35687         });
35688     }
35689     
35690     if(skipConfig !== true){
35691         this.applyConfig(config);
35692     }
35693 };
35694
35695 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35696 {
35697     getPanelId : function(p){
35698         return p.getId();
35699     },
35700     
35701     applyConfig : function(config){
35702         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35703         this.config = config;
35704         
35705     },
35706     
35707     /**
35708      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35709      * the width, for horizontal (north, south) the height.
35710      * @param {Number} newSize The new width or height
35711      */
35712     resizeTo : function(newSize){
35713         var el = this.el ? this.el :
35714                  (this.activePanel ? this.activePanel.getEl() : null);
35715         if(el){
35716             switch(this.position){
35717                 case "east":
35718                 case "west":
35719                     el.setWidth(newSize);
35720                     this.fireEvent("resized", this, newSize);
35721                 break;
35722                 case "north":
35723                 case "south":
35724                     el.setHeight(newSize);
35725                     this.fireEvent("resized", this, newSize);
35726                 break;                
35727             }
35728         }
35729     },
35730     
35731     getBox : function(){
35732         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35733     },
35734     
35735     getMargins : function(){
35736         return this.margins;
35737     },
35738     
35739     updateBox : function(box){
35740         this.box = box;
35741         var el = this.activePanel.getEl();
35742         el.dom.style.left = box.x + "px";
35743         el.dom.style.top = box.y + "px";
35744         this.activePanel.setSize(box.width, box.height);
35745     },
35746     
35747     /**
35748      * Returns the container element for this region.
35749      * @return {Roo.Element}
35750      */
35751     getEl : function(){
35752         return this.activePanel;
35753     },
35754     
35755     /**
35756      * Returns true if this region is currently visible.
35757      * @return {Boolean}
35758      */
35759     isVisible : function(){
35760         return this.activePanel ? true : false;
35761     },
35762     
35763     setActivePanel : function(panel){
35764         panel = this.getPanel(panel);
35765         if(this.activePanel && this.activePanel != panel){
35766             this.activePanel.setActiveState(false);
35767             this.activePanel.getEl().setLeftTop(-10000,-10000);
35768         }
35769         this.activePanel = panel;
35770         panel.setActiveState(true);
35771         if(this.box){
35772             panel.setSize(this.box.width, this.box.height);
35773         }
35774         this.fireEvent("panelactivated", this, panel);
35775         this.fireEvent("invalidated");
35776     },
35777     
35778     /**
35779      * Show the specified panel.
35780      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35781      * @return {Roo.ContentPanel} The shown panel or null
35782      */
35783     showPanel : function(panel){
35784         panel = this.getPanel(panel);
35785         if(panel){
35786             this.setActivePanel(panel);
35787         }
35788         return panel;
35789     },
35790     
35791     /**
35792      * Get the active panel for this region.
35793      * @return {Roo.ContentPanel} The active panel or null
35794      */
35795     getActivePanel : function(){
35796         return this.activePanel;
35797     },
35798     
35799     /**
35800      * Add the passed ContentPanel(s)
35801      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35802      * @return {Roo.ContentPanel} The panel added (if only one was added)
35803      */
35804     add : function(panel){
35805         if(arguments.length > 1){
35806             for(var i = 0, len = arguments.length; i < len; i++) {
35807                 this.add(arguments[i]);
35808             }
35809             return null;
35810         }
35811         if(this.hasPanel(panel)){
35812             this.showPanel(panel);
35813             return panel;
35814         }
35815         var el = panel.getEl();
35816         if(el.dom.parentNode != this.mgr.el.dom){
35817             this.mgr.el.dom.appendChild(el.dom);
35818         }
35819         if(panel.setRegion){
35820             panel.setRegion(this);
35821         }
35822         this.panels.add(panel);
35823         el.setStyle("position", "absolute");
35824         if(!panel.background){
35825             this.setActivePanel(panel);
35826             if(this.config.initialSize && this.panels.getCount()==1){
35827                 this.resizeTo(this.config.initialSize);
35828             }
35829         }
35830         this.fireEvent("paneladded", this, panel);
35831         return panel;
35832     },
35833     
35834     /**
35835      * Returns true if the panel is in this region.
35836      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35837      * @return {Boolean}
35838      */
35839     hasPanel : function(panel){
35840         if(typeof panel == "object"){ // must be panel obj
35841             panel = panel.getId();
35842         }
35843         return this.getPanel(panel) ? true : false;
35844     },
35845     
35846     /**
35847      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35848      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35849      * @param {Boolean} preservePanel Overrides the config preservePanel option
35850      * @return {Roo.ContentPanel} The panel that was removed
35851      */
35852     remove : function(panel, preservePanel){
35853         panel = this.getPanel(panel);
35854         if(!panel){
35855             return null;
35856         }
35857         var e = {};
35858         this.fireEvent("beforeremove", this, panel, e);
35859         if(e.cancel === true){
35860             return null;
35861         }
35862         var panelId = panel.getId();
35863         this.panels.removeKey(panelId);
35864         return panel;
35865     },
35866     
35867     /**
35868      * Returns the panel specified or null if it's not in this region.
35869      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35870      * @return {Roo.ContentPanel}
35871      */
35872     getPanel : function(id){
35873         if(typeof id == "object"){ // must be panel obj
35874             return id;
35875         }
35876         return this.panels.get(id);
35877     },
35878     
35879     /**
35880      * Returns this regions position (north/south/east/west/center).
35881      * @return {String} 
35882      */
35883     getPosition: function(){
35884         return this.position;    
35885     }
35886 });/*
35887  * Based on:
35888  * Ext JS Library 1.1.1
35889  * Copyright(c) 2006-2007, Ext JS, LLC.
35890  *
35891  * Originally Released Under LGPL - original licence link has changed is not relivant.
35892  *
35893  * Fork - LGPL
35894  * <script type="text/javascript">
35895  */
35896  
35897 /**
35898  * @class Roo.bootstrap.layout.Region
35899  * @extends Roo.bootstrap.layout.Basic
35900  * This class represents a region in a layout manager.
35901  
35902  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35903  * @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})
35904  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35905  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35906  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35907  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35908  * @cfg {String}    title           The title for the region (overrides panel titles)
35909  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35910  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35911  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35912  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35913  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35914  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35915  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35916  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35917  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35918  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35919
35920  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35921  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35922  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35923  * @cfg {Number}    width           For East/West panels
35924  * @cfg {Number}    height          For North/South panels
35925  * @cfg {Boolean}   split           To show the splitter
35926  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35927  * 
35928  * @cfg {string}   cls             Extra CSS classes to add to region
35929  * 
35930  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35931  * @cfg {string}   region  the region that it inhabits..
35932  *
35933
35934  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35935  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35936
35937  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35938  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35939  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35940  */
35941 Roo.bootstrap.layout.Region = function(config)
35942 {
35943     this.applyConfig(config);
35944
35945     var mgr = config.mgr;
35946     var pos = config.region;
35947     config.skipConfig = true;
35948     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35949     
35950     if (mgr.el) {
35951         this.onRender(mgr.el);   
35952     }
35953      
35954     this.visible = true;
35955     this.collapsed = false;
35956     this.unrendered_panels = [];
35957 };
35958
35959 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35960
35961     position: '', // set by wrapper (eg. north/south etc..)
35962     unrendered_panels : null,  // unrendered panels.
35963     
35964     tabPosition : false,
35965     
35966     mgr: false, // points to 'Border'
35967     
35968     
35969     createBody : function(){
35970         /** This region's body element 
35971         * @type Roo.Element */
35972         this.bodyEl = this.el.createChild({
35973                 tag: "div",
35974                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35975         });
35976     },
35977
35978     onRender: function(ctr, pos)
35979     {
35980         var dh = Roo.DomHelper;
35981         /** This region's container element 
35982         * @type Roo.Element */
35983         this.el = dh.append(ctr.dom, {
35984                 tag: "div",
35985                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35986             }, true);
35987         /** This region's title element 
35988         * @type Roo.Element */
35989     
35990         this.titleEl = dh.append(this.el.dom,  {
35991                 tag: "div",
35992                 unselectable: "on",
35993                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35994                 children:[
35995                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35996                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35997                 ]
35998             }, true);
35999         
36000         this.titleEl.enableDisplayMode();
36001         /** This region's title text element 
36002         * @type HTMLElement */
36003         this.titleTextEl = this.titleEl.dom.firstChild;
36004         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
36005         /*
36006         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
36007         this.closeBtn.enableDisplayMode();
36008         this.closeBtn.on("click", this.closeClicked, this);
36009         this.closeBtn.hide();
36010     */
36011         this.createBody(this.config);
36012         if(this.config.hideWhenEmpty){
36013             this.hide();
36014             this.on("paneladded", this.validateVisibility, this);
36015             this.on("panelremoved", this.validateVisibility, this);
36016         }
36017         if(this.autoScroll){
36018             this.bodyEl.setStyle("overflow", "auto");
36019         }else{
36020             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
36021         }
36022         //if(c.titlebar !== false){
36023             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
36024                 this.titleEl.hide();
36025             }else{
36026                 this.titleEl.show();
36027                 if(this.config.title){
36028                     this.titleTextEl.innerHTML = this.config.title;
36029                 }
36030             }
36031         //}
36032         if(this.config.collapsed){
36033             this.collapse(true);
36034         }
36035         if(this.config.hidden){
36036             this.hide();
36037         }
36038         
36039         if (this.unrendered_panels && this.unrendered_panels.length) {
36040             for (var i =0;i< this.unrendered_panels.length; i++) {
36041                 this.add(this.unrendered_panels[i]);
36042             }
36043             this.unrendered_panels = null;
36044             
36045         }
36046         
36047     },
36048     
36049     applyConfig : function(c)
36050     {
36051         /*
36052          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
36053             var dh = Roo.DomHelper;
36054             if(c.titlebar !== false){
36055                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
36056                 this.collapseBtn.on("click", this.collapse, this);
36057                 this.collapseBtn.enableDisplayMode();
36058                 /*
36059                 if(c.showPin === true || this.showPin){
36060                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
36061                     this.stickBtn.enableDisplayMode();
36062                     this.stickBtn.on("click", this.expand, this);
36063                     this.stickBtn.hide();
36064                 }
36065                 
36066             }
36067             */
36068             /** This region's collapsed element
36069             * @type Roo.Element */
36070             /*
36071              *
36072             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
36073                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
36074             ]}, true);
36075             
36076             if(c.floatable !== false){
36077                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
36078                this.collapsedEl.on("click", this.collapseClick, this);
36079             }
36080
36081             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
36082                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
36083                    id: "message", unselectable: "on", style:{"float":"left"}});
36084                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
36085              }
36086             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
36087             this.expandBtn.on("click", this.expand, this);
36088             
36089         }
36090         
36091         if(this.collapseBtn){
36092             this.collapseBtn.setVisible(c.collapsible == true);
36093         }
36094         
36095         this.cmargins = c.cmargins || this.cmargins ||
36096                          (this.position == "west" || this.position == "east" ?
36097                              {top: 0, left: 2, right:2, bottom: 0} :
36098                              {top: 2, left: 0, right:0, bottom: 2});
36099         */
36100         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36101         
36102         
36103         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
36104         
36105         this.autoScroll = c.autoScroll || false;
36106         
36107         
36108        
36109         
36110         this.duration = c.duration || .30;
36111         this.slideDuration = c.slideDuration || .45;
36112         this.config = c;
36113        
36114     },
36115     /**
36116      * Returns true if this region is currently visible.
36117      * @return {Boolean}
36118      */
36119     isVisible : function(){
36120         return this.visible;
36121     },
36122
36123     /**
36124      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
36125      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
36126      */
36127     //setCollapsedTitle : function(title){
36128     //    title = title || "&#160;";
36129      //   if(this.collapsedTitleTextEl){
36130       //      this.collapsedTitleTextEl.innerHTML = title;
36131        // }
36132     //},
36133
36134     getBox : function(){
36135         var b;
36136       //  if(!this.collapsed){
36137             b = this.el.getBox(false, true);
36138        // }else{
36139           //  b = this.collapsedEl.getBox(false, true);
36140         //}
36141         return b;
36142     },
36143
36144     getMargins : function(){
36145         return this.margins;
36146         //return this.collapsed ? this.cmargins : this.margins;
36147     },
36148 /*
36149     highlight : function(){
36150         this.el.addClass("x-layout-panel-dragover");
36151     },
36152
36153     unhighlight : function(){
36154         this.el.removeClass("x-layout-panel-dragover");
36155     },
36156 */
36157     updateBox : function(box)
36158     {
36159         if (!this.bodyEl) {
36160             return; // not rendered yet..
36161         }
36162         
36163         this.box = box;
36164         if(!this.collapsed){
36165             this.el.dom.style.left = box.x + "px";
36166             this.el.dom.style.top = box.y + "px";
36167             this.updateBody(box.width, box.height);
36168         }else{
36169             this.collapsedEl.dom.style.left = box.x + "px";
36170             this.collapsedEl.dom.style.top = box.y + "px";
36171             this.collapsedEl.setSize(box.width, box.height);
36172         }
36173         if(this.tabs){
36174             this.tabs.autoSizeTabs();
36175         }
36176     },
36177
36178     updateBody : function(w, h)
36179     {
36180         if(w !== null){
36181             this.el.setWidth(w);
36182             w -= this.el.getBorderWidth("rl");
36183             if(this.config.adjustments){
36184                 w += this.config.adjustments[0];
36185             }
36186         }
36187         if(h !== null && h > 0){
36188             this.el.setHeight(h);
36189             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36190             h -= this.el.getBorderWidth("tb");
36191             if(this.config.adjustments){
36192                 h += this.config.adjustments[1];
36193             }
36194             this.bodyEl.setHeight(h);
36195             if(this.tabs){
36196                 h = this.tabs.syncHeight(h);
36197             }
36198         }
36199         if(this.panelSize){
36200             w = w !== null ? w : this.panelSize.width;
36201             h = h !== null ? h : this.panelSize.height;
36202         }
36203         if(this.activePanel){
36204             var el = this.activePanel.getEl();
36205             w = w !== null ? w : el.getWidth();
36206             h = h !== null ? h : el.getHeight();
36207             this.panelSize = {width: w, height: h};
36208             this.activePanel.setSize(w, h);
36209         }
36210         if(Roo.isIE && this.tabs){
36211             this.tabs.el.repaint();
36212         }
36213     },
36214
36215     /**
36216      * Returns the container element for this region.
36217      * @return {Roo.Element}
36218      */
36219     getEl : function(){
36220         return this.el;
36221     },
36222
36223     /**
36224      * Hides this region.
36225      */
36226     hide : function(){
36227         //if(!this.collapsed){
36228             this.el.dom.style.left = "-2000px";
36229             this.el.hide();
36230         //}else{
36231          //   this.collapsedEl.dom.style.left = "-2000px";
36232          //   this.collapsedEl.hide();
36233        // }
36234         this.visible = false;
36235         this.fireEvent("visibilitychange", this, false);
36236     },
36237
36238     /**
36239      * Shows this region if it was previously hidden.
36240      */
36241     show : function(){
36242         //if(!this.collapsed){
36243             this.el.show();
36244         //}else{
36245         //    this.collapsedEl.show();
36246        // }
36247         this.visible = true;
36248         this.fireEvent("visibilitychange", this, true);
36249     },
36250 /*
36251     closeClicked : function(){
36252         if(this.activePanel){
36253             this.remove(this.activePanel);
36254         }
36255     },
36256
36257     collapseClick : function(e){
36258         if(this.isSlid){
36259            e.stopPropagation();
36260            this.slideIn();
36261         }else{
36262            e.stopPropagation();
36263            this.slideOut();
36264         }
36265     },
36266 */
36267     /**
36268      * Collapses this region.
36269      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36270      */
36271     /*
36272     collapse : function(skipAnim, skipCheck = false){
36273         if(this.collapsed) {
36274             return;
36275         }
36276         
36277         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36278             
36279             this.collapsed = true;
36280             if(this.split){
36281                 this.split.el.hide();
36282             }
36283             if(this.config.animate && skipAnim !== true){
36284                 this.fireEvent("invalidated", this);
36285                 this.animateCollapse();
36286             }else{
36287                 this.el.setLocation(-20000,-20000);
36288                 this.el.hide();
36289                 this.collapsedEl.show();
36290                 this.fireEvent("collapsed", this);
36291                 this.fireEvent("invalidated", this);
36292             }
36293         }
36294         
36295     },
36296 */
36297     animateCollapse : function(){
36298         // overridden
36299     },
36300
36301     /**
36302      * Expands this region if it was previously collapsed.
36303      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36304      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36305      */
36306     /*
36307     expand : function(e, skipAnim){
36308         if(e) {
36309             e.stopPropagation();
36310         }
36311         if(!this.collapsed || this.el.hasActiveFx()) {
36312             return;
36313         }
36314         if(this.isSlid){
36315             this.afterSlideIn();
36316             skipAnim = true;
36317         }
36318         this.collapsed = false;
36319         if(this.config.animate && skipAnim !== true){
36320             this.animateExpand();
36321         }else{
36322             this.el.show();
36323             if(this.split){
36324                 this.split.el.show();
36325             }
36326             this.collapsedEl.setLocation(-2000,-2000);
36327             this.collapsedEl.hide();
36328             this.fireEvent("invalidated", this);
36329             this.fireEvent("expanded", this);
36330         }
36331     },
36332 */
36333     animateExpand : function(){
36334         // overridden
36335     },
36336
36337     initTabs : function()
36338     {
36339         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36340         
36341         var ts = new Roo.bootstrap.panel.Tabs({
36342             el: this.bodyEl.dom,
36343             region : this,
36344             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
36345             disableTooltips: this.config.disableTabTips,
36346             toolbar : this.config.toolbar
36347         });
36348         
36349         if(this.config.hideTabs){
36350             ts.stripWrap.setDisplayed(false);
36351         }
36352         this.tabs = ts;
36353         ts.resizeTabs = this.config.resizeTabs === true;
36354         ts.minTabWidth = this.config.minTabWidth || 40;
36355         ts.maxTabWidth = this.config.maxTabWidth || 250;
36356         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36357         ts.monitorResize = false;
36358         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36359         ts.bodyEl.addClass('roo-layout-tabs-body');
36360         this.panels.each(this.initPanelAsTab, this);
36361     },
36362
36363     initPanelAsTab : function(panel){
36364         var ti = this.tabs.addTab(
36365             panel.getEl().id,
36366             panel.getTitle(),
36367             null,
36368             this.config.closeOnTab && panel.isClosable(),
36369             panel.tpl
36370         );
36371         if(panel.tabTip !== undefined){
36372             ti.setTooltip(panel.tabTip);
36373         }
36374         ti.on("activate", function(){
36375               this.setActivePanel(panel);
36376         }, this);
36377         
36378         if(this.config.closeOnTab){
36379             ti.on("beforeclose", function(t, e){
36380                 e.cancel = true;
36381                 this.remove(panel);
36382             }, this);
36383         }
36384         
36385         panel.tabItem = ti;
36386         
36387         return ti;
36388     },
36389
36390     updatePanelTitle : function(panel, title)
36391     {
36392         if(this.activePanel == panel){
36393             this.updateTitle(title);
36394         }
36395         if(this.tabs){
36396             var ti = this.tabs.getTab(panel.getEl().id);
36397             ti.setText(title);
36398             if(panel.tabTip !== undefined){
36399                 ti.setTooltip(panel.tabTip);
36400             }
36401         }
36402     },
36403
36404     updateTitle : function(title){
36405         if(this.titleTextEl && !this.config.title){
36406             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36407         }
36408     },
36409
36410     setActivePanel : function(panel)
36411     {
36412         panel = this.getPanel(panel);
36413         if(this.activePanel && this.activePanel != panel){
36414             if(this.activePanel.setActiveState(false) === false){
36415                 return;
36416             }
36417         }
36418         this.activePanel = panel;
36419         panel.setActiveState(true);
36420         if(this.panelSize){
36421             panel.setSize(this.panelSize.width, this.panelSize.height);
36422         }
36423         if(this.closeBtn){
36424             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36425         }
36426         this.updateTitle(panel.getTitle());
36427         if(this.tabs){
36428             this.fireEvent("invalidated", this);
36429         }
36430         this.fireEvent("panelactivated", this, panel);
36431     },
36432
36433     /**
36434      * Shows the specified panel.
36435      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36436      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36437      */
36438     showPanel : function(panel)
36439     {
36440         panel = this.getPanel(panel);
36441         if(panel){
36442             if(this.tabs){
36443                 var tab = this.tabs.getTab(panel.getEl().id);
36444                 if(tab.isHidden()){
36445                     this.tabs.unhideTab(tab.id);
36446                 }
36447                 tab.activate();
36448             }else{
36449                 this.setActivePanel(panel);
36450             }
36451         }
36452         return panel;
36453     },
36454
36455     /**
36456      * Get the active panel for this region.
36457      * @return {Roo.ContentPanel} The active panel or null
36458      */
36459     getActivePanel : function(){
36460         return this.activePanel;
36461     },
36462
36463     validateVisibility : function(){
36464         if(this.panels.getCount() < 1){
36465             this.updateTitle("&#160;");
36466             this.closeBtn.hide();
36467             this.hide();
36468         }else{
36469             if(!this.isVisible()){
36470                 this.show();
36471             }
36472         }
36473     },
36474
36475     /**
36476      * Adds the passed ContentPanel(s) to this region.
36477      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36478      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36479      */
36480     add : function(panel)
36481     {
36482         if(arguments.length > 1){
36483             for(var i = 0, len = arguments.length; i < len; i++) {
36484                 this.add(arguments[i]);
36485             }
36486             return null;
36487         }
36488         
36489         // if we have not been rendered yet, then we can not really do much of this..
36490         if (!this.bodyEl) {
36491             this.unrendered_panels.push(panel);
36492             return panel;
36493         }
36494         
36495         
36496         
36497         
36498         if(this.hasPanel(panel)){
36499             this.showPanel(panel);
36500             return panel;
36501         }
36502         panel.setRegion(this);
36503         this.panels.add(panel);
36504        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36505             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36506             // and hide them... ???
36507             this.bodyEl.dom.appendChild(panel.getEl().dom);
36508             if(panel.background !== true){
36509                 this.setActivePanel(panel);
36510             }
36511             this.fireEvent("paneladded", this, panel);
36512             return panel;
36513         }
36514         */
36515         if(!this.tabs){
36516             this.initTabs();
36517         }else{
36518             this.initPanelAsTab(panel);
36519         }
36520         
36521         
36522         if(panel.background !== true){
36523             this.tabs.activate(panel.getEl().id);
36524         }
36525         this.fireEvent("paneladded", this, panel);
36526         return panel;
36527     },
36528
36529     /**
36530      * Hides the tab for the specified panel.
36531      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36532      */
36533     hidePanel : function(panel){
36534         if(this.tabs && (panel = this.getPanel(panel))){
36535             this.tabs.hideTab(panel.getEl().id);
36536         }
36537     },
36538
36539     /**
36540      * Unhides the tab for a previously hidden panel.
36541      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36542      */
36543     unhidePanel : function(panel){
36544         if(this.tabs && (panel = this.getPanel(panel))){
36545             this.tabs.unhideTab(panel.getEl().id);
36546         }
36547     },
36548
36549     clearPanels : function(){
36550         while(this.panels.getCount() > 0){
36551              this.remove(this.panels.first());
36552         }
36553     },
36554
36555     /**
36556      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36557      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36558      * @param {Boolean} preservePanel Overrides the config preservePanel option
36559      * @return {Roo.ContentPanel} The panel that was removed
36560      */
36561     remove : function(panel, preservePanel)
36562     {
36563         panel = this.getPanel(panel);
36564         if(!panel){
36565             return null;
36566         }
36567         var e = {};
36568         this.fireEvent("beforeremove", this, panel, e);
36569         if(e.cancel === true){
36570             return null;
36571         }
36572         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36573         var panelId = panel.getId();
36574         this.panels.removeKey(panelId);
36575         if(preservePanel){
36576             document.body.appendChild(panel.getEl().dom);
36577         }
36578         if(this.tabs){
36579             this.tabs.removeTab(panel.getEl().id);
36580         }else if (!preservePanel){
36581             this.bodyEl.dom.removeChild(panel.getEl().dom);
36582         }
36583         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36584             var p = this.panels.first();
36585             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36586             tempEl.appendChild(p.getEl().dom);
36587             this.bodyEl.update("");
36588             this.bodyEl.dom.appendChild(p.getEl().dom);
36589             tempEl = null;
36590             this.updateTitle(p.getTitle());
36591             this.tabs = null;
36592             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36593             this.setActivePanel(p);
36594         }
36595         panel.setRegion(null);
36596         if(this.activePanel == panel){
36597             this.activePanel = null;
36598         }
36599         if(this.config.autoDestroy !== false && preservePanel !== true){
36600             try{panel.destroy();}catch(e){}
36601         }
36602         this.fireEvent("panelremoved", this, panel);
36603         return panel;
36604     },
36605
36606     /**
36607      * Returns the TabPanel component used by this region
36608      * @return {Roo.TabPanel}
36609      */
36610     getTabs : function(){
36611         return this.tabs;
36612     },
36613
36614     createTool : function(parentEl, className){
36615         var btn = Roo.DomHelper.append(parentEl, {
36616             tag: "div",
36617             cls: "x-layout-tools-button",
36618             children: [ {
36619                 tag: "div",
36620                 cls: "roo-layout-tools-button-inner " + className,
36621                 html: "&#160;"
36622             }]
36623         }, true);
36624         btn.addClassOnOver("roo-layout-tools-button-over");
36625         return btn;
36626     }
36627 });/*
36628  * Based on:
36629  * Ext JS Library 1.1.1
36630  * Copyright(c) 2006-2007, Ext JS, LLC.
36631  *
36632  * Originally Released Under LGPL - original licence link has changed is not relivant.
36633  *
36634  * Fork - LGPL
36635  * <script type="text/javascript">
36636  */
36637  
36638
36639
36640 /**
36641  * @class Roo.SplitLayoutRegion
36642  * @extends Roo.LayoutRegion
36643  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36644  */
36645 Roo.bootstrap.layout.Split = function(config){
36646     this.cursor = config.cursor;
36647     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36648 };
36649
36650 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36651 {
36652     splitTip : "Drag to resize.",
36653     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36654     useSplitTips : false,
36655
36656     applyConfig : function(config){
36657         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36658     },
36659     
36660     onRender : function(ctr,pos) {
36661         
36662         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36663         if(!this.config.split){
36664             return;
36665         }
36666         if(!this.split){
36667             
36668             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36669                             tag: "div",
36670                             id: this.el.id + "-split",
36671                             cls: "roo-layout-split roo-layout-split-"+this.position,
36672                             html: "&#160;"
36673             });
36674             /** The SplitBar for this region 
36675             * @type Roo.SplitBar */
36676             // does not exist yet...
36677             Roo.log([this.position, this.orientation]);
36678             
36679             this.split = new Roo.bootstrap.SplitBar({
36680                 dragElement : splitEl,
36681                 resizingElement: this.el,
36682                 orientation : this.orientation
36683             });
36684             
36685             this.split.on("moved", this.onSplitMove, this);
36686             this.split.useShim = this.config.useShim === true;
36687             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36688             if(this.useSplitTips){
36689                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36690             }
36691             //if(config.collapsible){
36692             //    this.split.el.on("dblclick", this.collapse,  this);
36693             //}
36694         }
36695         if(typeof this.config.minSize != "undefined"){
36696             this.split.minSize = this.config.minSize;
36697         }
36698         if(typeof this.config.maxSize != "undefined"){
36699             this.split.maxSize = this.config.maxSize;
36700         }
36701         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36702             this.hideSplitter();
36703         }
36704         
36705     },
36706
36707     getHMaxSize : function(){
36708          var cmax = this.config.maxSize || 10000;
36709          var center = this.mgr.getRegion("center");
36710          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36711     },
36712
36713     getVMaxSize : function(){
36714          var cmax = this.config.maxSize || 10000;
36715          var center = this.mgr.getRegion("center");
36716          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36717     },
36718
36719     onSplitMove : function(split, newSize){
36720         this.fireEvent("resized", this, newSize);
36721     },
36722     
36723     /** 
36724      * Returns the {@link Roo.SplitBar} for this region.
36725      * @return {Roo.SplitBar}
36726      */
36727     getSplitBar : function(){
36728         return this.split;
36729     },
36730     
36731     hide : function(){
36732         this.hideSplitter();
36733         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36734     },
36735
36736     hideSplitter : function(){
36737         if(this.split){
36738             this.split.el.setLocation(-2000,-2000);
36739             this.split.el.hide();
36740         }
36741     },
36742
36743     show : function(){
36744         if(this.split){
36745             this.split.el.show();
36746         }
36747         Roo.bootstrap.layout.Split.superclass.show.call(this);
36748     },
36749     
36750     beforeSlide: function(){
36751         if(Roo.isGecko){// firefox overflow auto bug workaround
36752             this.bodyEl.clip();
36753             if(this.tabs) {
36754                 this.tabs.bodyEl.clip();
36755             }
36756             if(this.activePanel){
36757                 this.activePanel.getEl().clip();
36758                 
36759                 if(this.activePanel.beforeSlide){
36760                     this.activePanel.beforeSlide();
36761                 }
36762             }
36763         }
36764     },
36765     
36766     afterSlide : function(){
36767         if(Roo.isGecko){// firefox overflow auto bug workaround
36768             this.bodyEl.unclip();
36769             if(this.tabs) {
36770                 this.tabs.bodyEl.unclip();
36771             }
36772             if(this.activePanel){
36773                 this.activePanel.getEl().unclip();
36774                 if(this.activePanel.afterSlide){
36775                     this.activePanel.afterSlide();
36776                 }
36777             }
36778         }
36779     },
36780
36781     initAutoHide : function(){
36782         if(this.autoHide !== false){
36783             if(!this.autoHideHd){
36784                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36785                 this.autoHideHd = {
36786                     "mouseout": function(e){
36787                         if(!e.within(this.el, true)){
36788                             st.delay(500);
36789                         }
36790                     },
36791                     "mouseover" : function(e){
36792                         st.cancel();
36793                     },
36794                     scope : this
36795                 };
36796             }
36797             this.el.on(this.autoHideHd);
36798         }
36799     },
36800
36801     clearAutoHide : function(){
36802         if(this.autoHide !== false){
36803             this.el.un("mouseout", this.autoHideHd.mouseout);
36804             this.el.un("mouseover", this.autoHideHd.mouseover);
36805         }
36806     },
36807
36808     clearMonitor : function(){
36809         Roo.get(document).un("click", this.slideInIf, this);
36810     },
36811
36812     // these names are backwards but not changed for compat
36813     slideOut : function(){
36814         if(this.isSlid || this.el.hasActiveFx()){
36815             return;
36816         }
36817         this.isSlid = true;
36818         if(this.collapseBtn){
36819             this.collapseBtn.hide();
36820         }
36821         this.closeBtnState = this.closeBtn.getStyle('display');
36822         this.closeBtn.hide();
36823         if(this.stickBtn){
36824             this.stickBtn.show();
36825         }
36826         this.el.show();
36827         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36828         this.beforeSlide();
36829         this.el.setStyle("z-index", 10001);
36830         this.el.slideIn(this.getSlideAnchor(), {
36831             callback: function(){
36832                 this.afterSlide();
36833                 this.initAutoHide();
36834                 Roo.get(document).on("click", this.slideInIf, this);
36835                 this.fireEvent("slideshow", this);
36836             },
36837             scope: this,
36838             block: true
36839         });
36840     },
36841
36842     afterSlideIn : function(){
36843         this.clearAutoHide();
36844         this.isSlid = false;
36845         this.clearMonitor();
36846         this.el.setStyle("z-index", "");
36847         if(this.collapseBtn){
36848             this.collapseBtn.show();
36849         }
36850         this.closeBtn.setStyle('display', this.closeBtnState);
36851         if(this.stickBtn){
36852             this.stickBtn.hide();
36853         }
36854         this.fireEvent("slidehide", this);
36855     },
36856
36857     slideIn : function(cb){
36858         if(!this.isSlid || this.el.hasActiveFx()){
36859             Roo.callback(cb);
36860             return;
36861         }
36862         this.isSlid = false;
36863         this.beforeSlide();
36864         this.el.slideOut(this.getSlideAnchor(), {
36865             callback: function(){
36866                 this.el.setLeftTop(-10000, -10000);
36867                 this.afterSlide();
36868                 this.afterSlideIn();
36869                 Roo.callback(cb);
36870             },
36871             scope: this,
36872             block: true
36873         });
36874     },
36875     
36876     slideInIf : function(e){
36877         if(!e.within(this.el)){
36878             this.slideIn();
36879         }
36880     },
36881
36882     animateCollapse : function(){
36883         this.beforeSlide();
36884         this.el.setStyle("z-index", 20000);
36885         var anchor = this.getSlideAnchor();
36886         this.el.slideOut(anchor, {
36887             callback : function(){
36888                 this.el.setStyle("z-index", "");
36889                 this.collapsedEl.slideIn(anchor, {duration:.3});
36890                 this.afterSlide();
36891                 this.el.setLocation(-10000,-10000);
36892                 this.el.hide();
36893                 this.fireEvent("collapsed", this);
36894             },
36895             scope: this,
36896             block: true
36897         });
36898     },
36899
36900     animateExpand : function(){
36901         this.beforeSlide();
36902         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36903         this.el.setStyle("z-index", 20000);
36904         this.collapsedEl.hide({
36905             duration:.1
36906         });
36907         this.el.slideIn(this.getSlideAnchor(), {
36908             callback : function(){
36909                 this.el.setStyle("z-index", "");
36910                 this.afterSlide();
36911                 if(this.split){
36912                     this.split.el.show();
36913                 }
36914                 this.fireEvent("invalidated", this);
36915                 this.fireEvent("expanded", this);
36916             },
36917             scope: this,
36918             block: true
36919         });
36920     },
36921
36922     anchors : {
36923         "west" : "left",
36924         "east" : "right",
36925         "north" : "top",
36926         "south" : "bottom"
36927     },
36928
36929     sanchors : {
36930         "west" : "l",
36931         "east" : "r",
36932         "north" : "t",
36933         "south" : "b"
36934     },
36935
36936     canchors : {
36937         "west" : "tl-tr",
36938         "east" : "tr-tl",
36939         "north" : "tl-bl",
36940         "south" : "bl-tl"
36941     },
36942
36943     getAnchor : function(){
36944         return this.anchors[this.position];
36945     },
36946
36947     getCollapseAnchor : function(){
36948         return this.canchors[this.position];
36949     },
36950
36951     getSlideAnchor : function(){
36952         return this.sanchors[this.position];
36953     },
36954
36955     getAlignAdj : function(){
36956         var cm = this.cmargins;
36957         switch(this.position){
36958             case "west":
36959                 return [0, 0];
36960             break;
36961             case "east":
36962                 return [0, 0];
36963             break;
36964             case "north":
36965                 return [0, 0];
36966             break;
36967             case "south":
36968                 return [0, 0];
36969             break;
36970         }
36971     },
36972
36973     getExpandAdj : function(){
36974         var c = this.collapsedEl, cm = this.cmargins;
36975         switch(this.position){
36976             case "west":
36977                 return [-(cm.right+c.getWidth()+cm.left), 0];
36978             break;
36979             case "east":
36980                 return [cm.right+c.getWidth()+cm.left, 0];
36981             break;
36982             case "north":
36983                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36984             break;
36985             case "south":
36986                 return [0, cm.top+cm.bottom+c.getHeight()];
36987             break;
36988         }
36989     }
36990 });/*
36991  * Based on:
36992  * Ext JS Library 1.1.1
36993  * Copyright(c) 2006-2007, Ext JS, LLC.
36994  *
36995  * Originally Released Under LGPL - original licence link has changed is not relivant.
36996  *
36997  * Fork - LGPL
36998  * <script type="text/javascript">
36999  */
37000 /*
37001  * These classes are private internal classes
37002  */
37003 Roo.bootstrap.layout.Center = function(config){
37004     config.region = "center";
37005     Roo.bootstrap.layout.Region.call(this, config);
37006     this.visible = true;
37007     this.minWidth = config.minWidth || 20;
37008     this.minHeight = config.minHeight || 20;
37009 };
37010
37011 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
37012     hide : function(){
37013         // center panel can't be hidden
37014     },
37015     
37016     show : function(){
37017         // center panel can't be hidden
37018     },
37019     
37020     getMinWidth: function(){
37021         return this.minWidth;
37022     },
37023     
37024     getMinHeight: function(){
37025         return this.minHeight;
37026     }
37027 });
37028
37029
37030
37031
37032  
37033
37034
37035
37036
37037
37038
37039 Roo.bootstrap.layout.North = function(config)
37040 {
37041     config.region = 'north';
37042     config.cursor = 'n-resize';
37043     
37044     Roo.bootstrap.layout.Split.call(this, config);
37045     
37046     
37047     if(this.split){
37048         this.split.placement = Roo.bootstrap.SplitBar.TOP;
37049         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37050         this.split.el.addClass("roo-layout-split-v");
37051     }
37052     var size = config.initialSize || config.height;
37053     if(typeof size != "undefined"){
37054         this.el.setHeight(size);
37055     }
37056 };
37057 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
37058 {
37059     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37060     
37061     
37062     
37063     getBox : function(){
37064         if(this.collapsed){
37065             return this.collapsedEl.getBox();
37066         }
37067         var box = this.el.getBox();
37068         if(this.split){
37069             box.height += this.split.el.getHeight();
37070         }
37071         return box;
37072     },
37073     
37074     updateBox : function(box){
37075         if(this.split && !this.collapsed){
37076             box.height -= this.split.el.getHeight();
37077             this.split.el.setLeft(box.x);
37078             this.split.el.setTop(box.y+box.height);
37079             this.split.el.setWidth(box.width);
37080         }
37081         if(this.collapsed){
37082             this.updateBody(box.width, null);
37083         }
37084         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37085     }
37086 });
37087
37088
37089
37090
37091
37092 Roo.bootstrap.layout.South = function(config){
37093     config.region = 'south';
37094     config.cursor = 's-resize';
37095     Roo.bootstrap.layout.Split.call(this, config);
37096     if(this.split){
37097         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
37098         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37099         this.split.el.addClass("roo-layout-split-v");
37100     }
37101     var size = config.initialSize || config.height;
37102     if(typeof size != "undefined"){
37103         this.el.setHeight(size);
37104     }
37105 };
37106
37107 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
37108     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37109     getBox : function(){
37110         if(this.collapsed){
37111             return this.collapsedEl.getBox();
37112         }
37113         var box = this.el.getBox();
37114         if(this.split){
37115             var sh = this.split.el.getHeight();
37116             box.height += sh;
37117             box.y -= sh;
37118         }
37119         return box;
37120     },
37121     
37122     updateBox : function(box){
37123         if(this.split && !this.collapsed){
37124             var sh = this.split.el.getHeight();
37125             box.height -= sh;
37126             box.y += sh;
37127             this.split.el.setLeft(box.x);
37128             this.split.el.setTop(box.y-sh);
37129             this.split.el.setWidth(box.width);
37130         }
37131         if(this.collapsed){
37132             this.updateBody(box.width, null);
37133         }
37134         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37135     }
37136 });
37137
37138 Roo.bootstrap.layout.East = function(config){
37139     config.region = "east";
37140     config.cursor = "e-resize";
37141     Roo.bootstrap.layout.Split.call(this, config);
37142     if(this.split){
37143         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
37144         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37145         this.split.el.addClass("roo-layout-split-h");
37146     }
37147     var size = config.initialSize || config.width;
37148     if(typeof size != "undefined"){
37149         this.el.setWidth(size);
37150     }
37151 };
37152 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
37153     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37154     getBox : function(){
37155         if(this.collapsed){
37156             return this.collapsedEl.getBox();
37157         }
37158         var box = this.el.getBox();
37159         if(this.split){
37160             var sw = this.split.el.getWidth();
37161             box.width += sw;
37162             box.x -= sw;
37163         }
37164         return box;
37165     },
37166
37167     updateBox : function(box){
37168         if(this.split && !this.collapsed){
37169             var sw = this.split.el.getWidth();
37170             box.width -= sw;
37171             this.split.el.setLeft(box.x);
37172             this.split.el.setTop(box.y);
37173             this.split.el.setHeight(box.height);
37174             box.x += sw;
37175         }
37176         if(this.collapsed){
37177             this.updateBody(null, box.height);
37178         }
37179         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37180     }
37181 });
37182
37183 Roo.bootstrap.layout.West = function(config){
37184     config.region = "west";
37185     config.cursor = "w-resize";
37186     
37187     Roo.bootstrap.layout.Split.call(this, config);
37188     if(this.split){
37189         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37190         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37191         this.split.el.addClass("roo-layout-split-h");
37192     }
37193     
37194 };
37195 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37196     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37197     
37198     onRender: function(ctr, pos)
37199     {
37200         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37201         var size = this.config.initialSize || this.config.width;
37202         if(typeof size != "undefined"){
37203             this.el.setWidth(size);
37204         }
37205     },
37206     
37207     getBox : function(){
37208         if(this.collapsed){
37209             return this.collapsedEl.getBox();
37210         }
37211         var box = this.el.getBox();
37212         if(this.split){
37213             box.width += this.split.el.getWidth();
37214         }
37215         return box;
37216     },
37217     
37218     updateBox : function(box){
37219         if(this.split && !this.collapsed){
37220             var sw = this.split.el.getWidth();
37221             box.width -= sw;
37222             this.split.el.setLeft(box.x+box.width);
37223             this.split.el.setTop(box.y);
37224             this.split.el.setHeight(box.height);
37225         }
37226         if(this.collapsed){
37227             this.updateBody(null, box.height);
37228         }
37229         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37230     }
37231 });Roo.namespace("Roo.bootstrap.panel");/*
37232  * Based on:
37233  * Ext JS Library 1.1.1
37234  * Copyright(c) 2006-2007, Ext JS, LLC.
37235  *
37236  * Originally Released Under LGPL - original licence link has changed is not relivant.
37237  *
37238  * Fork - LGPL
37239  * <script type="text/javascript">
37240  */
37241 /**
37242  * @class Roo.ContentPanel
37243  * @extends Roo.util.Observable
37244  * A basic ContentPanel element.
37245  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
37246  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
37247  * @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
37248  * @cfg {Boolean}   closable      True if the panel can be closed/removed
37249  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
37250  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37251  * @cfg {Toolbar}   toolbar       A toolbar for this panel
37252  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
37253  * @cfg {String} title          The title for this panel
37254  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37255  * @cfg {String} url            Calls {@link #setUrl} with this value
37256  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37257  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
37258  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
37259  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
37260  * @cfg {Boolean} badges render the badges
37261
37262  * @constructor
37263  * Create a new ContentPanel.
37264  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37265  * @param {String/Object} config A string to set only the title or a config object
37266  * @param {String} content (optional) Set the HTML content for this panel
37267  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37268  */
37269 Roo.bootstrap.panel.Content = function( config){
37270     
37271     this.tpl = config.tpl || false;
37272     
37273     var el = config.el;
37274     var content = config.content;
37275
37276     if(config.autoCreate){ // xtype is available if this is called from factory
37277         el = Roo.id();
37278     }
37279     this.el = Roo.get(el);
37280     if(!this.el && config && config.autoCreate){
37281         if(typeof config.autoCreate == "object"){
37282             if(!config.autoCreate.id){
37283                 config.autoCreate.id = config.id||el;
37284             }
37285             this.el = Roo.DomHelper.append(document.body,
37286                         config.autoCreate, true);
37287         }else{
37288             var elcfg =  {   tag: "div",
37289                             cls: "roo-layout-inactive-content",
37290                             id: config.id||el
37291                             };
37292             if (config.html) {
37293                 elcfg.html = config.html;
37294                 
37295             }
37296                         
37297             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37298         }
37299     } 
37300     this.closable = false;
37301     this.loaded = false;
37302     this.active = false;
37303    
37304       
37305     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37306         
37307         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37308         
37309         this.wrapEl = this.el; //this.el.wrap();
37310         var ti = [];
37311         if (config.toolbar.items) {
37312             ti = config.toolbar.items ;
37313             delete config.toolbar.items ;
37314         }
37315         
37316         var nitems = [];
37317         this.toolbar.render(this.wrapEl, 'before');
37318         for(var i =0;i < ti.length;i++) {
37319           //  Roo.log(['add child', items[i]]);
37320             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37321         }
37322         this.toolbar.items = nitems;
37323         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37324         delete config.toolbar;
37325         
37326     }
37327     /*
37328     // xtype created footer. - not sure if will work as we normally have to render first..
37329     if (this.footer && !this.footer.el && this.footer.xtype) {
37330         if (!this.wrapEl) {
37331             this.wrapEl = this.el.wrap();
37332         }
37333     
37334         this.footer.container = this.wrapEl.createChild();
37335          
37336         this.footer = Roo.factory(this.footer, Roo);
37337         
37338     }
37339     */
37340     
37341      if(typeof config == "string"){
37342         this.title = config;
37343     }else{
37344         Roo.apply(this, config);
37345     }
37346     
37347     if(this.resizeEl){
37348         this.resizeEl = Roo.get(this.resizeEl, true);
37349     }else{
37350         this.resizeEl = this.el;
37351     }
37352     // handle view.xtype
37353     
37354  
37355     
37356     
37357     this.addEvents({
37358         /**
37359          * @event activate
37360          * Fires when this panel is activated. 
37361          * @param {Roo.ContentPanel} this
37362          */
37363         "activate" : true,
37364         /**
37365          * @event deactivate
37366          * Fires when this panel is activated. 
37367          * @param {Roo.ContentPanel} this
37368          */
37369         "deactivate" : true,
37370
37371         /**
37372          * @event resize
37373          * Fires when this panel is resized if fitToFrame is true.
37374          * @param {Roo.ContentPanel} this
37375          * @param {Number} width The width after any component adjustments
37376          * @param {Number} height The height after any component adjustments
37377          */
37378         "resize" : true,
37379         
37380          /**
37381          * @event render
37382          * Fires when this tab is created
37383          * @param {Roo.ContentPanel} this
37384          */
37385         "render" : true
37386         
37387         
37388         
37389     });
37390     
37391
37392     
37393     
37394     if(this.autoScroll){
37395         this.resizeEl.setStyle("overflow", "auto");
37396     } else {
37397         // fix randome scrolling
37398         //this.el.on('scroll', function() {
37399         //    Roo.log('fix random scolling');
37400         //    this.scrollTo('top',0); 
37401         //});
37402     }
37403     content = content || this.content;
37404     if(content){
37405         this.setContent(content);
37406     }
37407     if(config && config.url){
37408         this.setUrl(this.url, this.params, this.loadOnce);
37409     }
37410     
37411     
37412     
37413     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37414     
37415     if (this.view && typeof(this.view.xtype) != 'undefined') {
37416         this.view.el = this.el.appendChild(document.createElement("div"));
37417         this.view = Roo.factory(this.view); 
37418         this.view.render  &&  this.view.render(false, '');  
37419     }
37420     
37421     
37422     this.fireEvent('render', this);
37423 };
37424
37425 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37426     
37427     tabTip : '',
37428     
37429     setRegion : function(region){
37430         this.region = region;
37431         this.setActiveClass(region && !this.background);
37432     },
37433     
37434     
37435     setActiveClass: function(state)
37436     {
37437         if(state){
37438            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37439            this.el.setStyle('position','relative');
37440         }else{
37441            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37442            this.el.setStyle('position', 'absolute');
37443         } 
37444     },
37445     
37446     /**
37447      * Returns the toolbar for this Panel if one was configured. 
37448      * @return {Roo.Toolbar} 
37449      */
37450     getToolbar : function(){
37451         return this.toolbar;
37452     },
37453     
37454     setActiveState : function(active)
37455     {
37456         this.active = active;
37457         this.setActiveClass(active);
37458         if(!active){
37459             if(this.fireEvent("deactivate", this) === false){
37460                 return false;
37461             }
37462             return true;
37463         }
37464         this.fireEvent("activate", this);
37465         return true;
37466     },
37467     /**
37468      * Updates this panel's element
37469      * @param {String} content The new content
37470      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37471     */
37472     setContent : function(content, loadScripts){
37473         this.el.update(content, loadScripts);
37474     },
37475
37476     ignoreResize : function(w, h){
37477         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37478             return true;
37479         }else{
37480             this.lastSize = {width: w, height: h};
37481             return false;
37482         }
37483     },
37484     /**
37485      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37486      * @return {Roo.UpdateManager} The UpdateManager
37487      */
37488     getUpdateManager : function(){
37489         return this.el.getUpdateManager();
37490     },
37491      /**
37492      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37493      * @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:
37494 <pre><code>
37495 panel.load({
37496     url: "your-url.php",
37497     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37498     callback: yourFunction,
37499     scope: yourObject, //(optional scope)
37500     discardUrl: false,
37501     nocache: false,
37502     text: "Loading...",
37503     timeout: 30,
37504     scripts: false
37505 });
37506 </code></pre>
37507      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37508      * 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.
37509      * @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}
37510      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37511      * @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.
37512      * @return {Roo.ContentPanel} this
37513      */
37514     load : function(){
37515         var um = this.el.getUpdateManager();
37516         um.update.apply(um, arguments);
37517         return this;
37518     },
37519
37520
37521     /**
37522      * 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.
37523      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37524      * @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)
37525      * @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)
37526      * @return {Roo.UpdateManager} The UpdateManager
37527      */
37528     setUrl : function(url, params, loadOnce){
37529         if(this.refreshDelegate){
37530             this.removeListener("activate", this.refreshDelegate);
37531         }
37532         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37533         this.on("activate", this.refreshDelegate);
37534         return this.el.getUpdateManager();
37535     },
37536     
37537     _handleRefresh : function(url, params, loadOnce){
37538         if(!loadOnce || !this.loaded){
37539             var updater = this.el.getUpdateManager();
37540             updater.update(url, params, this._setLoaded.createDelegate(this));
37541         }
37542     },
37543     
37544     _setLoaded : function(){
37545         this.loaded = true;
37546     }, 
37547     
37548     /**
37549      * Returns this panel's id
37550      * @return {String} 
37551      */
37552     getId : function(){
37553         return this.el.id;
37554     },
37555     
37556     /** 
37557      * Returns this panel's element - used by regiosn to add.
37558      * @return {Roo.Element} 
37559      */
37560     getEl : function(){
37561         return this.wrapEl || this.el;
37562     },
37563     
37564    
37565     
37566     adjustForComponents : function(width, height)
37567     {
37568         //Roo.log('adjustForComponents ');
37569         if(this.resizeEl != this.el){
37570             width -= this.el.getFrameWidth('lr');
37571             height -= this.el.getFrameWidth('tb');
37572         }
37573         if(this.toolbar){
37574             var te = this.toolbar.getEl();
37575             te.setWidth(width);
37576             height -= te.getHeight();
37577         }
37578         if(this.footer){
37579             var te = this.footer.getEl();
37580             te.setWidth(width);
37581             height -= te.getHeight();
37582         }
37583         
37584         
37585         if(this.adjustments){
37586             width += this.adjustments[0];
37587             height += this.adjustments[1];
37588         }
37589         return {"width": width, "height": height};
37590     },
37591     
37592     setSize : function(width, height){
37593         if(this.fitToFrame && !this.ignoreResize(width, height)){
37594             if(this.fitContainer && this.resizeEl != this.el){
37595                 this.el.setSize(width, height);
37596             }
37597             var size = this.adjustForComponents(width, height);
37598             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37599             this.fireEvent('resize', this, size.width, size.height);
37600         }
37601     },
37602     
37603     /**
37604      * Returns this panel's title
37605      * @return {String} 
37606      */
37607     getTitle : function(){
37608         
37609         if (typeof(this.title) != 'object') {
37610             return this.title;
37611         }
37612         
37613         var t = '';
37614         for (var k in this.title) {
37615             if (!this.title.hasOwnProperty(k)) {
37616                 continue;
37617             }
37618             
37619             if (k.indexOf('-') >= 0) {
37620                 var s = k.split('-');
37621                 for (var i = 0; i<s.length; i++) {
37622                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37623                 }
37624             } else {
37625                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37626             }
37627         }
37628         return t;
37629     },
37630     
37631     /**
37632      * Set this panel's title
37633      * @param {String} title
37634      */
37635     setTitle : function(title){
37636         this.title = title;
37637         if(this.region){
37638             this.region.updatePanelTitle(this, title);
37639         }
37640     },
37641     
37642     /**
37643      * Returns true is this panel was configured to be closable
37644      * @return {Boolean} 
37645      */
37646     isClosable : function(){
37647         return this.closable;
37648     },
37649     
37650     beforeSlide : function(){
37651         this.el.clip();
37652         this.resizeEl.clip();
37653     },
37654     
37655     afterSlide : function(){
37656         this.el.unclip();
37657         this.resizeEl.unclip();
37658     },
37659     
37660     /**
37661      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37662      *   Will fail silently if the {@link #setUrl} method has not been called.
37663      *   This does not activate the panel, just updates its content.
37664      */
37665     refresh : function(){
37666         if(this.refreshDelegate){
37667            this.loaded = false;
37668            this.refreshDelegate();
37669         }
37670     },
37671     
37672     /**
37673      * Destroys this panel
37674      */
37675     destroy : function(){
37676         this.el.removeAllListeners();
37677         var tempEl = document.createElement("span");
37678         tempEl.appendChild(this.el.dom);
37679         tempEl.innerHTML = "";
37680         this.el.remove();
37681         this.el = null;
37682     },
37683     
37684     /**
37685      * form - if the content panel contains a form - this is a reference to it.
37686      * @type {Roo.form.Form}
37687      */
37688     form : false,
37689     /**
37690      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37691      *    This contains a reference to it.
37692      * @type {Roo.View}
37693      */
37694     view : false,
37695     
37696       /**
37697      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37698      * <pre><code>
37699
37700 layout.addxtype({
37701        xtype : 'Form',
37702        items: [ .... ]
37703    }
37704 );
37705
37706 </code></pre>
37707      * @param {Object} cfg Xtype definition of item to add.
37708      */
37709     
37710     
37711     getChildContainer: function () {
37712         return this.getEl();
37713     }
37714     
37715     
37716     /*
37717         var  ret = new Roo.factory(cfg);
37718         return ret;
37719         
37720         
37721         // add form..
37722         if (cfg.xtype.match(/^Form$/)) {
37723             
37724             var el;
37725             //if (this.footer) {
37726             //    el = this.footer.container.insertSibling(false, 'before');
37727             //} else {
37728                 el = this.el.createChild();
37729             //}
37730
37731             this.form = new  Roo.form.Form(cfg);
37732             
37733             
37734             if ( this.form.allItems.length) {
37735                 this.form.render(el.dom);
37736             }
37737             return this.form;
37738         }
37739         // should only have one of theses..
37740         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37741             // views.. should not be just added - used named prop 'view''
37742             
37743             cfg.el = this.el.appendChild(document.createElement("div"));
37744             // factory?
37745             
37746             var ret = new Roo.factory(cfg);
37747              
37748              ret.render && ret.render(false, ''); // render blank..
37749             this.view = ret;
37750             return ret;
37751         }
37752         return false;
37753     }
37754     \*/
37755 });
37756  
37757 /**
37758  * @class Roo.bootstrap.panel.Grid
37759  * @extends Roo.bootstrap.panel.Content
37760  * @constructor
37761  * Create a new GridPanel.
37762  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37763  * @param {Object} config A the config object
37764   
37765  */
37766
37767
37768
37769 Roo.bootstrap.panel.Grid = function(config)
37770 {
37771     
37772       
37773     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37774         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37775
37776     config.el = this.wrapper;
37777     //this.el = this.wrapper;
37778     
37779       if (config.container) {
37780         // ctor'ed from a Border/panel.grid
37781         
37782         
37783         this.wrapper.setStyle("overflow", "hidden");
37784         this.wrapper.addClass('roo-grid-container');
37785
37786     }
37787     
37788     
37789     if(config.toolbar){
37790         var tool_el = this.wrapper.createChild();    
37791         this.toolbar = Roo.factory(config.toolbar);
37792         var ti = [];
37793         if (config.toolbar.items) {
37794             ti = config.toolbar.items ;
37795             delete config.toolbar.items ;
37796         }
37797         
37798         var nitems = [];
37799         this.toolbar.render(tool_el);
37800         for(var i =0;i < ti.length;i++) {
37801           //  Roo.log(['add child', items[i]]);
37802             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37803         }
37804         this.toolbar.items = nitems;
37805         
37806         delete config.toolbar;
37807     }
37808     
37809     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37810     config.grid.scrollBody = true;;
37811     config.grid.monitorWindowResize = false; // turn off autosizing
37812     config.grid.autoHeight = false;
37813     config.grid.autoWidth = false;
37814     
37815     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37816     
37817     if (config.background) {
37818         // render grid on panel activation (if panel background)
37819         this.on('activate', function(gp) {
37820             if (!gp.grid.rendered) {
37821                 gp.grid.render(this.wrapper);
37822                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37823             }
37824         });
37825             
37826     } else {
37827         this.grid.render(this.wrapper);
37828         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37829
37830     }
37831     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37832     // ??? needed ??? config.el = this.wrapper;
37833     
37834     
37835     
37836   
37837     // xtype created footer. - not sure if will work as we normally have to render first..
37838     if (this.footer && !this.footer.el && this.footer.xtype) {
37839         
37840         var ctr = this.grid.getView().getFooterPanel(true);
37841         this.footer.dataSource = this.grid.dataSource;
37842         this.footer = Roo.factory(this.footer, Roo);
37843         this.footer.render(ctr);
37844         
37845     }
37846     
37847     
37848     
37849     
37850      
37851 };
37852
37853 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37854     getId : function(){
37855         return this.grid.id;
37856     },
37857     
37858     /**
37859      * Returns the grid for this panel
37860      * @return {Roo.bootstrap.Table} 
37861      */
37862     getGrid : function(){
37863         return this.grid;    
37864     },
37865     
37866     setSize : function(width, height){
37867         if(!this.ignoreResize(width, height)){
37868             var grid = this.grid;
37869             var size = this.adjustForComponents(width, height);
37870             var gridel = grid.getGridEl();
37871             gridel.setSize(size.width, size.height);
37872             /*
37873             var thd = grid.getGridEl().select('thead',true).first();
37874             var tbd = grid.getGridEl().select('tbody', true).first();
37875             if (tbd) {
37876                 tbd.setSize(width, height - thd.getHeight());
37877             }
37878             */
37879             grid.autoSize();
37880         }
37881     },
37882      
37883     
37884     
37885     beforeSlide : function(){
37886         this.grid.getView().scroller.clip();
37887     },
37888     
37889     afterSlide : function(){
37890         this.grid.getView().scroller.unclip();
37891     },
37892     
37893     destroy : function(){
37894         this.grid.destroy();
37895         delete this.grid;
37896         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37897     }
37898 });
37899
37900 /**
37901  * @class Roo.bootstrap.panel.Nest
37902  * @extends Roo.bootstrap.panel.Content
37903  * @constructor
37904  * Create a new Panel, that can contain a layout.Border.
37905  * 
37906  * 
37907  * @param {Roo.BorderLayout} layout The layout for this panel
37908  * @param {String/Object} config A string to set only the title or a config object
37909  */
37910 Roo.bootstrap.panel.Nest = function(config)
37911 {
37912     // construct with only one argument..
37913     /* FIXME - implement nicer consturctors
37914     if (layout.layout) {
37915         config = layout;
37916         layout = config.layout;
37917         delete config.layout;
37918     }
37919     if (layout.xtype && !layout.getEl) {
37920         // then layout needs constructing..
37921         layout = Roo.factory(layout, Roo);
37922     }
37923     */
37924     
37925     config.el =  config.layout.getEl();
37926     
37927     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37928     
37929     config.layout.monitorWindowResize = false; // turn off autosizing
37930     this.layout = config.layout;
37931     this.layout.getEl().addClass("roo-layout-nested-layout");
37932     this.layout.parent = this;
37933     
37934     
37935     
37936     
37937 };
37938
37939 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37940
37941     setSize : function(width, height){
37942         if(!this.ignoreResize(width, height)){
37943             var size = this.adjustForComponents(width, height);
37944             var el = this.layout.getEl();
37945             if (size.height < 1) {
37946                 el.setWidth(size.width);   
37947             } else {
37948                 el.setSize(size.width, size.height);
37949             }
37950             var touch = el.dom.offsetWidth;
37951             this.layout.layout();
37952             // ie requires a double layout on the first pass
37953             if(Roo.isIE && !this.initialized){
37954                 this.initialized = true;
37955                 this.layout.layout();
37956             }
37957         }
37958     },
37959     
37960     // activate all subpanels if not currently active..
37961     
37962     setActiveState : function(active){
37963         this.active = active;
37964         this.setActiveClass(active);
37965         
37966         if(!active){
37967             this.fireEvent("deactivate", this);
37968             return;
37969         }
37970         
37971         this.fireEvent("activate", this);
37972         // not sure if this should happen before or after..
37973         if (!this.layout) {
37974             return; // should not happen..
37975         }
37976         var reg = false;
37977         for (var r in this.layout.regions) {
37978             reg = this.layout.getRegion(r);
37979             if (reg.getActivePanel()) {
37980                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37981                 reg.setActivePanel(reg.getActivePanel());
37982                 continue;
37983             }
37984             if (!reg.panels.length) {
37985                 continue;
37986             }
37987             reg.showPanel(reg.getPanel(0));
37988         }
37989         
37990         
37991         
37992         
37993     },
37994     
37995     /**
37996      * Returns the nested BorderLayout for this panel
37997      * @return {Roo.BorderLayout} 
37998      */
37999     getLayout : function(){
38000         return this.layout;
38001     },
38002     
38003      /**
38004      * Adds a xtype elements to the layout of the nested panel
38005      * <pre><code>
38006
38007 panel.addxtype({
38008        xtype : 'ContentPanel',
38009        region: 'west',
38010        items: [ .... ]
38011    }
38012 );
38013
38014 panel.addxtype({
38015         xtype : 'NestedLayoutPanel',
38016         region: 'west',
38017         layout: {
38018            center: { },
38019            west: { }   
38020         },
38021         items : [ ... list of content panels or nested layout panels.. ]
38022    }
38023 );
38024 </code></pre>
38025      * @param {Object} cfg Xtype definition of item to add.
38026      */
38027     addxtype : function(cfg) {
38028         return this.layout.addxtype(cfg);
38029     
38030     }
38031 });/*
38032  * Based on:
38033  * Ext JS Library 1.1.1
38034  * Copyright(c) 2006-2007, Ext JS, LLC.
38035  *
38036  * Originally Released Under LGPL - original licence link has changed is not relivant.
38037  *
38038  * Fork - LGPL
38039  * <script type="text/javascript">
38040  */
38041 /**
38042  * @class Roo.TabPanel
38043  * @extends Roo.util.Observable
38044  * A lightweight tab container.
38045  * <br><br>
38046  * Usage:
38047  * <pre><code>
38048 // basic tabs 1, built from existing content
38049 var tabs = new Roo.TabPanel("tabs1");
38050 tabs.addTab("script", "View Script");
38051 tabs.addTab("markup", "View Markup");
38052 tabs.activate("script");
38053
38054 // more advanced tabs, built from javascript
38055 var jtabs = new Roo.TabPanel("jtabs");
38056 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
38057
38058 // set up the UpdateManager
38059 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
38060 var updater = tab2.getUpdateManager();
38061 updater.setDefaultUrl("ajax1.htm");
38062 tab2.on('activate', updater.refresh, updater, true);
38063
38064 // Use setUrl for Ajax loading
38065 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
38066 tab3.setUrl("ajax2.htm", null, true);
38067
38068 // Disabled tab
38069 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
38070 tab4.disable();
38071
38072 jtabs.activate("jtabs-1");
38073  * </code></pre>
38074  * @constructor
38075  * Create a new TabPanel.
38076  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
38077  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
38078  */
38079 Roo.bootstrap.panel.Tabs = function(config){
38080     /**
38081     * The container element for this TabPanel.
38082     * @type Roo.Element
38083     */
38084     this.el = Roo.get(config.el);
38085     delete config.el;
38086     if(config){
38087         if(typeof config == "boolean"){
38088             this.tabPosition = config ? "bottom" : "top";
38089         }else{
38090             Roo.apply(this, config);
38091         }
38092     }
38093     
38094     if(this.tabPosition == "bottom"){
38095         // if tabs are at the bottom = create the body first.
38096         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38097         this.el.addClass("roo-tabs-bottom");
38098     }
38099     // next create the tabs holders
38100     
38101     if (this.tabPosition == "west"){
38102         
38103         var reg = this.region; // fake it..
38104         while (reg) {
38105             if (!reg.mgr.parent) {
38106                 break;
38107             }
38108             reg = reg.mgr.parent.region;
38109         }
38110         Roo.log("got nest?");
38111         Roo.log(reg);
38112         if (reg.mgr.getRegion('west')) {
38113             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
38114             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
38115             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38116             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38117             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38118         
38119             
38120         }
38121         
38122         
38123     } else {
38124      
38125         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
38126         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38127         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38128         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38129     }
38130     
38131     
38132     if(Roo.isIE){
38133         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
38134     }
38135     
38136     // finally - if tabs are at the top, then create the body last..
38137     if(this.tabPosition != "bottom"){
38138         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
38139          * @type Roo.Element
38140          */
38141         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38142         this.el.addClass("roo-tabs-top");
38143     }
38144     this.items = [];
38145
38146     this.bodyEl.setStyle("position", "relative");
38147
38148     this.active = null;
38149     this.activateDelegate = this.activate.createDelegate(this);
38150
38151     this.addEvents({
38152         /**
38153          * @event tabchange
38154          * Fires when the active tab changes
38155          * @param {Roo.TabPanel} this
38156          * @param {Roo.TabPanelItem} activePanel The new active tab
38157          */
38158         "tabchange": true,
38159         /**
38160          * @event beforetabchange
38161          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
38162          * @param {Roo.TabPanel} this
38163          * @param {Object} e Set cancel to true on this object to cancel the tab change
38164          * @param {Roo.TabPanelItem} tab The tab being changed to
38165          */
38166         "beforetabchange" : true
38167     });
38168
38169     Roo.EventManager.onWindowResize(this.onResize, this);
38170     this.cpad = this.el.getPadding("lr");
38171     this.hiddenCount = 0;
38172
38173
38174     // toolbar on the tabbar support...
38175     if (this.toolbar) {
38176         alert("no toolbar support yet");
38177         this.toolbar  = false;
38178         /*
38179         var tcfg = this.toolbar;
38180         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
38181         this.toolbar = new Roo.Toolbar(tcfg);
38182         if (Roo.isSafari) {
38183             var tbl = tcfg.container.child('table', true);
38184             tbl.setAttribute('width', '100%');
38185         }
38186         */
38187         
38188     }
38189    
38190
38191
38192     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
38193 };
38194
38195 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
38196     /*
38197      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
38198      */
38199     tabPosition : "top",
38200     /*
38201      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
38202      */
38203     currentTabWidth : 0,
38204     /*
38205      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
38206      */
38207     minTabWidth : 40,
38208     /*
38209      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
38210      */
38211     maxTabWidth : 250,
38212     /*
38213      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
38214      */
38215     preferredTabWidth : 175,
38216     /*
38217      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
38218      */
38219     resizeTabs : false,
38220     /*
38221      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38222      */
38223     monitorResize : true,
38224     /*
38225      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
38226      */
38227     toolbar : false,  // set by caller..
38228     
38229     region : false, /// set by caller
38230     
38231     disableTooltips : true, // not used yet...
38232
38233     /**
38234      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38235      * @param {String} id The id of the div to use <b>or create</b>
38236      * @param {String} text The text for the tab
38237      * @param {String} content (optional) Content to put in the TabPanelItem body
38238      * @param {Boolean} closable (optional) True to create a close icon on the tab
38239      * @return {Roo.TabPanelItem} The created TabPanelItem
38240      */
38241     addTab : function(id, text, content, closable, tpl)
38242     {
38243         var item = new Roo.bootstrap.panel.TabItem({
38244             panel: this,
38245             id : id,
38246             text : text,
38247             closable : closable,
38248             tpl : tpl
38249         });
38250         this.addTabItem(item);
38251         if(content){
38252             item.setContent(content);
38253         }
38254         return item;
38255     },
38256
38257     /**
38258      * Returns the {@link Roo.TabPanelItem} with the specified id/index
38259      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38260      * @return {Roo.TabPanelItem}
38261      */
38262     getTab : function(id){
38263         return this.items[id];
38264     },
38265
38266     /**
38267      * Hides the {@link Roo.TabPanelItem} with the specified id/index
38268      * @param {String/Number} id The id or index of the TabPanelItem to hide.
38269      */
38270     hideTab : function(id){
38271         var t = this.items[id];
38272         if(!t.isHidden()){
38273            t.setHidden(true);
38274            this.hiddenCount++;
38275            this.autoSizeTabs();
38276         }
38277     },
38278
38279     /**
38280      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38281      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38282      */
38283     unhideTab : function(id){
38284         var t = this.items[id];
38285         if(t.isHidden()){
38286            t.setHidden(false);
38287            this.hiddenCount--;
38288            this.autoSizeTabs();
38289         }
38290     },
38291
38292     /**
38293      * Adds an existing {@link Roo.TabPanelItem}.
38294      * @param {Roo.TabPanelItem} item The TabPanelItem to add
38295      */
38296     addTabItem : function(item)
38297     {
38298         this.items[item.id] = item;
38299         this.items.push(item);
38300         this.autoSizeTabs();
38301       //  if(this.resizeTabs){
38302     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38303   //         this.autoSizeTabs();
38304 //        }else{
38305 //            item.autoSize();
38306        // }
38307     },
38308
38309     /**
38310      * Removes a {@link Roo.TabPanelItem}.
38311      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38312      */
38313     removeTab : function(id){
38314         var items = this.items;
38315         var tab = items[id];
38316         if(!tab) { return; }
38317         var index = items.indexOf(tab);
38318         if(this.active == tab && items.length > 1){
38319             var newTab = this.getNextAvailable(index);
38320             if(newTab) {
38321                 newTab.activate();
38322             }
38323         }
38324         this.stripEl.dom.removeChild(tab.pnode.dom);
38325         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38326             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38327         }
38328         items.splice(index, 1);
38329         delete this.items[tab.id];
38330         tab.fireEvent("close", tab);
38331         tab.purgeListeners();
38332         this.autoSizeTabs();
38333     },
38334
38335     getNextAvailable : function(start){
38336         var items = this.items;
38337         var index = start;
38338         // look for a next tab that will slide over to
38339         // replace the one being removed
38340         while(index < items.length){
38341             var item = items[++index];
38342             if(item && !item.isHidden()){
38343                 return item;
38344             }
38345         }
38346         // if one isn't found select the previous tab (on the left)
38347         index = start;
38348         while(index >= 0){
38349             var item = items[--index];
38350             if(item && !item.isHidden()){
38351                 return item;
38352             }
38353         }
38354         return null;
38355     },
38356
38357     /**
38358      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38359      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38360      */
38361     disableTab : function(id){
38362         var tab = this.items[id];
38363         if(tab && this.active != tab){
38364             tab.disable();
38365         }
38366     },
38367
38368     /**
38369      * Enables a {@link Roo.TabPanelItem} that is disabled.
38370      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38371      */
38372     enableTab : function(id){
38373         var tab = this.items[id];
38374         tab.enable();
38375     },
38376
38377     /**
38378      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38379      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38380      * @return {Roo.TabPanelItem} The TabPanelItem.
38381      */
38382     activate : function(id)
38383     {
38384         //Roo.log('activite:'  + id);
38385         
38386         var tab = this.items[id];
38387         if(!tab){
38388             return null;
38389         }
38390         if(tab == this.active || tab.disabled){
38391             return tab;
38392         }
38393         var e = {};
38394         this.fireEvent("beforetabchange", this, e, tab);
38395         if(e.cancel !== true && !tab.disabled){
38396             if(this.active){
38397                 this.active.hide();
38398             }
38399             this.active = this.items[id];
38400             this.active.show();
38401             this.fireEvent("tabchange", this, this.active);
38402         }
38403         return tab;
38404     },
38405
38406     /**
38407      * Gets the active {@link Roo.TabPanelItem}.
38408      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38409      */
38410     getActiveTab : function(){
38411         return this.active;
38412     },
38413
38414     /**
38415      * Updates the tab body element to fit the height of the container element
38416      * for overflow scrolling
38417      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38418      */
38419     syncHeight : function(targetHeight){
38420         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38421         var bm = this.bodyEl.getMargins();
38422         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38423         this.bodyEl.setHeight(newHeight);
38424         return newHeight;
38425     },
38426
38427     onResize : function(){
38428         if(this.monitorResize){
38429             this.autoSizeTabs();
38430         }
38431     },
38432
38433     /**
38434      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38435      */
38436     beginUpdate : function(){
38437         this.updating = true;
38438     },
38439
38440     /**
38441      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38442      */
38443     endUpdate : function(){
38444         this.updating = false;
38445         this.autoSizeTabs();
38446     },
38447
38448     /**
38449      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38450      */
38451     autoSizeTabs : function()
38452     {
38453         var count = this.items.length;
38454         var vcount = count - this.hiddenCount;
38455         
38456         if (vcount < 2) {
38457             this.stripEl.hide();
38458         } else {
38459             this.stripEl.show();
38460         }
38461         
38462         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38463             return;
38464         }
38465         
38466         
38467         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38468         var availWidth = Math.floor(w / vcount);
38469         var b = this.stripBody;
38470         if(b.getWidth() > w){
38471             var tabs = this.items;
38472             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38473             if(availWidth < this.minTabWidth){
38474                 /*if(!this.sleft){    // incomplete scrolling code
38475                     this.createScrollButtons();
38476                 }
38477                 this.showScroll();
38478                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38479             }
38480         }else{
38481             if(this.currentTabWidth < this.preferredTabWidth){
38482                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38483             }
38484         }
38485     },
38486
38487     /**
38488      * Returns the number of tabs in this TabPanel.
38489      * @return {Number}
38490      */
38491      getCount : function(){
38492          return this.items.length;
38493      },
38494
38495     /**
38496      * Resizes all the tabs to the passed width
38497      * @param {Number} The new width
38498      */
38499     setTabWidth : function(width){
38500         this.currentTabWidth = width;
38501         for(var i = 0, len = this.items.length; i < len; i++) {
38502                 if(!this.items[i].isHidden()) {
38503                 this.items[i].setWidth(width);
38504             }
38505         }
38506     },
38507
38508     /**
38509      * Destroys this TabPanel
38510      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38511      */
38512     destroy : function(removeEl){
38513         Roo.EventManager.removeResizeListener(this.onResize, this);
38514         for(var i = 0, len = this.items.length; i < len; i++){
38515             this.items[i].purgeListeners();
38516         }
38517         if(removeEl === true){
38518             this.el.update("");
38519             this.el.remove();
38520         }
38521     },
38522     
38523     createStrip : function(container)
38524     {
38525         var strip = document.createElement("nav");
38526         strip.className = Roo.bootstrap.version == 4 ?
38527             "navbar-light bg-light" : 
38528             "navbar navbar-default"; //"x-tabs-wrap";
38529         container.appendChild(strip);
38530         return strip;
38531     },
38532     
38533     createStripList : function(strip)
38534     {
38535         // div wrapper for retard IE
38536         // returns the "tr" element.
38537         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38538         //'<div class="x-tabs-strip-wrap">'+
38539           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38540           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38541         return strip.firstChild; //.firstChild.firstChild.firstChild;
38542     },
38543     createBody : function(container)
38544     {
38545         var body = document.createElement("div");
38546         Roo.id(body, "tab-body");
38547         //Roo.fly(body).addClass("x-tabs-body");
38548         Roo.fly(body).addClass("tab-content");
38549         container.appendChild(body);
38550         return body;
38551     },
38552     createItemBody :function(bodyEl, id){
38553         var body = Roo.getDom(id);
38554         if(!body){
38555             body = document.createElement("div");
38556             body.id = id;
38557         }
38558         //Roo.fly(body).addClass("x-tabs-item-body");
38559         Roo.fly(body).addClass("tab-pane");
38560          bodyEl.insertBefore(body, bodyEl.firstChild);
38561         return body;
38562     },
38563     /** @private */
38564     createStripElements :  function(stripEl, text, closable, tpl)
38565     {
38566         var td = document.createElement("li"); // was td..
38567         td.className = 'nav-item';
38568         
38569         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38570         
38571         
38572         stripEl.appendChild(td);
38573         /*if(closable){
38574             td.className = "x-tabs-closable";
38575             if(!this.closeTpl){
38576                 this.closeTpl = new Roo.Template(
38577                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38578                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38579                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38580                 );
38581             }
38582             var el = this.closeTpl.overwrite(td, {"text": text});
38583             var close = el.getElementsByTagName("div")[0];
38584             var inner = el.getElementsByTagName("em")[0];
38585             return {"el": el, "close": close, "inner": inner};
38586         } else {
38587         */
38588         // not sure what this is..
38589 //            if(!this.tabTpl){
38590                 //this.tabTpl = new Roo.Template(
38591                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38592                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38593                 //);
38594 //                this.tabTpl = new Roo.Template(
38595 //                   '<a href="#">' +
38596 //                   '<span unselectable="on"' +
38597 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38598 //                            ' >{text}</span></a>'
38599 //                );
38600 //                
38601 //            }
38602
38603
38604             var template = tpl || this.tabTpl || false;
38605             
38606             if(!template){
38607                 template =  new Roo.Template(
38608                         Roo.bootstrap.version == 4 ? 
38609                             (
38610                                 '<a class="nav-link" href="#" unselectable="on"' +
38611                                      (this.disableTooltips ? '' : ' title="{text}"') +
38612                                      ' >{text}</a>'
38613                             ) : (
38614                                 '<a class="nav-link" href="#">' +
38615                                 '<span unselectable="on"' +
38616                                          (this.disableTooltips ? '' : ' title="{text}"') +
38617                                     ' >{text}</span></a>'
38618                             )
38619                 );
38620             }
38621             
38622             switch (typeof(template)) {
38623                 case 'object' :
38624                     break;
38625                 case 'string' :
38626                     template = new Roo.Template(template);
38627                     break;
38628                 default :
38629                     break;
38630             }
38631             
38632             var el = template.overwrite(td, {"text": text});
38633             
38634             var inner = el.getElementsByTagName("span")[0];
38635             
38636             return {"el": el, "inner": inner};
38637             
38638     }
38639         
38640     
38641 });
38642
38643 /**
38644  * @class Roo.TabPanelItem
38645  * @extends Roo.util.Observable
38646  * Represents an individual item (tab plus body) in a TabPanel.
38647  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38648  * @param {String} id The id of this TabPanelItem
38649  * @param {String} text The text for the tab of this TabPanelItem
38650  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38651  */
38652 Roo.bootstrap.panel.TabItem = function(config){
38653     /**
38654      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38655      * @type Roo.TabPanel
38656      */
38657     this.tabPanel = config.panel;
38658     /**
38659      * The id for this TabPanelItem
38660      * @type String
38661      */
38662     this.id = config.id;
38663     /** @private */
38664     this.disabled = false;
38665     /** @private */
38666     this.text = config.text;
38667     /** @private */
38668     this.loaded = false;
38669     this.closable = config.closable;
38670
38671     /**
38672      * The body element for this TabPanelItem.
38673      * @type Roo.Element
38674      */
38675     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38676     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38677     this.bodyEl.setStyle("display", "block");
38678     this.bodyEl.setStyle("zoom", "1");
38679     //this.hideAction();
38680
38681     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38682     /** @private */
38683     this.el = Roo.get(els.el);
38684     this.inner = Roo.get(els.inner, true);
38685      this.textEl = Roo.bootstrap.version == 4 ?
38686         this.el : Roo.get(this.el.dom.firstChild, true);
38687
38688     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
38689     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
38690
38691     
38692 //    this.el.on("mousedown", this.onTabMouseDown, this);
38693     this.el.on("click", this.onTabClick, this);
38694     /** @private */
38695     if(config.closable){
38696         var c = Roo.get(els.close, true);
38697         c.dom.title = this.closeText;
38698         c.addClassOnOver("close-over");
38699         c.on("click", this.closeClick, this);
38700      }
38701
38702     this.addEvents({
38703          /**
38704          * @event activate
38705          * Fires when this tab becomes the active tab.
38706          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38707          * @param {Roo.TabPanelItem} this
38708          */
38709         "activate": true,
38710         /**
38711          * @event beforeclose
38712          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38713          * @param {Roo.TabPanelItem} this
38714          * @param {Object} e Set cancel to true on this object to cancel the close.
38715          */
38716         "beforeclose": true,
38717         /**
38718          * @event close
38719          * Fires when this tab is closed.
38720          * @param {Roo.TabPanelItem} this
38721          */
38722          "close": true,
38723         /**
38724          * @event deactivate
38725          * Fires when this tab is no longer the active tab.
38726          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38727          * @param {Roo.TabPanelItem} this
38728          */
38729          "deactivate" : true
38730     });
38731     this.hidden = false;
38732
38733     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38734 };
38735
38736 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38737            {
38738     purgeListeners : function(){
38739        Roo.util.Observable.prototype.purgeListeners.call(this);
38740        this.el.removeAllListeners();
38741     },
38742     /**
38743      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38744      */
38745     show : function(){
38746         this.status_node.addClass("active");
38747         this.showAction();
38748         if(Roo.isOpera){
38749             this.tabPanel.stripWrap.repaint();
38750         }
38751         this.fireEvent("activate", this.tabPanel, this);
38752     },
38753
38754     /**
38755      * Returns true if this tab is the active tab.
38756      * @return {Boolean}
38757      */
38758     isActive : function(){
38759         return this.tabPanel.getActiveTab() == this;
38760     },
38761
38762     /**
38763      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38764      */
38765     hide : function(){
38766         this.status_node.removeClass("active");
38767         this.hideAction();
38768         this.fireEvent("deactivate", this.tabPanel, this);
38769     },
38770
38771     hideAction : function(){
38772         this.bodyEl.hide();
38773         this.bodyEl.setStyle("position", "absolute");
38774         this.bodyEl.setLeft("-20000px");
38775         this.bodyEl.setTop("-20000px");
38776     },
38777
38778     showAction : function(){
38779         this.bodyEl.setStyle("position", "relative");
38780         this.bodyEl.setTop("");
38781         this.bodyEl.setLeft("");
38782         this.bodyEl.show();
38783     },
38784
38785     /**
38786      * Set the tooltip for the tab.
38787      * @param {String} tooltip The tab's tooltip
38788      */
38789     setTooltip : function(text){
38790         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38791             this.textEl.dom.qtip = text;
38792             this.textEl.dom.removeAttribute('title');
38793         }else{
38794             this.textEl.dom.title = text;
38795         }
38796     },
38797
38798     onTabClick : function(e){
38799         e.preventDefault();
38800         this.tabPanel.activate(this.id);
38801     },
38802
38803     onTabMouseDown : function(e){
38804         e.preventDefault();
38805         this.tabPanel.activate(this.id);
38806     },
38807 /*
38808     getWidth : function(){
38809         return this.inner.getWidth();
38810     },
38811
38812     setWidth : function(width){
38813         var iwidth = width - this.linode.getPadding("lr");
38814         this.inner.setWidth(iwidth);
38815         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38816         this.linode.setWidth(width);
38817     },
38818 */
38819     /**
38820      * Show or hide the tab
38821      * @param {Boolean} hidden True to hide or false to show.
38822      */
38823     setHidden : function(hidden){
38824         this.hidden = hidden;
38825         this.linode.setStyle("display", hidden ? "none" : "");
38826     },
38827
38828     /**
38829      * Returns true if this tab is "hidden"
38830      * @return {Boolean}
38831      */
38832     isHidden : function(){
38833         return this.hidden;
38834     },
38835
38836     /**
38837      * Returns the text for this tab
38838      * @return {String}
38839      */
38840     getText : function(){
38841         return this.text;
38842     },
38843     /*
38844     autoSize : function(){
38845         //this.el.beginMeasure();
38846         this.textEl.setWidth(1);
38847         /*
38848          *  #2804 [new] Tabs in Roojs
38849          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38850          */
38851         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38852         //this.el.endMeasure();
38853     //},
38854
38855     /**
38856      * Sets the text for the tab (Note: this also sets the tooltip text)
38857      * @param {String} text The tab's text and tooltip
38858      */
38859     setText : function(text){
38860         this.text = text;
38861         this.textEl.update(text);
38862         this.setTooltip(text);
38863         //if(!this.tabPanel.resizeTabs){
38864         //    this.autoSize();
38865         //}
38866     },
38867     /**
38868      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38869      */
38870     activate : function(){
38871         this.tabPanel.activate(this.id);
38872     },
38873
38874     /**
38875      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38876      */
38877     disable : function(){
38878         if(this.tabPanel.active != this){
38879             this.disabled = true;
38880             this.status_node.addClass("disabled");
38881         }
38882     },
38883
38884     /**
38885      * Enables this TabPanelItem if it was previously disabled.
38886      */
38887     enable : function(){
38888         this.disabled = false;
38889         this.status_node.removeClass("disabled");
38890     },
38891
38892     /**
38893      * Sets the content for this TabPanelItem.
38894      * @param {String} content The content
38895      * @param {Boolean} loadScripts true to look for and load scripts
38896      */
38897     setContent : function(content, loadScripts){
38898         this.bodyEl.update(content, loadScripts);
38899     },
38900
38901     /**
38902      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38903      * @return {Roo.UpdateManager} The UpdateManager
38904      */
38905     getUpdateManager : function(){
38906         return this.bodyEl.getUpdateManager();
38907     },
38908
38909     /**
38910      * Set a URL to be used to load the content for this TabPanelItem.
38911      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38912      * @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)
38913      * @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)
38914      * @return {Roo.UpdateManager} The UpdateManager
38915      */
38916     setUrl : function(url, params, loadOnce){
38917         if(this.refreshDelegate){
38918             this.un('activate', this.refreshDelegate);
38919         }
38920         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38921         this.on("activate", this.refreshDelegate);
38922         return this.bodyEl.getUpdateManager();
38923     },
38924
38925     /** @private */
38926     _handleRefresh : function(url, params, loadOnce){
38927         if(!loadOnce || !this.loaded){
38928             var updater = this.bodyEl.getUpdateManager();
38929             updater.update(url, params, this._setLoaded.createDelegate(this));
38930         }
38931     },
38932
38933     /**
38934      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38935      *   Will fail silently if the setUrl method has not been called.
38936      *   This does not activate the panel, just updates its content.
38937      */
38938     refresh : function(){
38939         if(this.refreshDelegate){
38940            this.loaded = false;
38941            this.refreshDelegate();
38942         }
38943     },
38944
38945     /** @private */
38946     _setLoaded : function(){
38947         this.loaded = true;
38948     },
38949
38950     /** @private */
38951     closeClick : function(e){
38952         var o = {};
38953         e.stopEvent();
38954         this.fireEvent("beforeclose", this, o);
38955         if(o.cancel !== true){
38956             this.tabPanel.removeTab(this.id);
38957         }
38958     },
38959     /**
38960      * The text displayed in the tooltip for the close icon.
38961      * @type String
38962      */
38963     closeText : "Close this tab"
38964 });
38965 /**
38966 *    This script refer to:
38967 *    Title: International Telephone Input
38968 *    Author: Jack O'Connor
38969 *    Code version:  v12.1.12
38970 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38971 **/
38972
38973 Roo.bootstrap.PhoneInputData = function() {
38974     var d = [
38975       [
38976         "Afghanistan (‫افغانستان‬‎)",
38977         "af",
38978         "93"
38979       ],
38980       [
38981         "Albania (Shqipëri)",
38982         "al",
38983         "355"
38984       ],
38985       [
38986         "Algeria (‫الجزائر‬‎)",
38987         "dz",
38988         "213"
38989       ],
38990       [
38991         "American Samoa",
38992         "as",
38993         "1684"
38994       ],
38995       [
38996         "Andorra",
38997         "ad",
38998         "376"
38999       ],
39000       [
39001         "Angola",
39002         "ao",
39003         "244"
39004       ],
39005       [
39006         "Anguilla",
39007         "ai",
39008         "1264"
39009       ],
39010       [
39011         "Antigua and Barbuda",
39012         "ag",
39013         "1268"
39014       ],
39015       [
39016         "Argentina",
39017         "ar",
39018         "54"
39019       ],
39020       [
39021         "Armenia (Հայաստան)",
39022         "am",
39023         "374"
39024       ],
39025       [
39026         "Aruba",
39027         "aw",
39028         "297"
39029       ],
39030       [
39031         "Australia",
39032         "au",
39033         "61",
39034         0
39035       ],
39036       [
39037         "Austria (Österreich)",
39038         "at",
39039         "43"
39040       ],
39041       [
39042         "Azerbaijan (Azərbaycan)",
39043         "az",
39044         "994"
39045       ],
39046       [
39047         "Bahamas",
39048         "bs",
39049         "1242"
39050       ],
39051       [
39052         "Bahrain (‫البحرين‬‎)",
39053         "bh",
39054         "973"
39055       ],
39056       [
39057         "Bangladesh (বাংলাদেশ)",
39058         "bd",
39059         "880"
39060       ],
39061       [
39062         "Barbados",
39063         "bb",
39064         "1246"
39065       ],
39066       [
39067         "Belarus (Беларусь)",
39068         "by",
39069         "375"
39070       ],
39071       [
39072         "Belgium (België)",
39073         "be",
39074         "32"
39075       ],
39076       [
39077         "Belize",
39078         "bz",
39079         "501"
39080       ],
39081       [
39082         "Benin (Bénin)",
39083         "bj",
39084         "229"
39085       ],
39086       [
39087         "Bermuda",
39088         "bm",
39089         "1441"
39090       ],
39091       [
39092         "Bhutan (འབྲུག)",
39093         "bt",
39094         "975"
39095       ],
39096       [
39097         "Bolivia",
39098         "bo",
39099         "591"
39100       ],
39101       [
39102         "Bosnia and Herzegovina (Босна и Херцеговина)",
39103         "ba",
39104         "387"
39105       ],
39106       [
39107         "Botswana",
39108         "bw",
39109         "267"
39110       ],
39111       [
39112         "Brazil (Brasil)",
39113         "br",
39114         "55"
39115       ],
39116       [
39117         "British Indian Ocean Territory",
39118         "io",
39119         "246"
39120       ],
39121       [
39122         "British Virgin Islands",
39123         "vg",
39124         "1284"
39125       ],
39126       [
39127         "Brunei",
39128         "bn",
39129         "673"
39130       ],
39131       [
39132         "Bulgaria (България)",
39133         "bg",
39134         "359"
39135       ],
39136       [
39137         "Burkina Faso",
39138         "bf",
39139         "226"
39140       ],
39141       [
39142         "Burundi (Uburundi)",
39143         "bi",
39144         "257"
39145       ],
39146       [
39147         "Cambodia (កម្ពុជា)",
39148         "kh",
39149         "855"
39150       ],
39151       [
39152         "Cameroon (Cameroun)",
39153         "cm",
39154         "237"
39155       ],
39156       [
39157         "Canada",
39158         "ca",
39159         "1",
39160         1,
39161         ["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"]
39162       ],
39163       [
39164         "Cape Verde (Kabu Verdi)",
39165         "cv",
39166         "238"
39167       ],
39168       [
39169         "Caribbean Netherlands",
39170         "bq",
39171         "599",
39172         1
39173       ],
39174       [
39175         "Cayman Islands",
39176         "ky",
39177         "1345"
39178       ],
39179       [
39180         "Central African Republic (République centrafricaine)",
39181         "cf",
39182         "236"
39183       ],
39184       [
39185         "Chad (Tchad)",
39186         "td",
39187         "235"
39188       ],
39189       [
39190         "Chile",
39191         "cl",
39192         "56"
39193       ],
39194       [
39195         "China (中国)",
39196         "cn",
39197         "86"
39198       ],
39199       [
39200         "Christmas Island",
39201         "cx",
39202         "61",
39203         2
39204       ],
39205       [
39206         "Cocos (Keeling) Islands",
39207         "cc",
39208         "61",
39209         1
39210       ],
39211       [
39212         "Colombia",
39213         "co",
39214         "57"
39215       ],
39216       [
39217         "Comoros (‫جزر القمر‬‎)",
39218         "km",
39219         "269"
39220       ],
39221       [
39222         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
39223         "cd",
39224         "243"
39225       ],
39226       [
39227         "Congo (Republic) (Congo-Brazzaville)",
39228         "cg",
39229         "242"
39230       ],
39231       [
39232         "Cook Islands",
39233         "ck",
39234         "682"
39235       ],
39236       [
39237         "Costa Rica",
39238         "cr",
39239         "506"
39240       ],
39241       [
39242         "Côte d’Ivoire",
39243         "ci",
39244         "225"
39245       ],
39246       [
39247         "Croatia (Hrvatska)",
39248         "hr",
39249         "385"
39250       ],
39251       [
39252         "Cuba",
39253         "cu",
39254         "53"
39255       ],
39256       [
39257         "Curaçao",
39258         "cw",
39259         "599",
39260         0
39261       ],
39262       [
39263         "Cyprus (Κύπρος)",
39264         "cy",
39265         "357"
39266       ],
39267       [
39268         "Czech Republic (Česká republika)",
39269         "cz",
39270         "420"
39271       ],
39272       [
39273         "Denmark (Danmark)",
39274         "dk",
39275         "45"
39276       ],
39277       [
39278         "Djibouti",
39279         "dj",
39280         "253"
39281       ],
39282       [
39283         "Dominica",
39284         "dm",
39285         "1767"
39286       ],
39287       [
39288         "Dominican Republic (República Dominicana)",
39289         "do",
39290         "1",
39291         2,
39292         ["809", "829", "849"]
39293       ],
39294       [
39295         "Ecuador",
39296         "ec",
39297         "593"
39298       ],
39299       [
39300         "Egypt (‫مصر‬‎)",
39301         "eg",
39302         "20"
39303       ],
39304       [
39305         "El Salvador",
39306         "sv",
39307         "503"
39308       ],
39309       [
39310         "Equatorial Guinea (Guinea Ecuatorial)",
39311         "gq",
39312         "240"
39313       ],
39314       [
39315         "Eritrea",
39316         "er",
39317         "291"
39318       ],
39319       [
39320         "Estonia (Eesti)",
39321         "ee",
39322         "372"
39323       ],
39324       [
39325         "Ethiopia",
39326         "et",
39327         "251"
39328       ],
39329       [
39330         "Falkland Islands (Islas Malvinas)",
39331         "fk",
39332         "500"
39333       ],
39334       [
39335         "Faroe Islands (Føroyar)",
39336         "fo",
39337         "298"
39338       ],
39339       [
39340         "Fiji",
39341         "fj",
39342         "679"
39343       ],
39344       [
39345         "Finland (Suomi)",
39346         "fi",
39347         "358",
39348         0
39349       ],
39350       [
39351         "France",
39352         "fr",
39353         "33"
39354       ],
39355       [
39356         "French Guiana (Guyane française)",
39357         "gf",
39358         "594"
39359       ],
39360       [
39361         "French Polynesia (Polynésie française)",
39362         "pf",
39363         "689"
39364       ],
39365       [
39366         "Gabon",
39367         "ga",
39368         "241"
39369       ],
39370       [
39371         "Gambia",
39372         "gm",
39373         "220"
39374       ],
39375       [
39376         "Georgia (საქართველო)",
39377         "ge",
39378         "995"
39379       ],
39380       [
39381         "Germany (Deutschland)",
39382         "de",
39383         "49"
39384       ],
39385       [
39386         "Ghana (Gaana)",
39387         "gh",
39388         "233"
39389       ],
39390       [
39391         "Gibraltar",
39392         "gi",
39393         "350"
39394       ],
39395       [
39396         "Greece (Ελλάδα)",
39397         "gr",
39398         "30"
39399       ],
39400       [
39401         "Greenland (Kalaallit Nunaat)",
39402         "gl",
39403         "299"
39404       ],
39405       [
39406         "Grenada",
39407         "gd",
39408         "1473"
39409       ],
39410       [
39411         "Guadeloupe",
39412         "gp",
39413         "590",
39414         0
39415       ],
39416       [
39417         "Guam",
39418         "gu",
39419         "1671"
39420       ],
39421       [
39422         "Guatemala",
39423         "gt",
39424         "502"
39425       ],
39426       [
39427         "Guernsey",
39428         "gg",
39429         "44",
39430         1
39431       ],
39432       [
39433         "Guinea (Guinée)",
39434         "gn",
39435         "224"
39436       ],
39437       [
39438         "Guinea-Bissau (Guiné Bissau)",
39439         "gw",
39440         "245"
39441       ],
39442       [
39443         "Guyana",
39444         "gy",
39445         "592"
39446       ],
39447       [
39448         "Haiti",
39449         "ht",
39450         "509"
39451       ],
39452       [
39453         "Honduras",
39454         "hn",
39455         "504"
39456       ],
39457       [
39458         "Hong Kong (香港)",
39459         "hk",
39460         "852"
39461       ],
39462       [
39463         "Hungary (Magyarország)",
39464         "hu",
39465         "36"
39466       ],
39467       [
39468         "Iceland (Ísland)",
39469         "is",
39470         "354"
39471       ],
39472       [
39473         "India (भारत)",
39474         "in",
39475         "91"
39476       ],
39477       [
39478         "Indonesia",
39479         "id",
39480         "62"
39481       ],
39482       [
39483         "Iran (‫ایران‬‎)",
39484         "ir",
39485         "98"
39486       ],
39487       [
39488         "Iraq (‫العراق‬‎)",
39489         "iq",
39490         "964"
39491       ],
39492       [
39493         "Ireland",
39494         "ie",
39495         "353"
39496       ],
39497       [
39498         "Isle of Man",
39499         "im",
39500         "44",
39501         2
39502       ],
39503       [
39504         "Israel (‫ישראל‬‎)",
39505         "il",
39506         "972"
39507       ],
39508       [
39509         "Italy (Italia)",
39510         "it",
39511         "39",
39512         0
39513       ],
39514       [
39515         "Jamaica",
39516         "jm",
39517         "1876"
39518       ],
39519       [
39520         "Japan (日本)",
39521         "jp",
39522         "81"
39523       ],
39524       [
39525         "Jersey",
39526         "je",
39527         "44",
39528         3
39529       ],
39530       [
39531         "Jordan (‫الأردن‬‎)",
39532         "jo",
39533         "962"
39534       ],
39535       [
39536         "Kazakhstan (Казахстан)",
39537         "kz",
39538         "7",
39539         1
39540       ],
39541       [
39542         "Kenya",
39543         "ke",
39544         "254"
39545       ],
39546       [
39547         "Kiribati",
39548         "ki",
39549         "686"
39550       ],
39551       [
39552         "Kosovo",
39553         "xk",
39554         "383"
39555       ],
39556       [
39557         "Kuwait (‫الكويت‬‎)",
39558         "kw",
39559         "965"
39560       ],
39561       [
39562         "Kyrgyzstan (Кыргызстан)",
39563         "kg",
39564         "996"
39565       ],
39566       [
39567         "Laos (ລາວ)",
39568         "la",
39569         "856"
39570       ],
39571       [
39572         "Latvia (Latvija)",
39573         "lv",
39574         "371"
39575       ],
39576       [
39577         "Lebanon (‫لبنان‬‎)",
39578         "lb",
39579         "961"
39580       ],
39581       [
39582         "Lesotho",
39583         "ls",
39584         "266"
39585       ],
39586       [
39587         "Liberia",
39588         "lr",
39589         "231"
39590       ],
39591       [
39592         "Libya (‫ليبيا‬‎)",
39593         "ly",
39594         "218"
39595       ],
39596       [
39597         "Liechtenstein",
39598         "li",
39599         "423"
39600       ],
39601       [
39602         "Lithuania (Lietuva)",
39603         "lt",
39604         "370"
39605       ],
39606       [
39607         "Luxembourg",
39608         "lu",
39609         "352"
39610       ],
39611       [
39612         "Macau (澳門)",
39613         "mo",
39614         "853"
39615       ],
39616       [
39617         "Macedonia (FYROM) (Македонија)",
39618         "mk",
39619         "389"
39620       ],
39621       [
39622         "Madagascar (Madagasikara)",
39623         "mg",
39624         "261"
39625       ],
39626       [
39627         "Malawi",
39628         "mw",
39629         "265"
39630       ],
39631       [
39632         "Malaysia",
39633         "my",
39634         "60"
39635       ],
39636       [
39637         "Maldives",
39638         "mv",
39639         "960"
39640       ],
39641       [
39642         "Mali",
39643         "ml",
39644         "223"
39645       ],
39646       [
39647         "Malta",
39648         "mt",
39649         "356"
39650       ],
39651       [
39652         "Marshall Islands",
39653         "mh",
39654         "692"
39655       ],
39656       [
39657         "Martinique",
39658         "mq",
39659         "596"
39660       ],
39661       [
39662         "Mauritania (‫موريتانيا‬‎)",
39663         "mr",
39664         "222"
39665       ],
39666       [
39667         "Mauritius (Moris)",
39668         "mu",
39669         "230"
39670       ],
39671       [
39672         "Mayotte",
39673         "yt",
39674         "262",
39675         1
39676       ],
39677       [
39678         "Mexico (México)",
39679         "mx",
39680         "52"
39681       ],
39682       [
39683         "Micronesia",
39684         "fm",
39685         "691"
39686       ],
39687       [
39688         "Moldova (Republica Moldova)",
39689         "md",
39690         "373"
39691       ],
39692       [
39693         "Monaco",
39694         "mc",
39695         "377"
39696       ],
39697       [
39698         "Mongolia (Монгол)",
39699         "mn",
39700         "976"
39701       ],
39702       [
39703         "Montenegro (Crna Gora)",
39704         "me",
39705         "382"
39706       ],
39707       [
39708         "Montserrat",
39709         "ms",
39710         "1664"
39711       ],
39712       [
39713         "Morocco (‫المغرب‬‎)",
39714         "ma",
39715         "212",
39716         0
39717       ],
39718       [
39719         "Mozambique (Moçambique)",
39720         "mz",
39721         "258"
39722       ],
39723       [
39724         "Myanmar (Burma) (မြန်မာ)",
39725         "mm",
39726         "95"
39727       ],
39728       [
39729         "Namibia (Namibië)",
39730         "na",
39731         "264"
39732       ],
39733       [
39734         "Nauru",
39735         "nr",
39736         "674"
39737       ],
39738       [
39739         "Nepal (नेपाल)",
39740         "np",
39741         "977"
39742       ],
39743       [
39744         "Netherlands (Nederland)",
39745         "nl",
39746         "31"
39747       ],
39748       [
39749         "New Caledonia (Nouvelle-Calédonie)",
39750         "nc",
39751         "687"
39752       ],
39753       [
39754         "New Zealand",
39755         "nz",
39756         "64"
39757       ],
39758       [
39759         "Nicaragua",
39760         "ni",
39761         "505"
39762       ],
39763       [
39764         "Niger (Nijar)",
39765         "ne",
39766         "227"
39767       ],
39768       [
39769         "Nigeria",
39770         "ng",
39771         "234"
39772       ],
39773       [
39774         "Niue",
39775         "nu",
39776         "683"
39777       ],
39778       [
39779         "Norfolk Island",
39780         "nf",
39781         "672"
39782       ],
39783       [
39784         "North Korea (조선 민주주의 인민 공화국)",
39785         "kp",
39786         "850"
39787       ],
39788       [
39789         "Northern Mariana Islands",
39790         "mp",
39791         "1670"
39792       ],
39793       [
39794         "Norway (Norge)",
39795         "no",
39796         "47",
39797         0
39798       ],
39799       [
39800         "Oman (‫عُمان‬‎)",
39801         "om",
39802         "968"
39803       ],
39804       [
39805         "Pakistan (‫پاکستان‬‎)",
39806         "pk",
39807         "92"
39808       ],
39809       [
39810         "Palau",
39811         "pw",
39812         "680"
39813       ],
39814       [
39815         "Palestine (‫فلسطين‬‎)",
39816         "ps",
39817         "970"
39818       ],
39819       [
39820         "Panama (Panamá)",
39821         "pa",
39822         "507"
39823       ],
39824       [
39825         "Papua New Guinea",
39826         "pg",
39827         "675"
39828       ],
39829       [
39830         "Paraguay",
39831         "py",
39832         "595"
39833       ],
39834       [
39835         "Peru (Perú)",
39836         "pe",
39837         "51"
39838       ],
39839       [
39840         "Philippines",
39841         "ph",
39842         "63"
39843       ],
39844       [
39845         "Poland (Polska)",
39846         "pl",
39847         "48"
39848       ],
39849       [
39850         "Portugal",
39851         "pt",
39852         "351"
39853       ],
39854       [
39855         "Puerto Rico",
39856         "pr",
39857         "1",
39858         3,
39859         ["787", "939"]
39860       ],
39861       [
39862         "Qatar (‫قطر‬‎)",
39863         "qa",
39864         "974"
39865       ],
39866       [
39867         "Réunion (La Réunion)",
39868         "re",
39869         "262",
39870         0
39871       ],
39872       [
39873         "Romania (România)",
39874         "ro",
39875         "40"
39876       ],
39877       [
39878         "Russia (Россия)",
39879         "ru",
39880         "7",
39881         0
39882       ],
39883       [
39884         "Rwanda",
39885         "rw",
39886         "250"
39887       ],
39888       [
39889         "Saint Barthélemy",
39890         "bl",
39891         "590",
39892         1
39893       ],
39894       [
39895         "Saint Helena",
39896         "sh",
39897         "290"
39898       ],
39899       [
39900         "Saint Kitts and Nevis",
39901         "kn",
39902         "1869"
39903       ],
39904       [
39905         "Saint Lucia",
39906         "lc",
39907         "1758"
39908       ],
39909       [
39910         "Saint Martin (Saint-Martin (partie française))",
39911         "mf",
39912         "590",
39913         2
39914       ],
39915       [
39916         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39917         "pm",
39918         "508"
39919       ],
39920       [
39921         "Saint Vincent and the Grenadines",
39922         "vc",
39923         "1784"
39924       ],
39925       [
39926         "Samoa",
39927         "ws",
39928         "685"
39929       ],
39930       [
39931         "San Marino",
39932         "sm",
39933         "378"
39934       ],
39935       [
39936         "São Tomé and Príncipe (São Tomé e Príncipe)",
39937         "st",
39938         "239"
39939       ],
39940       [
39941         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39942         "sa",
39943         "966"
39944       ],
39945       [
39946         "Senegal (Sénégal)",
39947         "sn",
39948         "221"
39949       ],
39950       [
39951         "Serbia (Србија)",
39952         "rs",
39953         "381"
39954       ],
39955       [
39956         "Seychelles",
39957         "sc",
39958         "248"
39959       ],
39960       [
39961         "Sierra Leone",
39962         "sl",
39963         "232"
39964       ],
39965       [
39966         "Singapore",
39967         "sg",
39968         "65"
39969       ],
39970       [
39971         "Sint Maarten",
39972         "sx",
39973         "1721"
39974       ],
39975       [
39976         "Slovakia (Slovensko)",
39977         "sk",
39978         "421"
39979       ],
39980       [
39981         "Slovenia (Slovenija)",
39982         "si",
39983         "386"
39984       ],
39985       [
39986         "Solomon Islands",
39987         "sb",
39988         "677"
39989       ],
39990       [
39991         "Somalia (Soomaaliya)",
39992         "so",
39993         "252"
39994       ],
39995       [
39996         "South Africa",
39997         "za",
39998         "27"
39999       ],
40000       [
40001         "South Korea (대한민국)",
40002         "kr",
40003         "82"
40004       ],
40005       [
40006         "South Sudan (‫جنوب السودان‬‎)",
40007         "ss",
40008         "211"
40009       ],
40010       [
40011         "Spain (España)",
40012         "es",
40013         "34"
40014       ],
40015       [
40016         "Sri Lanka (ශ්‍රී ලංකාව)",
40017         "lk",
40018         "94"
40019       ],
40020       [
40021         "Sudan (‫السودان‬‎)",
40022         "sd",
40023         "249"
40024       ],
40025       [
40026         "Suriname",
40027         "sr",
40028         "597"
40029       ],
40030       [
40031         "Svalbard and Jan Mayen",
40032         "sj",
40033         "47",
40034         1
40035       ],
40036       [
40037         "Swaziland",
40038         "sz",
40039         "268"
40040       ],
40041       [
40042         "Sweden (Sverige)",
40043         "se",
40044         "46"
40045       ],
40046       [
40047         "Switzerland (Schweiz)",
40048         "ch",
40049         "41"
40050       ],
40051       [
40052         "Syria (‫سوريا‬‎)",
40053         "sy",
40054         "963"
40055       ],
40056       [
40057         "Taiwan (台灣)",
40058         "tw",
40059         "886"
40060       ],
40061       [
40062         "Tajikistan",
40063         "tj",
40064         "992"
40065       ],
40066       [
40067         "Tanzania",
40068         "tz",
40069         "255"
40070       ],
40071       [
40072         "Thailand (ไทย)",
40073         "th",
40074         "66"
40075       ],
40076       [
40077         "Timor-Leste",
40078         "tl",
40079         "670"
40080       ],
40081       [
40082         "Togo",
40083         "tg",
40084         "228"
40085       ],
40086       [
40087         "Tokelau",
40088         "tk",
40089         "690"
40090       ],
40091       [
40092         "Tonga",
40093         "to",
40094         "676"
40095       ],
40096       [
40097         "Trinidad and Tobago",
40098         "tt",
40099         "1868"
40100       ],
40101       [
40102         "Tunisia (‫تونس‬‎)",
40103         "tn",
40104         "216"
40105       ],
40106       [
40107         "Turkey (Türkiye)",
40108         "tr",
40109         "90"
40110       ],
40111       [
40112         "Turkmenistan",
40113         "tm",
40114         "993"
40115       ],
40116       [
40117         "Turks and Caicos Islands",
40118         "tc",
40119         "1649"
40120       ],
40121       [
40122         "Tuvalu",
40123         "tv",
40124         "688"
40125       ],
40126       [
40127         "U.S. Virgin Islands",
40128         "vi",
40129         "1340"
40130       ],
40131       [
40132         "Uganda",
40133         "ug",
40134         "256"
40135       ],
40136       [
40137         "Ukraine (Україна)",
40138         "ua",
40139         "380"
40140       ],
40141       [
40142         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
40143         "ae",
40144         "971"
40145       ],
40146       [
40147         "United Kingdom",
40148         "gb",
40149         "44",
40150         0
40151       ],
40152       [
40153         "United States",
40154         "us",
40155         "1",
40156         0
40157       ],
40158       [
40159         "Uruguay",
40160         "uy",
40161         "598"
40162       ],
40163       [
40164         "Uzbekistan (Oʻzbekiston)",
40165         "uz",
40166         "998"
40167       ],
40168       [
40169         "Vanuatu",
40170         "vu",
40171         "678"
40172       ],
40173       [
40174         "Vatican City (Città del Vaticano)",
40175         "va",
40176         "39",
40177         1
40178       ],
40179       [
40180         "Venezuela",
40181         "ve",
40182         "58"
40183       ],
40184       [
40185         "Vietnam (Việt Nam)",
40186         "vn",
40187         "84"
40188       ],
40189       [
40190         "Wallis and Futuna (Wallis-et-Futuna)",
40191         "wf",
40192         "681"
40193       ],
40194       [
40195         "Western Sahara (‫الصحراء الغربية‬‎)",
40196         "eh",
40197         "212",
40198         1
40199       ],
40200       [
40201         "Yemen (‫اليمن‬‎)",
40202         "ye",
40203         "967"
40204       ],
40205       [
40206         "Zambia",
40207         "zm",
40208         "260"
40209       ],
40210       [
40211         "Zimbabwe",
40212         "zw",
40213         "263"
40214       ],
40215       [
40216         "Åland Islands",
40217         "ax",
40218         "358",
40219         1
40220       ]
40221   ];
40222   
40223   return d;
40224 }/**
40225 *    This script refer to:
40226 *    Title: International Telephone Input
40227 *    Author: Jack O'Connor
40228 *    Code version:  v12.1.12
40229 *    Availability: https://github.com/jackocnr/intl-tel-input.git
40230 **/
40231
40232 /**
40233  * @class Roo.bootstrap.PhoneInput
40234  * @extends Roo.bootstrap.TriggerField
40235  * An input with International dial-code selection
40236  
40237  * @cfg {String} defaultDialCode default '+852'
40238  * @cfg {Array} preferedCountries default []
40239   
40240  * @constructor
40241  * Create a new PhoneInput.
40242  * @param {Object} config Configuration options
40243  */
40244
40245 Roo.bootstrap.PhoneInput = function(config) {
40246     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
40247 };
40248
40249 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
40250         
40251         listWidth: undefined,
40252         
40253         selectedClass: 'active',
40254         
40255         invalidClass : "has-warning",
40256         
40257         validClass: 'has-success',
40258         
40259         allowed: '0123456789',
40260         
40261         max_length: 15,
40262         
40263         /**
40264          * @cfg {String} defaultDialCode The default dial code when initializing the input
40265          */
40266         defaultDialCode: '+852',
40267         
40268         /**
40269          * @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
40270          */
40271         preferedCountries: false,
40272         
40273         getAutoCreate : function()
40274         {
40275             var data = Roo.bootstrap.PhoneInputData();
40276             var align = this.labelAlign || this.parentLabelAlign();
40277             var id = Roo.id();
40278             
40279             this.allCountries = [];
40280             this.dialCodeMapping = [];
40281             
40282             for (var i = 0; i < data.length; i++) {
40283               var c = data[i];
40284               this.allCountries[i] = {
40285                 name: c[0],
40286                 iso2: c[1],
40287                 dialCode: c[2],
40288                 priority: c[3] || 0,
40289                 areaCodes: c[4] || null
40290               };
40291               this.dialCodeMapping[c[2]] = {
40292                   name: c[0],
40293                   iso2: c[1],
40294                   priority: c[3] || 0,
40295                   areaCodes: c[4] || null
40296               };
40297             }
40298             
40299             var cfg = {
40300                 cls: 'form-group',
40301                 cn: []
40302             };
40303             
40304             var input =  {
40305                 tag: 'input',
40306                 id : id,
40307                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40308                 maxlength: this.max_length,
40309                 cls : 'form-control tel-input',
40310                 autocomplete: 'new-password'
40311             };
40312             
40313             var hiddenInput = {
40314                 tag: 'input',
40315                 type: 'hidden',
40316                 cls: 'hidden-tel-input'
40317             };
40318             
40319             if (this.name) {
40320                 hiddenInput.name = this.name;
40321             }
40322             
40323             if (this.disabled) {
40324                 input.disabled = true;
40325             }
40326             
40327             var flag_container = {
40328                 tag: 'div',
40329                 cls: 'flag-box',
40330                 cn: [
40331                     {
40332                         tag: 'div',
40333                         cls: 'flag'
40334                     },
40335                     {
40336                         tag: 'div',
40337                         cls: 'caret'
40338                     }
40339                 ]
40340             };
40341             
40342             var box = {
40343                 tag: 'div',
40344                 cls: this.hasFeedback ? 'has-feedback' : '',
40345                 cn: [
40346                     hiddenInput,
40347                     input,
40348                     {
40349                         tag: 'input',
40350                         cls: 'dial-code-holder',
40351                         disabled: true
40352                     }
40353                 ]
40354             };
40355             
40356             var container = {
40357                 cls: 'roo-select2-container input-group',
40358                 cn: [
40359                     flag_container,
40360                     box
40361                 ]
40362             };
40363             
40364             if (this.fieldLabel.length) {
40365                 var indicator = {
40366                     tag: 'i',
40367                     tooltip: 'This field is required'
40368                 };
40369                 
40370                 var label = {
40371                     tag: 'label',
40372                     'for':  id,
40373                     cls: 'control-label',
40374                     cn: []
40375                 };
40376                 
40377                 var label_text = {
40378                     tag: 'span',
40379                     html: this.fieldLabel
40380                 };
40381                 
40382                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40383                 label.cn = [
40384                     indicator,
40385                     label_text
40386                 ];
40387                 
40388                 if(this.indicatorpos == 'right') {
40389                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40390                     label.cn = [
40391                         label_text,
40392                         indicator
40393                     ];
40394                 }
40395                 
40396                 if(align == 'left') {
40397                     container = {
40398                         tag: 'div',
40399                         cn: [
40400                             container
40401                         ]
40402                     };
40403                     
40404                     if(this.labelWidth > 12){
40405                         label.style = "width: " + this.labelWidth + 'px';
40406                     }
40407                     if(this.labelWidth < 13 && this.labelmd == 0){
40408                         this.labelmd = this.labelWidth;
40409                     }
40410                     if(this.labellg > 0){
40411                         label.cls += ' col-lg-' + this.labellg;
40412                         input.cls += ' col-lg-' + (12 - this.labellg);
40413                     }
40414                     if(this.labelmd > 0){
40415                         label.cls += ' col-md-' + this.labelmd;
40416                         container.cls += ' col-md-' + (12 - this.labelmd);
40417                     }
40418                     if(this.labelsm > 0){
40419                         label.cls += ' col-sm-' + this.labelsm;
40420                         container.cls += ' col-sm-' + (12 - this.labelsm);
40421                     }
40422                     if(this.labelxs > 0){
40423                         label.cls += ' col-xs-' + this.labelxs;
40424                         container.cls += ' col-xs-' + (12 - this.labelxs);
40425                     }
40426                 }
40427             }
40428             
40429             cfg.cn = [
40430                 label,
40431                 container
40432             ];
40433             
40434             var settings = this;
40435             
40436             ['xs','sm','md','lg'].map(function(size){
40437                 if (settings[size]) {
40438                     cfg.cls += ' col-' + size + '-' + settings[size];
40439                 }
40440             });
40441             
40442             this.store = new Roo.data.Store({
40443                 proxy : new Roo.data.MemoryProxy({}),
40444                 reader : new Roo.data.JsonReader({
40445                     fields : [
40446                         {
40447                             'name' : 'name',
40448                             'type' : 'string'
40449                         },
40450                         {
40451                             'name' : 'iso2',
40452                             'type' : 'string'
40453                         },
40454                         {
40455                             'name' : 'dialCode',
40456                             'type' : 'string'
40457                         },
40458                         {
40459                             'name' : 'priority',
40460                             'type' : 'string'
40461                         },
40462                         {
40463                             'name' : 'areaCodes',
40464                             'type' : 'string'
40465                         }
40466                     ]
40467                 })
40468             });
40469             
40470             if(!this.preferedCountries) {
40471                 this.preferedCountries = [
40472                     'hk',
40473                     'gb',
40474                     'us'
40475                 ];
40476             }
40477             
40478             var p = this.preferedCountries.reverse();
40479             
40480             if(p) {
40481                 for (var i = 0; i < p.length; i++) {
40482                     for (var j = 0; j < this.allCountries.length; j++) {
40483                         if(this.allCountries[j].iso2 == p[i]) {
40484                             var t = this.allCountries[j];
40485                             this.allCountries.splice(j,1);
40486                             this.allCountries.unshift(t);
40487                         }
40488                     } 
40489                 }
40490             }
40491             
40492             this.store.proxy.data = {
40493                 success: true,
40494                 data: this.allCountries
40495             };
40496             
40497             return cfg;
40498         },
40499         
40500         initEvents : function()
40501         {
40502             this.createList();
40503             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40504             
40505             this.indicator = this.indicatorEl();
40506             this.flag = this.flagEl();
40507             this.dialCodeHolder = this.dialCodeHolderEl();
40508             
40509             this.trigger = this.el.select('div.flag-box',true).first();
40510             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40511             
40512             var _this = this;
40513             
40514             (function(){
40515                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40516                 _this.list.setWidth(lw);
40517             }).defer(100);
40518             
40519             this.list.on('mouseover', this.onViewOver, this);
40520             this.list.on('mousemove', this.onViewMove, this);
40521             this.inputEl().on("keyup", this.onKeyUp, this);
40522             this.inputEl().on("keypress", this.onKeyPress, this);
40523             
40524             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40525
40526             this.view = new Roo.View(this.list, this.tpl, {
40527                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40528             });
40529             
40530             this.view.on('click', this.onViewClick, this);
40531             this.setValue(this.defaultDialCode);
40532         },
40533         
40534         onTriggerClick : function(e)
40535         {
40536             Roo.log('trigger click');
40537             if(this.disabled){
40538                 return;
40539             }
40540             
40541             if(this.isExpanded()){
40542                 this.collapse();
40543                 this.hasFocus = false;
40544             }else {
40545                 this.store.load({});
40546                 this.hasFocus = true;
40547                 this.expand();
40548             }
40549         },
40550         
40551         isExpanded : function()
40552         {
40553             return this.list.isVisible();
40554         },
40555         
40556         collapse : function()
40557         {
40558             if(!this.isExpanded()){
40559                 return;
40560             }
40561             this.list.hide();
40562             Roo.get(document).un('mousedown', this.collapseIf, this);
40563             Roo.get(document).un('mousewheel', this.collapseIf, this);
40564             this.fireEvent('collapse', this);
40565             this.validate();
40566         },
40567         
40568         expand : function()
40569         {
40570             Roo.log('expand');
40571
40572             if(this.isExpanded() || !this.hasFocus){
40573                 return;
40574             }
40575             
40576             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40577             this.list.setWidth(lw);
40578             
40579             this.list.show();
40580             this.restrictHeight();
40581             
40582             Roo.get(document).on('mousedown', this.collapseIf, this);
40583             Roo.get(document).on('mousewheel', this.collapseIf, this);
40584             
40585             this.fireEvent('expand', this);
40586         },
40587         
40588         restrictHeight : function()
40589         {
40590             this.list.alignTo(this.inputEl(), this.listAlign);
40591             this.list.alignTo(this.inputEl(), this.listAlign);
40592         },
40593         
40594         onViewOver : function(e, t)
40595         {
40596             if(this.inKeyMode){
40597                 return;
40598             }
40599             var item = this.view.findItemFromChild(t);
40600             
40601             if(item){
40602                 var index = this.view.indexOf(item);
40603                 this.select(index, false);
40604             }
40605         },
40606
40607         // private
40608         onViewClick : function(view, doFocus, el, e)
40609         {
40610             var index = this.view.getSelectedIndexes()[0];
40611             
40612             var r = this.store.getAt(index);
40613             
40614             if(r){
40615                 this.onSelect(r, index);
40616             }
40617             if(doFocus !== false && !this.blockFocus){
40618                 this.inputEl().focus();
40619             }
40620         },
40621         
40622         onViewMove : function(e, t)
40623         {
40624             this.inKeyMode = false;
40625         },
40626         
40627         select : function(index, scrollIntoView)
40628         {
40629             this.selectedIndex = index;
40630             this.view.select(index);
40631             if(scrollIntoView !== false){
40632                 var el = this.view.getNode(index);
40633                 if(el){
40634                     this.list.scrollChildIntoView(el, false);
40635                 }
40636             }
40637         },
40638         
40639         createList : function()
40640         {
40641             this.list = Roo.get(document.body).createChild({
40642                 tag: 'ul',
40643                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40644                 style: 'display:none'
40645             });
40646             
40647             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40648         },
40649         
40650         collapseIf : function(e)
40651         {
40652             var in_combo  = e.within(this.el);
40653             var in_list =  e.within(this.list);
40654             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40655             
40656             if (in_combo || in_list || is_list) {
40657                 return;
40658             }
40659             this.collapse();
40660         },
40661         
40662         onSelect : function(record, index)
40663         {
40664             if(this.fireEvent('beforeselect', this, record, index) !== false){
40665                 
40666                 this.setFlagClass(record.data.iso2);
40667                 this.setDialCode(record.data.dialCode);
40668                 this.hasFocus = false;
40669                 this.collapse();
40670                 this.fireEvent('select', this, record, index);
40671             }
40672         },
40673         
40674         flagEl : function()
40675         {
40676             var flag = this.el.select('div.flag',true).first();
40677             if(!flag){
40678                 return false;
40679             }
40680             return flag;
40681         },
40682         
40683         dialCodeHolderEl : function()
40684         {
40685             var d = this.el.select('input.dial-code-holder',true).first();
40686             if(!d){
40687                 return false;
40688             }
40689             return d;
40690         },
40691         
40692         setDialCode : function(v)
40693         {
40694             this.dialCodeHolder.dom.value = '+'+v;
40695         },
40696         
40697         setFlagClass : function(n)
40698         {
40699             this.flag.dom.className = 'flag '+n;
40700         },
40701         
40702         getValue : function()
40703         {
40704             var v = this.inputEl().getValue();
40705             if(this.dialCodeHolder) {
40706                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40707             }
40708             return v;
40709         },
40710         
40711         setValue : function(v)
40712         {
40713             var d = this.getDialCode(v);
40714             
40715             //invalid dial code
40716             if(v.length == 0 || !d || d.length == 0) {
40717                 if(this.rendered){
40718                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40719                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40720                 }
40721                 return;
40722             }
40723             
40724             //valid dial code
40725             this.setFlagClass(this.dialCodeMapping[d].iso2);
40726             this.setDialCode(d);
40727             this.inputEl().dom.value = v.replace('+'+d,'');
40728             this.hiddenEl().dom.value = this.getValue();
40729             
40730             this.validate();
40731         },
40732         
40733         getDialCode : function(v)
40734         {
40735             v = v ||  '';
40736             
40737             if (v.length == 0) {
40738                 return this.dialCodeHolder.dom.value;
40739             }
40740             
40741             var dialCode = "";
40742             if (v.charAt(0) != "+") {
40743                 return false;
40744             }
40745             var numericChars = "";
40746             for (var i = 1; i < v.length; i++) {
40747               var c = v.charAt(i);
40748               if (!isNaN(c)) {
40749                 numericChars += c;
40750                 if (this.dialCodeMapping[numericChars]) {
40751                   dialCode = v.substr(1, i);
40752                 }
40753                 if (numericChars.length == 4) {
40754                   break;
40755                 }
40756               }
40757             }
40758             return dialCode;
40759         },
40760         
40761         reset : function()
40762         {
40763             this.setValue(this.defaultDialCode);
40764             this.validate();
40765         },
40766         
40767         hiddenEl : function()
40768         {
40769             return this.el.select('input.hidden-tel-input',true).first();
40770         },
40771         
40772         // after setting val
40773         onKeyUp : function(e){
40774             this.setValue(this.getValue());
40775         },
40776         
40777         onKeyPress : function(e){
40778             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40779                 e.stopEvent();
40780             }
40781         }
40782         
40783 });
40784 /**
40785  * @class Roo.bootstrap.MoneyField
40786  * @extends Roo.bootstrap.ComboBox
40787  * Bootstrap MoneyField class
40788  * 
40789  * @constructor
40790  * Create a new MoneyField.
40791  * @param {Object} config Configuration options
40792  */
40793
40794 Roo.bootstrap.MoneyField = function(config) {
40795     
40796     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40797     
40798 };
40799
40800 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40801     
40802     /**
40803      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40804      */
40805     allowDecimals : true,
40806     /**
40807      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40808      */
40809     decimalSeparator : ".",
40810     /**
40811      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40812      */
40813     decimalPrecision : 0,
40814     /**
40815      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40816      */
40817     allowNegative : true,
40818     /**
40819      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40820      */
40821     allowZero: true,
40822     /**
40823      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40824      */
40825     minValue : Number.NEGATIVE_INFINITY,
40826     /**
40827      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40828      */
40829     maxValue : Number.MAX_VALUE,
40830     /**
40831      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40832      */
40833     minText : "The minimum value for this field is {0}",
40834     /**
40835      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40836      */
40837     maxText : "The maximum value for this field is {0}",
40838     /**
40839      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40840      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40841      */
40842     nanText : "{0} is not a valid number",
40843     /**
40844      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40845      */
40846     castInt : true,
40847     /**
40848      * @cfg {String} defaults currency of the MoneyField
40849      * value should be in lkey
40850      */
40851     defaultCurrency : false,
40852     /**
40853      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40854      */
40855     thousandsDelimiter : false,
40856     /**
40857      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40858      */
40859     max_length: false,
40860     
40861     inputlg : 9,
40862     inputmd : 9,
40863     inputsm : 9,
40864     inputxs : 6,
40865     
40866     store : false,
40867     
40868     getAutoCreate : function()
40869     {
40870         var align = this.labelAlign || this.parentLabelAlign();
40871         
40872         var id = Roo.id();
40873
40874         var cfg = {
40875             cls: 'form-group',
40876             cn: []
40877         };
40878
40879         var input =  {
40880             tag: 'input',
40881             id : id,
40882             cls : 'form-control roo-money-amount-input',
40883             autocomplete: 'new-password'
40884         };
40885         
40886         var hiddenInput = {
40887             tag: 'input',
40888             type: 'hidden',
40889             id: Roo.id(),
40890             cls: 'hidden-number-input'
40891         };
40892         
40893         if(this.max_length) {
40894             input.maxlength = this.max_length; 
40895         }
40896         
40897         if (this.name) {
40898             hiddenInput.name = this.name;
40899         }
40900
40901         if (this.disabled) {
40902             input.disabled = true;
40903         }
40904
40905         var clg = 12 - this.inputlg;
40906         var cmd = 12 - this.inputmd;
40907         var csm = 12 - this.inputsm;
40908         var cxs = 12 - this.inputxs;
40909         
40910         var container = {
40911             tag : 'div',
40912             cls : 'row roo-money-field',
40913             cn : [
40914                 {
40915                     tag : 'div',
40916                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40917                     cn : [
40918                         {
40919                             tag : 'div',
40920                             cls: 'roo-select2-container input-group',
40921                             cn: [
40922                                 {
40923                                     tag : 'input',
40924                                     cls : 'form-control roo-money-currency-input',
40925                                     autocomplete: 'new-password',
40926                                     readOnly : 1,
40927                                     name : this.currencyName
40928                                 },
40929                                 {
40930                                     tag :'span',
40931                                     cls : 'input-group-addon',
40932                                     cn : [
40933                                         {
40934                                             tag: 'span',
40935                                             cls: 'caret'
40936                                         }
40937                                     ]
40938                                 }
40939                             ]
40940                         }
40941                     ]
40942                 },
40943                 {
40944                     tag : 'div',
40945                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40946                     cn : [
40947                         {
40948                             tag: 'div',
40949                             cls: this.hasFeedback ? 'has-feedback' : '',
40950                             cn: [
40951                                 input
40952                             ]
40953                         }
40954                     ]
40955                 }
40956             ]
40957             
40958         };
40959         
40960         if (this.fieldLabel.length) {
40961             var indicator = {
40962                 tag: 'i',
40963                 tooltip: 'This field is required'
40964             };
40965
40966             var label = {
40967                 tag: 'label',
40968                 'for':  id,
40969                 cls: 'control-label',
40970                 cn: []
40971             };
40972
40973             var label_text = {
40974                 tag: 'span',
40975                 html: this.fieldLabel
40976             };
40977
40978             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40979             label.cn = [
40980                 indicator,
40981                 label_text
40982             ];
40983
40984             if(this.indicatorpos == 'right') {
40985                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40986                 label.cn = [
40987                     label_text,
40988                     indicator
40989                 ];
40990             }
40991
40992             if(align == 'left') {
40993                 container = {
40994                     tag: 'div',
40995                     cn: [
40996                         container
40997                     ]
40998                 };
40999
41000                 if(this.labelWidth > 12){
41001                     label.style = "width: " + this.labelWidth + 'px';
41002                 }
41003                 if(this.labelWidth < 13 && this.labelmd == 0){
41004                     this.labelmd = this.labelWidth;
41005                 }
41006                 if(this.labellg > 0){
41007                     label.cls += ' col-lg-' + this.labellg;
41008                     input.cls += ' col-lg-' + (12 - this.labellg);
41009                 }
41010                 if(this.labelmd > 0){
41011                     label.cls += ' col-md-' + this.labelmd;
41012                     container.cls += ' col-md-' + (12 - this.labelmd);
41013                 }
41014                 if(this.labelsm > 0){
41015                     label.cls += ' col-sm-' + this.labelsm;
41016                     container.cls += ' col-sm-' + (12 - this.labelsm);
41017                 }
41018                 if(this.labelxs > 0){
41019                     label.cls += ' col-xs-' + this.labelxs;
41020                     container.cls += ' col-xs-' + (12 - this.labelxs);
41021                 }
41022             }
41023         }
41024
41025         cfg.cn = [
41026             label,
41027             container,
41028             hiddenInput
41029         ];
41030         
41031         var settings = this;
41032
41033         ['xs','sm','md','lg'].map(function(size){
41034             if (settings[size]) {
41035                 cfg.cls += ' col-' + size + '-' + settings[size];
41036             }
41037         });
41038         
41039         return cfg;
41040     },
41041     
41042     initEvents : function()
41043     {
41044         this.indicator = this.indicatorEl();
41045         
41046         this.initCurrencyEvent();
41047         
41048         this.initNumberEvent();
41049     },
41050     
41051     initCurrencyEvent : function()
41052     {
41053         if (!this.store) {
41054             throw "can not find store for combo";
41055         }
41056         
41057         this.store = Roo.factory(this.store, Roo.data);
41058         this.store.parent = this;
41059         
41060         this.createList();
41061         
41062         this.triggerEl = this.el.select('.input-group-addon', true).first();
41063         
41064         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
41065         
41066         var _this = this;
41067         
41068         (function(){
41069             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
41070             _this.list.setWidth(lw);
41071         }).defer(100);
41072         
41073         this.list.on('mouseover', this.onViewOver, this);
41074         this.list.on('mousemove', this.onViewMove, this);
41075         this.list.on('scroll', this.onViewScroll, this);
41076         
41077         if(!this.tpl){
41078             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
41079         }
41080         
41081         this.view = new Roo.View(this.list, this.tpl, {
41082             singleSelect:true, store: this.store, selectedClass: this.selectedClass
41083         });
41084         
41085         this.view.on('click', this.onViewClick, this);
41086         
41087         this.store.on('beforeload', this.onBeforeLoad, this);
41088         this.store.on('load', this.onLoad, this);
41089         this.store.on('loadexception', this.onLoadException, this);
41090         
41091         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
41092             "up" : function(e){
41093                 this.inKeyMode = true;
41094                 this.selectPrev();
41095             },
41096
41097             "down" : function(e){
41098                 if(!this.isExpanded()){
41099                     this.onTriggerClick();
41100                 }else{
41101                     this.inKeyMode = true;
41102                     this.selectNext();
41103                 }
41104             },
41105
41106             "enter" : function(e){
41107                 this.collapse();
41108                 
41109                 if(this.fireEvent("specialkey", this, e)){
41110                     this.onViewClick(false);
41111                 }
41112                 
41113                 return true;
41114             },
41115
41116             "esc" : function(e){
41117                 this.collapse();
41118             },
41119
41120             "tab" : function(e){
41121                 this.collapse();
41122                 
41123                 if(this.fireEvent("specialkey", this, e)){
41124                     this.onViewClick(false);
41125                 }
41126                 
41127                 return true;
41128             },
41129
41130             scope : this,
41131
41132             doRelay : function(foo, bar, hname){
41133                 if(hname == 'down' || this.scope.isExpanded()){
41134                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41135                 }
41136                 return true;
41137             },
41138
41139             forceKeyDown: true
41140         });
41141         
41142         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
41143         
41144     },
41145     
41146     initNumberEvent : function(e)
41147     {
41148         this.inputEl().on("keydown" , this.fireKey,  this);
41149         this.inputEl().on("focus", this.onFocus,  this);
41150         this.inputEl().on("blur", this.onBlur,  this);
41151         
41152         this.inputEl().relayEvent('keyup', this);
41153         
41154         if(this.indicator){
41155             this.indicator.addClass('invisible');
41156         }
41157  
41158         this.originalValue = this.getValue();
41159         
41160         if(this.validationEvent == 'keyup'){
41161             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
41162             this.inputEl().on('keyup', this.filterValidation, this);
41163         }
41164         else if(this.validationEvent !== false){
41165             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
41166         }
41167         
41168         if(this.selectOnFocus){
41169             this.on("focus", this.preFocus, this);
41170             
41171         }
41172         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41173             this.inputEl().on("keypress", this.filterKeys, this);
41174         } else {
41175             this.inputEl().relayEvent('keypress', this);
41176         }
41177         
41178         var allowed = "0123456789";
41179         
41180         if(this.allowDecimals){
41181             allowed += this.decimalSeparator;
41182         }
41183         
41184         if(this.allowNegative){
41185             allowed += "-";
41186         }
41187         
41188         if(this.thousandsDelimiter) {
41189             allowed += ",";
41190         }
41191         
41192         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41193         
41194         var keyPress = function(e){
41195             
41196             var k = e.getKey();
41197             
41198             var c = e.getCharCode();
41199             
41200             if(
41201                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41202                     allowed.indexOf(String.fromCharCode(c)) === -1
41203             ){
41204                 e.stopEvent();
41205                 return;
41206             }
41207             
41208             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41209                 return;
41210             }
41211             
41212             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41213                 e.stopEvent();
41214             }
41215         };
41216         
41217         this.inputEl().on("keypress", keyPress, this);
41218         
41219     },
41220     
41221     onTriggerClick : function(e)
41222     {   
41223         if(this.disabled){
41224             return;
41225         }
41226         
41227         this.page = 0;
41228         this.loadNext = false;
41229         
41230         if(this.isExpanded()){
41231             this.collapse();
41232             return;
41233         }
41234         
41235         this.hasFocus = true;
41236         
41237         if(this.triggerAction == 'all') {
41238             this.doQuery(this.allQuery, true);
41239             return;
41240         }
41241         
41242         this.doQuery(this.getRawValue());
41243     },
41244     
41245     getCurrency : function()
41246     {   
41247         var v = this.currencyEl().getValue();
41248         
41249         return v;
41250     },
41251     
41252     restrictHeight : function()
41253     {
41254         this.list.alignTo(this.currencyEl(), this.listAlign);
41255         this.list.alignTo(this.currencyEl(), this.listAlign);
41256     },
41257     
41258     onViewClick : function(view, doFocus, el, e)
41259     {
41260         var index = this.view.getSelectedIndexes()[0];
41261         
41262         var r = this.store.getAt(index);
41263         
41264         if(r){
41265             this.onSelect(r, index);
41266         }
41267     },
41268     
41269     onSelect : function(record, index){
41270         
41271         if(this.fireEvent('beforeselect', this, record, index) !== false){
41272         
41273             this.setFromCurrencyData(index > -1 ? record.data : false);
41274             
41275             this.collapse();
41276             
41277             this.fireEvent('select', this, record, index);
41278         }
41279     },
41280     
41281     setFromCurrencyData : function(o)
41282     {
41283         var currency = '';
41284         
41285         this.lastCurrency = o;
41286         
41287         if (this.currencyField) {
41288             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41289         } else {
41290             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
41291         }
41292         
41293         this.lastSelectionText = currency;
41294         
41295         //setting default currency
41296         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41297             this.setCurrency(this.defaultCurrency);
41298             return;
41299         }
41300         
41301         this.setCurrency(currency);
41302     },
41303     
41304     setFromData : function(o)
41305     {
41306         var c = {};
41307         
41308         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41309         
41310         this.setFromCurrencyData(c);
41311         
41312         var value = '';
41313         
41314         if (this.name) {
41315             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41316         } else {
41317             Roo.log('no value set for '+ (this.name ? this.name : this.id));
41318         }
41319         
41320         this.setValue(value);
41321         
41322     },
41323     
41324     setCurrency : function(v)
41325     {   
41326         this.currencyValue = v;
41327         
41328         if(this.rendered){
41329             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41330             this.validate();
41331         }
41332     },
41333     
41334     setValue : function(v)
41335     {
41336         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41337         
41338         this.value = v;
41339         
41340         if(this.rendered){
41341             
41342             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41343             
41344             this.inputEl().dom.value = (v == '') ? '' :
41345                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41346             
41347             if(!this.allowZero && v === '0') {
41348                 this.hiddenEl().dom.value = '';
41349                 this.inputEl().dom.value = '';
41350             }
41351             
41352             this.validate();
41353         }
41354     },
41355     
41356     getRawValue : function()
41357     {
41358         var v = this.inputEl().getValue();
41359         
41360         return v;
41361     },
41362     
41363     getValue : function()
41364     {
41365         return this.fixPrecision(this.parseValue(this.getRawValue()));
41366     },
41367     
41368     parseValue : function(value)
41369     {
41370         if(this.thousandsDelimiter) {
41371             value += "";
41372             r = new RegExp(",", "g");
41373             value = value.replace(r, "");
41374         }
41375         
41376         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41377         return isNaN(value) ? '' : value;
41378         
41379     },
41380     
41381     fixPrecision : function(value)
41382     {
41383         if(this.thousandsDelimiter) {
41384             value += "";
41385             r = new RegExp(",", "g");
41386             value = value.replace(r, "");
41387         }
41388         
41389         var nan = isNaN(value);
41390         
41391         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41392             return nan ? '' : value;
41393         }
41394         return parseFloat(value).toFixed(this.decimalPrecision);
41395     },
41396     
41397     decimalPrecisionFcn : function(v)
41398     {
41399         return Math.floor(v);
41400     },
41401     
41402     validateValue : function(value)
41403     {
41404         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41405             return false;
41406         }
41407         
41408         var num = this.parseValue(value);
41409         
41410         if(isNaN(num)){
41411             this.markInvalid(String.format(this.nanText, value));
41412             return false;
41413         }
41414         
41415         if(num < this.minValue){
41416             this.markInvalid(String.format(this.minText, this.minValue));
41417             return false;
41418         }
41419         
41420         if(num > this.maxValue){
41421             this.markInvalid(String.format(this.maxText, this.maxValue));
41422             return false;
41423         }
41424         
41425         return true;
41426     },
41427     
41428     validate : function()
41429     {
41430         if(this.disabled || this.allowBlank){
41431             this.markValid();
41432             return true;
41433         }
41434         
41435         var currency = this.getCurrency();
41436         
41437         if(this.validateValue(this.getRawValue()) && currency.length){
41438             this.markValid();
41439             return true;
41440         }
41441         
41442         this.markInvalid();
41443         return false;
41444     },
41445     
41446     getName: function()
41447     {
41448         return this.name;
41449     },
41450     
41451     beforeBlur : function()
41452     {
41453         if(!this.castInt){
41454             return;
41455         }
41456         
41457         var v = this.parseValue(this.getRawValue());
41458         
41459         if(v || v == 0){
41460             this.setValue(v);
41461         }
41462     },
41463     
41464     onBlur : function()
41465     {
41466         this.beforeBlur();
41467         
41468         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41469             //this.el.removeClass(this.focusClass);
41470         }
41471         
41472         this.hasFocus = false;
41473         
41474         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41475             this.validate();
41476         }
41477         
41478         var v = this.getValue();
41479         
41480         if(String(v) !== String(this.startValue)){
41481             this.fireEvent('change', this, v, this.startValue);
41482         }
41483         
41484         this.fireEvent("blur", this);
41485     },
41486     
41487     inputEl : function()
41488     {
41489         return this.el.select('.roo-money-amount-input', true).first();
41490     },
41491     
41492     currencyEl : function()
41493     {
41494         return this.el.select('.roo-money-currency-input', true).first();
41495     },
41496     
41497     hiddenEl : function()
41498     {
41499         return this.el.select('input.hidden-number-input',true).first();
41500     }
41501     
41502 });